@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.cjs
CHANGED
|
@@ -33,8 +33,8 @@ __export(index_exports, {
|
|
|
33
33
|
ASSET_SOURCE_ADAPTERS: () => ASSET_SOURCE_ADAPTERS,
|
|
34
34
|
ASSET_WGSL_CONTENT_TYPE: () => ASSET_WGSL_CONTENT_TYPE,
|
|
35
35
|
CANONICAL_MODEL_COORDINATE_SYSTEM: () => CANONICAL_MODEL_COORDINATE_SYSTEM,
|
|
36
|
-
GPU_SHADER_STORE_FEATURE_FLAG: () =>
|
|
37
|
-
GPU_SHADER_STYLE_SELECTION_CAPABILITY: () =>
|
|
36
|
+
GPU_SHADER_STORE_FEATURE_FLAG: () => import_gpu_shader4.GPU_SHADER_STORE_FEATURE_FLAG,
|
|
37
|
+
GPU_SHADER_STYLE_SELECTION_CAPABILITY: () => import_gpu_shader4.GPU_SHADER_STYLE_SELECTION_CAPABILITY,
|
|
38
38
|
MODEL_ASSET_KINDS: () => MODEL_ASSET_KINDS,
|
|
39
39
|
MODEL_CANDIDATE_HARD_GATE_KINDS: () => MODEL_CANDIDATE_HARD_GATE_KINDS,
|
|
40
40
|
MODEL_CONFIRMATION_VIEW_KINDS: () => MODEL_CONFIRMATION_VIEW_KINDS,
|
|
@@ -52,8 +52,10 @@ __export(index_exports, {
|
|
|
52
52
|
MODEL_RESOLUTION_V2_CONTRACT_VERSION: () => MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
53
53
|
MODEL_RESOLUTION_V2_JSON_SCHEMAS: () => MODEL_RESOLUTION_V2_JSON_SCHEMAS,
|
|
54
54
|
MODEL_RESOLUTION_V2_STATES: () => MODEL_RESOLUTION_V2_STATES,
|
|
55
|
+
MODEL_RESOURCE_MANIFEST_LIMITS: () => MODEL_RESOURCE_MANIFEST_LIMITS,
|
|
55
56
|
MODEL_RIGHTS_STATUSES: () => MODEL_RIGHTS_STATUSES,
|
|
56
57
|
MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE: () => MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
|
|
58
|
+
ModelResourceManifestError: () => ModelResourceManifestError,
|
|
57
59
|
PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN: () => PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
|
|
58
60
|
PVOX_ASSET_MANIFEST_VERSION: () => PVOX_ASSET_MANIFEST_VERSION,
|
|
59
61
|
PVOX_BINARY_CLOSURE_HASH_DOMAIN: () => PVOX_BINARY_CLOSURE_HASH_DOMAIN,
|
|
@@ -244,11 +246,12 @@ __export(index_exports, {
|
|
|
244
246
|
normalizeAssetId: () => normalizeAssetId,
|
|
245
247
|
normalizePvoxRootHeaderForHashV1: () => normalizePvoxRootHeaderForHashV1,
|
|
246
248
|
projectPvoxNamedJsonClosureV1: () => projectPvoxNamedJsonClosureV1,
|
|
249
|
+
qualifyModelResourceManifest: () => qualifyModelResourceManifest,
|
|
247
250
|
toLegacyModelResolutionState: () => toLegacyModelResolutionState,
|
|
248
251
|
validateGpuAssetFiles: () => validateGpuAssetFiles
|
|
249
252
|
});
|
|
250
253
|
module.exports = __toCommonJS(index_exports);
|
|
251
|
-
var
|
|
254
|
+
var import_gpu_shader4 = require("@plasius/gpu-shader");
|
|
252
255
|
|
|
253
256
|
// src/asset-version.ts
|
|
254
257
|
var ASSET_VERSION_PATTERN = /^[0-9A-Za-z][0-9A-Za-z._-]{0,127}$/u;
|
|
@@ -396,14 +399,14 @@ function assertRecord(value, fieldName) {
|
|
|
396
399
|
}
|
|
397
400
|
return value;
|
|
398
401
|
}
|
|
399
|
-
function assertAllowedKeys(
|
|
400
|
-
const unexpected = Object.keys(
|
|
402
|
+
function assertAllowedKeys(record2, allowedKeys, fieldName) {
|
|
403
|
+
const unexpected = Object.keys(record2).find((key) => !allowedKeys.includes(key));
|
|
401
404
|
if (unexpected !== void 0) {
|
|
402
405
|
throw new Error(`${fieldName} contains unexpected field ${unexpected}.`);
|
|
403
406
|
}
|
|
404
407
|
}
|
|
405
|
-
function assertContractVersion(
|
|
406
|
-
if (
|
|
408
|
+
function assertContractVersion(record2, fieldName) {
|
|
409
|
+
if (record2.contractVersion !== void 0 && record2.contractVersion !== MODEL_RESOLUTION_CONTRACT_VERSION) {
|
|
407
410
|
throw new Error(`${fieldName} contractVersion is not supported.`);
|
|
408
411
|
}
|
|
409
412
|
}
|
|
@@ -526,22 +529,22 @@ function requiredVector3(value, fieldName, positive = false) {
|
|
|
526
529
|
];
|
|
527
530
|
}
|
|
528
531
|
function createBounds(value, fieldName) {
|
|
529
|
-
const
|
|
530
|
-
assertAllowedKeys(
|
|
531
|
-
const min = requiredVector3(
|
|
532
|
-
const max = requiredVector3(
|
|
532
|
+
const record2 = assertRecord(value, fieldName);
|
|
533
|
+
assertAllowedKeys(record2, ["min", "max"], fieldName);
|
|
534
|
+
const min = requiredVector3(record2.min, `${fieldName}.min`);
|
|
535
|
+
const max = requiredVector3(record2.max, `${fieldName}.max`);
|
|
533
536
|
if (min.some((axis, index) => axis >= max[index])) {
|
|
534
537
|
throw new Error(`${fieldName} min axes must be lower than max axes.`);
|
|
535
538
|
}
|
|
536
539
|
return { min, max };
|
|
537
540
|
}
|
|
538
541
|
function createDimensions(value, fieldName) {
|
|
539
|
-
const
|
|
540
|
-
assertAllowedKeys(
|
|
542
|
+
const record2 = assertRecord(value, fieldName);
|
|
543
|
+
assertAllowedKeys(record2, ["width", "height", "depth"], fieldName);
|
|
541
544
|
return {
|
|
542
|
-
width: requiredFiniteNumber(
|
|
543
|
-
height: requiredFiniteNumber(
|
|
544
|
-
depth: requiredFiniteNumber(
|
|
545
|
+
width: requiredFiniteNumber(record2.width, `${fieldName}.width`, Number.EPSILON),
|
|
546
|
+
height: requiredFiniteNumber(record2.height, `${fieldName}.height`, Number.EPSILON),
|
|
547
|
+
depth: requiredFiniteNumber(record2.depth, `${fieldName}.depth`, Number.EPSILON)
|
|
545
548
|
};
|
|
546
549
|
}
|
|
547
550
|
function assertModelUri(value, fieldName) {
|
|
@@ -625,8 +628,8 @@ function opaqueSourceAssetId(value, fieldName = "ModelProvenance.sourceAssetId")
|
|
|
625
628
|
return value;
|
|
626
629
|
}
|
|
627
630
|
function createHardConstraints(value) {
|
|
628
|
-
const
|
|
629
|
-
assertAllowedKeys(
|
|
631
|
+
const record2 = value === void 0 ? {} : assertRecord(value, "ModelRequestSpec.hardConstraints");
|
|
632
|
+
assertAllowedKeys(record2, [
|
|
630
633
|
"boundsMetres",
|
|
631
634
|
"dimensionsMetres",
|
|
632
635
|
"maxTriangles",
|
|
@@ -648,62 +651,62 @@ function createHardConstraints(value) {
|
|
|
648
651
|
collision: "optional",
|
|
649
652
|
partition: "allowed"
|
|
650
653
|
};
|
|
651
|
-
if (
|
|
652
|
-
result.boundsMetres = createBounds(
|
|
654
|
+
if (record2.boundsMetres !== void 0) {
|
|
655
|
+
result.boundsMetres = createBounds(record2.boundsMetres, "ModelRequestSpec.hardConstraints.boundsMetres");
|
|
653
656
|
}
|
|
654
|
-
if (
|
|
655
|
-
result.dimensionsMetres = createDimensions(
|
|
657
|
+
if (record2.dimensionsMetres !== void 0) {
|
|
658
|
+
result.dimensionsMetres = createDimensions(record2.dimensionsMetres, "ModelRequestSpec.hardConstraints.dimensionsMetres");
|
|
656
659
|
}
|
|
657
|
-
if (
|
|
660
|
+
if (record2.maxTriangles !== void 0) {
|
|
658
661
|
result.maxTriangles = requiredInteger(
|
|
659
|
-
|
|
662
|
+
record2.maxTriangles,
|
|
660
663
|
"ModelRequestSpec.hardConstraints.maxTriangles",
|
|
661
664
|
1,
|
|
662
665
|
STATIC_WORLD_V1_MODEL_POLICY.maxTriangles
|
|
663
666
|
);
|
|
664
667
|
}
|
|
665
|
-
if (
|
|
668
|
+
if (record2.maxBytes !== void 0) {
|
|
666
669
|
result.maxBytes = requiredInteger(
|
|
667
|
-
|
|
670
|
+
record2.maxBytes,
|
|
668
671
|
"ModelRequestSpec.hardConstraints.maxBytes",
|
|
669
672
|
1,
|
|
670
673
|
STATIC_WORLD_V1_MODEL_POLICY.maxBytes
|
|
671
674
|
);
|
|
672
675
|
}
|
|
673
|
-
if (
|
|
676
|
+
if (record2.maxTextureBytes !== void 0) {
|
|
674
677
|
result.maxTextureBytes = requiredInteger(
|
|
675
|
-
|
|
678
|
+
record2.maxTextureBytes,
|
|
676
679
|
"ModelRequestSpec.hardConstraints.maxTextureBytes",
|
|
677
680
|
1,
|
|
678
681
|
STATIC_WORLD_V1_MODEL_POLICY.maxTextureBytes
|
|
679
682
|
);
|
|
680
683
|
}
|
|
681
|
-
if (
|
|
684
|
+
if (record2.maxTextureDimensionPx !== void 0) {
|
|
682
685
|
result.maxTextureDimensionPx = requiredInteger(
|
|
683
|
-
|
|
686
|
+
record2.maxTextureDimensionPx,
|
|
684
687
|
"ModelRequestSpec.hardConstraints.maxTextureDimensionPx",
|
|
685
688
|
1,
|
|
686
689
|
STATIC_WORLD_V1_MODEL_POLICY.maxTextureDimensionPx
|
|
687
690
|
);
|
|
688
691
|
}
|
|
689
|
-
if (
|
|
692
|
+
if (record2.maxPartitionCellMetres !== void 0) {
|
|
690
693
|
result.maxPartitionCellMetres = requiredFiniteNumber(
|
|
691
|
-
|
|
694
|
+
record2.maxPartitionCellMetres,
|
|
692
695
|
"ModelRequestSpec.hardConstraints.maxPartitionCellMetres",
|
|
693
696
|
Number.EPSILON,
|
|
694
697
|
STATIC_WORLD_V1_MODEL_POLICY.maxPartitionCellMetres
|
|
695
698
|
);
|
|
696
699
|
}
|
|
697
|
-
if (
|
|
698
|
-
result.lod = requiredEnum(
|
|
700
|
+
if (record2.lod !== void 0) {
|
|
701
|
+
result.lod = requiredEnum(record2.lod, ["required", "optional", "forbidden"], "ModelRequestSpec.hardConstraints.lod");
|
|
699
702
|
}
|
|
700
|
-
if (
|
|
701
|
-
result.collision = requiredEnum(
|
|
703
|
+
if (record2.collision !== void 0) {
|
|
704
|
+
result.collision = requiredEnum(record2.collision, ["required", "optional", "forbidden"], "ModelRequestSpec.hardConstraints.collision");
|
|
702
705
|
}
|
|
703
|
-
if (
|
|
704
|
-
result.partition = requiredEnum(
|
|
706
|
+
if (record2.partition !== void 0) {
|
|
707
|
+
result.partition = requiredEnum(record2.partition, ["single", "allowed", "required"], "ModelRequestSpec.hardConstraints.partition");
|
|
705
708
|
}
|
|
706
|
-
if (
|
|
709
|
+
if (record2.maxTextureBytes === void 0 && result.maxBytes !== void 0) {
|
|
707
710
|
result.maxTextureBytes = Math.min(result.maxTextureBytes ?? result.maxBytes, result.maxBytes);
|
|
708
711
|
}
|
|
709
712
|
if (result.maxBytes !== void 0 && result.maxTextureBytes !== void 0 && result.maxTextureBytes > result.maxBytes) {
|
|
@@ -728,8 +731,8 @@ function createSoftPreferences(value) {
|
|
|
728
731
|
if (value === void 0) {
|
|
729
732
|
return {};
|
|
730
733
|
}
|
|
731
|
-
const
|
|
732
|
-
assertAllowedKeys(
|
|
734
|
+
const record2 = assertRecord(value, "ModelRequestSpec.softPreferences");
|
|
735
|
+
assertAllowedKeys(record2, [
|
|
733
736
|
"category",
|
|
734
737
|
"style",
|
|
735
738
|
"materials",
|
|
@@ -740,22 +743,22 @@ function createSoftPreferences(value) {
|
|
|
740
743
|
], "ModelRequestSpec.softPreferences");
|
|
741
744
|
const result = {};
|
|
742
745
|
for (const field of ["category", "style", "era", "condition"]) {
|
|
743
|
-
const normalized = optionalString(
|
|
746
|
+
const normalized = optionalString(record2[field], `ModelRequestSpec.softPreferences.${field}`, 80);
|
|
744
747
|
if (normalized !== void 0) {
|
|
745
748
|
assertNoDirectUrl(normalized, `ModelRequestSpec.softPreferences.${field}`);
|
|
746
749
|
result[field] = normalized;
|
|
747
750
|
}
|
|
748
751
|
}
|
|
749
752
|
for (const field of ["materials", "colors", "tags"]) {
|
|
750
|
-
if (
|
|
751
|
-
result[field] = stringList(
|
|
753
|
+
if (record2[field] !== void 0) {
|
|
754
|
+
result[field] = stringList(record2[field], `ModelRequestSpec.softPreferences.${field}`);
|
|
752
755
|
}
|
|
753
756
|
}
|
|
754
757
|
return result;
|
|
755
758
|
}
|
|
756
759
|
function createModelRequestSpec(input) {
|
|
757
|
-
const
|
|
758
|
-
assertAllowedKeys(
|
|
760
|
+
const record2 = assertRecord(input, "ModelRequestSpec");
|
|
761
|
+
assertAllowedKeys(record2, [
|
|
759
762
|
"contractVersion",
|
|
760
763
|
"policyProfileId",
|
|
761
764
|
"query",
|
|
@@ -766,21 +769,21 @@ function createModelRequestSpec(input) {
|
|
|
766
769
|
"softPreferences",
|
|
767
770
|
"exclusions"
|
|
768
771
|
], "ModelRequestSpec");
|
|
769
|
-
assertContractVersion(
|
|
770
|
-
if (
|
|
772
|
+
assertContractVersion(record2, "ModelRequestSpec");
|
|
773
|
+
if (record2.policyProfileId !== void 0 && record2.policyProfileId !== STATIC_WORLD_V1_MODEL_POLICY.id) {
|
|
771
774
|
throw new Error("ModelRequestSpec.policyProfileId must be static-world-v1.");
|
|
772
775
|
}
|
|
773
|
-
const query = requiredString(
|
|
776
|
+
const query = requiredString(record2.query, "ModelRequestSpec.query", MAX_QUERY_LENGTH);
|
|
774
777
|
assertNoDirectUrl(query, "ModelRequestSpec.query");
|
|
775
778
|
const revision = requiredInteger(
|
|
776
|
-
|
|
779
|
+
record2.revision,
|
|
777
780
|
`ModelRequestSpec revision (maximum ${MODEL_REQUEST_MAX_REVISION})`,
|
|
778
781
|
0,
|
|
779
782
|
MODEL_REQUEST_MAX_REVISION
|
|
780
783
|
);
|
|
781
784
|
let locale;
|
|
782
|
-
if (
|
|
783
|
-
const requestedLocale = requiredString(
|
|
785
|
+
if (record2.locale !== void 0) {
|
|
786
|
+
const requestedLocale = requiredString(record2.locale, "ModelRequestSpec.locale", 64);
|
|
784
787
|
try {
|
|
785
788
|
[locale] = Intl.getCanonicalLocales(requestedLocale);
|
|
786
789
|
} catch {
|
|
@@ -790,7 +793,7 @@ function createModelRequestSpec(input) {
|
|
|
790
793
|
throw new Error("ModelRequestSpec.locale must be a valid BCP 47 locale.");
|
|
791
794
|
}
|
|
792
795
|
}
|
|
793
|
-
const rankerId =
|
|
796
|
+
const rankerId = record2.rankerId === void 0 ? void 0 : requiredToken(record2.rankerId, "ModelRequestSpec.rankerId");
|
|
794
797
|
const result = {
|
|
795
798
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
796
799
|
policyProfileId: STATIC_WORLD_V1_MODEL_POLICY.id,
|
|
@@ -798,9 +801,9 @@ function createModelRequestSpec(input) {
|
|
|
798
801
|
revision,
|
|
799
802
|
...locale === void 0 ? {} : { locale },
|
|
800
803
|
...rankerId === void 0 ? {} : { rankerId },
|
|
801
|
-
hardConstraints: createHardConstraints(
|
|
802
|
-
softPreferences: createSoftPreferences(
|
|
803
|
-
exclusions:
|
|
804
|
+
hardConstraints: createHardConstraints(record2.hardConstraints),
|
|
805
|
+
softPreferences: createSoftPreferences(record2.softPreferences),
|
|
806
|
+
exclusions: record2.exclusions === void 0 ? [] : stringList(record2.exclusions, "ModelRequestSpec.exclusions")
|
|
804
807
|
};
|
|
805
808
|
return deepFreeze(result);
|
|
806
809
|
}
|
|
@@ -838,8 +841,8 @@ function classifyModelMatchAssurance(score, hardConstraintPass, evidenceMode, ex
|
|
|
838
841
|
return MODEL_MATCH_ASSURANCE_ORDER[rawAssurance] <= MODEL_MATCH_ASSURANCE_ORDER[assuranceCeiling] ? rawAssurance : assuranceCeiling;
|
|
839
842
|
}
|
|
840
843
|
function createRankerRef(value) {
|
|
841
|
-
const
|
|
842
|
-
assertAllowedKeys(
|
|
844
|
+
const record2 = assertRecord(value, "ModelMatchAssessment.ranker");
|
|
845
|
+
assertAllowedKeys(record2, [
|
|
843
846
|
"id",
|
|
844
847
|
"version",
|
|
845
848
|
"calibrationId",
|
|
@@ -848,25 +851,25 @@ function createRankerRef(value) {
|
|
|
848
851
|
"assuranceCeiling"
|
|
849
852
|
], "ModelMatchAssessment.ranker");
|
|
850
853
|
return {
|
|
851
|
-
id: requiredToken(
|
|
852
|
-
version: assetVersion(
|
|
853
|
-
calibrationId: requiredToken(
|
|
854
|
-
calibrationVersion: assetVersion(
|
|
854
|
+
id: requiredToken(record2.id, "ModelMatchAssessment.ranker.id"),
|
|
855
|
+
version: assetVersion(record2.version, "ModelMatchAssessment.ranker.version"),
|
|
856
|
+
calibrationId: requiredToken(record2.calibrationId, "ModelMatchAssessment.ranker.calibrationId"),
|
|
857
|
+
calibrationVersion: assetVersion(record2.calibrationVersion, "ModelMatchAssessment.ranker.calibrationVersion"),
|
|
855
858
|
evidenceMode: requiredEnum(
|
|
856
|
-
|
|
859
|
+
record2.evidenceMode,
|
|
857
860
|
MODEL_RANKER_EVIDENCE_MODES,
|
|
858
861
|
"ModelMatchAssessment.ranker.evidenceMode"
|
|
859
862
|
),
|
|
860
863
|
assuranceCeiling: requiredEnum(
|
|
861
|
-
|
|
864
|
+
record2.assuranceCeiling,
|
|
862
865
|
MODEL_MATCH_ASSURANCE_BANDS,
|
|
863
866
|
"ModelMatchAssessment.ranker.assuranceCeiling"
|
|
864
867
|
)
|
|
865
868
|
};
|
|
866
869
|
}
|
|
867
870
|
function createModelMatchAssessment(input) {
|
|
868
|
-
const
|
|
869
|
-
assertAllowedKeys(
|
|
871
|
+
const record2 = assertRecord(input, "ModelMatchAssessment");
|
|
872
|
+
assertAllowedKeys(record2, [
|
|
870
873
|
"contractVersion",
|
|
871
874
|
"score",
|
|
872
875
|
"assurance",
|
|
@@ -879,19 +882,19 @@ function createModelMatchAssessment(input) {
|
|
|
879
882
|
"candidateId",
|
|
880
883
|
"candidateContentHash"
|
|
881
884
|
], "ModelMatchAssessment");
|
|
882
|
-
assertContractVersion(
|
|
883
|
-
const score = requiredFiniteNumber(
|
|
884
|
-
const hardConstraintPass = requiredBoolean(
|
|
885
|
-
const exactMatch = requiredBoolean(
|
|
886
|
-
const ranker = createRankerRef(
|
|
887
|
-
const request = createModelRequestSpec(
|
|
885
|
+
assertContractVersion(record2, "ModelMatchAssessment");
|
|
886
|
+
const score = requiredFiniteNumber(record2.score, "ModelMatchAssessment.score", 0, 1);
|
|
887
|
+
const hardConstraintPass = requiredBoolean(record2.hardConstraintPass, "ModelMatchAssessment.hardConstraintPass");
|
|
888
|
+
const exactMatch = requiredBoolean(record2.exactMatch, "ModelMatchAssessment.exactMatch");
|
|
889
|
+
const ranker = createRankerRef(record2.ranker);
|
|
890
|
+
const request = createModelRequestSpec(record2.request);
|
|
888
891
|
if (request.rankerId !== void 0 && request.rankerId !== ranker.id) {
|
|
889
892
|
throw new Error("ModelMatchAssessment ranker.id must match the caller-selected request rankerId.");
|
|
890
893
|
}
|
|
891
|
-
if (
|
|
892
|
-
requiredEnum(
|
|
894
|
+
if (record2.assurance !== void 0) {
|
|
895
|
+
requiredEnum(record2.assurance, MODEL_MATCH_ASSURANCE_BANDS, "ModelMatchAssessment.assurance");
|
|
893
896
|
}
|
|
894
|
-
let reasonCodes2 = stringList(
|
|
897
|
+
let reasonCodes2 = stringList(record2.reasonCodes, "ModelMatchAssessment.reasonCodes", {
|
|
895
898
|
tokens: true
|
|
896
899
|
});
|
|
897
900
|
if (!hardConstraintPass && reasonCodes2.length === 0) {
|
|
@@ -924,18 +927,18 @@ function createModelMatchAssessment(input) {
|
|
|
924
927
|
exactMatch,
|
|
925
928
|
reasonCodes: reasonCodes2,
|
|
926
929
|
ranker,
|
|
927
|
-
fidelityWarnings: stringList(
|
|
930
|
+
fidelityWarnings: stringList(record2.fidelityWarnings ?? [], "ModelMatchAssessment.fidelityWarnings"),
|
|
928
931
|
request,
|
|
929
|
-
candidateId: requiredToken(
|
|
932
|
+
candidateId: requiredToken(record2.candidateId, "ModelMatchAssessment.candidateId"),
|
|
930
933
|
candidateContentHash: requiredSha256(
|
|
931
|
-
|
|
934
|
+
record2.candidateContentHash,
|
|
932
935
|
"ModelMatchAssessment.candidateContentHash"
|
|
933
936
|
)
|
|
934
937
|
});
|
|
935
938
|
}
|
|
936
939
|
function createModelTechnicalProfile(input) {
|
|
937
|
-
const
|
|
938
|
-
assertAllowedKeys(
|
|
940
|
+
const record2 = assertRecord(input, "ModelTechnicalProfile");
|
|
941
|
+
assertAllowedKeys(record2, [
|
|
939
942
|
"boundsMetres",
|
|
940
943
|
"dimensionsMetres",
|
|
941
944
|
"triangleCount",
|
|
@@ -947,22 +950,22 @@ function createModelTechnicalProfile(input) {
|
|
|
947
950
|
"partitionCount",
|
|
948
951
|
"partitionCellMetres"
|
|
949
952
|
], "ModelTechnicalProfile");
|
|
950
|
-
const boundsMetres = createBounds(
|
|
951
|
-
const dimensionsMetres = createDimensions(
|
|
953
|
+
const boundsMetres = createBounds(record2.boundsMetres, "ModelTechnicalProfile.boundsMetres");
|
|
954
|
+
const dimensionsMetres = createDimensions(record2.dimensionsMetres, "ModelTechnicalProfile.dimensionsMetres");
|
|
952
955
|
const derivedDimensions = boundsMetres.max.map((axis, index) => axis - boundsMetres.min[index]);
|
|
953
956
|
const suppliedDimensions = [dimensionsMetres.width, dimensionsMetres.height, dimensionsMetres.depth];
|
|
954
957
|
if (derivedDimensions.some((axis, index) => Math.abs(axis - suppliedDimensions[index]) > Math.max(1e-6, Math.abs(axis) * 1e-6))) {
|
|
955
958
|
throw new Error("ModelTechnicalProfile dimensionsMetres must match boundsMetres.");
|
|
956
959
|
}
|
|
957
|
-
const byteLength = requiredInteger(
|
|
960
|
+
const byteLength = requiredInteger(record2.byteLength, "ModelTechnicalProfile.byteLength", 1, Number.MAX_SAFE_INTEGER);
|
|
958
961
|
const textureByteLength = requiredInteger(
|
|
959
|
-
|
|
962
|
+
record2.textureByteLength,
|
|
960
963
|
"ModelTechnicalProfile.textureByteLength",
|
|
961
964
|
0,
|
|
962
965
|
Number.MAX_SAFE_INTEGER
|
|
963
966
|
);
|
|
964
967
|
const maxTextureDimensionPx = requiredInteger(
|
|
965
|
-
|
|
968
|
+
record2.maxTextureDimensionPx,
|
|
966
969
|
"ModelTechnicalProfile.maxTextureDimensionPx",
|
|
967
970
|
0,
|
|
968
971
|
32768
|
|
@@ -978,15 +981,15 @@ function createModelTechnicalProfile(input) {
|
|
|
978
981
|
return deepFreeze({
|
|
979
982
|
boundsMetres,
|
|
980
983
|
dimensionsMetres,
|
|
981
|
-
triangleCount: requiredInteger(
|
|
984
|
+
triangleCount: requiredInteger(record2.triangleCount, "ModelTechnicalProfile.triangleCount", 1, 1e9),
|
|
982
985
|
byteLength,
|
|
983
986
|
textureByteLength,
|
|
984
987
|
maxTextureDimensionPx,
|
|
985
|
-
lodCount: requiredInteger(
|
|
986
|
-
hasCollision: requiredBoolean(
|
|
987
|
-
partitionCount: requiredInteger(
|
|
988
|
+
lodCount: requiredInteger(record2.lodCount, "ModelTechnicalProfile.lodCount", 1, 4),
|
|
989
|
+
hasCollision: requiredBoolean(record2.hasCollision, "ModelTechnicalProfile.hasCollision"),
|
|
990
|
+
partitionCount: requiredInteger(record2.partitionCount, "ModelTechnicalProfile.partitionCount", 1, 1e4),
|
|
988
991
|
partitionCellMetres: requiredFiniteNumber(
|
|
989
|
-
|
|
992
|
+
record2.partitionCellMetres,
|
|
990
993
|
"ModelTechnicalProfile.partitionCellMetres",
|
|
991
994
|
Number.EPSILON,
|
|
992
995
|
1e6
|
|
@@ -994,19 +997,19 @@ function createModelTechnicalProfile(input) {
|
|
|
994
997
|
});
|
|
995
998
|
}
|
|
996
999
|
function createAttribution(value) {
|
|
997
|
-
const
|
|
998
|
-
assertAllowedKeys(
|
|
999
|
-
const sourcePageUri =
|
|
1000
|
+
const record2 = assertRecord(value, "ModelRightsAssessment.attribution");
|
|
1001
|
+
assertAllowedKeys(record2, ["title", "creator", "notice", "sourcePageUri"], "ModelRightsAssessment.attribution");
|
|
1002
|
+
const sourcePageUri = record2.sourcePageUri === void 0 ? void 0 : assertPublicPageUri(record2.sourcePageUri, "ModelRightsAssessment.attribution.sourcePageUri");
|
|
1000
1003
|
return {
|
|
1001
|
-
title: requiredString(
|
|
1002
|
-
creator: requiredString(
|
|
1003
|
-
notice: requiredString(
|
|
1004
|
+
title: requiredString(record2.title, "ModelRightsAssessment.attribution.title", 256),
|
|
1005
|
+
creator: requiredString(record2.creator, "ModelRightsAssessment.attribution.creator", 256),
|
|
1006
|
+
notice: requiredString(record2.notice, "ModelRightsAssessment.attribution.notice", 512),
|
|
1004
1007
|
...sourcePageUri === void 0 ? {} : { sourcePageUri }
|
|
1005
1008
|
};
|
|
1006
1009
|
}
|
|
1007
1010
|
function createModelRightsAssessment(input) {
|
|
1008
|
-
const
|
|
1009
|
-
assertAllowedKeys(
|
|
1011
|
+
const record2 = assertRecord(input, "ModelRightsAssessment");
|
|
1012
|
+
assertAllowedKeys(record2, [
|
|
1010
1013
|
"decisionId",
|
|
1011
1014
|
"decisionToken",
|
|
1012
1015
|
"policyId",
|
|
@@ -1020,8 +1023,8 @@ function createModelRightsAssessment(input) {
|
|
|
1020
1023
|
"attribution",
|
|
1021
1024
|
"reviewedAt"
|
|
1022
1025
|
], "ModelRightsAssessment");
|
|
1023
|
-
const status = requiredEnum(
|
|
1024
|
-
const attribution =
|
|
1026
|
+
const status = requiredEnum(record2.status, MODEL_RIGHTS_STATUSES, "ModelRightsAssessment.status");
|
|
1027
|
+
const attribution = record2.attribution === void 0 ? void 0 : createAttribution(record2.attribution);
|
|
1025
1028
|
if (status === "attribution-required" && attribution === void 0) {
|
|
1026
1029
|
throw new Error("ModelRightsAssessment attribution is required for attribution-required rights.");
|
|
1027
1030
|
}
|
|
@@ -1029,29 +1032,29 @@ function createModelRightsAssessment(input) {
|
|
|
1029
1032
|
throw new Error("ModelRightsAssessment attribution-required rights require a public attribution sourcePageUri.");
|
|
1030
1033
|
}
|
|
1031
1034
|
return deepFreeze({
|
|
1032
|
-
decisionId: requiredToken(
|
|
1035
|
+
decisionId: requiredToken(record2.decisionId, "ModelRightsAssessment.decisionId"),
|
|
1033
1036
|
decisionToken: requiredAttestationToken(
|
|
1034
|
-
|
|
1037
|
+
record2.decisionToken,
|
|
1035
1038
|
"ModelRightsAssessment.decisionToken"
|
|
1036
1039
|
),
|
|
1037
|
-
policyId: requiredToken(
|
|
1038
|
-
policyVersion: assetVersion(
|
|
1039
|
-
sourceId: requiredToken(
|
|
1040
|
-
sourceAssetId: opaqueSourceAssetId(
|
|
1040
|
+
policyId: requiredToken(record2.policyId, "ModelRightsAssessment.policyId"),
|
|
1041
|
+
policyVersion: assetVersion(record2.policyVersion, "ModelRightsAssessment.policyVersion"),
|
|
1042
|
+
sourceId: requiredToken(record2.sourceId, "ModelRightsAssessment.sourceId"),
|
|
1043
|
+
sourceAssetId: opaqueSourceAssetId(record2.sourceAssetId, "ModelRightsAssessment.sourceAssetId"),
|
|
1041
1044
|
sourceContentHash: requiredSha256(
|
|
1042
|
-
|
|
1045
|
+
record2.sourceContentHash,
|
|
1043
1046
|
"ModelRightsAssessment.sourceContentHash"
|
|
1044
1047
|
),
|
|
1045
1048
|
status,
|
|
1046
|
-
licenseId: requiredToken(
|
|
1047
|
-
evidencePageUri: assertPublicPageUri(
|
|
1049
|
+
licenseId: requiredToken(record2.licenseId, "ModelRightsAssessment.licenseId"),
|
|
1050
|
+
evidencePageUri: assertPublicPageUri(record2.evidencePageUri, "ModelRightsAssessment.evidencePageUri"),
|
|
1048
1051
|
...attribution === void 0 ? {} : { attribution },
|
|
1049
|
-
reviewedAt: requiredTimestamp(
|
|
1052
|
+
reviewedAt: requiredTimestamp(record2.reviewedAt, "ModelRightsAssessment.reviewedAt")
|
|
1050
1053
|
});
|
|
1051
1054
|
}
|
|
1052
1055
|
function createModelProvenance(input) {
|
|
1053
|
-
const
|
|
1054
|
-
assertAllowedKeys(
|
|
1056
|
+
const record2 = assertRecord(input, "ModelProvenance");
|
|
1057
|
+
assertAllowedKeys(record2, [
|
|
1055
1058
|
"kind",
|
|
1056
1059
|
"sourceId",
|
|
1057
1060
|
"sourceAssetId",
|
|
@@ -1059,9 +1062,9 @@ function createModelProvenance(input) {
|
|
|
1059
1062
|
"contentHash",
|
|
1060
1063
|
"capturedAt"
|
|
1061
1064
|
], "ModelProvenance");
|
|
1062
|
-
const kind = requiredEnum(
|
|
1063
|
-
const sourceAssetId = opaqueSourceAssetId(
|
|
1064
|
-
const sourcePageUri =
|
|
1065
|
+
const kind = requiredEnum(record2.kind, MODEL_PROVENANCE_KINDS, "ModelProvenance kind");
|
|
1066
|
+
const sourceAssetId = opaqueSourceAssetId(record2.sourceAssetId);
|
|
1067
|
+
const sourcePageUri = record2.sourcePageUri === void 0 ? void 0 : assertPublicPageUri(record2.sourcePageUri, "ModelProvenance.sourcePageUri");
|
|
1065
1068
|
if (kind === "provider" && sourcePageUri === void 0) {
|
|
1066
1069
|
throw new Error("ModelProvenance provider provenance requires sourcePageUri.");
|
|
1067
1070
|
}
|
|
@@ -1070,16 +1073,16 @@ function createModelProvenance(input) {
|
|
|
1070
1073
|
}
|
|
1071
1074
|
return deepFreeze({
|
|
1072
1075
|
kind,
|
|
1073
|
-
sourceId: requiredToken(
|
|
1076
|
+
sourceId: requiredToken(record2.sourceId, "ModelProvenance.sourceId"),
|
|
1074
1077
|
sourceAssetId,
|
|
1075
1078
|
...sourcePageUri === void 0 ? {} : { sourcePageUri },
|
|
1076
|
-
contentHash: requiredSha256(
|
|
1077
|
-
capturedAt: requiredTimestamp(
|
|
1079
|
+
contentHash: requiredSha256(record2.contentHash, "ModelProvenance.contentHash"),
|
|
1080
|
+
capturedAt: requiredTimestamp(record2.capturedAt, "ModelProvenance.capturedAt")
|
|
1078
1081
|
});
|
|
1079
1082
|
}
|
|
1080
1083
|
function createModelAssetRef(input) {
|
|
1081
|
-
const
|
|
1082
|
-
assertAllowedKeys(
|
|
1084
|
+
const record2 = assertRecord(input, "ModelAssetRef");
|
|
1085
|
+
assertAllowedKeys(record2, [
|
|
1083
1086
|
"contractVersion",
|
|
1084
1087
|
"assetId",
|
|
1085
1088
|
"version",
|
|
@@ -1087,10 +1090,10 @@ function createModelAssetRef(input) {
|
|
|
1087
1090
|
"contentHash",
|
|
1088
1091
|
"runtimeManifestUri"
|
|
1089
1092
|
], "ModelAssetRef");
|
|
1090
|
-
assertContractVersion(
|
|
1091
|
-
const validatedAssetId = assetId(
|
|
1092
|
-
const validatedVersion = assertImmutableAssetVersion(
|
|
1093
|
-
const runtimeManifestUri = assertModelUri(
|
|
1093
|
+
assertContractVersion(record2, "ModelAssetRef");
|
|
1094
|
+
const validatedAssetId = assetId(record2.assetId, "ModelAssetRef.assetId");
|
|
1095
|
+
const validatedVersion = assertImmutableAssetVersion(record2.version);
|
|
1096
|
+
const runtimeManifestUri = assertModelUri(record2.runtimeManifestUri, "ModelAssetRef.runtimeManifestUri");
|
|
1094
1097
|
const expectedManifestUri = `mcp://models/catalog/${validatedAssetId}/versions/${validatedVersion}/manifest`;
|
|
1095
1098
|
if (runtimeManifestUri !== expectedManifestUri) {
|
|
1096
1099
|
throw new Error(
|
|
@@ -1101,21 +1104,21 @@ function createModelAssetRef(input) {
|
|
|
1101
1104
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1102
1105
|
assetId: validatedAssetId,
|
|
1103
1106
|
version: validatedVersion,
|
|
1104
|
-
kind: requiredEnum(
|
|
1105
|
-
contentHash: requiredSha256(
|
|
1107
|
+
kind: requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelAssetRef.kind"),
|
|
1108
|
+
contentHash: requiredSha256(record2.contentHash, "ModelAssetRef.contentHash"),
|
|
1106
1109
|
runtimeManifestUri
|
|
1107
1110
|
});
|
|
1108
1111
|
}
|
|
1109
1112
|
function createCandidateAssetRef(value) {
|
|
1110
|
-
const
|
|
1111
|
-
const disposition = requiredEnum(
|
|
1113
|
+
const record2 = assertRecord(value, "ModelCandidate.assetRef");
|
|
1114
|
+
const disposition = requiredEnum(record2.disposition, ["existing", "proposed"], "ModelCandidate.assetRef.disposition");
|
|
1112
1115
|
if (disposition === "existing") {
|
|
1113
|
-
assertAllowedKeys(
|
|
1114
|
-
const asset = createModelAssetRef(
|
|
1115
|
-
if (
|
|
1116
|
+
assertAllowedKeys(record2, ["disposition", "asset", "kind", "contentHash"], "ModelCandidate.assetRef");
|
|
1117
|
+
const asset = createModelAssetRef(record2.asset);
|
|
1118
|
+
if (record2.kind !== void 0 && record2.kind !== asset.kind) {
|
|
1116
1119
|
throw new Error("ModelCandidate existing assetRef kind must match asset.");
|
|
1117
1120
|
}
|
|
1118
|
-
if (
|
|
1121
|
+
if (record2.contentHash !== void 0 && record2.contentHash !== asset.contentHash) {
|
|
1119
1122
|
throw new Error("ModelCandidate existing assetRef contentHash must match asset.");
|
|
1120
1123
|
}
|
|
1121
1124
|
return {
|
|
@@ -1125,27 +1128,27 @@ function createCandidateAssetRef(value) {
|
|
|
1125
1128
|
asset
|
|
1126
1129
|
};
|
|
1127
1130
|
}
|
|
1128
|
-
assertAllowedKeys(
|
|
1131
|
+
assertAllowedKeys(record2, ["disposition", "proposalId", "kind", "contentHash"], "ModelCandidate.assetRef");
|
|
1129
1132
|
return {
|
|
1130
1133
|
disposition,
|
|
1131
|
-
proposalId: requiredToken(
|
|
1132
|
-
kind: requiredEnum(
|
|
1133
|
-
contentHash: requiredSha256(
|
|
1134
|
+
proposalId: requiredToken(record2.proposalId, "ModelCandidate.assetRef.proposalId"),
|
|
1135
|
+
kind: requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelCandidate.assetRef.kind"),
|
|
1136
|
+
contentHash: requiredSha256(record2.contentHash, "ModelCandidate.assetRef.contentHash")
|
|
1134
1137
|
};
|
|
1135
1138
|
}
|
|
1136
1139
|
function createConfirmationView(value, index) {
|
|
1137
1140
|
const fieldName = `ModelCandidate.views[${index}]`;
|
|
1138
|
-
const
|
|
1139
|
-
assertAllowedKeys(
|
|
1140
|
-
const width = requiredInteger(
|
|
1141
|
-
const height = requiredInteger(
|
|
1142
|
-
if (
|
|
1141
|
+
const record2 = assertRecord(value, fieldName);
|
|
1142
|
+
assertAllowedKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
|
|
1143
|
+
const width = requiredInteger(record2.width, `${fieldName}.width`, MODEL_CONFIRMATION_VIEW_SIZE_PX, MODEL_CONFIRMATION_VIEW_SIZE_PX);
|
|
1144
|
+
const height = requiredInteger(record2.height, `${fieldName}.height`, MODEL_CONFIRMATION_VIEW_SIZE_PX, MODEL_CONFIRMATION_VIEW_SIZE_PX);
|
|
1145
|
+
if (record2.contentType !== "image/png") {
|
|
1143
1146
|
throw new Error(`${fieldName}.contentType must be image/png.`);
|
|
1144
1147
|
}
|
|
1145
1148
|
return {
|
|
1146
|
-
kind: requiredEnum(
|
|
1147
|
-
imageUri: assertModelUri(
|
|
1148
|
-
sha256: requiredSha256(
|
|
1149
|
+
kind: requiredEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`),
|
|
1150
|
+
imageUri: assertModelUri(record2.imageUri, `${fieldName}.imageUri`),
|
|
1151
|
+
sha256: requiredSha256(record2.sha256, `${fieldName}.sha256`),
|
|
1149
1152
|
contentType: "image/png",
|
|
1150
1153
|
width,
|
|
1151
1154
|
height
|
|
@@ -1179,8 +1182,8 @@ function createConfirmationViews(value, resolutionId, candidateId) {
|
|
|
1179
1182
|
return [ordered[0], ordered[1], ordered[2], ordered[3]];
|
|
1180
1183
|
}
|
|
1181
1184
|
function createRenderEvidence(value, manifest, views) {
|
|
1182
|
-
const
|
|
1183
|
-
assertAllowedKeys(
|
|
1185
|
+
const record2 = assertRecord(value, "ModelCandidate.renderEvidence");
|
|
1186
|
+
assertAllowedKeys(record2, [
|
|
1184
1187
|
"renderId",
|
|
1185
1188
|
"rendererId",
|
|
1186
1189
|
"rendererVersion",
|
|
@@ -1192,35 +1195,35 @@ function createRenderEvidence(value, manifest, views) {
|
|
|
1192
1195
|
"renderedAt",
|
|
1193
1196
|
"attestationToken"
|
|
1194
1197
|
], "ModelCandidate.renderEvidence");
|
|
1195
|
-
if (
|
|
1198
|
+
if (record2.processingManifestId !== manifest.manifestId) {
|
|
1196
1199
|
throw new Error("ModelCandidate.renderEvidence.processingManifestId must match processingManifest.");
|
|
1197
1200
|
}
|
|
1198
|
-
if (
|
|
1201
|
+
if (record2.sourceContentHash !== manifest.lods[0]?.resource.sha256) {
|
|
1199
1202
|
throw new Error("ModelCandidate.renderEvidence.sourceContentHash must match canonical LOD0.");
|
|
1200
1203
|
}
|
|
1201
|
-
if (!Array.isArray(
|
|
1204
|
+
if (!Array.isArray(record2.viewSha256s) || record2.viewSha256s.length !== 4) {
|
|
1202
1205
|
throw new Error("ModelCandidate.renderEvidence.viewSha256s must contain four ordered hashes.");
|
|
1203
1206
|
}
|
|
1204
|
-
const viewSha256s =
|
|
1207
|
+
const viewSha256s = record2.viewSha256s.map((digest, index) => requiredSha256(digest, `ModelCandidate.renderEvidence.viewSha256s[${index}]`));
|
|
1205
1208
|
if (viewSha256s.some((digest, index) => digest !== views[index]?.sha256)) {
|
|
1206
1209
|
throw new Error("ModelCandidate.renderEvidence view hashes must match the candidate view pack.");
|
|
1207
1210
|
}
|
|
1208
|
-
const renderedAt = requiredTimestamp(
|
|
1211
|
+
const renderedAt = requiredTimestamp(record2.renderedAt, "ModelCandidate.renderEvidence.renderedAt");
|
|
1209
1212
|
if (Date.parse(renderedAt) < Date.parse(manifest.processedAt)) {
|
|
1210
1213
|
throw new Error("ModelCandidate.renderEvidence.renderedAt must not precede processing.");
|
|
1211
1214
|
}
|
|
1212
1215
|
return {
|
|
1213
|
-
renderId: requiredToken(
|
|
1214
|
-
rendererId: requiredToken(
|
|
1215
|
-
rendererVersion: assetVersion(
|
|
1216
|
-
settingsId: requiredToken(
|
|
1217
|
-
settingsVersion: assetVersion(
|
|
1216
|
+
renderId: requiredToken(record2.renderId, "ModelCandidate.renderEvidence.renderId"),
|
|
1217
|
+
rendererId: requiredToken(record2.rendererId, "ModelCandidate.renderEvidence.rendererId"),
|
|
1218
|
+
rendererVersion: assetVersion(record2.rendererVersion, "ModelCandidate.renderEvidence.rendererVersion"),
|
|
1219
|
+
settingsId: requiredToken(record2.settingsId, "ModelCandidate.renderEvidence.settingsId"),
|
|
1220
|
+
settingsVersion: assetVersion(record2.settingsVersion, "ModelCandidate.renderEvidence.settingsVersion"),
|
|
1218
1221
|
processingManifestId: manifest.manifestId,
|
|
1219
1222
|
sourceContentHash: manifest.contentHash,
|
|
1220
1223
|
viewSha256s: [viewSha256s[0], viewSha256s[1], viewSha256s[2], viewSha256s[3]],
|
|
1221
1224
|
renderedAt,
|
|
1222
1225
|
attestationToken: requiredAttestationToken(
|
|
1223
|
-
|
|
1226
|
+
record2.attestationToken,
|
|
1224
1227
|
"ModelCandidate.renderEvidence.attestationToken"
|
|
1225
1228
|
)
|
|
1226
1229
|
};
|
|
@@ -1231,8 +1234,8 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
1231
1234
|
}
|
|
1232
1235
|
const gates = value.map((item, index) => {
|
|
1233
1236
|
const fieldName = `ModelCandidate.hardGates[${index}]`;
|
|
1234
|
-
const
|
|
1235
|
-
assertAllowedKeys(
|
|
1237
|
+
const record2 = assertRecord(item, fieldName);
|
|
1238
|
+
assertAllowedKeys(record2, [
|
|
1236
1239
|
"kind",
|
|
1237
1240
|
"outcome",
|
|
1238
1241
|
"validatorId",
|
|
@@ -1242,26 +1245,26 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
1242
1245
|
"evaluatedAt",
|
|
1243
1246
|
"attestationToken"
|
|
1244
1247
|
], fieldName);
|
|
1245
|
-
const kind = requiredEnum(
|
|
1246
|
-
const subjectContentHash = requiredSha256(
|
|
1248
|
+
const kind = requiredEnum(record2.kind, MODEL_CANDIDATE_HARD_GATE_KINDS, `${fieldName}.kind`);
|
|
1249
|
+
const subjectContentHash = requiredSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`);
|
|
1247
1250
|
const expectedHash = kind === "malware-scan" ? provenance.contentHash : manifest.closureHash;
|
|
1248
1251
|
if (subjectContentHash !== expectedHash) {
|
|
1249
1252
|
throw new Error(`${fieldName}.subjectContentHash must match the gate subject.`);
|
|
1250
1253
|
}
|
|
1251
|
-
const outcome = requiredEnum(
|
|
1252
|
-
const reasonCodes2 = stringList(
|
|
1254
|
+
const outcome = requiredEnum(record2.outcome, ["passed", "blocked"], `${fieldName}.outcome`);
|
|
1255
|
+
const reasonCodes2 = stringList(record2.reasonCodes ?? [], `${fieldName}.reasonCodes`, { tokens: true });
|
|
1253
1256
|
if (outcome === "blocked" && reasonCodes2.length === 0) {
|
|
1254
1257
|
throw new Error(`${fieldName}.reasonCodes must explain a blocked hard gate.`);
|
|
1255
1258
|
}
|
|
1256
1259
|
return {
|
|
1257
1260
|
kind,
|
|
1258
1261
|
outcome,
|
|
1259
|
-
validatorId: requiredToken(
|
|
1260
|
-
validatorVersion: assetVersion(
|
|
1262
|
+
validatorId: requiredToken(record2.validatorId, `${fieldName}.validatorId`),
|
|
1263
|
+
validatorVersion: assetVersion(record2.validatorVersion, `${fieldName}.validatorVersion`),
|
|
1261
1264
|
subjectContentHash,
|
|
1262
1265
|
reasonCodes: reasonCodes2,
|
|
1263
|
-
evaluatedAt: requiredTimestamp(
|
|
1264
|
-
attestationToken: requiredAttestationToken(
|
|
1266
|
+
evaluatedAt: requiredTimestamp(record2.evaluatedAt, `${fieldName}.evaluatedAt`),
|
|
1267
|
+
attestationToken: requiredAttestationToken(record2.attestationToken, `${fieldName}.attestationToken`)
|
|
1265
1268
|
};
|
|
1266
1269
|
});
|
|
1267
1270
|
if (new Set(gates.map((gate) => gate.kind)).size !== MODEL_CANDIDATE_HARD_GATE_KINDS.length) {
|
|
@@ -1274,8 +1277,8 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
1274
1277
|
return MODEL_CANDIDATE_HARD_GATE_KINDS.map((kind) => byKind.get(kind));
|
|
1275
1278
|
}
|
|
1276
1279
|
function createModelCandidate(input) {
|
|
1277
|
-
const
|
|
1278
|
-
assertAllowedKeys(
|
|
1280
|
+
const record2 = assertRecord(input, "ModelCandidate");
|
|
1281
|
+
assertAllowedKeys(record2, [
|
|
1279
1282
|
"contractVersion",
|
|
1280
1283
|
"resolutionId",
|
|
1281
1284
|
"candidateId",
|
|
@@ -1291,24 +1294,24 @@ function createModelCandidate(input) {
|
|
|
1291
1294
|
"confirmationToken",
|
|
1292
1295
|
"confirmationRequired"
|
|
1293
1296
|
], "ModelCandidate");
|
|
1294
|
-
assertContractVersion(
|
|
1295
|
-
if (
|
|
1297
|
+
assertContractVersion(record2, "ModelCandidate");
|
|
1298
|
+
if (record2.confirmationRequired !== void 0 && record2.confirmationRequired !== true) {
|
|
1296
1299
|
throw new Error("ModelCandidate confirmationRequired must always be true.");
|
|
1297
1300
|
}
|
|
1298
|
-
if (typeof
|
|
1301
|
+
if (typeof record2.confirmationToken !== "string" || !CONFIRMATION_TOKEN_PATTERN.test(record2.confirmationToken)) {
|
|
1299
1302
|
throw new Error("ModelCandidate confirmationToken must be a bounded opaque token.");
|
|
1300
1303
|
}
|
|
1301
|
-
const resolutionId = requiredModelPathSegment(
|
|
1302
|
-
const candidateId = requiredModelPathSegment(
|
|
1303
|
-
const assetRef = createCandidateAssetRef(
|
|
1304
|
-
const technicalProfile = createModelTechnicalProfile(
|
|
1305
|
-
const processingManifest = createModelProcessingManifest(
|
|
1304
|
+
const resolutionId = requiredModelPathSegment(record2.resolutionId, "ModelCandidate.resolutionId");
|
|
1305
|
+
const candidateId = requiredModelPathSegment(record2.candidateId, "ModelCandidate.candidateId");
|
|
1306
|
+
const assetRef = createCandidateAssetRef(record2.assetRef);
|
|
1307
|
+
const technicalProfile = createModelTechnicalProfile(record2.technicalProfile);
|
|
1308
|
+
const processingManifest = createModelProcessingManifest(record2.processingManifest);
|
|
1306
1309
|
if (processingManifest.resolutionId !== resolutionId || processingManifest.candidateId !== candidateId || processingManifest.kind !== assetRef.kind || processingManifest.contentHash !== assetRef.contentHash || JSON.stringify(processingManifest.technicalProfile) !== JSON.stringify(technicalProfile)) {
|
|
1307
1310
|
throw new Error(
|
|
1308
1311
|
"ModelCandidate processingManifest must match candidateId, asset kind/contentHash, and technicalProfile."
|
|
1309
1312
|
);
|
|
1310
1313
|
}
|
|
1311
|
-
const match = createModelMatchAssessment(
|
|
1314
|
+
const match = createModelMatchAssessment(record2.match);
|
|
1312
1315
|
if (match.candidateId !== candidateId || match.candidateContentHash !== assetRef.contentHash) {
|
|
1313
1316
|
throw new Error("ModelCandidate match assessment must bind candidateId and candidate contentHash.");
|
|
1314
1317
|
}
|
|
@@ -1328,11 +1331,11 @@ function createModelCandidate(input) {
|
|
|
1328
1331
|
"ModelCandidate match reasonCodes must include every failed hard constraint."
|
|
1329
1332
|
);
|
|
1330
1333
|
}
|
|
1331
|
-
const provenance = createModelProvenance(
|
|
1334
|
+
const provenance = createModelProvenance(record2.provenance);
|
|
1332
1335
|
if (provenance.contentHash !== processingManifest.converter.sourceContentHash) {
|
|
1333
1336
|
throw new Error("ModelCandidate provenance contentHash must match converter sourceContentHash.");
|
|
1334
1337
|
}
|
|
1335
|
-
const rights = createModelRightsAssessment(
|
|
1338
|
+
const rights = createModelRightsAssessment(record2.rights);
|
|
1336
1339
|
if (rights.sourceId !== provenance.sourceId || rights.sourceAssetId !== provenance.sourceAssetId || rights.sourceContentHash !== provenance.contentHash) {
|
|
1337
1340
|
throw new Error("ModelCandidate rights decision must bind the exact provenance source and content hash.");
|
|
1338
1341
|
}
|
|
@@ -1344,9 +1347,9 @@ function createModelCandidate(input) {
|
|
|
1344
1347
|
if (processingManifest.fidelityGate.outcome === "blocked" && !match.reasonCodes.includes("fidelity-blocked")) {
|
|
1345
1348
|
throw new Error("ModelCandidate blocked fidelity must be recorded in match reasonCodes.");
|
|
1346
1349
|
}
|
|
1347
|
-
const views = createConfirmationViews(
|
|
1348
|
-
const renderEvidence = createRenderEvidence(
|
|
1349
|
-
const hardGates = createCandidateHardGates(
|
|
1350
|
+
const views = createConfirmationViews(record2.views, resolutionId, candidateId);
|
|
1351
|
+
const renderEvidence = createRenderEvidence(record2.renderEvidence, processingManifest, views);
|
|
1352
|
+
const hardGates = createCandidateHardGates(record2.hardGates, provenance, processingManifest);
|
|
1350
1353
|
return deepFreeze({
|
|
1351
1354
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1352
1355
|
resolutionId,
|
|
@@ -1360,7 +1363,7 @@ function createModelCandidate(input) {
|
|
|
1360
1363
|
views,
|
|
1361
1364
|
renderEvidence,
|
|
1362
1365
|
hardGates,
|
|
1363
|
-
confirmationToken:
|
|
1366
|
+
confirmationToken: record2.confirmationToken,
|
|
1364
1367
|
confirmationRequired: true
|
|
1365
1368
|
});
|
|
1366
1369
|
}
|
|
@@ -1372,8 +1375,8 @@ function isModelCandidateConfirmable(candidate) {
|
|
|
1372
1375
|
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;
|
|
1373
1376
|
}
|
|
1374
1377
|
function createModelCandidateConfirmation(input, expectedCandidate, expectedResolutionId) {
|
|
1375
|
-
const
|
|
1376
|
-
assertAllowedKeys(
|
|
1378
|
+
const record2 = assertRecord(input, "ModelCandidateConfirmation");
|
|
1379
|
+
assertAllowedKeys(record2, [
|
|
1377
1380
|
"contractVersion",
|
|
1378
1381
|
"confirmationId",
|
|
1379
1382
|
"resolutionId",
|
|
@@ -1384,7 +1387,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1384
1387
|
"confirmedAt",
|
|
1385
1388
|
"semanticRiskAccepted"
|
|
1386
1389
|
], "ModelCandidateConfirmation");
|
|
1387
|
-
assertContractVersion(
|
|
1390
|
+
assertContractVersion(record2, "ModelCandidateConfirmation");
|
|
1388
1391
|
const candidate = createModelCandidate(expectedCandidate);
|
|
1389
1392
|
const resolutionId = requiredModelPathSegment(
|
|
1390
1393
|
expectedResolutionId,
|
|
@@ -1395,19 +1398,19 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1395
1398
|
"ModelCandidateConfirmation expected candidate must belong to the expected resolutionId."
|
|
1396
1399
|
);
|
|
1397
1400
|
}
|
|
1398
|
-
if (
|
|
1401
|
+
if (record2.resolutionId !== resolutionId) {
|
|
1399
1402
|
throw new Error("ModelCandidateConfirmation.resolutionId must match the expected resolutionId.");
|
|
1400
1403
|
}
|
|
1401
|
-
if (
|
|
1404
|
+
if (record2.candidateId !== candidate.candidateId) {
|
|
1402
1405
|
throw new Error("ModelCandidateConfirmation.candidateId must match the confirmed candidate.");
|
|
1403
1406
|
}
|
|
1404
|
-
if (typeof
|
|
1407
|
+
if (typeof record2.confirmationToken !== "string" || !CONFIRMATION_TOKEN_PATTERN.test(record2.confirmationToken) || record2.confirmationToken !== candidate.confirmationToken) {
|
|
1405
1408
|
throw new Error("ModelCandidateConfirmation.confirmationToken must match the signed candidate token.");
|
|
1406
1409
|
}
|
|
1407
|
-
if (!Array.isArray(
|
|
1410
|
+
if (!Array.isArray(record2.viewSha256s) || record2.viewSha256s.length !== 4) {
|
|
1408
1411
|
throw new Error("ModelCandidateConfirmation.viewSha256s must contain the ordered four candidate view hashes.");
|
|
1409
1412
|
}
|
|
1410
|
-
const viewSha256s =
|
|
1413
|
+
const viewSha256s = record2.viewSha256s.map((value, index) => requiredSha256(value, `ModelCandidateConfirmation.viewSha256s[${index}]`));
|
|
1411
1414
|
const expectedHashes = candidate.views.map((view) => view.sha256);
|
|
1412
1415
|
if (viewSha256s.some((digest, index) => digest !== expectedHashes[index])) {
|
|
1413
1416
|
throw new Error("ModelCandidateConfirmation view hashes must match the ordered candidate views.");
|
|
@@ -1416,7 +1419,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1416
1419
|
throw new Error("ModelCandidateConfirmation candidate is not confirmable because a hard review gate is blocking.");
|
|
1417
1420
|
}
|
|
1418
1421
|
const semanticRiskAccepted = requiredBoolean(
|
|
1419
|
-
|
|
1422
|
+
record2.semanticRiskAccepted,
|
|
1420
1423
|
"ModelCandidateConfirmation.semanticRiskAccepted"
|
|
1421
1424
|
);
|
|
1422
1425
|
if (candidate.match.assurance === "low" && !semanticRiskAccepted) {
|
|
@@ -1425,10 +1428,10 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1425
1428
|
if (candidate.match.assurance === "high" && semanticRiskAccepted) {
|
|
1426
1429
|
throw new Error("ModelCandidateConfirmation high assurance must not record a semantic risk override.");
|
|
1427
1430
|
}
|
|
1428
|
-
const confirmedBy = requiredString(
|
|
1431
|
+
const confirmedBy = requiredString(record2.confirmedBy, "ModelCandidateConfirmation.confirmedBy", 256);
|
|
1429
1432
|
assertNoDirectUrl(confirmedBy, "ModelCandidateConfirmation.confirmedBy");
|
|
1430
1433
|
const confirmedAt = requiredTimestamp(
|
|
1431
|
-
|
|
1434
|
+
record2.confirmedAt,
|
|
1432
1435
|
"ModelCandidateConfirmation.confirmedAt"
|
|
1433
1436
|
);
|
|
1434
1437
|
const latestEvidenceTime = Math.max(
|
|
@@ -1446,7 +1449,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1446
1449
|
}
|
|
1447
1450
|
return deepFreeze({
|
|
1448
1451
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1449
|
-
confirmationId: requiredToken(
|
|
1452
|
+
confirmationId: requiredToken(record2.confirmationId, "ModelCandidateConfirmation.confirmationId"),
|
|
1450
1453
|
resolutionId,
|
|
1451
1454
|
candidateId: candidate.candidateId,
|
|
1452
1455
|
confirmationToken: candidate.confirmationToken,
|
|
@@ -1457,33 +1460,33 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1457
1460
|
});
|
|
1458
1461
|
}
|
|
1459
1462
|
function createModelResourceRef(input) {
|
|
1460
|
-
const
|
|
1461
|
-
assertAllowedKeys(
|
|
1462
|
-
const
|
|
1463
|
-
if (!CONTENT_TYPE_PATTERN.test(
|
|
1463
|
+
const record2 = assertRecord(input, "ModelResourceRef");
|
|
1464
|
+
assertAllowedKeys(record2, ["uri", "byteLength", "sha256", "contentType"], "ModelResourceRef");
|
|
1465
|
+
const contentType2 = requiredString(record2.contentType, "ModelResourceRef.contentType", 128);
|
|
1466
|
+
if (!CONTENT_TYPE_PATTERN.test(contentType2)) {
|
|
1464
1467
|
throw new Error("ModelResourceRef.contentType must be a valid media type.");
|
|
1465
1468
|
}
|
|
1466
1469
|
return deepFreeze({
|
|
1467
|
-
uri: assertModelUri(
|
|
1468
|
-
byteLength: requiredInteger(
|
|
1469
|
-
sha256: requiredSha256(
|
|
1470
|
-
contentType
|
|
1470
|
+
uri: assertModelUri(record2.uri, "ModelResourceRef.uri"),
|
|
1471
|
+
byteLength: requiredInteger(record2.byteLength, "ModelResourceRef.byteLength", 1, Number.MAX_SAFE_INTEGER),
|
|
1472
|
+
sha256: requiredSha256(record2.sha256, "ModelResourceRef.sha256"),
|
|
1473
|
+
contentType: contentType2
|
|
1471
1474
|
});
|
|
1472
1475
|
}
|
|
1473
1476
|
function createLodRecord(value, index) {
|
|
1474
1477
|
const fieldName = `ModelProcessingManifest.lods[${index}]`;
|
|
1475
|
-
const
|
|
1476
|
-
assertAllowedKeys(
|
|
1477
|
-
const resource = createModelResourceRef(
|
|
1478
|
+
const record2 = assertRecord(value, fieldName);
|
|
1479
|
+
assertAllowedKeys(record2, ["level", "resource", "triangleCount", "geometricErrorMetres"], fieldName);
|
|
1480
|
+
const resource = createModelResourceRef(record2.resource);
|
|
1478
1481
|
if (resource.contentType !== "model/gltf-binary") {
|
|
1479
1482
|
throw new Error(`${fieldName}.resource.contentType must be model/gltf-binary for canonical GLB output.`);
|
|
1480
1483
|
}
|
|
1481
1484
|
return {
|
|
1482
|
-
level: requiredInteger(
|
|
1485
|
+
level: requiredInteger(record2.level, `${fieldName}.level`, 0, 3),
|
|
1483
1486
|
resource,
|
|
1484
|
-
triangleCount: requiredInteger(
|
|
1487
|
+
triangleCount: requiredInteger(record2.triangleCount, `${fieldName}.triangleCount`, 1, 1e9),
|
|
1485
1488
|
geometricErrorMetres: requiredFiniteNumber(
|
|
1486
|
-
|
|
1489
|
+
record2.geometricErrorMetres,
|
|
1487
1490
|
`${fieldName}.geometricErrorMetres`,
|
|
1488
1491
|
0,
|
|
1489
1492
|
1e6
|
|
@@ -1491,40 +1494,40 @@ function createLodRecord(value, index) {
|
|
|
1491
1494
|
};
|
|
1492
1495
|
}
|
|
1493
1496
|
function createCollisionRecord(value) {
|
|
1494
|
-
const
|
|
1495
|
-
assertAllowedKeys(
|
|
1497
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.collision");
|
|
1498
|
+
assertAllowedKeys(record2, ["kind", "resource"], "ModelProcessingManifest.collision");
|
|
1496
1499
|
const kind = requiredEnum(
|
|
1497
|
-
|
|
1500
|
+
record2.kind,
|
|
1498
1501
|
["none", "box", "convex-hull", "triangle-mesh", "compound"],
|
|
1499
1502
|
"ModelProcessingManifest.collision.kind"
|
|
1500
1503
|
);
|
|
1501
1504
|
if (kind === "none") {
|
|
1502
|
-
if (
|
|
1505
|
+
if (record2.resource !== void 0) {
|
|
1503
1506
|
throw new Error("ModelProcessingManifest collision kind none must not include a resource.");
|
|
1504
1507
|
}
|
|
1505
1508
|
return { kind };
|
|
1506
1509
|
}
|
|
1507
|
-
if (
|
|
1510
|
+
if (record2.resource === void 0) {
|
|
1508
1511
|
throw new Error("ModelProcessingManifest non-empty collision kinds require a resource.");
|
|
1509
1512
|
}
|
|
1510
|
-
const resource = createModelResourceRef(
|
|
1513
|
+
const resource = createModelResourceRef(record2.resource);
|
|
1511
1514
|
if (resource.contentType !== "model/gltf-binary") {
|
|
1512
1515
|
throw new Error("ModelProcessingManifest collision resources must be model/gltf-binary GLBs.");
|
|
1513
1516
|
}
|
|
1514
1517
|
return { kind, resource };
|
|
1515
1518
|
}
|
|
1516
1519
|
function createTransform(value, fieldName) {
|
|
1517
|
-
const
|
|
1518
|
-
assertAllowedKeys(
|
|
1519
|
-
const translationMetres = requiredVector3(
|
|
1520
|
-
if (!Array.isArray(
|
|
1520
|
+
const record2 = assertRecord(value, fieldName);
|
|
1521
|
+
assertAllowedKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
|
|
1522
|
+
const translationMetres = requiredVector3(record2.translationMetres, `${fieldName}.translationMetres`);
|
|
1523
|
+
if (!Array.isArray(record2.rotationQuaternion) || record2.rotationQuaternion.length !== 4) {
|
|
1521
1524
|
throw new Error(`${fieldName}.rotationQuaternion must contain exactly four numbers.`);
|
|
1522
1525
|
}
|
|
1523
1526
|
const rotationQuaternion = [
|
|
1524
|
-
requiredFiniteNumber(
|
|
1525
|
-
requiredFiniteNumber(
|
|
1526
|
-
requiredFiniteNumber(
|
|
1527
|
-
requiredFiniteNumber(
|
|
1527
|
+
requiredFiniteNumber(record2.rotationQuaternion[0], `${fieldName}.rotationQuaternion[0]`, -1, 1),
|
|
1528
|
+
requiredFiniteNumber(record2.rotationQuaternion[1], `${fieldName}.rotationQuaternion[1]`, -1, 1),
|
|
1529
|
+
requiredFiniteNumber(record2.rotationQuaternion[2], `${fieldName}.rotationQuaternion[2]`, -1, 1),
|
|
1530
|
+
requiredFiniteNumber(record2.rotationQuaternion[3], `${fieldName}.rotationQuaternion[3]`, -1, 1)
|
|
1528
1531
|
];
|
|
1529
1532
|
const magnitude = Math.hypot(...rotationQuaternion);
|
|
1530
1533
|
if (Math.abs(magnitude - 1) > 1e-4) {
|
|
@@ -1533,31 +1536,31 @@ function createTransform(value, fieldName) {
|
|
|
1533
1536
|
return {
|
|
1534
1537
|
translationMetres,
|
|
1535
1538
|
rotationQuaternion,
|
|
1536
|
-
scale: requiredVector3(
|
|
1539
|
+
scale: requiredVector3(record2.scale, `${fieldName}.scale`, true)
|
|
1537
1540
|
};
|
|
1538
1541
|
}
|
|
1539
1542
|
function createAssemblyChildAssetRef(value, resolutionId, candidateId, fieldName) {
|
|
1540
|
-
const
|
|
1541
|
-
if (
|
|
1543
|
+
const record2 = assertRecord(value, fieldName);
|
|
1544
|
+
if (record2.disposition !== "staged-derived") {
|
|
1542
1545
|
const catalogRef = createModelAssetRef(value);
|
|
1543
1546
|
if (catalogRef.kind !== "leaf") {
|
|
1544
1547
|
throw new Error("ModelProcessingManifest v1 assembly child assetRef values must be leaf assets.");
|
|
1545
1548
|
}
|
|
1546
1549
|
return catalogRef;
|
|
1547
1550
|
}
|
|
1548
|
-
assertAllowedKeys(
|
|
1551
|
+
assertAllowedKeys(record2, [
|
|
1549
1552
|
"disposition",
|
|
1550
1553
|
"derivedId",
|
|
1551
1554
|
"kind",
|
|
1552
1555
|
"contentHash",
|
|
1553
1556
|
"processingManifestUri"
|
|
1554
1557
|
], fieldName);
|
|
1555
|
-
if (
|
|
1558
|
+
if (record2.kind !== "leaf") {
|
|
1556
1559
|
throw new Error("ModelProcessingManifest staged-derived child assetRef kind must be leaf.");
|
|
1557
1560
|
}
|
|
1558
|
-
const derivedId = requiredModelPathSegment(
|
|
1561
|
+
const derivedId = requiredModelPathSegment(record2.derivedId, `${fieldName}.derivedId`);
|
|
1559
1562
|
const processingManifestUri = assertModelUri(
|
|
1560
|
-
|
|
1563
|
+
record2.processingManifestUri,
|
|
1561
1564
|
`${fieldName}.processingManifestUri`
|
|
1562
1565
|
);
|
|
1563
1566
|
const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/children/${derivedId}/manifest`;
|
|
@@ -1568,18 +1571,18 @@ function createAssemblyChildAssetRef(value, resolutionId, candidateId, fieldName
|
|
|
1568
1571
|
disposition: "staged-derived",
|
|
1569
1572
|
derivedId,
|
|
1570
1573
|
kind: "leaf",
|
|
1571
|
-
contentHash: requiredSha256(
|
|
1574
|
+
contentHash: requiredSha256(record2.contentHash, `${fieldName}.contentHash`),
|
|
1572
1575
|
processingManifestUri
|
|
1573
1576
|
};
|
|
1574
1577
|
}
|
|
1575
1578
|
function createAssemblyChild(value, index, resolutionId, candidateId) {
|
|
1576
1579
|
const fieldName = `ModelProcessingManifest.children[${index}]`;
|
|
1577
|
-
const
|
|
1578
|
-
assertAllowedKeys(
|
|
1579
|
-
const instanceId = requiredToken(
|
|
1580
|
-
const parentInstanceId =
|
|
1580
|
+
const record2 = assertRecord(value, fieldName);
|
|
1581
|
+
assertAllowedKeys(record2, ["instanceId", "parentInstanceId", "assetRef", "transform"], fieldName);
|
|
1582
|
+
const instanceId = requiredToken(record2.instanceId, `${fieldName}.instanceId`);
|
|
1583
|
+
const parentInstanceId = record2.parentInstanceId === void 0 ? void 0 : requiredToken(record2.parentInstanceId, `${fieldName}.parentInstanceId`);
|
|
1581
1584
|
const childAssetRef = createAssemblyChildAssetRef(
|
|
1582
|
-
|
|
1585
|
+
record2.assetRef,
|
|
1583
1586
|
resolutionId,
|
|
1584
1587
|
candidateId,
|
|
1585
1588
|
`${fieldName}.assetRef`
|
|
@@ -1588,21 +1591,21 @@ function createAssemblyChild(value, index, resolutionId, candidateId) {
|
|
|
1588
1591
|
instanceId,
|
|
1589
1592
|
...parentInstanceId === void 0 ? {} : { parentInstanceId },
|
|
1590
1593
|
assetRef: childAssetRef,
|
|
1591
|
-
transform: createTransform(
|
|
1594
|
+
transform: createTransform(record2.transform, `${fieldName}.transform`)
|
|
1592
1595
|
};
|
|
1593
1596
|
}
|
|
1594
1597
|
function createDiagnostic(value, fieldName) {
|
|
1595
|
-
const
|
|
1596
|
-
assertAllowedKeys(
|
|
1598
|
+
const record2 = assertRecord(value, fieldName);
|
|
1599
|
+
assertAllowedKeys(record2, ["severity", "code", "message"], fieldName);
|
|
1597
1600
|
return {
|
|
1598
|
-
severity: requiredEnum(
|
|
1599
|
-
code: requiredToken(
|
|
1600
|
-
message: requiredString(
|
|
1601
|
+
severity: requiredEnum(record2.severity, ["info", "warning", "blocking"], `${fieldName}.severity`),
|
|
1602
|
+
code: requiredToken(record2.code, `${fieldName}.code`),
|
|
1603
|
+
message: requiredString(record2.message, `${fieldName}.message`, 1024)
|
|
1601
1604
|
};
|
|
1602
1605
|
}
|
|
1603
1606
|
function createConverterEvidence(value) {
|
|
1604
|
-
const
|
|
1605
|
-
assertAllowedKeys(
|
|
1607
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.converter");
|
|
1608
|
+
assertAllowedKeys(record2, [
|
|
1606
1609
|
"id",
|
|
1607
1610
|
"version",
|
|
1608
1611
|
"sourceFormat",
|
|
@@ -1612,42 +1615,42 @@ function createConverterEvidence(value) {
|
|
|
1612
1615
|
"diagnostics",
|
|
1613
1616
|
"losses"
|
|
1614
1617
|
], "ModelProcessingManifest.converter");
|
|
1615
|
-
if (!Array.isArray(
|
|
1618
|
+
if (!Array.isArray(record2.diagnostics) || record2.diagnostics.length > 100) {
|
|
1616
1619
|
throw new Error("ModelProcessingManifest.converter.diagnostics must be a bounded array.");
|
|
1617
1620
|
}
|
|
1618
|
-
if (!Array.isArray(
|
|
1621
|
+
if (!Array.isArray(record2.losses) || record2.losses.length > 100) {
|
|
1619
1622
|
throw new Error("ModelProcessingManifest.converter.losses must be a bounded array.");
|
|
1620
1623
|
}
|
|
1621
1624
|
return {
|
|
1622
|
-
id: requiredToken(
|
|
1623
|
-
version: assetVersion(
|
|
1624
|
-
sourceFormat: requiredToken(
|
|
1625
|
-
targetFormat: requiredToken(
|
|
1626
|
-
sourceContentHash: requiredSha256(
|
|
1627
|
-
outputContentHash: requiredSha256(
|
|
1628
|
-
diagnostics:
|
|
1629
|
-
losses:
|
|
1625
|
+
id: requiredToken(record2.id, "ModelProcessingManifest.converter.id"),
|
|
1626
|
+
version: assetVersion(record2.version, "ModelProcessingManifest.converter.version"),
|
|
1627
|
+
sourceFormat: requiredToken(record2.sourceFormat, "ModelProcessingManifest.converter.sourceFormat"),
|
|
1628
|
+
targetFormat: requiredToken(record2.targetFormat, "ModelProcessingManifest.converter.targetFormat"),
|
|
1629
|
+
sourceContentHash: requiredSha256(record2.sourceContentHash, "ModelProcessingManifest.converter.sourceContentHash"),
|
|
1630
|
+
outputContentHash: requiredSha256(record2.outputContentHash, "ModelProcessingManifest.converter.outputContentHash"),
|
|
1631
|
+
diagnostics: record2.diagnostics.map((item, index) => createDiagnostic(item, `ModelProcessingManifest.converter.diagnostics[${index}]`)),
|
|
1632
|
+
losses: record2.losses.map((item, index) => createDiagnostic(item, `ModelProcessingManifest.converter.losses[${index}]`))
|
|
1630
1633
|
};
|
|
1631
1634
|
}
|
|
1632
1635
|
function createFidelityEvidence(value, index) {
|
|
1633
1636
|
const fieldName = `ModelProcessingManifest.fidelityEvidence[${index}]`;
|
|
1634
|
-
const
|
|
1635
|
-
assertAllowedKeys(
|
|
1636
|
-
const evidenceResource =
|
|
1637
|
+
const record2 = assertRecord(value, fieldName);
|
|
1638
|
+
assertAllowedKeys(record2, ["aspect", "outcome", "message", "evidenceResource"], fieldName);
|
|
1639
|
+
const evidenceResource = record2.evidenceResource === void 0 ? void 0 : createModelResourceRef(record2.evidenceResource);
|
|
1637
1640
|
return {
|
|
1638
1641
|
aspect: requiredEnum(
|
|
1639
|
-
|
|
1642
|
+
record2.aspect,
|
|
1640
1643
|
["geometry", "materials", "textures", "rigging", "animation", "metadata"],
|
|
1641
1644
|
`${fieldName}.aspect`
|
|
1642
1645
|
),
|
|
1643
|
-
outcome: requiredEnum(
|
|
1644
|
-
message: requiredString(
|
|
1646
|
+
outcome: requiredEnum(record2.outcome, ["preserved", "approximated", "lost"], `${fieldName}.outcome`),
|
|
1647
|
+
message: requiredString(record2.message, `${fieldName}.message`, 1024),
|
|
1645
1648
|
...evidenceResource === void 0 ? {} : { evidenceResource }
|
|
1646
1649
|
};
|
|
1647
1650
|
}
|
|
1648
1651
|
function createCollisionPolicyEvidence(value, collision) {
|
|
1649
|
-
const
|
|
1650
|
-
assertAllowedKeys(
|
|
1652
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.collisionPolicy");
|
|
1653
|
+
assertAllowedKeys(record2, [
|
|
1651
1654
|
"profileId",
|
|
1652
1655
|
"profileVersion",
|
|
1653
1656
|
"disposition",
|
|
@@ -1655,7 +1658,7 @@ function createCollisionPolicyEvidence(value, collision) {
|
|
|
1655
1658
|
"decisionToken"
|
|
1656
1659
|
], "ModelProcessingManifest.collisionPolicy");
|
|
1657
1660
|
const disposition = requiredEnum(
|
|
1658
|
-
|
|
1661
|
+
record2.disposition,
|
|
1659
1662
|
["proxy-required", "none-allowed"],
|
|
1660
1663
|
"ModelProcessingManifest.collisionPolicy.disposition"
|
|
1661
1664
|
);
|
|
@@ -1663,22 +1666,22 @@ function createCollisionPolicyEvidence(value, collision) {
|
|
|
1663
1666
|
throw new Error("ModelProcessingManifest collisionPolicy must explicitly authorize collision none or require a proxy.");
|
|
1664
1667
|
}
|
|
1665
1668
|
return {
|
|
1666
|
-
profileId: requiredToken(
|
|
1669
|
+
profileId: requiredToken(record2.profileId, "ModelProcessingManifest.collisionPolicy.profileId"),
|
|
1667
1670
|
profileVersion: assetVersion(
|
|
1668
|
-
|
|
1671
|
+
record2.profileVersion,
|
|
1669
1672
|
"ModelProcessingManifest.collisionPolicy.profileVersion"
|
|
1670
1673
|
),
|
|
1671
1674
|
disposition,
|
|
1672
|
-
category: requiredToken(
|
|
1675
|
+
category: requiredToken(record2.category, "ModelProcessingManifest.collisionPolicy.category"),
|
|
1673
1676
|
decisionToken: requiredAttestationToken(
|
|
1674
|
-
|
|
1677
|
+
record2.decisionToken,
|
|
1675
1678
|
"ModelProcessingManifest.collisionPolicy.decisionToken"
|
|
1676
1679
|
)
|
|
1677
1680
|
};
|
|
1678
1681
|
}
|
|
1679
1682
|
function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
1680
|
-
const
|
|
1681
|
-
assertAllowedKeys(
|
|
1683
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.fidelityGate");
|
|
1684
|
+
assertAllowedKeys(record2, [
|
|
1682
1685
|
"profileId",
|
|
1683
1686
|
"profileVersion",
|
|
1684
1687
|
"outcome",
|
|
@@ -1687,7 +1690,7 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1687
1690
|
"decisionToken"
|
|
1688
1691
|
], "ModelProcessingManifest.fidelityGate");
|
|
1689
1692
|
const expectedAspects = ["geometry", "materials", "textures"];
|
|
1690
|
-
if (!Array.isArray(
|
|
1693
|
+
if (!Array.isArray(record2.requiredAspects) || record2.requiredAspects.length !== expectedAspects.length || record2.requiredAspects.some((aspect, index) => aspect !== expectedAspects[index])) {
|
|
1691
1694
|
throw new Error("ModelProcessingManifest.fidelityGate.requiredAspects must be geometry, materials, textures.");
|
|
1692
1695
|
}
|
|
1693
1696
|
const byAspect = new Map(fidelityEvidence.map((evidence) => [evidence.aspect, evidence]));
|
|
@@ -1705,7 +1708,7 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1705
1708
|
) || hasWarningLoss;
|
|
1706
1709
|
const derivedOutcome = hasBlockingConverterEvidence || geometryLost ? "blocked" : hasUnresolvedFidelity ? "low-only" : "passed";
|
|
1707
1710
|
const outcome = requiredEnum(
|
|
1708
|
-
|
|
1711
|
+
record2.outcome,
|
|
1709
1712
|
["passed", "low-only", "blocked"],
|
|
1710
1713
|
"ModelProcessingManifest.fidelityGate.outcome"
|
|
1711
1714
|
);
|
|
@@ -1713,13 +1716,13 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1713
1716
|
throw new Error("ModelProcessingManifest fidelityGate outcome must match converter and fidelity evidence.");
|
|
1714
1717
|
}
|
|
1715
1718
|
return {
|
|
1716
|
-
profileId: requiredToken(
|
|
1717
|
-
profileVersion: assetVersion(
|
|
1719
|
+
profileId: requiredToken(record2.profileId, "ModelProcessingManifest.fidelityGate.profileId"),
|
|
1720
|
+
profileVersion: assetVersion(record2.profileVersion, "ModelProcessingManifest.fidelityGate.profileVersion"),
|
|
1718
1721
|
outcome,
|
|
1719
1722
|
requiredAspects: expectedAspects,
|
|
1720
|
-
evaluatedAt: requiredTimestamp(
|
|
1723
|
+
evaluatedAt: requiredTimestamp(record2.evaluatedAt, "ModelProcessingManifest.fidelityGate.evaluatedAt"),
|
|
1721
1724
|
decisionToken: requiredAttestationToken(
|
|
1722
|
-
|
|
1725
|
+
record2.decisionToken,
|
|
1723
1726
|
"ModelProcessingManifest.fidelityGate.decisionToken"
|
|
1724
1727
|
)
|
|
1725
1728
|
};
|
|
@@ -1728,15 +1731,15 @@ function assertCoordinateSystem(value) {
|
|
|
1728
1731
|
if (value === void 0) {
|
|
1729
1732
|
return;
|
|
1730
1733
|
}
|
|
1731
|
-
const
|
|
1732
|
-
assertAllowedKeys(
|
|
1734
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.coordinateSystem");
|
|
1735
|
+
assertAllowedKeys(record2, [
|
|
1733
1736
|
"unit",
|
|
1734
1737
|
"upAxis",
|
|
1735
1738
|
"forwardAxis",
|
|
1736
1739
|
"origin",
|
|
1737
1740
|
"outwardFaceWinding"
|
|
1738
1741
|
], "ModelProcessingManifest.coordinateSystem");
|
|
1739
|
-
if (
|
|
1742
|
+
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) {
|
|
1740
1743
|
throw new Error(
|
|
1741
1744
|
"ModelProcessingManifest.coordinateSystem must be metres, Y-up, -Z forward, floor-centred, and counter-clockwise outward."
|
|
1742
1745
|
);
|
|
@@ -1780,8 +1783,8 @@ function assertAssemblyHierarchy(children) {
|
|
|
1780
1783
|
}
|
|
1781
1784
|
}
|
|
1782
1785
|
function createModelProcessingManifest(input) {
|
|
1783
|
-
const
|
|
1784
|
-
assertAllowedKeys(
|
|
1786
|
+
const record2 = assertRecord(input, "ModelProcessingManifest");
|
|
1787
|
+
assertAllowedKeys(record2, [
|
|
1785
1788
|
"contractVersion",
|
|
1786
1789
|
"manifestId",
|
|
1787
1790
|
"resolutionId",
|
|
@@ -1800,23 +1803,23 @@ function createModelProcessingManifest(input) {
|
|
|
1800
1803
|
"fidelityGate",
|
|
1801
1804
|
"processedAt"
|
|
1802
1805
|
], "ModelProcessingManifest");
|
|
1803
|
-
assertContractVersion(
|
|
1806
|
+
assertContractVersion(record2, "ModelProcessingManifest");
|
|
1804
1807
|
const resolutionId = requiredModelPathSegment(
|
|
1805
|
-
|
|
1808
|
+
record2.resolutionId,
|
|
1806
1809
|
"ModelProcessingManifest.resolutionId"
|
|
1807
1810
|
);
|
|
1808
1811
|
const candidateId = requiredModelPathSegment(
|
|
1809
|
-
|
|
1812
|
+
record2.candidateId,
|
|
1810
1813
|
"ModelProcessingManifest.candidateId"
|
|
1811
1814
|
);
|
|
1812
|
-
assertCoordinateSystem(
|
|
1813
|
-
const kind = requiredEnum(
|
|
1814
|
-
const contentHash = requiredSha256(
|
|
1815
|
-
const closureHash = requiredSha256(
|
|
1815
|
+
assertCoordinateSystem(record2.coordinateSystem);
|
|
1816
|
+
const kind = requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelProcessingManifest.kind");
|
|
1817
|
+
const contentHash = requiredSha256(record2.contentHash, "ModelProcessingManifest.contentHash");
|
|
1818
|
+
const closureHash = requiredSha256(record2.closureHash, "ModelProcessingManifest.closureHash");
|
|
1816
1819
|
if (kind === "leaf" && closureHash !== contentHash) {
|
|
1817
1820
|
throw new Error("ModelProcessingManifest leaf closureHash must match contentHash.");
|
|
1818
1821
|
}
|
|
1819
|
-
const technicalProfile = createModelTechnicalProfile(
|
|
1822
|
+
const technicalProfile = createModelTechnicalProfile(record2.technicalProfile);
|
|
1820
1823
|
const [minimumX, minimumY, minimumZ] = technicalProfile.boundsMetres.min;
|
|
1821
1824
|
const [maximumX, , maximumZ] = technicalProfile.boundsMetres.max;
|
|
1822
1825
|
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) {
|
|
@@ -1824,10 +1827,10 @@ function createModelProcessingManifest(input) {
|
|
|
1824
1827
|
"ModelProcessingManifest floor-centred origin requires minY and the X/Z bounds centre to be zero."
|
|
1825
1828
|
);
|
|
1826
1829
|
}
|
|
1827
|
-
if (!Array.isArray(
|
|
1830
|
+
if (!Array.isArray(record2.lods) || record2.lods.length < 1 || record2.lods.length > 4) {
|
|
1828
1831
|
throw new Error("ModelProcessingManifest.lods must contain one to four adaptive levels beginning with LOD0.");
|
|
1829
1832
|
}
|
|
1830
|
-
const lods =
|
|
1833
|
+
const lods = record2.lods.map(createLodRecord);
|
|
1831
1834
|
for (const [index, lod] of lods.entries()) {
|
|
1832
1835
|
if (lod.level !== index) {
|
|
1833
1836
|
throw new Error("ModelProcessingManifest LOD levels must be contiguous and begin with LOD0.");
|
|
@@ -1873,8 +1876,8 @@ function createModelProcessingManifest(input) {
|
|
|
1873
1876
|
`ModelProcessingManifest.lods[${index}].resource`
|
|
1874
1877
|
);
|
|
1875
1878
|
}
|
|
1876
|
-
const collision = createCollisionRecord(
|
|
1877
|
-
const collisionPolicy = createCollisionPolicyEvidence(
|
|
1879
|
+
const collision = createCollisionRecord(record2.collision);
|
|
1880
|
+
const collisionPolicy = createCollisionPolicyEvidence(record2.collisionPolicy, collision);
|
|
1878
1881
|
if (technicalProfile.hasCollision !== (collision.kind !== "none")) {
|
|
1879
1882
|
throw new Error("ModelProcessingManifest technicalProfile.hasCollision must match collision evidence.");
|
|
1880
1883
|
}
|
|
@@ -1889,10 +1892,10 @@ function createModelProcessingManifest(input) {
|
|
|
1889
1892
|
"ModelProcessingManifest.collision.resource"
|
|
1890
1893
|
);
|
|
1891
1894
|
}
|
|
1892
|
-
if (!Array.isArray(
|
|
1895
|
+
if (!Array.isArray(record2.children) || record2.children.length > 256) {
|
|
1893
1896
|
throw new Error("ModelProcessingManifest.children must be a bounded array.");
|
|
1894
1897
|
}
|
|
1895
|
-
const children =
|
|
1898
|
+
const children = record2.children.map((child, index) => createAssemblyChild(child, index, resolutionId, candidateId));
|
|
1896
1899
|
if (kind === "leaf" && children.length !== 0) {
|
|
1897
1900
|
throw new Error("ModelProcessingManifest leaf models must not contain assembly children.");
|
|
1898
1901
|
}
|
|
@@ -1906,17 +1909,17 @@ function createModelProcessingManifest(input) {
|
|
|
1906
1909
|
throw new Error("ModelProcessingManifest assembly instanceId values must be unique.");
|
|
1907
1910
|
}
|
|
1908
1911
|
assertAssemblyHierarchy(children);
|
|
1909
|
-
const converter = createConverterEvidence(
|
|
1912
|
+
const converter = createConverterEvidence(record2.converter);
|
|
1910
1913
|
if (converter.targetFormat !== "glb") {
|
|
1911
1914
|
throw new Error("ModelProcessingManifest converter.targetFormat must be glb.");
|
|
1912
1915
|
}
|
|
1913
1916
|
if (converter.outputContentHash !== contentHash) {
|
|
1914
1917
|
throw new Error("ModelProcessingManifest converter outputContentHash must match contentHash.");
|
|
1915
1918
|
}
|
|
1916
|
-
if (!Array.isArray(
|
|
1919
|
+
if (!Array.isArray(record2.fidelityEvidence) || record2.fidelityEvidence.length > 100) {
|
|
1917
1920
|
throw new Error("ModelProcessingManifest.fidelityEvidence must be a bounded array.");
|
|
1918
1921
|
}
|
|
1919
|
-
const fidelityEvidence =
|
|
1922
|
+
const fidelityEvidence = record2.fidelityEvidence.map(createFidelityEvidence);
|
|
1920
1923
|
if (new Set(fidelityEvidence.map((evidence) => evidence.aspect)).size !== fidelityEvidence.length) {
|
|
1921
1924
|
throw new Error("ModelProcessingManifest fidelity evidence aspects must be unique.");
|
|
1922
1925
|
}
|
|
@@ -1930,10 +1933,10 @@ function createModelProcessingManifest(input) {
|
|
|
1930
1933
|
);
|
|
1931
1934
|
}
|
|
1932
1935
|
}
|
|
1933
|
-
const fidelityGate = createFidelityGateEvidence(
|
|
1936
|
+
const fidelityGate = createFidelityGateEvidence(record2.fidelityGate, fidelityEvidence, converter);
|
|
1934
1937
|
return deepFreeze({
|
|
1935
1938
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1936
|
-
manifestId: requiredToken(
|
|
1939
|
+
manifestId: requiredToken(record2.manifestId, "ModelProcessingManifest.manifestId"),
|
|
1937
1940
|
resolutionId,
|
|
1938
1941
|
candidateId,
|
|
1939
1942
|
kind,
|
|
@@ -1948,7 +1951,7 @@ function createModelProcessingManifest(input) {
|
|
|
1948
1951
|
converter,
|
|
1949
1952
|
fidelityEvidence,
|
|
1950
1953
|
fidelityGate,
|
|
1951
|
-
processedAt: requiredTimestamp(
|
|
1954
|
+
processedAt: requiredTimestamp(record2.processedAt, "ModelProcessingManifest.processedAt")
|
|
1952
1955
|
});
|
|
1953
1956
|
}
|
|
1954
1957
|
function modelCandidateEquivalent(left, right) {
|
|
@@ -1967,8 +1970,8 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
1967
1970
|
if (candidate.assetRef.disposition !== "proposed") {
|
|
1968
1971
|
throw new Error("ModelPromotionReceipt is only valid for a staged proposed candidate.");
|
|
1969
1972
|
}
|
|
1970
|
-
const
|
|
1971
|
-
assertAllowedKeys(
|
|
1973
|
+
const record2 = assertRecord(input, "ModelPromotionReceipt");
|
|
1974
|
+
assertAllowedKeys(record2, [
|
|
1972
1975
|
"contractVersion",
|
|
1973
1976
|
"promotionId",
|
|
1974
1977
|
"resolutionId",
|
|
@@ -1982,17 +1985,17 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
1982
1985
|
"promotedAt",
|
|
1983
1986
|
"publicationToken"
|
|
1984
1987
|
], "ModelPromotionReceipt");
|
|
1985
|
-
assertContractVersion(
|
|
1986
|
-
if (
|
|
1988
|
+
assertContractVersion(record2, "ModelPromotionReceipt");
|
|
1989
|
+
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) {
|
|
1987
1990
|
throw new Error("ModelPromotionReceipt must bind the exact proposal, confirmation, manifest, and closure.");
|
|
1988
1991
|
}
|
|
1989
|
-
const receiptAssetRef = createModelAssetRef(
|
|
1992
|
+
const receiptAssetRef = createModelAssetRef(record2.finalAssetRef);
|
|
1990
1993
|
if (JSON.stringify(receiptAssetRef) !== JSON.stringify(finalAssetRef)) {
|
|
1991
1994
|
throw new Error("ModelPromotionReceipt.finalAssetRef must match the completed finalAssetRef.");
|
|
1992
1995
|
}
|
|
1993
1996
|
return deepFreeze({
|
|
1994
1997
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1995
|
-
promotionId: requiredToken(
|
|
1998
|
+
promotionId: requiredToken(record2.promotionId, "ModelPromotionReceipt.promotionId"),
|
|
1996
1999
|
resolutionId,
|
|
1997
2000
|
candidateId: candidate.candidateId,
|
|
1998
2001
|
proposalId: candidate.assetRef.proposalId,
|
|
@@ -2001,9 +2004,9 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
2001
2004
|
processingContentHash: candidate.processingManifest.contentHash,
|
|
2002
2005
|
closureHash: candidate.processingManifest.closureHash,
|
|
2003
2006
|
finalAssetRef: receiptAssetRef,
|
|
2004
|
-
promotedAt: requiredTimestamp(
|
|
2007
|
+
promotedAt: requiredTimestamp(record2.promotedAt, "ModelPromotionReceipt.promotedAt"),
|
|
2005
2008
|
publicationToken: requiredAttestationToken(
|
|
2006
|
-
|
|
2009
|
+
record2.publicationToken,
|
|
2007
2010
|
"ModelPromotionReceipt.publicationToken"
|
|
2008
2011
|
)
|
|
2009
2012
|
});
|
|
@@ -2064,8 +2067,8 @@ function isModelResolutionState(value) {
|
|
|
2064
2067
|
return typeof value === "string" && MODEL_RESOLUTION_STATES.includes(value);
|
|
2065
2068
|
}
|
|
2066
2069
|
function createModelResolution(input) {
|
|
2067
|
-
const
|
|
2068
|
-
assertAllowedKeys(
|
|
2070
|
+
const record2 = assertRecord(input, "ModelResolution");
|
|
2071
|
+
assertAllowedKeys(record2, [
|
|
2069
2072
|
"contractVersion",
|
|
2070
2073
|
"resolutionId",
|
|
2071
2074
|
"request",
|
|
@@ -2081,19 +2084,19 @@ function createModelResolution(input) {
|
|
|
2081
2084
|
"createdAt",
|
|
2082
2085
|
"updatedAt"
|
|
2083
2086
|
], "ModelResolution");
|
|
2084
|
-
assertContractVersion(
|
|
2085
|
-
const resolutionId = requiredModelPathSegment(
|
|
2086
|
-
const request = createModelRequestSpec(
|
|
2087
|
-
const createdAt = requiredTimestamp(
|
|
2088
|
-
const updatedAt = requiredTimestamp(
|
|
2087
|
+
assertContractVersion(record2, "ModelResolution");
|
|
2088
|
+
const resolutionId = requiredModelPathSegment(record2.resolutionId, "ModelResolution.resolutionId");
|
|
2089
|
+
const request = createModelRequestSpec(record2.request);
|
|
2090
|
+
const createdAt = requiredTimestamp(record2.createdAt, "ModelResolution.createdAt");
|
|
2091
|
+
const updatedAt = requiredTimestamp(record2.updatedAt, "ModelResolution.updatedAt");
|
|
2089
2092
|
if (Date.parse(updatedAt) < Date.parse(createdAt)) {
|
|
2090
2093
|
throw new Error("ModelResolution.updatedAt must not precede createdAt.");
|
|
2091
2094
|
}
|
|
2092
|
-
const state = requiredEnum(
|
|
2093
|
-
if (!Array.isArray(
|
|
2095
|
+
const state = requiredEnum(record2.state, MODEL_RESOLUTION_STATES, "ModelResolution.state");
|
|
2096
|
+
if (!Array.isArray(record2.candidates) || record2.candidates.length > MAX_CANDIDATES) {
|
|
2094
2097
|
throw new Error(`ModelResolution.candidates must contain at most ${MAX_CANDIDATES} items.`);
|
|
2095
2098
|
}
|
|
2096
|
-
const candidates =
|
|
2099
|
+
const candidates = record2.candidates.map(createModelCandidate);
|
|
2097
2100
|
if (new Set(candidates.map((candidate) => candidate.candidateId)).size !== candidates.length) {
|
|
2098
2101
|
throw new Error("ModelResolution candidateId values must be unique.");
|
|
2099
2102
|
}
|
|
@@ -2127,8 +2130,8 @@ function createModelResolution(input) {
|
|
|
2127
2130
|
}
|
|
2128
2131
|
}
|
|
2129
2132
|
let bestCandidate;
|
|
2130
|
-
if (
|
|
2131
|
-
const requestedBest = createModelCandidate(
|
|
2133
|
+
if (record2.bestCandidate !== void 0) {
|
|
2134
|
+
const requestedBest = createModelCandidate(record2.bestCandidate);
|
|
2132
2135
|
bestCandidate = candidates.find((candidate) => candidate.candidateId === requestedBest.candidateId);
|
|
2133
2136
|
if (bestCandidate === void 0 || !modelCandidateEquivalent(bestCandidate, requestedBest)) {
|
|
2134
2137
|
throw new Error("ModelResolution.bestCandidate must exactly match a candidate.");
|
|
@@ -2143,11 +2146,11 @@ function createModelResolution(input) {
|
|
|
2143
2146
|
);
|
|
2144
2147
|
}
|
|
2145
2148
|
let confirmation;
|
|
2146
|
-
if (
|
|
2149
|
+
if (record2.confirmation !== void 0) {
|
|
2147
2150
|
if (bestCandidate === void 0) {
|
|
2148
2151
|
throw new Error("ModelResolution confirmation requires bestCandidate.");
|
|
2149
2152
|
}
|
|
2150
|
-
confirmation = createModelCandidateConfirmation(
|
|
2153
|
+
confirmation = createModelCandidateConfirmation(record2.confirmation, bestCandidate, resolutionId);
|
|
2151
2154
|
if (Date.parse(confirmation.confirmedAt) < Date.parse(createdAt) || Date.parse(confirmation.confirmedAt) > Date.parse(updatedAt)) {
|
|
2152
2155
|
throw new Error("ModelResolution confirmation.confirmedAt must fall between createdAt and updatedAt.");
|
|
2153
2156
|
}
|
|
@@ -2161,15 +2164,15 @@ function createModelResolution(input) {
|
|
|
2161
2164
|
if (confirmation !== void 0 && !["promoting", "completed", "failed", "cancelled"].includes(state)) {
|
|
2162
2165
|
throw new Error(`ModelResolution confirmation evidence is not allowed in ${state} state.`);
|
|
2163
2166
|
}
|
|
2164
|
-
const refinementQuestions =
|
|
2165
|
-
const stateReasonCode =
|
|
2167
|
+
const refinementQuestions = record2.refinementQuestions === void 0 ? [] : stringList(record2.refinementQuestions, "ModelResolution.refinementQuestions", { maxItems: 3 });
|
|
2168
|
+
const stateReasonCode = record2.stateReasonCode === void 0 ? void 0 : requiredToken(record2.stateReasonCode, "ModelResolution.stateReasonCode");
|
|
2166
2169
|
if (["failed", "cancelled", "unresolved"].includes(state) && stateReasonCode === void 0) {
|
|
2167
2170
|
throw new Error(`ModelResolution ${state} state requires stateReasonCode.`);
|
|
2168
2171
|
}
|
|
2169
2172
|
if (state === "awaiting-confirmation" && bestCandidate?.match.assurance === "low" && refinementQuestions.length === 0) {
|
|
2170
2173
|
throw new Error("ModelResolution low-assurance bestCandidate requires refinementQuestions.");
|
|
2171
2174
|
}
|
|
2172
|
-
const finalAssetRef =
|
|
2175
|
+
const finalAssetRef = record2.finalAssetRef === void 0 ? void 0 : createModelAssetRef(record2.finalAssetRef);
|
|
2173
2176
|
if (state === "completed" && finalAssetRef === void 0) {
|
|
2174
2177
|
throw new Error("ModelResolution completed state requires finalAssetRef.");
|
|
2175
2178
|
}
|
|
@@ -2180,12 +2183,12 @@ function createModelResolution(input) {
|
|
|
2180
2183
|
throw new Error("ModelResolution finalAssetRef must match bestCandidate kind, hash, and immutable identity.");
|
|
2181
2184
|
}
|
|
2182
2185
|
let promotionReceipt;
|
|
2183
|
-
if (
|
|
2186
|
+
if (record2.promotionReceipt !== void 0) {
|
|
2184
2187
|
if (state !== "completed" || bestCandidate === void 0 || confirmation === void 0 || finalAssetRef === void 0) {
|
|
2185
2188
|
throw new Error("ModelResolution promotionReceipt is only allowed for a completed staged promotion.");
|
|
2186
2189
|
}
|
|
2187
2190
|
promotionReceipt = createModelPromotionReceipt(
|
|
2188
|
-
|
|
2191
|
+
record2.promotionReceipt,
|
|
2189
2192
|
resolutionId,
|
|
2190
2193
|
bestCandidate,
|
|
2191
2194
|
confirmation,
|
|
@@ -2205,7 +2208,7 @@ function createModelResolution(input) {
|
|
|
2205
2208
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
2206
2209
|
resolutionId,
|
|
2207
2210
|
request,
|
|
2208
|
-
attempts: requiredInteger(
|
|
2211
|
+
attempts: requiredInteger(record2.attempts, "ModelResolution.attempts", 1, MAX_ATTEMPTS),
|
|
2209
2212
|
state,
|
|
2210
2213
|
candidates,
|
|
2211
2214
|
...bestCandidate === void 0 ? {} : { bestCandidate },
|
|
@@ -2227,15 +2230,15 @@ function createGeneratorSeed(value, fieldName) {
|
|
|
2227
2230
|
return seed;
|
|
2228
2231
|
}
|
|
2229
2232
|
function createModelGeneratorRequest(input) {
|
|
2230
|
-
const
|
|
2231
|
-
assertAllowedKeys(
|
|
2233
|
+
const record2 = assertRecord(input, "ModelGeneratorRequest");
|
|
2234
|
+
assertAllowedKeys(record2, [
|
|
2232
2235
|
"generationId",
|
|
2233
2236
|
"request",
|
|
2234
2237
|
"budgets",
|
|
2235
2238
|
"seed",
|
|
2236
2239
|
"deadline"
|
|
2237
2240
|
], "ModelGeneratorRequest");
|
|
2238
|
-
const budgets = assertRecord(
|
|
2241
|
+
const budgets = assertRecord(record2.budgets, "ModelGeneratorRequest.budgets");
|
|
2239
2242
|
assertAllowedKeys(budgets, [
|
|
2240
2243
|
"maxDurationMs",
|
|
2241
2244
|
"maxTriangles",
|
|
@@ -2243,7 +2246,7 @@ function createModelGeneratorRequest(input) {
|
|
|
2243
2246
|
"maxTextureBytes",
|
|
2244
2247
|
"maxTextureDimensionPx"
|
|
2245
2248
|
], "ModelGeneratorRequest.budgets");
|
|
2246
|
-
const seed = createGeneratorSeed(
|
|
2249
|
+
const seed = createGeneratorSeed(record2.seed, "ModelGeneratorRequest.seed");
|
|
2247
2250
|
const maxBytes = requiredInteger(
|
|
2248
2251
|
budgets.maxBytes,
|
|
2249
2252
|
"ModelGeneratorRequest budget maxBytes",
|
|
@@ -2260,8 +2263,8 @@ function createModelGeneratorRequest(input) {
|
|
|
2260
2263
|
throw new Error("ModelGeneratorRequest maxTextureBytes must not exceed maxBytes.");
|
|
2261
2264
|
}
|
|
2262
2265
|
return deepFreeze({
|
|
2263
|
-
generationId: requiredModelPathSegment(
|
|
2264
|
-
request: createModelRequestSpec(
|
|
2266
|
+
generationId: requiredModelPathSegment(record2.generationId, "ModelGeneratorRequest.generationId"),
|
|
2267
|
+
request: createModelRequestSpec(record2.request),
|
|
2265
2268
|
budgets: {
|
|
2266
2269
|
maxDurationMs: requiredInteger(budgets.maxDurationMs, "ModelGeneratorRequest budget maxDurationMs", 1, 3e5),
|
|
2267
2270
|
maxTriangles: requiredInteger(budgets.maxTriangles, "ModelGeneratorRequest budget maxTriangles", 1, 1e7),
|
|
@@ -2270,7 +2273,7 @@ function createModelGeneratorRequest(input) {
|
|
|
2270
2273
|
maxTextureDimensionPx: requiredInteger(budgets.maxTextureDimensionPx, "ModelGeneratorRequest budget maxTextureDimensionPx", 1, 32768)
|
|
2271
2274
|
},
|
|
2272
2275
|
seed,
|
|
2273
|
-
deadline: requiredTimestamp(
|
|
2276
|
+
deadline: requiredTimestamp(record2.deadline, "ModelGeneratorRequest.deadline")
|
|
2274
2277
|
});
|
|
2275
2278
|
}
|
|
2276
2279
|
var MODEL_GENERATOR_ARTIFACT_ROLES = [
|
|
@@ -2308,8 +2311,8 @@ function assertGeneratorRetryability(reasonCode2, retryable) {
|
|
|
2308
2311
|
}
|
|
2309
2312
|
function createModelGeneratorArtifact(value, index) {
|
|
2310
2313
|
const fieldName = `ModelGeneratorResult.sourceBundle.artifacts[${index}]`;
|
|
2311
|
-
const
|
|
2312
|
-
assertAllowedKeys(
|
|
2314
|
+
const record2 = assertRecord(value, fieldName);
|
|
2315
|
+
assertAllowedKeys(record2, [
|
|
2313
2316
|
"uri",
|
|
2314
2317
|
"byteLength",
|
|
2315
2318
|
"sha256",
|
|
@@ -2319,12 +2322,12 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2319
2322
|
"maxTextureDimensionPx"
|
|
2320
2323
|
], fieldName);
|
|
2321
2324
|
const resource = createModelResourceRef({
|
|
2322
|
-
uri:
|
|
2323
|
-
byteLength:
|
|
2324
|
-
sha256:
|
|
2325
|
-
contentType:
|
|
2325
|
+
uri: record2.uri,
|
|
2326
|
+
byteLength: record2.byteLength,
|
|
2327
|
+
sha256: record2.sha256,
|
|
2328
|
+
contentType: record2.contentType
|
|
2326
2329
|
});
|
|
2327
|
-
const role = requiredEnum(
|
|
2330
|
+
const role = requiredEnum(record2.role, MODEL_GENERATOR_ARTIFACT_ROLES, `${fieldName}.role`);
|
|
2328
2331
|
const compatibleContentTypes = {
|
|
2329
2332
|
"model-entrypoint": ["model/gltf-binary", "model/gltf+json"],
|
|
2330
2333
|
"model-dependency": ["model/gltf-binary", "model/gltf+json"],
|
|
@@ -2336,13 +2339,13 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2336
2339
|
throw new Error(`${fieldName}.contentType is not compatible with artifact role ${role}.`);
|
|
2337
2340
|
}
|
|
2338
2341
|
const textureByteLength = requiredInteger(
|
|
2339
|
-
|
|
2342
|
+
record2.textureByteLength,
|
|
2340
2343
|
`${fieldName}.textureByteLength`,
|
|
2341
2344
|
0,
|
|
2342
2345
|
resource.byteLength
|
|
2343
2346
|
);
|
|
2344
2347
|
const maxTextureDimensionPx = requiredInteger(
|
|
2345
|
-
|
|
2348
|
+
record2.maxTextureDimensionPx,
|
|
2346
2349
|
`${fieldName}.maxTextureDimensionPx`,
|
|
2347
2350
|
0,
|
|
2348
2351
|
32768
|
|
@@ -2359,8 +2362,8 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2359
2362
|
return { ...resource, role, textureByteLength, maxTextureDimensionPx };
|
|
2360
2363
|
}
|
|
2361
2364
|
function createModelGeneratorUsage(value) {
|
|
2362
|
-
const
|
|
2363
|
-
assertAllowedKeys(
|
|
2365
|
+
const record2 = assertRecord(value, "ModelGeneratorResult.sourceBundle.usage");
|
|
2366
|
+
assertAllowedKeys(record2, [
|
|
2364
2367
|
"durationMs",
|
|
2365
2368
|
"triangleCount",
|
|
2366
2369
|
"byteLength",
|
|
@@ -2368,13 +2371,13 @@ function createModelGeneratorUsage(value) {
|
|
|
2368
2371
|
"maxTextureDimensionPx"
|
|
2369
2372
|
], "ModelGeneratorResult.sourceBundle.usage");
|
|
2370
2373
|
const textureByteLength = requiredInteger(
|
|
2371
|
-
|
|
2374
|
+
record2.textureByteLength,
|
|
2372
2375
|
"ModelGeneratorResult.sourceBundle.usage.textureByteLength",
|
|
2373
2376
|
0,
|
|
2374
2377
|
Number.MAX_SAFE_INTEGER
|
|
2375
2378
|
);
|
|
2376
2379
|
const maxTextureDimensionPx = requiredInteger(
|
|
2377
|
-
|
|
2380
|
+
record2.maxTextureDimensionPx,
|
|
2378
2381
|
"ModelGeneratorResult.sourceBundle.usage.maxTextureDimensionPx",
|
|
2379
2382
|
0,
|
|
2380
2383
|
32768
|
|
@@ -2383,30 +2386,30 @@ function createModelGeneratorUsage(value) {
|
|
|
2383
2386
|
throw new Error("ModelGeneratorResult texture usage bytes and dimensions must both be zero or both positive.");
|
|
2384
2387
|
}
|
|
2385
2388
|
return {
|
|
2386
|
-
durationMs: requiredInteger(
|
|
2387
|
-
triangleCount: requiredInteger(
|
|
2388
|
-
byteLength: requiredInteger(
|
|
2389
|
+
durationMs: requiredInteger(record2.durationMs, "ModelGeneratorResult.sourceBundle.usage.durationMs", 0, 3e5),
|
|
2390
|
+
triangleCount: requiredInteger(record2.triangleCount, "ModelGeneratorResult.sourceBundle.usage.triangleCount", 1, 1e7),
|
|
2391
|
+
byteLength: requiredInteger(record2.byteLength, "ModelGeneratorResult.sourceBundle.usage.byteLength", 1, Number.MAX_SAFE_INTEGER),
|
|
2389
2392
|
textureByteLength,
|
|
2390
2393
|
maxTextureDimensionPx
|
|
2391
2394
|
};
|
|
2392
2395
|
}
|
|
2393
|
-
function createGeneratorTerminalEvidence(
|
|
2394
|
-
const generationId = requiredModelPathSegment(
|
|
2396
|
+
function createGeneratorTerminalEvidence(record2, expected) {
|
|
2397
|
+
const generationId = requiredModelPathSegment(record2.generationId, "ModelGeneratorResult.generationId");
|
|
2395
2398
|
if (generationId !== expected.generationId) {
|
|
2396
2399
|
throw new Error("ModelGeneratorResult.generationId must match the generator request.");
|
|
2397
2400
|
}
|
|
2398
|
-
const diagnosticId =
|
|
2401
|
+
const diagnosticId = record2.diagnosticId === void 0 ? void 0 : requiredToken(record2.diagnosticId, "ModelGeneratorResult.diagnosticId");
|
|
2399
2402
|
return {
|
|
2400
2403
|
generationId,
|
|
2401
|
-
retryable: requiredBoolean(
|
|
2402
|
-
occurredAt: requiredTimestamp(
|
|
2404
|
+
retryable: requiredBoolean(record2.retryable, "ModelGeneratorResult.retryable"),
|
|
2405
|
+
occurredAt: requiredTimestamp(record2.occurredAt, "ModelGeneratorResult.occurredAt"),
|
|
2403
2406
|
...diagnosticId === void 0 ? {} : { diagnosticId }
|
|
2404
2407
|
};
|
|
2405
2408
|
}
|
|
2406
2409
|
function createModelGeneratorResult(input, expectedRequest) {
|
|
2407
2410
|
const expected = createModelGeneratorRequest(expectedRequest);
|
|
2408
|
-
const
|
|
2409
|
-
const status = requiredEnum(
|
|
2411
|
+
const record2 = assertRecord(input, "ModelGeneratorResult");
|
|
2412
|
+
const status = requiredEnum(record2.status, [
|
|
2410
2413
|
"disabled",
|
|
2411
2414
|
"generated",
|
|
2412
2415
|
"unavailable",
|
|
@@ -2414,13 +2417,13 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2414
2417
|
"cancelled",
|
|
2415
2418
|
"budget-exceeded"
|
|
2416
2419
|
], "ModelGeneratorResult.status");
|
|
2417
|
-
const generationId = requiredModelPathSegment(
|
|
2420
|
+
const generationId = requiredModelPathSegment(record2.generationId, "ModelGeneratorResult.generationId");
|
|
2418
2421
|
if (generationId !== expected.generationId) {
|
|
2419
2422
|
throw new Error("ModelGeneratorResult.generationId must match the generator request.");
|
|
2420
2423
|
}
|
|
2421
2424
|
if (status === "disabled") {
|
|
2422
|
-
assertAllowedKeys(
|
|
2423
|
-
if (
|
|
2425
|
+
assertAllowedKeys(record2, ["status", "generationId", "reasonCode"], "ModelGeneratorResult");
|
|
2426
|
+
if (record2.reasonCode !== MODEL_GENERATOR_DISABLED_REASON_CODE) {
|
|
2424
2427
|
throw new Error(
|
|
2425
2428
|
`ModelGeneratorResult.reasonCode must be ${MODEL_GENERATOR_DISABLED_REASON_CODE}.`
|
|
2426
2429
|
);
|
|
@@ -2432,8 +2435,8 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2432
2435
|
});
|
|
2433
2436
|
}
|
|
2434
2437
|
if (status === "generated") {
|
|
2435
|
-
assertAllowedKeys(
|
|
2436
|
-
const sourceBundle = assertRecord(
|
|
2438
|
+
assertAllowedKeys(record2, ["status", "generationId", "sourceBundle"], "ModelGeneratorResult");
|
|
2439
|
+
const sourceBundle = assertRecord(record2.sourceBundle, "ModelGeneratorResult.sourceBundle");
|
|
2437
2440
|
assertAllowedKeys(sourceBundle, [
|
|
2438
2441
|
"bundleId",
|
|
2439
2442
|
"generationId",
|
|
@@ -2533,14 +2536,14 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2533
2536
|
"diagnosticId"
|
|
2534
2537
|
];
|
|
2535
2538
|
if (status === "budget-exceeded") {
|
|
2536
|
-
assertAllowedKeys(
|
|
2537
|
-
if (
|
|
2539
|
+
assertAllowedKeys(record2, [...terminalAllowedKeys, "violations"], "ModelGeneratorResult");
|
|
2540
|
+
if (record2.reasonCode !== "generator-budget-exceeded" || record2.retryable !== false) {
|
|
2538
2541
|
throw new Error("ModelGeneratorResult budget-exceeded must use its fixed reasonCode and retryable false.");
|
|
2539
2542
|
}
|
|
2540
|
-
if (!Array.isArray(
|
|
2543
|
+
if (!Array.isArray(record2.violations) || record2.violations.length < 1 || record2.violations.length > 5) {
|
|
2541
2544
|
throw new Error("ModelGeneratorResult budget violations must contain one to five entries.");
|
|
2542
2545
|
}
|
|
2543
|
-
const violations =
|
|
2546
|
+
const violations = record2.violations.map((value, index) => {
|
|
2544
2547
|
const fieldName = `ModelGeneratorResult.violations[${index}]`;
|
|
2545
2548
|
const violation = assertRecord(value, fieldName);
|
|
2546
2549
|
assertAllowedKeys(violation, ["budget", "limit", "observed"], fieldName);
|
|
@@ -2555,7 +2558,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2555
2558
|
if (new Set(violations.map((violation) => violation.budget)).size !== violations.length) {
|
|
2556
2559
|
throw new Error("ModelGeneratorResult budget violation names must be unique.");
|
|
2557
2560
|
}
|
|
2558
|
-
const terminal2 = createGeneratorTerminalEvidence(
|
|
2561
|
+
const terminal2 = createGeneratorTerminalEvidence(record2, expected);
|
|
2559
2562
|
return deepFreeze({
|
|
2560
2563
|
status,
|
|
2561
2564
|
...terminal2,
|
|
@@ -2564,10 +2567,10 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2564
2567
|
violations
|
|
2565
2568
|
});
|
|
2566
2569
|
}
|
|
2567
|
-
assertAllowedKeys(
|
|
2568
|
-
const terminal = createGeneratorTerminalEvidence(
|
|
2570
|
+
assertAllowedKeys(record2, terminalAllowedKeys, "ModelGeneratorResult");
|
|
2571
|
+
const terminal = createGeneratorTerminalEvidence(record2, expected);
|
|
2569
2572
|
if (status === "unavailable") {
|
|
2570
|
-
const reasonCode3 = requiredEnum(
|
|
2573
|
+
const reasonCode3 = requiredEnum(record2.reasonCode, [
|
|
2571
2574
|
"capacity-unavailable",
|
|
2572
2575
|
"provider-unavailable",
|
|
2573
2576
|
"authentication-required",
|
|
@@ -2578,7 +2581,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2578
2581
|
return deepFreeze({ status, ...terminal, reasonCode: reasonCode3 });
|
|
2579
2582
|
}
|
|
2580
2583
|
if (status === "failed") {
|
|
2581
|
-
const reasonCode3 = requiredEnum(
|
|
2584
|
+
const reasonCode3 = requiredEnum(record2.reasonCode, [
|
|
2582
2585
|
"provider-failed",
|
|
2583
2586
|
"invalid-generator-output",
|
|
2584
2587
|
"internal-failure"
|
|
@@ -2586,7 +2589,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2586
2589
|
assertGeneratorRetryability(reasonCode3, terminal.retryable);
|
|
2587
2590
|
return deepFreeze({ status, ...terminal, reasonCode: reasonCode3 });
|
|
2588
2591
|
}
|
|
2589
|
-
const reasonCode2 = requiredEnum(
|
|
2592
|
+
const reasonCode2 = requiredEnum(record2.reasonCode, [
|
|
2590
2593
|
"caller-aborted",
|
|
2591
2594
|
"deadline-exceeded",
|
|
2592
2595
|
"superseded",
|
|
@@ -2935,9 +2938,9 @@ function asRecord(value, fieldName) {
|
|
|
2935
2938
|
}
|
|
2936
2939
|
return value;
|
|
2937
2940
|
}
|
|
2938
|
-
function exactKeys(
|
|
2941
|
+
function exactKeys(record2, allowed, fieldName) {
|
|
2939
2942
|
const set = new Set(allowed);
|
|
2940
|
-
const unsupported = Object.keys(
|
|
2943
|
+
const unsupported = Object.keys(record2).find((key) => !set.has(key));
|
|
2941
2944
|
if (unsupported !== void 0) throw new Error(`${fieldName} contains an unsupported field.`);
|
|
2942
2945
|
}
|
|
2943
2946
|
function asDenseArray(value, fieldName, minimum, maximum) {
|
|
@@ -2968,9 +2971,9 @@ function assertDataOnlyTree(value, fieldName, seen = /* @__PURE__ */ new WeakSet
|
|
|
2968
2971
|
assertDataOnlyTree(descriptor.value, `${fieldName}[${index}]`, seen, depth + 1);
|
|
2969
2972
|
}
|
|
2970
2973
|
} else {
|
|
2971
|
-
const
|
|
2972
|
-
for (const key of Object.keys(
|
|
2973
|
-
const descriptor = Object.getOwnPropertyDescriptor(
|
|
2974
|
+
const record2 = asRecord(value, fieldName);
|
|
2975
|
+
for (const key of Object.keys(record2)) {
|
|
2976
|
+
const descriptor = Object.getOwnPropertyDescriptor(record2, key);
|
|
2974
2977
|
assertDataOnlyTree(descriptor.value, `${fieldName}.${key}`, seen, depth + 1);
|
|
2975
2978
|
}
|
|
2976
2979
|
}
|
|
@@ -3055,10 +3058,10 @@ function requireVector(value, fieldName) {
|
|
|
3055
3058
|
];
|
|
3056
3059
|
}
|
|
3057
3060
|
function createBounds2(value, fieldName) {
|
|
3058
|
-
const
|
|
3059
|
-
exactKeys(
|
|
3060
|
-
const min = requireVector(
|
|
3061
|
-
const max = requireVector(
|
|
3061
|
+
const record2 = asRecord(value, fieldName);
|
|
3062
|
+
exactKeys(record2, ["min", "max"], fieldName);
|
|
3063
|
+
const min = requireVector(record2.min, `${fieldName}.min`);
|
|
3064
|
+
const max = requireVector(record2.max, `${fieldName}.max`);
|
|
3062
3065
|
if (min.some((coordinate, index) => coordinate >= max[index])) throw new Error(`${fieldName} minimum values must be below maximum values.`);
|
|
3063
3066
|
return { min, max };
|
|
3064
3067
|
}
|
|
@@ -3066,18 +3069,18 @@ function approximatelyEqual(left, right) {
|
|
|
3066
3069
|
return Math.abs(left - right) <= Math.max(1e-6, Math.abs(left) * 1e-6, Math.abs(right) * 1e-6);
|
|
3067
3070
|
}
|
|
3068
3071
|
function createHashAttestation(value, expectedDomain, expectedDigest, fieldName) {
|
|
3069
|
-
const
|
|
3070
|
-
exactKeys(
|
|
3071
|
-
if (
|
|
3072
|
+
const record2 = asRecord(value, fieldName);
|
|
3073
|
+
exactKeys(record2, ["algorithm", "domain", "digest", "attestationToken"], fieldName);
|
|
3074
|
+
if (record2.algorithm !== "sha256" || record2.domain !== expectedDomain) {
|
|
3072
3075
|
throw new Error(`${fieldName} must use the governed SHA-256 domain.`);
|
|
3073
3076
|
}
|
|
3074
|
-
const digest = requireSha256(
|
|
3077
|
+
const digest = requireSha256(record2.digest, `${fieldName}.digest`);
|
|
3075
3078
|
if (digest !== expectedDigest) throw new Error(`${fieldName}.digest must match its bound contract hash.`);
|
|
3076
3079
|
return {
|
|
3077
3080
|
algorithm: "sha256",
|
|
3078
3081
|
domain: expectedDomain,
|
|
3079
3082
|
digest,
|
|
3080
|
-
attestationToken: requireAttestation(
|
|
3083
|
+
attestationToken: requireAttestation(record2.attestationToken, `${fieldName}.attestationToken`)
|
|
3081
3084
|
};
|
|
3082
3085
|
}
|
|
3083
3086
|
function canonicalizePvoxHashProjectionV1(value) {
|
|
@@ -3134,19 +3137,19 @@ function assertFloorCentredBounds(bounds, fieldName) {
|
|
|
3134
3137
|
}
|
|
3135
3138
|
}
|
|
3136
3139
|
function createDimensions2(value, fieldName) {
|
|
3137
|
-
const
|
|
3138
|
-
exactKeys(
|
|
3140
|
+
const record2 = asRecord(value, fieldName);
|
|
3141
|
+
exactKeys(record2, ["width", "height", "depth"], fieldName);
|
|
3139
3142
|
return {
|
|
3140
|
-
width: requireNumber(
|
|
3141
|
-
height: requireNumber(
|
|
3142
|
-
depth: requireNumber(
|
|
3143
|
+
width: requireNumber(record2.width, `${fieldName}.width`, Number.EPSILON, 1e9),
|
|
3144
|
+
height: requireNumber(record2.height, `${fieldName}.height`, Number.EPSILON, 1e9),
|
|
3145
|
+
depth: requireNumber(record2.depth, `${fieldName}.depth`, Number.EPSILON, 1e9)
|
|
3143
3146
|
};
|
|
3144
3147
|
}
|
|
3145
3148
|
function assertCoordinateSystem2(value, fieldName) {
|
|
3146
|
-
const
|
|
3147
|
-
exactKeys(
|
|
3149
|
+
const record2 = asRecord(value, fieldName);
|
|
3150
|
+
exactKeys(record2, ["unit", "upAxis", "forwardAxis", "origin", "outwardFaceWinding"], fieldName);
|
|
3148
3151
|
for (const [key, expected] of Object.entries(CANONICAL_MODEL_COORDINATE_SYSTEM)) {
|
|
3149
|
-
if (
|
|
3152
|
+
if (record2[key] !== expected) throw new Error(`${fieldName} must use the canonical model coordinate system.`);
|
|
3150
3153
|
}
|
|
3151
3154
|
}
|
|
3152
3155
|
function parsePvoxResourceScope(uri) {
|
|
@@ -3171,20 +3174,20 @@ function parsePvoxResourceScope(uri) {
|
|
|
3171
3174
|
throw new Error("PVOX artifact URI must be an authenticated canonical PVOX MCP resource.");
|
|
3172
3175
|
}
|
|
3173
3176
|
function createResource(value) {
|
|
3174
|
-
const
|
|
3175
|
-
exactKeys(
|
|
3176
|
-
const uri = requireString(
|
|
3177
|
+
const record2 = asRecord(value, "PvoxAssetManifestV1.artifact");
|
|
3178
|
+
exactKeys(record2, ["uri", "byteLength", "sha256", "contentType", "authenticated"], "PvoxAssetManifestV1.artifact");
|
|
3179
|
+
const uri = requireString(record2.uri, "PvoxAssetManifestV1.artifact.uri", 512);
|
|
3177
3180
|
if (uri.includes("%") || uri.includes("?") || uri.includes("#") || uri.includes("..") || DIRECT_URL.test(uri)) {
|
|
3178
3181
|
throw new Error("PvoxAssetManifestV1.artifact.uri must be an authenticated canonical MCP resource.");
|
|
3179
3182
|
}
|
|
3180
3183
|
const scope = parsePvoxResourceScope(uri);
|
|
3181
|
-
const sha2562 = requireSha256(
|
|
3184
|
+
const sha2562 = requireSha256(record2.sha256, "PvoxAssetManifestV1.artifact.sha256");
|
|
3182
3185
|
if (scope.sha256 !== sha2562) throw new Error("PvoxAssetManifestV1.artifact URI hash must match sha256.");
|
|
3183
|
-
if (
|
|
3184
|
-
if (
|
|
3186
|
+
if (record2.contentType !== PVOX_CONTENT_TYPE) throw new Error("PvoxAssetManifestV1.artifact.contentType must be the PVOX content type.");
|
|
3187
|
+
if (record2.authenticated !== true) throw new Error("PvoxAssetManifestV1.artifact must be authenticated.");
|
|
3185
3188
|
return {
|
|
3186
3189
|
uri,
|
|
3187
|
-
byteLength: requireInteger(
|
|
3190
|
+
byteLength: requireInteger(record2.byteLength, "PvoxAssetManifestV1.artifact.byteLength", 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes),
|
|
3188
3191
|
sha256: sha2562,
|
|
3189
3192
|
contentType: PVOX_CONTENT_TYPE,
|
|
3190
3193
|
authenticated: true
|
|
@@ -3192,15 +3195,15 @@ function createResource(value) {
|
|
|
3192
3195
|
}
|
|
3193
3196
|
function createPage(value, index) {
|
|
3194
3197
|
const fieldName = `PvoxAssetManifestV1.pages[${index}]`;
|
|
3195
|
-
const
|
|
3196
|
-
const pageKind = requireEnum(
|
|
3197
|
-
exactKeys(
|
|
3198
|
-
const byteLength = requireInteger(
|
|
3199
|
-
const decodedByteLength = requireInteger(
|
|
3198
|
+
const record2 = asRecord(value, fieldName);
|
|
3199
|
+
const pageKind = requireEnum(record2.pageKind, PVOX_PAGE_KINDS, `${fieldName}.pageKind`);
|
|
3200
|
+
exactKeys(record2, ["pageIndex", "pageKind", "byteOffset", "byteLength", "decodedByteLength", "sha256", "spatialKey"], fieldName);
|
|
3201
|
+
const byteLength = requireInteger(record2.byteLength, `${fieldName}.byteLength`, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES);
|
|
3202
|
+
const decodedByteLength = requireInteger(record2.decodedByteLength, `${fieldName}.decodedByteLength`, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES);
|
|
3200
3203
|
if (decodedByteLength !== byteLength) throw new Error(`${fieldName} decodedByteLength must equal byteLength because PVOX pages are never compressed.`);
|
|
3201
3204
|
let spatialKey;
|
|
3202
|
-
if (
|
|
3203
|
-
const spatialRecord = asRecord(
|
|
3205
|
+
if (record2.spatialKey !== void 0) {
|
|
3206
|
+
const spatialRecord = asRecord(record2.spatialKey, `${fieldName}.spatialKey`);
|
|
3204
3207
|
exactKeys(spatialRecord, ["lodLevel", "partitionIndex", "hierarchyDepth", "minimumMortonCode", "maximumMortonCode"], `${fieldName}.spatialKey`);
|
|
3205
3208
|
const minimumMortonCode = requireString(spatialRecord.minimumMortonCode, `${fieldName}.spatialKey.minimumMortonCode`, 16);
|
|
3206
3209
|
const maximumMortonCode = requireString(spatialRecord.maximumMortonCode, `${fieldName}.spatialKey.maximumMortonCode`, 16);
|
|
@@ -3220,19 +3223,19 @@ function createPage(value, index) {
|
|
|
3220
3223
|
throw new Error(`${fieldName} render/collision field pages require spatialKey; metadata/LOD-structure pages forbid it.`);
|
|
3221
3224
|
}
|
|
3222
3225
|
return {
|
|
3223
|
-
pageIndex: requireInteger(
|
|
3226
|
+
pageIndex: requireInteger(record2.pageIndex, `${fieldName}.pageIndex`, 0, PVOX_DEFAULT_LIMITS.maximumPages - 1),
|
|
3224
3227
|
pageKind,
|
|
3225
|
-
byteOffset: requireInteger(
|
|
3228
|
+
byteOffset: requireInteger(record2.byteOffset, `${fieldName}.byteOffset`, 0, PVOX_DEFAULT_LIMITS.maximumArtifactBytes - 1),
|
|
3226
3229
|
byteLength,
|
|
3227
3230
|
decodedByteLength,
|
|
3228
|
-
sha256: requireSha256(
|
|
3231
|
+
sha256: requireSha256(record2.sha256, `${fieldName}.sha256`),
|
|
3229
3232
|
...spatialKey === void 0 ? {} : { spatialKey }
|
|
3230
3233
|
};
|
|
3231
3234
|
}
|
|
3232
3235
|
function createLod(value, index, pageCount) {
|
|
3233
3236
|
const fieldName = `PvoxAssetManifestV1.lods[${index}]`;
|
|
3234
|
-
const
|
|
3235
|
-
exactKeys(
|
|
3237
|
+
const record2 = asRecord(value, fieldName);
|
|
3238
|
+
exactKeys(record2, [
|
|
3236
3239
|
"level",
|
|
3237
3240
|
"firstPageIndex",
|
|
3238
3241
|
"pageCount",
|
|
@@ -3246,30 +3249,30 @@ function createLod(value, index, pageCount) {
|
|
|
3246
3249
|
"p95DeltaE2000",
|
|
3247
3250
|
"normalizedMaterialError"
|
|
3248
3251
|
], fieldName);
|
|
3249
|
-
const firstPageIndex = requireInteger(
|
|
3250
|
-
const lodPageCount = requireInteger(
|
|
3252
|
+
const firstPageIndex = requireInteger(record2.firstPageIndex, `${fieldName}.firstPageIndex`, 0, pageCount - 1);
|
|
3253
|
+
const lodPageCount = requireInteger(record2.pageCount, `${fieldName}.pageCount`, 1, pageCount);
|
|
3251
3254
|
if (firstPageIndex + lodPageCount > pageCount) throw new Error(`${fieldName} page range exceeds the PVOX page set.`);
|
|
3252
|
-
const maximumSurfaceErrorMetres = requireNumber(
|
|
3253
|
-
const p99SurfaceErrorMetres = requireNumber(
|
|
3255
|
+
const maximumSurfaceErrorMetres = requireNumber(record2.maximumSurfaceErrorMetres, `${fieldName}.maximumSurfaceErrorMetres`, 0, 1e6);
|
|
3256
|
+
const p99SurfaceErrorMetres = requireNumber(record2.p99SurfaceErrorMetres, `${fieldName}.p99SurfaceErrorMetres`, 0, maximumSurfaceErrorMetres);
|
|
3254
3257
|
return {
|
|
3255
|
-
level: requireInteger(
|
|
3258
|
+
level: requireInteger(record2.level, `${fieldName}.level`, 0, 3),
|
|
3256
3259
|
firstPageIndex,
|
|
3257
3260
|
pageCount: lodPageCount,
|
|
3258
|
-
brickCount: requireInteger(
|
|
3259
|
-
cellSizeMetres: requireNumber(
|
|
3261
|
+
brickCount: requireInteger(record2.brickCount, `${fieldName}.brickCount`, 1, PVOX_DEFAULT_LIMITS.maximumBricks),
|
|
3262
|
+
cellSizeMetres: requireNumber(record2.cellSizeMetres, `${fieldName}.cellSizeMetres`, Number.EPSILON, 1e6),
|
|
3260
3263
|
maximumSurfaceErrorMetres,
|
|
3261
3264
|
p99SurfaceErrorMetres,
|
|
3262
|
-
silhouetteIou: requireNumber(
|
|
3263
|
-
maximumContourDisplacementPx: requireNumber(
|
|
3264
|
-
renderedSsim: requireNumber(
|
|
3265
|
-
p95DeltaE2000: requireNumber(
|
|
3266
|
-
normalizedMaterialError: requireNumber(
|
|
3265
|
+
silhouetteIou: requireNumber(record2.silhouetteIou, `${fieldName}.silhouetteIou`, 0, 1),
|
|
3266
|
+
maximumContourDisplacementPx: requireNumber(record2.maximumContourDisplacementPx, `${fieldName}.maximumContourDisplacementPx`, 0, 1e6),
|
|
3267
|
+
renderedSsim: requireNumber(record2.renderedSsim, `${fieldName}.renderedSsim`, 0, 1),
|
|
3268
|
+
p95DeltaE2000: requireNumber(record2.p95DeltaE2000, `${fieldName}.p95DeltaE2000`, 0, 100),
|
|
3269
|
+
normalizedMaterialError: requireNumber(record2.normalizedMaterialError, `${fieldName}.normalizedMaterialError`, 0, 1)
|
|
3267
3270
|
};
|
|
3268
3271
|
}
|
|
3269
3272
|
function createSourceIngestionLimitsV2(value) {
|
|
3270
3273
|
const fieldName = "ModelRequestSpecV2.sourceIngestionLimits";
|
|
3271
|
-
const
|
|
3272
|
-
exactKeys(
|
|
3274
|
+
const record2 = asRecord(value, fieldName);
|
|
3275
|
+
exactKeys(record2, [
|
|
3273
3276
|
"maximumDownloadBytes",
|
|
3274
3277
|
"maximumExpandedBytes",
|
|
3275
3278
|
"maximumArchiveEntries",
|
|
@@ -3278,12 +3281,12 @@ function createSourceIngestionLimitsV2(value) {
|
|
|
3278
3281
|
"maximumTextureDimensionPx"
|
|
3279
3282
|
], fieldName);
|
|
3280
3283
|
const limits = {
|
|
3281
|
-
maximumDownloadBytes: requireInteger(
|
|
3282
|
-
maximumExpandedBytes: requireInteger(
|
|
3283
|
-
maximumArchiveEntries: requireInteger(
|
|
3284
|
-
maximumSourceFiles: requireInteger(
|
|
3285
|
-
maximumDecodedTextureBytes: requireInteger(
|
|
3286
|
-
maximumTextureDimensionPx: requireInteger(
|
|
3284
|
+
maximumDownloadBytes: requireInteger(record2.maximumDownloadBytes, `${fieldName}.maximumDownloadBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumDownloadBytes),
|
|
3285
|
+
maximumExpandedBytes: requireInteger(record2.maximumExpandedBytes, `${fieldName}.maximumExpandedBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumExpandedBytes),
|
|
3286
|
+
maximumArchiveEntries: requireInteger(record2.maximumArchiveEntries, `${fieldName}.maximumArchiveEntries`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumArchiveEntries),
|
|
3287
|
+
maximumSourceFiles: requireInteger(record2.maximumSourceFiles, `${fieldName}.maximumSourceFiles`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumSourceFiles),
|
|
3288
|
+
maximumDecodedTextureBytes: requireInteger(record2.maximumDecodedTextureBytes, `${fieldName}.maximumDecodedTextureBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumDecodedTextureBytes),
|
|
3289
|
+
maximumTextureDimensionPx: requireInteger(record2.maximumTextureDimensionPx, `${fieldName}.maximumTextureDimensionPx`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumTextureDimensionPx)
|
|
3287
3290
|
};
|
|
3288
3291
|
if (limits.maximumExpandedBytes < limits.maximumDownloadBytes) {
|
|
3289
3292
|
throw new Error(`${fieldName}.maximumExpandedBytes must cover maximumDownloadBytes.`);
|
|
@@ -3298,8 +3301,8 @@ function createSourceIngestionLimitsV2(value) {
|
|
|
3298
3301
|
}
|
|
3299
3302
|
function createPvoxRuntimeLimitsV1(value) {
|
|
3300
3303
|
const fieldName = "ModelRequestSpecV2.pvoxRuntimeProfile.limits";
|
|
3301
|
-
const
|
|
3302
|
-
exactKeys(
|
|
3304
|
+
const record2 = asRecord(value, fieldName);
|
|
3305
|
+
exactKeys(record2, [
|
|
3303
3306
|
"maximumArtifactBytes",
|
|
3304
3307
|
"maximumPages",
|
|
3305
3308
|
"maximumHierarchyDepth",
|
|
@@ -3320,24 +3323,24 @@ function createPvoxRuntimeLimitsV1(value) {
|
|
|
3320
3323
|
"maximumGpuResidentBytes"
|
|
3321
3324
|
], fieldName);
|
|
3322
3325
|
const limits = {
|
|
3323
|
-
maximumArtifactBytes: requireInteger(
|
|
3324
|
-
maximumPages: requireInteger(
|
|
3325
|
-
maximumHierarchyDepth: requireInteger(
|
|
3326
|
-
maximumHierarchyNodes: requireInteger(
|
|
3327
|
-
maximumBricks: requireInteger(
|
|
3328
|
-
maximumLogicalVoxels: requireInteger(
|
|
3329
|
-
maximumEncodedSurfaceSamples: requireInteger(
|
|
3330
|
-
maximumSurfaceProperties: requireInteger(
|
|
3331
|
-
maximumPhysicalPaletteRecords: requireInteger(
|
|
3332
|
-
maximumPhysicalEvidenceEntries: requireInteger(
|
|
3333
|
-
maximumMaterialRegions: requireInteger(
|
|
3334
|
-
maximumInteriorLayers: requireInteger(
|
|
3335
|
-
maximumMassPropertyRecords: requireInteger(
|
|
3336
|
-
maximumBondRecords: requireInteger(
|
|
3337
|
-
maximumPartitions: requireInteger(
|
|
3338
|
-
maximumLodCount: requireInteger(
|
|
3339
|
-
maximumCpuResidentBytes: requireInteger(
|
|
3340
|
-
maximumGpuResidentBytes: requireInteger(
|
|
3326
|
+
maximumArtifactBytes: requireInteger(record2.maximumArtifactBytes, `${fieldName}.maximumArtifactBytes`, 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes),
|
|
3327
|
+
maximumPages: requireInteger(record2.maximumPages, `${fieldName}.maximumPages`, 1, PVOX_DEFAULT_LIMITS.maximumPages),
|
|
3328
|
+
maximumHierarchyDepth: requireInteger(record2.maximumHierarchyDepth, `${fieldName}.maximumHierarchyDepth`, 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth),
|
|
3329
|
+
maximumHierarchyNodes: requireInteger(record2.maximumHierarchyNodes, `${fieldName}.maximumHierarchyNodes`, 1, PVOX_DEFAULT_LIMITS.maximumHierarchyNodes),
|
|
3330
|
+
maximumBricks: requireInteger(record2.maximumBricks, `${fieldName}.maximumBricks`, 1, PVOX_DEFAULT_LIMITS.maximumBricks),
|
|
3331
|
+
maximumLogicalVoxels: requireInteger(record2.maximumLogicalVoxels, `${fieldName}.maximumLogicalVoxels`, 1, PVOX_DEFAULT_LIMITS.maximumLogicalVoxels),
|
|
3332
|
+
maximumEncodedSurfaceSamples: requireInteger(record2.maximumEncodedSurfaceSamples, `${fieldName}.maximumEncodedSurfaceSamples`, 1, PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples),
|
|
3333
|
+
maximumSurfaceProperties: requireInteger(record2.maximumSurfaceProperties, `${fieldName}.maximumSurfaceProperties`, 1, PVOX_DEFAULT_LIMITS.maximumSurfaceProperties),
|
|
3334
|
+
maximumPhysicalPaletteRecords: requireInteger(record2.maximumPhysicalPaletteRecords, `${fieldName}.maximumPhysicalPaletteRecords`, 1, PVOX_DEFAULT_LIMITS.maximumPhysicalProperties),
|
|
3335
|
+
maximumPhysicalEvidenceEntries: requireInteger(record2.maximumPhysicalEvidenceEntries, `${fieldName}.maximumPhysicalEvidenceEntries`, 1, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries),
|
|
3336
|
+
maximumMaterialRegions: requireInteger(record2.maximumMaterialRegions, `${fieldName}.maximumMaterialRegions`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions),
|
|
3337
|
+
maximumInteriorLayers: requireInteger(record2.maximumInteriorLayers, `${fieldName}.maximumInteriorLayers`, 1, PVOX_DEFAULT_LIMITS.maximumInteriorLayers),
|
|
3338
|
+
maximumMassPropertyRecords: requireInteger(record2.maximumMassPropertyRecords, `${fieldName}.maximumMassPropertyRecords`, 1, PVOX_DEFAULT_LIMITS.maximumMassPropertyRecords),
|
|
3339
|
+
maximumBondRecords: requireInteger(record2.maximumBondRecords, `${fieldName}.maximumBondRecords`, 0, PVOX_DEFAULT_LIMITS.maximumBondRecords),
|
|
3340
|
+
maximumPartitions: requireInteger(record2.maximumPartitions, `${fieldName}.maximumPartitions`, 1, PVOX_DEFAULT_LIMITS.maximumPartitions),
|
|
3341
|
+
maximumLodCount: requireInteger(record2.maximumLodCount, `${fieldName}.maximumLodCount`, 1, PVOX_DEFAULT_LIMITS.maximumLodCount),
|
|
3342
|
+
maximumCpuResidentBytes: requireInteger(record2.maximumCpuResidentBytes, `${fieldName}.maximumCpuResidentBytes`, 1, PVOX_DEFAULT_LIMITS.maximumCpuResidentBytes),
|
|
3343
|
+
maximumGpuResidentBytes: requireInteger(record2.maximumGpuResidentBytes, `${fieldName}.maximumGpuResidentBytes`, 1, PVOX_DEFAULT_LIMITS.maximumGpuResidentBytes)
|
|
3341
3344
|
};
|
|
3342
3345
|
if (limits.maximumArtifactBytes > limits.maximumPages * PVOX_PAGE_SIZE_BYTES) {
|
|
3343
3346
|
throw new Error(`${fieldName}.maximumPages cannot contain maximumArtifactBytes.`);
|
|
@@ -3352,9 +3355,9 @@ function createPvoxRuntimeLimitsV1(value) {
|
|
|
3352
3355
|
}
|
|
3353
3356
|
function createPvoxRuntimeRequestProfileV1(value) {
|
|
3354
3357
|
const fieldName = "ModelRequestSpecV2.pvoxRuntimeProfile";
|
|
3355
|
-
const
|
|
3356
|
-
exactKeys(
|
|
3357
|
-
const requestedCapabilityEntries = asDenseArray(
|
|
3358
|
+
const record2 = asRecord(value, fieldName);
|
|
3359
|
+
exactKeys(record2, ["profileId", "fidelityProfileId", "capabilityProfileId", "geometryMode", "requiredCapabilities", "limits"], fieldName);
|
|
3360
|
+
const requestedCapabilityEntries = asDenseArray(record2.requiredCapabilities, `${fieldName}.requiredCapabilities`, 1, PVOX_CAPABILITIES.length);
|
|
3358
3361
|
const requiredCapabilities = requestedCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `${fieldName}.requiredCapabilities[${index}]`));
|
|
3359
3362
|
if (new Set(requiredCapabilities).size !== requiredCapabilities.length) {
|
|
3360
3363
|
throw new Error(`${fieldName}.requiredCapabilities must not contain duplicates.`);
|
|
@@ -3362,7 +3365,7 @@ function createPvoxRuntimeRequestProfileV1(value) {
|
|
|
3362
3365
|
if (!requiredCapabilities.includes("rendering")) {
|
|
3363
3366
|
throw new Error(`${fieldName}.requiredCapabilities must include rendering.`);
|
|
3364
3367
|
}
|
|
3365
|
-
const capabilityProfileId = requireEnum(
|
|
3368
|
+
const capabilityProfileId = requireEnum(record2.capabilityProfileId, PVOX_RUNTIME_CAPABILITY_PROFILE_IDS, `${fieldName}.capabilityProfileId`);
|
|
3366
3369
|
const profileRequirements = {
|
|
3367
3370
|
"static-render-v1": ["rendering"],
|
|
3368
3371
|
"world-editable-v1": ["rendering", "collision", "destruction", "thermal", "moisture", "fluid-boundary"],
|
|
@@ -3371,24 +3374,24 @@ function createPvoxRuntimeRequestProfileV1(value) {
|
|
|
3371
3374
|
if (profileRequirements[capabilityProfileId].some((capability) => !requiredCapabilities.includes(capability))) {
|
|
3372
3375
|
throw new Error(`${fieldName}.requiredCapabilities do not satisfy capabilityProfileId.`);
|
|
3373
3376
|
}
|
|
3374
|
-
const limits = createPvoxRuntimeLimitsV1(
|
|
3377
|
+
const limits = createPvoxRuntimeLimitsV1(record2.limits);
|
|
3375
3378
|
if (capabilityProfileId === "world-editable-v1" && limits.maximumPhysicalEvidenceEntries < limits.maximumMaterialRegions * PVOX_PHYSICAL_PROPERTIES.length) {
|
|
3376
3379
|
throw new Error(`${fieldName}.limits must budget a complete physical-property matrix for every allowed material region.`);
|
|
3377
3380
|
}
|
|
3378
3381
|
return {
|
|
3379
|
-
profileId: requireToken(
|
|
3380
|
-
fidelityProfileId: requireEnum(
|
|
3382
|
+
profileId: requireToken(record2.profileId, `${fieldName}.profileId`),
|
|
3383
|
+
fidelityProfileId: requireEnum(record2.fidelityProfileId, PVOX_FIDELITY_PROFILE_IDS, `${fieldName}.fidelityProfileId`),
|
|
3381
3384
|
capabilityProfileId,
|
|
3382
|
-
geometryMode: requireEnum(
|
|
3385
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed", "auto"], `${fieldName}.geometryMode`),
|
|
3383
3386
|
requiredCapabilities,
|
|
3384
3387
|
limits
|
|
3385
3388
|
};
|
|
3386
3389
|
}
|
|
3387
3390
|
function createModelRequestSpecV2(input) {
|
|
3388
3391
|
const fieldName = "ModelRequestSpecV2";
|
|
3389
|
-
const
|
|
3390
|
-
assertDataOnlyTree(
|
|
3391
|
-
exactKeys(
|
|
3392
|
+
const record2 = asRecord(input, fieldName);
|
|
3393
|
+
assertDataOnlyTree(record2, fieldName);
|
|
3394
|
+
exactKeys(record2, [
|
|
3392
3395
|
"contractVersion",
|
|
3393
3396
|
"policyProfileId",
|
|
3394
3397
|
"requestSemanticProfileHash",
|
|
@@ -3402,26 +3405,26 @@ function createModelRequestSpecV2(input) {
|
|
|
3402
3405
|
"sourceIngestionLimits",
|
|
3403
3406
|
"pvoxRuntimeProfile"
|
|
3404
3407
|
], fieldName);
|
|
3405
|
-
if (
|
|
3408
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) {
|
|
3406
3409
|
throw new Error(`${fieldName}.contractVersion is unsupported.`);
|
|
3407
3410
|
}
|
|
3408
|
-
if (
|
|
3411
|
+
if (record2.policyProfileId !== PVOX_MODEL_REQUEST_POLICY_ID) {
|
|
3409
3412
|
throw new Error(`${fieldName}.policyProfileId must be ${PVOX_MODEL_REQUEST_POLICY_ID}.`);
|
|
3410
3413
|
}
|
|
3411
|
-
const hardConstraintsRecord = asRecord(
|
|
3414
|
+
const hardConstraintsRecord = asRecord(record2.hardConstraints ?? {}, `${fieldName}.hardConstraints`);
|
|
3412
3415
|
exactKeys(hardConstraintsRecord, ["boundsMetres", "dimensionsMetres", "collision", "partition"], `${fieldName}.hardConstraints`);
|
|
3413
|
-
const softPreferencesRecord = asRecord(
|
|
3416
|
+
const softPreferencesRecord = asRecord(record2.softPreferences ?? {}, `${fieldName}.softPreferences`);
|
|
3414
3417
|
for (const field of ["materials", "colors", "tags"]) {
|
|
3415
3418
|
if (softPreferencesRecord[field] !== void 0) {
|
|
3416
3419
|
asDenseArray(softPreferencesRecord[field], `${fieldName}.softPreferences.${field}`, 0, 32);
|
|
3417
3420
|
}
|
|
3418
3421
|
}
|
|
3419
|
-
const exclusions = asDenseArray(
|
|
3422
|
+
const exclusions = asDenseArray(record2.exclusions ?? [], `${fieldName}.exclusions`, 0, 32);
|
|
3420
3423
|
const normalizedV1 = createModelRequestSpec({
|
|
3421
|
-
query:
|
|
3422
|
-
revision:
|
|
3423
|
-
...
|
|
3424
|
-
...
|
|
3424
|
+
query: record2.query,
|
|
3425
|
+
revision: record2.revision,
|
|
3426
|
+
...record2.locale === void 0 ? {} : { locale: record2.locale },
|
|
3427
|
+
...record2.rankerId === void 0 ? {} : { rankerId: record2.rankerId },
|
|
3425
3428
|
hardConstraints: hardConstraintsRecord,
|
|
3426
3429
|
softPreferences: softPreferencesRecord,
|
|
3427
3430
|
exclusions
|
|
@@ -3435,7 +3438,7 @@ function createModelRequestSpecV2(input) {
|
|
|
3435
3438
|
return deepFreeze2({
|
|
3436
3439
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
3437
3440
|
policyProfileId: PVOX_MODEL_REQUEST_POLICY_ID,
|
|
3438
|
-
requestSemanticProfileHash: requireSha256(
|
|
3441
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, `${fieldName}.requestSemanticProfileHash`),
|
|
3439
3442
|
query: normalizedV1.query,
|
|
3440
3443
|
revision: normalizedV1.revision,
|
|
3441
3444
|
...normalizedV1.locale === void 0 ? {} : { locale: normalizedV1.locale },
|
|
@@ -3443,13 +3446,13 @@ function createModelRequestSpecV2(input) {
|
|
|
3443
3446
|
hardConstraints,
|
|
3444
3447
|
softPreferences: normalizedV1.softPreferences,
|
|
3445
3448
|
exclusions: normalizedV1.exclusions,
|
|
3446
|
-
sourceIngestionLimits: createSourceIngestionLimitsV2(
|
|
3447
|
-
pvoxRuntimeProfile: createPvoxRuntimeRequestProfileV1(
|
|
3449
|
+
sourceIngestionLimits: createSourceIngestionLimitsV2(record2.sourceIngestionLimits),
|
|
3450
|
+
pvoxRuntimeProfile: createPvoxRuntimeRequestProfileV1(record2.pvoxRuntimeProfile)
|
|
3448
3451
|
});
|
|
3449
3452
|
}
|
|
3450
3453
|
function createPvoxAssetManifestV1(input) {
|
|
3451
|
-
const
|
|
3452
|
-
exactKeys(
|
|
3454
|
+
const record2 = asRecord(input, "PvoxAssetManifestV1");
|
|
3455
|
+
exactKeys(record2, [
|
|
3453
3456
|
"manifestVersion",
|
|
3454
3457
|
"representation",
|
|
3455
3458
|
"formatVersion",
|
|
@@ -3480,23 +3483,23 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3480
3483
|
"pages",
|
|
3481
3484
|
"lods"
|
|
3482
3485
|
], "PvoxAssetManifestV1");
|
|
3483
|
-
if (
|
|
3484
|
-
if (
|
|
3485
|
-
const formatVersion = asRecord(
|
|
3486
|
+
if (record2.manifestVersion !== PVOX_ASSET_MANIFEST_VERSION) throw new Error("PvoxAssetManifestV1.manifestVersion is unsupported.");
|
|
3487
|
+
if (record2.representation !== "pvox") throw new Error("PvoxAssetManifestV1.representation must be pvox.");
|
|
3488
|
+
const formatVersion = asRecord(record2.formatVersion, "PvoxAssetManifestV1.formatVersion");
|
|
3486
3489
|
exactKeys(formatVersion, ["major", "minor"], "PvoxAssetManifestV1.formatVersion");
|
|
3487
3490
|
if (formatVersion.major !== PVOX_FORMAT_VERSION.major || formatVersion.minor !== PVOX_FORMAT_VERSION.minor) throw new Error("PvoxAssetManifestV1.formatVersion is unsupported.");
|
|
3488
|
-
if (
|
|
3491
|
+
if (record2.contentType !== PVOX_CONTENT_TYPE || record2.fileExtension !== PVOX_FILE_EXTENSION || record2.magic !== PVOX_MAGIC) {
|
|
3489
3492
|
throw new Error("PvoxAssetManifestV1 must declare the canonical PVOX content type, extension, and magic.");
|
|
3490
3493
|
}
|
|
3491
|
-
if (
|
|
3494
|
+
if (record2.brickEdgeVoxels !== PVOX_BRICK_EDGE_VOXELS || record2.pageSizeBytes !== PVOX_PAGE_SIZE_BYTES) {
|
|
3492
3495
|
throw new Error("PvoxAssetManifestV1 must use fixed 8-cubed bricks and 64-KiB pages.");
|
|
3493
3496
|
}
|
|
3494
|
-
assertCoordinateSystem2(
|
|
3495
|
-
const boundsMetres = createBounds2(
|
|
3497
|
+
assertCoordinateSystem2(record2.coordinateSystem, "PvoxAssetManifestV1.coordinateSystem");
|
|
3498
|
+
const boundsMetres = createBounds2(record2.boundsMetres, "PvoxAssetManifestV1.boundsMetres");
|
|
3496
3499
|
assertFloorCentredBounds(boundsMetres, "PvoxAssetManifestV1.boundsMetres");
|
|
3497
|
-
const artifact = createResource(
|
|
3500
|
+
const artifact = createResource(record2.artifact);
|
|
3498
3501
|
if (artifact.byteLength % PVOX_PAGE_SIZE_BYTES !== 0) throw new Error("PvoxAssetManifestV1 artifact byteLength must contain whole 64-KiB pages.");
|
|
3499
|
-
const pageEntries = asDenseArray(
|
|
3502
|
+
const pageEntries = asDenseArray(record2.pages, "PvoxAssetManifestV1.pages", 1, PVOX_DEFAULT_LIMITS.maximumPages);
|
|
3500
3503
|
const pages = pageEntries.map(createPage);
|
|
3501
3504
|
let expectedOffset = 0;
|
|
3502
3505
|
const previousSpatialMaximumByScope = /* @__PURE__ */ new Map();
|
|
@@ -3517,7 +3520,7 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3517
3520
|
expectedOffset += current.byteLength;
|
|
3518
3521
|
}
|
|
3519
3522
|
if (expectedOffset !== artifact.byteLength) throw new Error("PvoxAssetManifestV1 pages must exactly cover the artifact bytes.");
|
|
3520
|
-
const lodEntries = asDenseArray(
|
|
3523
|
+
const lodEntries = asDenseArray(record2.lods, "PvoxAssetManifestV1.lods", 1, 4);
|
|
3521
3524
|
const lods = lodEntries.map((entry, index) => createLod(entry, index, pages.length));
|
|
3522
3525
|
for (const [index, current] of lods.entries()) {
|
|
3523
3526
|
if (current.level !== index) throw new Error("PvoxAssetManifestV1 LOD levels must be contiguous from LOD0.");
|
|
@@ -3527,14 +3530,14 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3527
3530
|
throw new Error("PvoxAssetManifestV1 LOD page ranges must reference render-field pages in the matching LOD scope.");
|
|
3528
3531
|
}
|
|
3529
3532
|
}
|
|
3530
|
-
const sourceContentHash = requireSha256(
|
|
3531
|
-
const canonicalDocumentHash = requireSha256(
|
|
3532
|
-
const compilationInputHash = requireSha256(
|
|
3533
|
-
const runtimeRequestProfileHash = requireSha256(
|
|
3533
|
+
const sourceContentHash = requireSha256(record2.sourceContentHash, "PvoxAssetManifestV1.sourceContentHash");
|
|
3534
|
+
const canonicalDocumentHash = requireSha256(record2.canonicalDocumentHash, "PvoxAssetManifestV1.canonicalDocumentHash");
|
|
3535
|
+
const compilationInputHash = requireSha256(record2.compilationInputHash, "PvoxAssetManifestV1.compilationInputHash");
|
|
3536
|
+
const runtimeRequestProfileHash = requireSha256(record2.runtimeRequestProfileHash, "PvoxAssetManifestV1.runtimeRequestProfileHash");
|
|
3534
3537
|
if ((/* @__PURE__ */ new Set([sourceContentHash, canonicalDocumentHash, compilationInputHash, runtimeRequestProfileHash])).size !== 4) {
|
|
3535
3538
|
throw new Error("PvoxAssetManifestV1 source, canonical document, compilation input, and runtime request profile hashes must be distinct attestations.");
|
|
3536
3539
|
}
|
|
3537
|
-
if (
|
|
3540
|
+
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) {
|
|
3538
3541
|
throw new Error("PvoxAssetManifestV1 must declare the fixed PVOX 1.0 layout and brick codec limits.");
|
|
3539
3542
|
}
|
|
3540
3543
|
return deepFreeze2({
|
|
@@ -3546,19 +3549,19 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3546
3549
|
magic: PVOX_MAGIC,
|
|
3547
3550
|
brickEdgeVoxels: PVOX_BRICK_EDGE_VOXELS,
|
|
3548
3551
|
pageSizeBytes: PVOX_PAGE_SIZE_BYTES,
|
|
3549
|
-
geometryMode: requireEnum(
|
|
3552
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed"], "PvoxAssetManifestV1.geometryMode"),
|
|
3550
3553
|
sourceContentHash,
|
|
3551
3554
|
canonicalDocumentHash,
|
|
3552
|
-
rootHash: requireSha256(
|
|
3553
|
-
directoryHash: requireSha256(
|
|
3554
|
-
pageSetHash: requireSha256(
|
|
3555
|
-
binaryClosureHash: requireSha256(
|
|
3556
|
-
validationEvidenceHash: requireSha256(
|
|
3555
|
+
rootHash: requireSha256(record2.rootHash, "PvoxAssetManifestV1.rootHash"),
|
|
3556
|
+
directoryHash: requireSha256(record2.directoryHash, "PvoxAssetManifestV1.directoryHash"),
|
|
3557
|
+
pageSetHash: requireSha256(record2.pageSetHash, "PvoxAssetManifestV1.pageSetHash"),
|
|
3558
|
+
binaryClosureHash: requireSha256(record2.binaryClosureHash, "PvoxAssetManifestV1.binaryClosureHash"),
|
|
3559
|
+
validationEvidenceHash: requireSha256(record2.validationEvidenceHash, "PvoxAssetManifestV1.validationEvidenceHash"),
|
|
3557
3560
|
compilationInputHash,
|
|
3558
3561
|
runtimeRequestProfileHash,
|
|
3559
3562
|
headerByteLength: PVOX_HEADER_BYTE_LENGTH,
|
|
3560
3563
|
directoryEntryByteLength: PVOX_DIRECTORY_ENTRY_BYTE_LENGTH,
|
|
3561
|
-
sectionCount: requireInteger(
|
|
3564
|
+
sectionCount: requireInteger(record2.sectionCount, "PvoxAssetManifestV1.sectionCount", PVOX_MIN_STATIC_SECTION_COUNT, PVOX_MAX_SECTIONS),
|
|
3562
3565
|
maximumRunsPerBrick: PVOX_MAX_RUNS_PER_BRICK,
|
|
3563
3566
|
maximumLocalSamplesPerBrick: PVOX_MAX_LOCAL_SAMPLES_PER_BRICK,
|
|
3564
3567
|
maximumEncodedBrickPayloadBytes: PVOX_MAX_ENCODED_BRICK_PAYLOAD_BYTES,
|
|
@@ -3573,8 +3576,8 @@ function createPvoxAssetManifest(input) {
|
|
|
3573
3576
|
return createPvoxAssetManifestV1(input);
|
|
3574
3577
|
}
|
|
3575
3578
|
function createVoxelTechnicalProfile(input) {
|
|
3576
|
-
const
|
|
3577
|
-
exactKeys(
|
|
3579
|
+
const record2 = asRecord(input, "VoxelTechnicalProfile");
|
|
3580
|
+
exactKeys(record2, [
|
|
3578
3581
|
"boundsMetres",
|
|
3579
3582
|
"dimensionsMetres",
|
|
3580
3583
|
"geometryMode",
|
|
@@ -3606,49 +3609,49 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3606
3609
|
"maximumPartitionExtentMetres",
|
|
3607
3610
|
"maximumPartitionDiagonalMetres"
|
|
3608
3611
|
], "VoxelTechnicalProfile");
|
|
3609
|
-
const boundsMetres = createBounds2(
|
|
3612
|
+
const boundsMetres = createBounds2(record2.boundsMetres, "VoxelTechnicalProfile.boundsMetres");
|
|
3610
3613
|
assertFloorCentredBounds(boundsMetres, "VoxelTechnicalProfile.boundsMetres");
|
|
3611
|
-
const dimensionsMetres = createDimensions2(
|
|
3614
|
+
const dimensionsMetres = createDimensions2(record2.dimensionsMetres, "VoxelTechnicalProfile.dimensionsMetres");
|
|
3612
3615
|
const derived = boundsMetres.max.map((coordinate, index) => coordinate - boundsMetres.min[index]);
|
|
3613
3616
|
const supplied = [dimensionsMetres.width, dimensionsMetres.height, dimensionsMetres.depth];
|
|
3614
3617
|
if (derived.some((coordinate, index) => Math.abs(coordinate - supplied[index]) > Math.max(1e-6, Math.abs(coordinate) * 1e-6))) {
|
|
3615
3618
|
throw new Error("VoxelTechnicalProfile dimensionsMetres must match boundsMetres.");
|
|
3616
3619
|
}
|
|
3617
|
-
const artifactByteLength = requireInteger(
|
|
3618
|
-
const pageCount = requireInteger(
|
|
3620
|
+
const artifactByteLength = requireInteger(record2.artifactByteLength, "VoxelTechnicalProfile.artifactByteLength", 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes);
|
|
3621
|
+
const pageCount = requireInteger(record2.pageCount, "VoxelTechnicalProfile.pageCount", 1, PVOX_DEFAULT_LIMITS.maximumPages);
|
|
3619
3622
|
if (artifactByteLength !== pageCount * PVOX_PAGE_SIZE_BYTES) {
|
|
3620
3623
|
throw new Error("VoxelTechnicalProfile artifactByteLength must equal pageCount multiplied by the fixed PVOX page size.");
|
|
3621
3624
|
}
|
|
3622
|
-
const brickCount = requireInteger(
|
|
3623
|
-
const logicalVoxelCapacity = requireInteger(
|
|
3625
|
+
const brickCount = requireInteger(record2.brickCount, "VoxelTechnicalProfile.brickCount", 1, PVOX_DEFAULT_LIMITS.maximumBricks);
|
|
3626
|
+
const logicalVoxelCapacity = requireInteger(record2.logicalVoxelCapacity, "VoxelTechnicalProfile.logicalVoxelCapacity", 1, PVOX_DEFAULT_LIMITS.maximumLogicalVoxels);
|
|
3624
3627
|
if (logicalVoxelCapacity !== brickCount * PVOX_BRICK_EDGE_VOXELS ** 3) {
|
|
3625
3628
|
throw new Error("VoxelTechnicalProfile logicalVoxelCapacity must equal brickCount multiplied by 512 voxels per brick.");
|
|
3626
3629
|
}
|
|
3627
|
-
const encodedSurfaceSampleCount = requireInteger(
|
|
3630
|
+
const encodedSurfaceSampleCount = requireInteger(record2.encodedSurfaceSampleCount, "VoxelTechnicalProfile.encodedSurfaceSampleCount", 1, PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples);
|
|
3628
3631
|
if (encodedSurfaceSampleCount > logicalVoxelCapacity) {
|
|
3629
3632
|
throw new Error("VoxelTechnicalProfile encodedSurfaceSampleCount cannot exceed logicalVoxelCapacity.");
|
|
3630
3633
|
}
|
|
3631
|
-
const surfacePropertyCount = requireInteger(
|
|
3632
|
-
const sectionCount = requireInteger(
|
|
3633
|
-
const hierarchyDepth = requireInteger(
|
|
3634
|
-
const hierarchyNodeCount = requireInteger(
|
|
3635
|
-
const lodCount = requireInteger(
|
|
3636
|
-
const partitionCount = requireInteger(
|
|
3637
|
-
const levelSpanRecordCount = requireInteger(
|
|
3638
|
-
const physicalPaletteRecordCount = requireInteger(
|
|
3639
|
-
const physicalEvidenceRecordCount = requireInteger(
|
|
3640
|
-
const materialRegionCount = requireInteger(
|
|
3641
|
-
const interiorLayerRecordCount = requireInteger(
|
|
3642
|
-
const massPropertyRecordCount = requireInteger(
|
|
3643
|
-
const bondRecordCount = requireInteger(
|
|
3644
|
-
const hasCollision = requireBoolean(
|
|
3645
|
-
const collisionHierarchyNodeCount = requireInteger(
|
|
3646
|
-
const collisionBrickCount = requireInteger(
|
|
3647
|
-
const collisionLogicalVoxelCapacity = requireInteger(
|
|
3634
|
+
const surfacePropertyCount = requireInteger(record2.surfacePropertyCount, "VoxelTechnicalProfile.surfacePropertyCount", 1, PVOX_DEFAULT_LIMITS.maximumSurfaceProperties);
|
|
3635
|
+
const sectionCount = requireInteger(record2.sectionCount, "VoxelTechnicalProfile.sectionCount", PVOX_MIN_STATIC_SECTION_COUNT, PVOX_MAX_SECTIONS);
|
|
3636
|
+
const hierarchyDepth = requireInteger(record2.hierarchyDepth, "VoxelTechnicalProfile.hierarchyDepth", 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth);
|
|
3637
|
+
const hierarchyNodeCount = requireInteger(record2.hierarchyNodeCount, "VoxelTechnicalProfile.hierarchyNodeCount", 1, PVOX_DEFAULT_LIMITS.maximumHierarchyNodes);
|
|
3638
|
+
const lodCount = requireInteger(record2.lodCount, "VoxelTechnicalProfile.lodCount", 1, PVOX_DEFAULT_LIMITS.maximumLodCount);
|
|
3639
|
+
const partitionCount = requireInteger(record2.partitionCount, "VoxelTechnicalProfile.partitionCount", 1, PVOX_DEFAULT_LIMITS.maximumPartitions);
|
|
3640
|
+
const levelSpanRecordCount = requireInteger(record2.levelSpanRecordCount, "VoxelTechnicalProfile.levelSpanRecordCount", partitionCount * lodCount, partitionCount * lodCount * (hierarchyDepth + 1));
|
|
3641
|
+
const physicalPaletteRecordCount = requireInteger(record2.physicalPaletteRecordCount, "VoxelTechnicalProfile.physicalPaletteRecordCount", 1, PVOX_DEFAULT_LIMITS.maximumPhysicalProperties);
|
|
3642
|
+
const physicalEvidenceRecordCount = requireInteger(record2.physicalEvidenceRecordCount, "VoxelTechnicalProfile.physicalEvidenceRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
|
|
3643
|
+
const materialRegionCount = requireInteger(record2.materialRegionCount, "VoxelTechnicalProfile.materialRegionCount", 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
|
|
3644
|
+
const interiorLayerRecordCount = requireInteger(record2.interiorLayerRecordCount, "VoxelTechnicalProfile.interiorLayerRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumInteriorLayers);
|
|
3645
|
+
const massPropertyRecordCount = requireInteger(record2.massPropertyRecordCount, "VoxelTechnicalProfile.massPropertyRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumMassPropertyRecords);
|
|
3646
|
+
const bondRecordCount = requireInteger(record2.bondRecordCount, "VoxelTechnicalProfile.bondRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumBondRecords);
|
|
3647
|
+
const hasCollision = requireBoolean(record2.hasCollision, "VoxelTechnicalProfile.hasCollision");
|
|
3648
|
+
const collisionHierarchyNodeCount = requireInteger(record2.collisionHierarchyNodeCount, "VoxelTechnicalProfile.collisionHierarchyNodeCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumHierarchyNodes : 0);
|
|
3649
|
+
const collisionBrickCount = requireInteger(record2.collisionBrickCount, "VoxelTechnicalProfile.collisionBrickCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumBricks : 0);
|
|
3650
|
+
const collisionLogicalVoxelCapacity = requireInteger(record2.collisionLogicalVoxelCapacity, "VoxelTechnicalProfile.collisionLogicalVoxelCapacity", hasCollision ? PVOX_BRICK_EDGE_VOXELS ** 3 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumLogicalVoxels : 0);
|
|
3648
3651
|
if (collisionLogicalVoxelCapacity !== collisionBrickCount * PVOX_BRICK_EDGE_VOXELS ** 3) {
|
|
3649
3652
|
throw new Error("VoxelTechnicalProfile collisionLogicalVoxelCapacity must equal collisionBrickCount multiplied by 512.");
|
|
3650
3653
|
}
|
|
3651
|
-
const collisionEncodedSurfaceSampleCount = requireInteger(
|
|
3654
|
+
const collisionEncodedSurfaceSampleCount = requireInteger(record2.collisionEncodedSurfaceSampleCount, "VoxelTechnicalProfile.collisionEncodedSurfaceSampleCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples : 0);
|
|
3652
3655
|
if (collisionEncodedSurfaceSampleCount > collisionLogicalVoxelCapacity) {
|
|
3653
3656
|
throw new Error("VoxelTechnicalProfile collision encoded sample count cannot exceed its logical voxel capacity.");
|
|
3654
3657
|
}
|
|
@@ -3658,15 +3661,15 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3658
3661
|
}
|
|
3659
3662
|
const maximumDimensionMetres = Math.max(...supplied);
|
|
3660
3663
|
const fullDiagonalMetres = Math.hypot(...supplied);
|
|
3661
|
-
const maximumPartitionExtentMetres = requireNumber(
|
|
3662
|
-
const maximumPartitionDiagonalMetres = requireNumber(
|
|
3664
|
+
const maximumPartitionExtentMetres = requireNumber(record2.maximumPartitionExtentMetres, "VoxelTechnicalProfile.maximumPartitionExtentMetres", Number.EPSILON, maximumDimensionMetres + Math.max(1e-9, maximumDimensionMetres * 1e-9));
|
|
3665
|
+
const maximumPartitionDiagonalMetres = requireNumber(record2.maximumPartitionDiagonalMetres, "VoxelTechnicalProfile.maximumPartitionDiagonalMetres", maximumPartitionExtentMetres, fullDiagonalMetres + Math.max(1e-9, fullDiagonalMetres * 1e-9));
|
|
3663
3666
|
if (partitionCount === 1 && (!approximatelyEqual(maximumPartitionExtentMetres, maximumDimensionMetres) || !approximatelyEqual(maximumPartitionDiagonalMetres, fullDiagonalMetres))) {
|
|
3664
3667
|
throw new Error("VoxelTechnicalProfile single-partition extent and diagonal must cover the complete bounds.");
|
|
3665
3668
|
}
|
|
3666
3669
|
const profile = {
|
|
3667
3670
|
boundsMetres,
|
|
3668
3671
|
dimensionsMetres,
|
|
3669
|
-
geometryMode: requireEnum(
|
|
3672
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed"], "VoxelTechnicalProfile.geometryMode"),
|
|
3670
3673
|
artifactByteLength,
|
|
3671
3674
|
pageCount,
|
|
3672
3675
|
brickCount,
|
|
@@ -3688,8 +3691,8 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3688
3691
|
collisionBrickCount,
|
|
3689
3692
|
collisionLogicalVoxelCapacity,
|
|
3690
3693
|
collisionEncodedSurfaceSampleCount,
|
|
3691
|
-
cpuResidentByteLength: requireInteger(
|
|
3692
|
-
gpuResidentByteLength: requireInteger(
|
|
3694
|
+
cpuResidentByteLength: requireInteger(record2.cpuResidentByteLength, "VoxelTechnicalProfile.cpuResidentByteLength", 1, PVOX_DEFAULT_LIMITS.maximumCpuResidentBytes),
|
|
3695
|
+
gpuResidentByteLength: requireInteger(record2.gpuResidentByteLength, "VoxelTechnicalProfile.gpuResidentByteLength", 1, PVOX_DEFAULT_LIMITS.maximumGpuResidentBytes),
|
|
3693
3696
|
hasCollision,
|
|
3694
3697
|
partitionCount,
|
|
3695
3698
|
maximumPartitionExtentMetres,
|
|
@@ -3702,21 +3705,21 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3702
3705
|
return deepFreeze2(profile);
|
|
3703
3706
|
}
|
|
3704
3707
|
function createVoxelCapabilityAssessment(input) {
|
|
3705
|
-
const
|
|
3706
|
-
exactKeys(
|
|
3707
|
-
const status = requireEnum(
|
|
3708
|
-
const reasonCodes2 = requireReasonCodes(
|
|
3708
|
+
const record2 = asRecord(input, "VoxelCapabilityAssessment");
|
|
3709
|
+
exactKeys(record2, ["capability", "status", "evaluatorId", "evaluatorVersion", "subjectContentHash", "evidenceHash", "reasonCodes", "evaluatedAt"], "VoxelCapabilityAssessment");
|
|
3710
|
+
const status = requireEnum(record2.status, ["supported", "unsupported", "not-applicable"], "VoxelCapabilityAssessment.status");
|
|
3711
|
+
const reasonCodes2 = requireReasonCodes(record2.reasonCodes, "VoxelCapabilityAssessment.reasonCodes");
|
|
3709
3712
|
if (status === "supported" && reasonCodes2.length !== 0) throw new Error("Supported capability evidence must not contain failure reason codes.");
|
|
3710
3713
|
if (status !== "supported" && reasonCodes2.length === 0) throw new Error("Unavailable capability evidence requires a reason code.");
|
|
3711
3714
|
return deepFreeze2({
|
|
3712
|
-
capability: requireEnum(
|
|
3715
|
+
capability: requireEnum(record2.capability, PVOX_CAPABILITIES, "VoxelCapabilityAssessment.capability"),
|
|
3713
3716
|
status,
|
|
3714
|
-
evaluatorId: requireToken(
|
|
3715
|
-
evaluatorVersion: assertImmutableAssetVersion(
|
|
3716
|
-
subjectContentHash: requireSha256(
|
|
3717
|
-
evidenceHash: requireSha256(
|
|
3717
|
+
evaluatorId: requireToken(record2.evaluatorId, "VoxelCapabilityAssessment.evaluatorId"),
|
|
3718
|
+
evaluatorVersion: assertImmutableAssetVersion(record2.evaluatorVersion),
|
|
3719
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "VoxelCapabilityAssessment.subjectContentHash"),
|
|
3720
|
+
evidenceHash: requireSha256(record2.evidenceHash, "VoxelCapabilityAssessment.evidenceHash"),
|
|
3718
3721
|
reasonCodes: reasonCodes2,
|
|
3719
|
-
evaluatedAt: requireTimestamp(
|
|
3722
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "VoxelCapabilityAssessment.evaluatedAt")
|
|
3720
3723
|
});
|
|
3721
3724
|
}
|
|
3722
3725
|
var PVOX_PHYSICAL_PROPERTY_POLICIES = deepFreeze2({
|
|
@@ -3741,42 +3744,42 @@ var PVOX_PHYSICAL_PROPERTY_POLICIES = deepFreeze2({
|
|
|
3741
3744
|
"interior-thickness": { unit: "m", minimum: Number.EPSILON, maximum: 1e6 }
|
|
3742
3745
|
});
|
|
3743
3746
|
function createPhysicalPropertyEvidence(input) {
|
|
3744
|
-
const
|
|
3745
|
-
exactKeys(
|
|
3746
|
-
const property = requireEnum(
|
|
3747
|
+
const record2 = asRecord(input, "PhysicalPropertyEvidence");
|
|
3748
|
+
exactKeys(record2, ["property", "value", "unit", "provenance", "confidence", "subjectContentHash", "regionId", "materialId", "policyId", "policyVersion", "evidenceHash", "reviewedBy", "reviewedAt", "reviewToken"], "PhysicalPropertyEvidence");
|
|
3749
|
+
const property = requireEnum(record2.property, PVOX_PHYSICAL_PROPERTIES, "PhysicalPropertyEvidence.property");
|
|
3747
3750
|
const policy = PVOX_PHYSICAL_PROPERTY_POLICIES[property];
|
|
3748
|
-
if (
|
|
3751
|
+
if (record2.policyId !== PVOX_PHYSICAL_PROPERTY_POLICY_ID || record2.policyVersion !== PVOX_PHYSICAL_PROPERTY_POLICY_VERSION) {
|
|
3749
3752
|
throw new Error("PhysicalPropertyEvidence must use the governed immutable physical-property policy.");
|
|
3750
3753
|
}
|
|
3751
|
-
assertImmutableAssetVersion(
|
|
3752
|
-
if (
|
|
3753
|
-
const provenance = requireEnum(
|
|
3754
|
-
const reviewedBy =
|
|
3755
|
-
const reviewedAt =
|
|
3756
|
-
const reviewToken =
|
|
3754
|
+
assertImmutableAssetVersion(record2.policyVersion);
|
|
3755
|
+
if (record2.unit !== policy.unit) throw new Error("PhysicalPropertyEvidence unit must match its governed property policy.");
|
|
3756
|
+
const provenance = requireEnum(record2.provenance, ["source", "authored", "derived", "inferred", "default"], "PhysicalPropertyEvidence.provenance");
|
|
3757
|
+
const reviewedBy = record2.reviewedBy === void 0 ? void 0 : requireToken(record2.reviewedBy, "PhysicalPropertyEvidence.reviewedBy");
|
|
3758
|
+
const reviewedAt = record2.reviewedAt === void 0 ? void 0 : requireTimestamp(record2.reviewedAt, "PhysicalPropertyEvidence.reviewedAt");
|
|
3759
|
+
const reviewToken = record2.reviewToken === void 0 ? void 0 : requireAttestation(record2.reviewToken, "PhysicalPropertyEvidence.reviewToken");
|
|
3757
3760
|
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.");
|
|
3758
3761
|
if (provenance === "inferred" && reviewedBy === void 0) throw new Error("Inferred PhysicalPropertyEvidence requires human review.");
|
|
3759
3762
|
return deepFreeze2({
|
|
3760
3763
|
property,
|
|
3761
|
-
value: requireNumber(
|
|
3764
|
+
value: requireNumber(record2.value, "PhysicalPropertyEvidence.value", policy.minimum, policy.maximum),
|
|
3762
3765
|
unit: policy.unit,
|
|
3763
3766
|
provenance,
|
|
3764
|
-
confidence: requireNumber(
|
|
3765
|
-
subjectContentHash: requireSha256(
|
|
3766
|
-
regionId: requireToken(
|
|
3767
|
-
materialId: requireToken(
|
|
3767
|
+
confidence: requireNumber(record2.confidence, "PhysicalPropertyEvidence.confidence", 0, 1),
|
|
3768
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "PhysicalPropertyEvidence.subjectContentHash"),
|
|
3769
|
+
regionId: requireToken(record2.regionId, "PhysicalPropertyEvidence.regionId"),
|
|
3770
|
+
materialId: requireToken(record2.materialId, "PhysicalPropertyEvidence.materialId"),
|
|
3768
3771
|
policyId: PVOX_PHYSICAL_PROPERTY_POLICY_ID,
|
|
3769
3772
|
policyVersion: PVOX_PHYSICAL_PROPERTY_POLICY_VERSION,
|
|
3770
|
-
evidenceHash: requireSha256(
|
|
3773
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PhysicalPropertyEvidence.evidenceHash"),
|
|
3771
3774
|
...reviewedBy === void 0 ? {} : { reviewedBy, reviewedAt, reviewToken }
|
|
3772
3775
|
});
|
|
3773
3776
|
}
|
|
3774
3777
|
function createPvoxPhysicalRegionInventoryV1(input) {
|
|
3775
3778
|
const fieldName = "PvoxPhysicalRegionInventoryV1";
|
|
3776
|
-
const
|
|
3777
|
-
exactKeys(
|
|
3778
|
-
if (
|
|
3779
|
-
const entriesInput = asDenseArray(
|
|
3779
|
+
const record2 = asRecord(input, fieldName);
|
|
3780
|
+
exactKeys(record2, ["inventoryVersion", "subjectContentHash", "inventoryHash", "inventoryHashAttestation", "validationEvidenceHash", "entries"], fieldName);
|
|
3781
|
+
if (record2.inventoryVersion !== PVOX_PHYSICAL_REGION_INVENTORY_VERSION) throw new Error(`${fieldName}.inventoryVersion is unsupported.`);
|
|
3782
|
+
const entriesInput = asDenseArray(record2.entries, `${fieldName}.entries`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
|
|
3780
3783
|
const entries = entriesInput.map((entry, index) => {
|
|
3781
3784
|
const entryName = `${fieldName}.entries[${index}]`;
|
|
3782
3785
|
const item = asRecord(entry, entryName);
|
|
@@ -3791,23 +3794,23 @@ function createPvoxPhysicalRegionInventoryV1(input) {
|
|
|
3791
3794
|
if (entries.some((entry, index) => entry.regionIndex !== index)) throw new Error(`${fieldName} regionIndex values must be contiguous and canonically ordered.`);
|
|
3792
3795
|
const pairs = entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
|
|
3793
3796
|
if (new Set(pairs).size !== pairs.length) throw new Error(`${fieldName} region/material pairs must be unique.`);
|
|
3794
|
-
const inventoryHash = requireSha256(
|
|
3797
|
+
const inventoryHash = requireSha256(record2.inventoryHash, `${fieldName}.inventoryHash`);
|
|
3795
3798
|
return deepFreeze2({
|
|
3796
3799
|
inventoryVersion: PVOX_PHYSICAL_REGION_INVENTORY_VERSION,
|
|
3797
|
-
subjectContentHash: requireSha256(
|
|
3800
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`),
|
|
3798
3801
|
inventoryHash,
|
|
3799
|
-
inventoryHashAttestation: createHashAttestation(
|
|
3800
|
-
validationEvidenceHash: requireSha256(
|
|
3802
|
+
inventoryHashAttestation: createHashAttestation(record2.inventoryHashAttestation, PVOX_PHYSICAL_INVENTORY_HASH_DOMAIN, inventoryHash, `${fieldName}.inventoryHashAttestation`),
|
|
3803
|
+
validationEvidenceHash: requireSha256(record2.validationEvidenceHash, `${fieldName}.validationEvidenceHash`),
|
|
3801
3804
|
entries
|
|
3802
3805
|
});
|
|
3803
3806
|
}
|
|
3804
3807
|
function createReviewedStructuralEvidence(input, expectedKind, expectedSubjectHash, expectedInventoryHash, expectedRecordCount, fieldName) {
|
|
3805
|
-
const
|
|
3806
|
-
exactKeys(
|
|
3807
|
-
if (
|
|
3808
|
-
const subjectContentHash = requireSha256(
|
|
3809
|
-
const physicalInventoryHash = requireSha256(
|
|
3810
|
-
const recordCount = requireInteger(
|
|
3808
|
+
const record2 = asRecord(input, fieldName);
|
|
3809
|
+
exactKeys(record2, ["kind", "subjectContentHash", "physicalInventoryHash", "recordCount", "evidenceHash", "reviewerId", "reviewedAt", "reviewToken"], fieldName);
|
|
3810
|
+
if (record2.kind !== expectedKind) throw new Error(`${fieldName}.kind must be ${expectedKind}.`);
|
|
3811
|
+
const subjectContentHash = requireSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`);
|
|
3812
|
+
const physicalInventoryHash = requireSha256(record2.physicalInventoryHash, `${fieldName}.physicalInventoryHash`);
|
|
3813
|
+
const recordCount = requireInteger(record2.recordCount, `${fieldName}.recordCount`, 0, PVOX_DEFAULT_LIMITS.maximumBondRecords);
|
|
3811
3814
|
if (subjectContentHash !== expectedSubjectHash || physicalInventoryHash !== expectedInventoryHash || recordCount !== expectedRecordCount) {
|
|
3812
3815
|
throw new Error(`${fieldName} must bind the exact PVOX closure, physical inventory, and binary record count.`);
|
|
3813
3816
|
}
|
|
@@ -3816,15 +3819,15 @@ function createReviewedStructuralEvidence(input, expectedKind, expectedSubjectHa
|
|
|
3816
3819
|
subjectContentHash,
|
|
3817
3820
|
physicalInventoryHash,
|
|
3818
3821
|
recordCount,
|
|
3819
|
-
evidenceHash: requireSha256(
|
|
3820
|
-
reviewerId: requireToken(
|
|
3821
|
-
reviewedAt: requireTimestamp(
|
|
3822
|
-
reviewToken: requireAttestation(
|
|
3822
|
+
evidenceHash: requireSha256(record2.evidenceHash, `${fieldName}.evidenceHash`),
|
|
3823
|
+
reviewerId: requireToken(record2.reviewerId, `${fieldName}.reviewerId`),
|
|
3824
|
+
reviewedAt: requireTimestamp(record2.reviewedAt, `${fieldName}.reviewedAt`),
|
|
3825
|
+
reviewToken: requireAttestation(record2.reviewToken, `${fieldName}.reviewToken`)
|
|
3823
3826
|
});
|
|
3824
3827
|
}
|
|
3825
3828
|
function createPvoxFidelityEvidence(input) {
|
|
3826
|
-
const
|
|
3827
|
-
exactKeys(
|
|
3829
|
+
const record2 = asRecord(input, "PvoxFidelityEvidence");
|
|
3830
|
+
exactKeys(record2, [
|
|
3828
3831
|
"profileId",
|
|
3829
3832
|
"surfaceProfileId",
|
|
3830
3833
|
"profileVersion",
|
|
@@ -3851,8 +3854,8 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3851
3854
|
"evaluatedAt",
|
|
3852
3855
|
"decisionToken"
|
|
3853
3856
|
], "PvoxFidelityEvidence");
|
|
3854
|
-
const profileId = requireEnum(
|
|
3855
|
-
const surfaceProfileId = requireEnum(
|
|
3857
|
+
const profileId = requireEnum(record2.profileId, PVOX_FIDELITY_PROFILE_IDS, "PvoxFidelityEvidence.profileId");
|
|
3858
|
+
const surfaceProfileId = requireEnum(record2.surfaceProfileId, [
|
|
3856
3859
|
"precision-hero-v1",
|
|
3857
3860
|
"props-furniture-v1",
|
|
3858
3861
|
"rocks-organic-shells-v1",
|
|
@@ -3861,12 +3864,12 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3861
3864
|
if (profileId !== "deformable-v1" && profileId !== surfaceProfileId) {
|
|
3862
3865
|
throw new Error("PvoxFidelityEvidence surfaceProfileId must match its governed profileId.");
|
|
3863
3866
|
}
|
|
3864
|
-
if (
|
|
3867
|
+
if (record2.profileVersion !== PVOX_FIDELITY_PROFILE_VERSION) {
|
|
3865
3868
|
throw new Error("PvoxFidelityEvidence.profileVersion must use the governed profile version.");
|
|
3866
3869
|
}
|
|
3867
|
-
assertImmutableAssetVersion(
|
|
3868
|
-
const evaluatedDiagonalMetres = requireNumber(
|
|
3869
|
-
const lod0CellSizeMetres = requireNumber(
|
|
3870
|
+
assertImmutableAssetVersion(record2.profileVersion);
|
|
3871
|
+
const evaluatedDiagonalMetres = requireNumber(record2.evaluatedDiagonalMetres, "PvoxFidelityEvidence.evaluatedDiagonalMetres", Number.EPSILON, 1e9);
|
|
3872
|
+
const lod0CellSizeMetres = requireNumber(record2.lod0CellSizeMetres, "PvoxFidelityEvidence.lod0CellSizeMetres", Number.EPSILON, 1e6);
|
|
3870
3873
|
const profileFormula = {
|
|
3871
3874
|
"precision-hero-v1": [25e-5, 1e-4],
|
|
3872
3875
|
"props-furniture-v1": [5e-4, 25e-5],
|
|
@@ -3876,16 +3879,16 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3876
3879
|
const [absoluteFloor, diagonalFactor] = profileFormula[surfaceProfileId];
|
|
3877
3880
|
const governedP99Ceiling = Math.max(absoluteFloor, evaluatedDiagonalMetres * diagonalFactor);
|
|
3878
3881
|
const governedMaximumCeiling = Math.min(3 * governedP99Ceiling, 1.5 * lod0CellSizeMetres);
|
|
3879
|
-
const suppliedP99Ceiling = requireNumber(
|
|
3880
|
-
const suppliedMaximumCeiling = requireNumber(
|
|
3882
|
+
const suppliedP99Ceiling = requireNumber(record2.p99AllowedSurfaceErrorMetres, "PvoxFidelityEvidence.p99AllowedSurfaceErrorMetres", 0, 1e6);
|
|
3883
|
+
const suppliedMaximumCeiling = requireNumber(record2.maximumAllowedSurfaceErrorMetres, "PvoxFidelityEvidence.maximumAllowedSurfaceErrorMetres", 0, 1e6);
|
|
3881
3884
|
if (!approximatelyEqual(suppliedP99Ceiling, governedP99Ceiling) || !approximatelyEqual(suppliedMaximumCeiling, governedMaximumCeiling)) {
|
|
3882
3885
|
throw new Error("PvoxFidelityEvidence surface ceilings must match the governed profile formula and LOD0 cell limit.");
|
|
3883
3886
|
}
|
|
3884
|
-
const minimumSilhouetteIou = requireNumber(
|
|
3885
|
-
const maximumAllowedContourDisplacementPx = requireNumber(
|
|
3886
|
-
const minimumRenderedSsim = requireNumber(
|
|
3887
|
-
const maximumP95DeltaE2000 = requireNumber(
|
|
3888
|
-
const maximumNormalizedMaterialError = requireNumber(
|
|
3887
|
+
const minimumSilhouetteIou = requireNumber(record2.minimumSilhouetteIou, "PvoxFidelityEvidence.minimumSilhouetteIou", 0, 1);
|
|
3888
|
+
const maximumAllowedContourDisplacementPx = requireNumber(record2.maximumAllowedContourDisplacementPx, "PvoxFidelityEvidence.maximumAllowedContourDisplacementPx", 0, 1e6);
|
|
3889
|
+
const minimumRenderedSsim = requireNumber(record2.minimumRenderedSsim, "PvoxFidelityEvidence.minimumRenderedSsim", 0, 1);
|
|
3890
|
+
const maximumP95DeltaE2000 = requireNumber(record2.maximumP95DeltaE2000, "PvoxFidelityEvidence.maximumP95DeltaE2000", 0, 100);
|
|
3891
|
+
const maximumNormalizedMaterialError = requireNumber(record2.maximumNormalizedMaterialError, "PvoxFidelityEvidence.maximumNormalizedMaterialError", 0, 1);
|
|
3889
3892
|
if (!approximatelyEqual(minimumSilhouetteIou, 0.995) || !approximatelyEqual(maximumAllowedContourDisplacementPx, 0.5) || !approximatelyEqual(minimumRenderedSsim, 0.98) || !approximatelyEqual(maximumP95DeltaE2000, 3) || !approximatelyEqual(maximumNormalizedMaterialError, 1 / 255)) {
|
|
3890
3893
|
throw new Error("PvoxFidelityEvidence must use the governed silhouette, contour, render, colour, and material thresholds.");
|
|
3891
3894
|
}
|
|
@@ -3893,38 +3896,38 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3893
3896
|
profileId,
|
|
3894
3897
|
surfaceProfileId,
|
|
3895
3898
|
profileVersion: PVOX_FIDELITY_PROFILE_VERSION,
|
|
3896
|
-
canonicalDocumentHash: requireSha256(
|
|
3897
|
-
pvoxBinaryClosureHash: requireSha256(
|
|
3899
|
+
canonicalDocumentHash: requireSha256(record2.canonicalDocumentHash, "PvoxFidelityEvidence.canonicalDocumentHash"),
|
|
3900
|
+
pvoxBinaryClosureHash: requireSha256(record2.pvoxBinaryClosureHash, "PvoxFidelityEvidence.pvoxBinaryClosureHash"),
|
|
3898
3901
|
evaluatedDiagonalMetres,
|
|
3899
3902
|
lod0CellSizeMetres,
|
|
3900
3903
|
maximumAllowedSurfaceErrorMetres: governedMaximumCeiling,
|
|
3901
|
-
maximumSurfaceErrorMetres: requireNumber(
|
|
3904
|
+
maximumSurfaceErrorMetres: requireNumber(record2.maximumSurfaceErrorMetres, "PvoxFidelityEvidence.maximumSurfaceErrorMetres", 0, 1e6),
|
|
3902
3905
|
p99AllowedSurfaceErrorMetres: governedP99Ceiling,
|
|
3903
|
-
p99SurfaceErrorMetres: requireNumber(
|
|
3906
|
+
p99SurfaceErrorMetres: requireNumber(record2.p99SurfaceErrorMetres, "PvoxFidelityEvidence.p99SurfaceErrorMetres", 0, 1e6),
|
|
3904
3907
|
minimumSilhouetteIou,
|
|
3905
|
-
silhouetteIou: requireNumber(
|
|
3908
|
+
silhouetteIou: requireNumber(record2.silhouetteIou, "PvoxFidelityEvidence.silhouetteIou", 0, 1),
|
|
3906
3909
|
maximumAllowedContourDisplacementPx,
|
|
3907
|
-
maximumContourDisplacementPx: requireNumber(
|
|
3910
|
+
maximumContourDisplacementPx: requireNumber(record2.maximumContourDisplacementPx, "PvoxFidelityEvidence.maximumContourDisplacementPx", 0, 1e6),
|
|
3908
3911
|
minimumRenderedSsim,
|
|
3909
|
-
renderedSsim: requireNumber(
|
|
3912
|
+
renderedSsim: requireNumber(record2.renderedSsim, "PvoxFidelityEvidence.renderedSsim", 0, 1),
|
|
3910
3913
|
maximumP95DeltaE2000,
|
|
3911
|
-
p95DeltaE2000: requireNumber(
|
|
3914
|
+
p95DeltaE2000: requireNumber(record2.p95DeltaE2000, "PvoxFidelityEvidence.p95DeltaE2000", 0, 100),
|
|
3912
3915
|
maximumNormalizedMaterialError,
|
|
3913
|
-
normalizedMaterialError: requireNumber(
|
|
3914
|
-
evidenceHash: requireSha256(
|
|
3915
|
-
evaluatedAt: requireTimestamp(
|
|
3916
|
-
decisionToken: requireAttestation(
|
|
3916
|
+
normalizedMaterialError: requireNumber(record2.normalizedMaterialError, "PvoxFidelityEvidence.normalizedMaterialError", 0, 1),
|
|
3917
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PvoxFidelityEvidence.evidenceHash"),
|
|
3918
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "PvoxFidelityEvidence.evaluatedAt"),
|
|
3919
|
+
decisionToken: requireAttestation(record2.decisionToken, "PvoxFidelityEvidence.decisionToken")
|
|
3917
3920
|
};
|
|
3918
3921
|
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;
|
|
3919
|
-
const outcome = requireEnum(
|
|
3922
|
+
const outcome = requireEnum(record2.outcome, ["passed", "blocked"], "PvoxFidelityEvidence.outcome");
|
|
3920
3923
|
if (outcome !== (passed ? "passed" : "blocked")) throw new Error("PvoxFidelityEvidence outcome must match its measured fidelity thresholds.");
|
|
3921
3924
|
return deepFreeze2({ ...output, outcome });
|
|
3922
3925
|
}
|
|
3923
3926
|
function createPvoxEditJournalCurrentState(input, baseManifest) {
|
|
3924
3927
|
const fieldName = "PvoxEditJournalCurrentState";
|
|
3925
|
-
const
|
|
3926
|
-
exactKeys(
|
|
3927
|
-
const pageEntries = asDenseArray(
|
|
3928
|
+
const record2 = asRecord(input, fieldName);
|
|
3929
|
+
exactKeys(record2, ["baseContentHash", "basePageSetHash", "placementId", "gridVersion", "revision", "rootHash", "latestJournalHash", "pageHashes"], fieldName);
|
|
3930
|
+
const pageEntries = asDenseArray(record2.pageHashes, `${fieldName}.pageHashes`, baseManifest.pages.length, baseManifest.pages.length);
|
|
3928
3931
|
const pageHashes = pageEntries.map((entry, index) => {
|
|
3929
3932
|
const entryName = `${fieldName}.pageHashes[${index}]`;
|
|
3930
3933
|
const page = asRecord(entry, entryName);
|
|
@@ -3934,38 +3937,38 @@ function createPvoxEditJournalCurrentState(input, baseManifest) {
|
|
|
3934
3937
|
return { pageIndex, sha256: requireSha256(page.sha256, `${entryName}.sha256`) };
|
|
3935
3938
|
});
|
|
3936
3939
|
return deepFreeze2({
|
|
3937
|
-
baseContentHash: requireSha256(
|
|
3938
|
-
basePageSetHash: requireSha256(
|
|
3939
|
-
placementId: requireToken(
|
|
3940
|
-
gridVersion: requireToken(
|
|
3941
|
-
revision: requireInteger(
|
|
3942
|
-
rootHash: requireSha256(
|
|
3943
|
-
latestJournalHash: requireSha256(
|
|
3940
|
+
baseContentHash: requireSha256(record2.baseContentHash, `${fieldName}.baseContentHash`),
|
|
3941
|
+
basePageSetHash: requireSha256(record2.basePageSetHash, `${fieldName}.basePageSetHash`),
|
|
3942
|
+
placementId: requireToken(record2.placementId, `${fieldName}.placementId`),
|
|
3943
|
+
gridVersion: requireToken(record2.gridVersion, `${fieldName}.gridVersion`),
|
|
3944
|
+
revision: requireInteger(record2.revision, `${fieldName}.revision`, 1, Number.MAX_SAFE_INTEGER),
|
|
3945
|
+
rootHash: requireSha256(record2.rootHash, `${fieldName}.rootHash`),
|
|
3946
|
+
latestJournalHash: requireSha256(record2.latestJournalHash, `${fieldName}.latestJournalHash`),
|
|
3944
3947
|
pageHashes
|
|
3945
3948
|
});
|
|
3946
3949
|
}
|
|
3947
3950
|
function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
3948
|
-
const
|
|
3949
|
-
exactKeys(
|
|
3950
|
-
if (
|
|
3951
|
+
const record2 = asRecord(input, "PvoxEditJournal");
|
|
3952
|
+
exactKeys(record2, ["journalVersion", "baseContentHash", "basePageSetHash", "placementId", "gridVersion", "operationId", "expectedRevision", "resultingRevision", "previousJournalHash", "expectedRootHash", "resultingRootHash", "resultingRootHashAttestation", "patches", "dirtyBoundsMetres", "massDeltaKg", "journalHash", "journalHashAttestation", "recordedAt"], "PvoxEditJournal");
|
|
3953
|
+
if (record2.journalVersion !== PVOX_EDIT_JOURNAL_VERSION) throw new Error("PvoxEditJournal.journalVersion is unsupported.");
|
|
3951
3954
|
const baseManifest = createPvoxAssetManifestV1(baseManifestInput);
|
|
3952
|
-
const baseContentHash = requireSha256(
|
|
3953
|
-
const basePageSetHash = requireSha256(
|
|
3955
|
+
const baseContentHash = requireSha256(record2.baseContentHash, "PvoxEditJournal.baseContentHash");
|
|
3956
|
+
const basePageSetHash = requireSha256(record2.basePageSetHash, "PvoxEditJournal.basePageSetHash");
|
|
3954
3957
|
if (baseContentHash !== baseManifest.artifact.sha256 || basePageSetHash !== baseManifest.pageSetHash) {
|
|
3955
3958
|
throw new Error("PvoxEditJournal must bind the exact base PVOX artifact and page inventory.");
|
|
3956
3959
|
}
|
|
3957
|
-
const expectedRevision = requireInteger(
|
|
3958
|
-
const resultingRevision = requireInteger(
|
|
3960
|
+
const expectedRevision = requireInteger(record2.expectedRevision, "PvoxEditJournal.expectedRevision", 0, Number.MAX_SAFE_INTEGER - 1);
|
|
3961
|
+
const resultingRevision = requireInteger(record2.resultingRevision, "PvoxEditJournal.resultingRevision", 1, Number.MAX_SAFE_INTEGER);
|
|
3959
3962
|
if (resultingRevision !== expectedRevision + 1) throw new Error("PvoxEditJournal revisions must be sequential.");
|
|
3960
|
-
const previousJournalHash = requireSha256(
|
|
3963
|
+
const previousJournalHash = requireSha256(record2.previousJournalHash, "PvoxEditJournal.previousJournalHash");
|
|
3961
3964
|
if (expectedRevision === 0 !== (previousJournalHash === PVOX_EDIT_JOURNAL_GENESIS_HASH)) {
|
|
3962
3965
|
throw new Error("PvoxEditJournal genesis revision must use only the canonical genesis previousJournalHash.");
|
|
3963
3966
|
}
|
|
3964
|
-
const expectedRootHash = requireSha256(
|
|
3965
|
-
const resultingRootHash = requireSha256(
|
|
3967
|
+
const expectedRootHash = requireSha256(record2.expectedRootHash, "PvoxEditJournal.expectedRootHash");
|
|
3968
|
+
const resultingRootHash = requireSha256(record2.resultingRootHash, "PvoxEditJournal.resultingRootHash");
|
|
3966
3969
|
if (expectedRootHash === resultingRootHash) throw new Error("PvoxEditJournal copy-on-write operation must change the overlay root hash.");
|
|
3967
|
-
const placementId = requireToken(
|
|
3968
|
-
const gridVersion = requireToken(
|
|
3970
|
+
const placementId = requireToken(record2.placementId, "PvoxEditJournal.placementId");
|
|
3971
|
+
const gridVersion = requireToken(record2.gridVersion, "PvoxEditJournal.gridVersion");
|
|
3969
3972
|
let currentState;
|
|
3970
3973
|
if (expectedRevision === 0) {
|
|
3971
3974
|
if (currentStateInput !== void 0) throw new Error("PvoxEditJournal genesis must not supply a non-genesis current state.");
|
|
@@ -3977,7 +3980,7 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
|
3977
3980
|
throw new Error("PvoxEditJournal non-genesis CAS subject must match the authenticated current placement state.");
|
|
3978
3981
|
}
|
|
3979
3982
|
}
|
|
3980
|
-
const patchEntries = asDenseArray(
|
|
3983
|
+
const patchEntries = asDenseArray(record2.patches, "PvoxEditJournal.patches", 1, PVOX_MAX_EDIT_PATCHES);
|
|
3981
3984
|
const patches = patchEntries.map((entry, index) => {
|
|
3982
3985
|
const fieldName = `PvoxEditJournal.patches[${index}]`;
|
|
3983
3986
|
const patch = asRecord(entry, fieldName);
|
|
@@ -4029,26 +4032,26 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
|
4029
4032
|
if (new Set(patches.map(({ expectedPageIndex }) => expectedPageIndex)).size !== patches.length) {
|
|
4030
4033
|
throw new Error("PvoxEditJournal permits only one coherent patch transition per page.");
|
|
4031
4034
|
}
|
|
4032
|
-
const journalHash = requireSha256(
|
|
4035
|
+
const journalHash = requireSha256(record2.journalHash, "PvoxEditJournal.journalHash");
|
|
4033
4036
|
return deepFreeze2({
|
|
4034
4037
|
journalVersion: PVOX_EDIT_JOURNAL_VERSION,
|
|
4035
4038
|
baseContentHash,
|
|
4036
4039
|
basePageSetHash,
|
|
4037
4040
|
placementId,
|
|
4038
4041
|
gridVersion,
|
|
4039
|
-
operationId: requireToken(
|
|
4042
|
+
operationId: requireToken(record2.operationId, "PvoxEditJournal.operationId"),
|
|
4040
4043
|
expectedRevision,
|
|
4041
4044
|
resultingRevision,
|
|
4042
4045
|
previousJournalHash,
|
|
4043
4046
|
expectedRootHash,
|
|
4044
4047
|
resultingRootHash,
|
|
4045
|
-
resultingRootHashAttestation: createHashAttestation(
|
|
4048
|
+
resultingRootHashAttestation: createHashAttestation(record2.resultingRootHashAttestation, PVOX_EDIT_OVERLAY_ROOT_HASH_DOMAIN, resultingRootHash, "PvoxEditJournal.resultingRootHashAttestation"),
|
|
4046
4049
|
patches,
|
|
4047
|
-
dirtyBoundsMetres: createBounds2(
|
|
4048
|
-
massDeltaKg: requireNumber(
|
|
4050
|
+
dirtyBoundsMetres: createBounds2(record2.dirtyBoundsMetres, "PvoxEditJournal.dirtyBoundsMetres"),
|
|
4051
|
+
massDeltaKg: requireNumber(record2.massDeltaKg, "PvoxEditJournal.massDeltaKg", -Number.MAX_VALUE, Number.MAX_VALUE),
|
|
4049
4052
|
journalHash,
|
|
4050
|
-
journalHashAttestation: createHashAttestation(
|
|
4051
|
-
recordedAt: requireTimestamp(
|
|
4053
|
+
journalHashAttestation: createHashAttestation(record2.journalHashAttestation, PVOX_EDIT_JOURNAL_HASH_DOMAIN, journalHash, "PvoxEditJournal.journalHashAttestation"),
|
|
4054
|
+
recordedAt: requireTimestamp(record2.recordedAt, "PvoxEditJournal.recordedAt")
|
|
4052
4055
|
});
|
|
4053
4056
|
}
|
|
4054
4057
|
function createPvoxEditJournalChain(input, baseManifestInput) {
|
|
@@ -4089,55 +4092,55 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
|
|
|
4089
4092
|
return deepFreeze2(journals);
|
|
4090
4093
|
}
|
|
4091
4094
|
function createCompilerEvidence(value) {
|
|
4092
|
-
const
|
|
4093
|
-
exactKeys(
|
|
4095
|
+
const record2 = asRecord(value, "ModelProcessingManifestV2.converter");
|
|
4096
|
+
exactKeys(record2, ["compilerId", "compilerVersion", "sourceFormat", "sourceContentHash", "canonicalDocumentHash", "compilationInputHash", "runtimeRequestProfileHash", "outputContentHash", "evidenceHash", "compiledAt"], "ModelProcessingManifestV2.converter");
|
|
4094
4097
|
return {
|
|
4095
|
-
compilerId: requireToken(
|
|
4096
|
-
compilerVersion: assertImmutableAssetVersion(
|
|
4097
|
-
sourceFormat: requireToken(
|
|
4098
|
-
sourceContentHash: requireSha256(
|
|
4099
|
-
canonicalDocumentHash: requireSha256(
|
|
4100
|
-
compilationInputHash: requireSha256(
|
|
4101
|
-
runtimeRequestProfileHash: requireSha256(
|
|
4102
|
-
outputContentHash: requireSha256(
|
|
4103
|
-
evidenceHash: requireSha256(
|
|
4104
|
-
compiledAt: requireTimestamp(
|
|
4098
|
+
compilerId: requireToken(record2.compilerId, "ModelProcessingManifestV2.converter.compilerId"),
|
|
4099
|
+
compilerVersion: assertImmutableAssetVersion(record2.compilerVersion),
|
|
4100
|
+
sourceFormat: requireToken(record2.sourceFormat, "ModelProcessingManifestV2.converter.sourceFormat"),
|
|
4101
|
+
sourceContentHash: requireSha256(record2.sourceContentHash, "ModelProcessingManifestV2.converter.sourceContentHash"),
|
|
4102
|
+
canonicalDocumentHash: requireSha256(record2.canonicalDocumentHash, "ModelProcessingManifestV2.converter.canonicalDocumentHash"),
|
|
4103
|
+
compilationInputHash: requireSha256(record2.compilationInputHash, "ModelProcessingManifestV2.converter.compilationInputHash"),
|
|
4104
|
+
runtimeRequestProfileHash: requireSha256(record2.runtimeRequestProfileHash, "ModelProcessingManifestV2.converter.runtimeRequestProfileHash"),
|
|
4105
|
+
outputContentHash: requireSha256(record2.outputContentHash, "ModelProcessingManifestV2.converter.outputContentHash"),
|
|
4106
|
+
evidenceHash: requireSha256(record2.evidenceHash, "ModelProcessingManifestV2.converter.evidenceHash"),
|
|
4107
|
+
compiledAt: requireTimestamp(record2.compiledAt, "ModelProcessingManifestV2.converter.compiledAt")
|
|
4105
4108
|
};
|
|
4106
4109
|
}
|
|
4107
4110
|
function createPvoxGateEvidence(input) {
|
|
4108
|
-
const
|
|
4109
|
-
exactKeys(
|
|
4110
|
-
const outcome = requireEnum(
|
|
4111
|
-
const reasonCodes2 = requireReasonCodes(
|
|
4111
|
+
const record2 = asRecord(input, "PvoxGateEvidence");
|
|
4112
|
+
exactKeys(record2, ["kind", "outcome", "validatorId", "validatorVersion", "subjectContentHash", "evidenceHash", "reasonCodes", "evaluatedAt", "attestationToken"], "PvoxGateEvidence");
|
|
4113
|
+
const outcome = requireEnum(record2.outcome, ["passed", "blocked"], "PvoxGateEvidence.outcome");
|
|
4114
|
+
const reasonCodes2 = requireReasonCodes(record2.reasonCodes, "PvoxGateEvidence.reasonCodes");
|
|
4112
4115
|
if (outcome === "blocked" && reasonCodes2.length === 0) throw new Error("Blocked PvoxGateEvidence requires a reason code.");
|
|
4113
4116
|
return deepFreeze2({
|
|
4114
|
-
kind: requireEnum(
|
|
4117
|
+
kind: requireEnum(record2.kind, PVOX_NON_OVERRIDABLE_GATE_KINDS, "PvoxGateEvidence.kind"),
|
|
4115
4118
|
outcome,
|
|
4116
|
-
validatorId: requireToken(
|
|
4117
|
-
validatorVersion: assertImmutableAssetVersion(
|
|
4118
|
-
subjectContentHash: requireSha256(
|
|
4119
|
-
evidenceHash: requireSha256(
|
|
4119
|
+
validatorId: requireToken(record2.validatorId, "PvoxGateEvidence.validatorId"),
|
|
4120
|
+
validatorVersion: assertImmutableAssetVersion(record2.validatorVersion),
|
|
4121
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "PvoxGateEvidence.subjectContentHash"),
|
|
4122
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PvoxGateEvidence.evidenceHash"),
|
|
4120
4123
|
reasonCodes: reasonCodes2,
|
|
4121
|
-
evaluatedAt: requireTimestamp(
|
|
4122
|
-
attestationToken: requireAttestation(
|
|
4124
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "PvoxGateEvidence.evaluatedAt"),
|
|
4125
|
+
attestationToken: requireAttestation(record2.attestationToken, "PvoxGateEvidence.attestationToken")
|
|
4123
4126
|
});
|
|
4124
4127
|
}
|
|
4125
4128
|
function createTransform2(value, fieldName) {
|
|
4126
|
-
const
|
|
4127
|
-
exactKeys(
|
|
4128
|
-
const translationMetres = requireVector(
|
|
4129
|
-
const rotationEntries = asDenseArray(
|
|
4129
|
+
const record2 = asRecord(value, fieldName);
|
|
4130
|
+
exactKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
|
|
4131
|
+
const translationMetres = requireVector(record2.translationMetres, `${fieldName}.translationMetres`);
|
|
4132
|
+
const rotationEntries = asDenseArray(record2.rotationQuaternion, `${fieldName}.rotationQuaternion`, 4, 4);
|
|
4130
4133
|
const rotationQuaternion = rotationEntries.map((entry, index) => requireNumber(entry, `${fieldName}.rotationQuaternion[${index}]`, -1, 1));
|
|
4131
4134
|
if (Math.abs(Math.hypot(...rotationQuaternion) - 1) > 1e-4) throw new Error(`${fieldName}.rotationQuaternion must be normalized.`);
|
|
4132
|
-
const scale = requireVector(
|
|
4135
|
+
const scale = requireVector(record2.scale, `${fieldName}.scale`);
|
|
4133
4136
|
if (scale.some((entry) => entry <= 0)) throw new Error(`${fieldName}.scale values must be positive.`);
|
|
4134
4137
|
return { translationMetres, rotationQuaternion, scale };
|
|
4135
4138
|
}
|
|
4136
4139
|
function createAssemblyChild2(value, index, resolutionId, candidateId) {
|
|
4137
4140
|
const fieldName = `ModelProcessingManifestV2.children[${index}]`;
|
|
4138
|
-
const
|
|
4139
|
-
exactKeys(
|
|
4140
|
-
const childRef = asRecord(
|
|
4141
|
+
const record2 = asRecord(value, fieldName);
|
|
4142
|
+
exactKeys(record2, ["instanceId", "parentInstanceId", "assetRef", "transform"], fieldName);
|
|
4143
|
+
const childRef = asRecord(record2.assetRef, `${fieldName}.assetRef`);
|
|
4141
4144
|
let assetRef;
|
|
4142
4145
|
if (childRef.disposition === "staged-derived") {
|
|
4143
4146
|
exactKeys(childRef, ["disposition", "representation", "derivedId", "kind", "contentHash", "binaryClosureHash", "processingClosureHash", "processingManifestId", "processingManifestUri"], `${fieldName}.assetRef`);
|
|
@@ -4174,12 +4177,12 @@ function createAssemblyChild2(value, index, resolutionId, candidateId) {
|
|
|
4174
4177
|
processingManifestUri: expectedUri
|
|
4175
4178
|
};
|
|
4176
4179
|
}
|
|
4177
|
-
const parentInstanceId =
|
|
4180
|
+
const parentInstanceId = record2.parentInstanceId === void 0 ? void 0 : requireToken(record2.parentInstanceId, `${fieldName}.parentInstanceId`);
|
|
4178
4181
|
return {
|
|
4179
|
-
instanceId: requireToken(
|
|
4182
|
+
instanceId: requireToken(record2.instanceId, `${fieldName}.instanceId`),
|
|
4180
4183
|
...parentInstanceId === void 0 ? {} : { parentInstanceId },
|
|
4181
4184
|
assetRef,
|
|
4182
|
-
transform: createTransform2(
|
|
4185
|
+
transform: createTransform2(record2.transform, `${fieldName}.transform`)
|
|
4183
4186
|
};
|
|
4184
4187
|
}
|
|
4185
4188
|
function assertAssembly(children) {
|
|
@@ -4221,8 +4224,8 @@ var NON_DEFAULT_CRITICAL_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
4221
4224
|
"melting-temperature"
|
|
4222
4225
|
]);
|
|
4223
4226
|
function createModelProcessingManifestV2(input) {
|
|
4224
|
-
const
|
|
4225
|
-
exactKeys(
|
|
4227
|
+
const record2 = asRecord(input, "ModelProcessingManifestV2");
|
|
4228
|
+
exactKeys(record2, [
|
|
4226
4229
|
"contractVersion",
|
|
4227
4230
|
"representation",
|
|
4228
4231
|
"manifestId",
|
|
@@ -4259,24 +4262,24 @@ function createModelProcessingManifestV2(input) {
|
|
|
4259
4262
|
"children",
|
|
4260
4263
|
"processedAt"
|
|
4261
4264
|
], "ModelProcessingManifestV2");
|
|
4262
|
-
if (
|
|
4263
|
-
if (
|
|
4264
|
-
const resolutionId = requirePathSegment(
|
|
4265
|
-
const candidateId = requirePathSegment(
|
|
4266
|
-
assertCoordinateSystem2(
|
|
4267
|
-
const pvox = createPvoxAssetManifestV1(
|
|
4265
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelProcessingManifestV2.contractVersion is unsupported.");
|
|
4266
|
+
if (record2.representation !== "pvox") throw new Error("ModelProcessingManifestV2.representation must be pvox.");
|
|
4267
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelProcessingManifestV2.resolutionId");
|
|
4268
|
+
const candidateId = requirePathSegment(record2.candidateId, "ModelProcessingManifestV2.candidateId");
|
|
4269
|
+
assertCoordinateSystem2(record2.coordinateSystem, "ModelProcessingManifestV2.coordinateSystem");
|
|
4270
|
+
const pvox = createPvoxAssetManifestV1(record2.pvox);
|
|
4268
4271
|
const resourceScope = parsePvoxResourceScope(pvox.artifact.uri);
|
|
4269
4272
|
if (resourceScope.kind === "resolution" && (resourceScope.resolutionId !== resolutionId || resourceScope.candidateId !== candidateId)) {
|
|
4270
4273
|
throw new Error("ModelProcessingManifestV2 PVOX resource namespace must match resolutionId and candidateId.");
|
|
4271
4274
|
}
|
|
4272
|
-
const technicalProfile = createVoxelTechnicalProfile(
|
|
4273
|
-
const contentHash = requireSha256(
|
|
4274
|
-
const binaryClosureHash = requireSha256(
|
|
4275
|
-
const processingClosureHash = requireSha256(
|
|
4276
|
-
const assemblyClosureHash = requireSha256(
|
|
4277
|
-
const requestSemanticProfileHash = requireSha256(
|
|
4278
|
-
const compilationInputHash = requireSha256(
|
|
4279
|
-
const runtimeRequestProfileHash = requireSha256(
|
|
4275
|
+
const technicalProfile = createVoxelTechnicalProfile(record2.technicalProfile);
|
|
4276
|
+
const contentHash = requireSha256(record2.contentHash, "ModelProcessingManifestV2.contentHash");
|
|
4277
|
+
const binaryClosureHash = requireSha256(record2.binaryClosureHash, "ModelProcessingManifestV2.binaryClosureHash");
|
|
4278
|
+
const processingClosureHash = requireSha256(record2.processingClosureHash, "ModelProcessingManifestV2.processingClosureHash");
|
|
4279
|
+
const assemblyClosureHash = requireSha256(record2.assemblyClosureHash, "ModelProcessingManifestV2.assemblyClosureHash");
|
|
4280
|
+
const requestSemanticProfileHash = requireSha256(record2.requestSemanticProfileHash, "ModelProcessingManifestV2.requestSemanticProfileHash");
|
|
4281
|
+
const compilationInputHash = requireSha256(record2.compilationInputHash, "ModelProcessingManifestV2.compilationInputHash");
|
|
4282
|
+
const runtimeRequestProfileHash = requireSha256(record2.runtimeRequestProfileHash, "ModelProcessingManifestV2.runtimeRequestProfileHash");
|
|
4280
4283
|
if (contentHash !== pvox.artifact.sha256) throw new Error("ModelProcessingManifestV2 contentHash must match the PVOX artifact.");
|
|
4281
4284
|
if (binaryClosureHash !== pvox.binaryClosureHash) throw new Error("ModelProcessingManifestV2 binaryClosureHash must match the PVOX binary closure.");
|
|
4282
4285
|
if (compilationInputHash !== pvox.compilationInputHash || runtimeRequestProfileHash !== pvox.runtimeRequestProfileHash) {
|
|
@@ -4296,20 +4299,20 @@ function createModelProcessingManifestV2(input) {
|
|
|
4296
4299
|
if (technicalProfile.maximumPartitionExtentMetres > maximumGridExtentMetres && !approximatelyEqual(technicalProfile.maximumPartitionExtentMetres, maximumGridExtentMetres)) {
|
|
4297
4300
|
throw new Error("ModelProcessingManifestV2 partition extent exceeds the LOD0 cell and hierarchy grid capacity; use smaller fidelity-driven partitions.");
|
|
4298
4301
|
}
|
|
4299
|
-
const capabilityEntries = asDenseArray(
|
|
4302
|
+
const capabilityEntries = asDenseArray(record2.capabilities, "ModelProcessingManifestV2.capabilities", PVOX_CAPABILITIES.length, PVOX_CAPABILITIES.length);
|
|
4300
4303
|
const capabilities = capabilityEntries.map(createVoxelCapabilityAssessment);
|
|
4301
4304
|
const byCapability = new Map(capabilities.map((entry) => [entry.capability, entry]));
|
|
4302
4305
|
if (byCapability.size !== PVOX_CAPABILITIES.length || PVOX_CAPABILITIES.some((name) => !byCapability.has(name))) throw new Error("ModelProcessingManifestV2 must include every PVOX capability exactly once.");
|
|
4303
4306
|
const orderedCapabilities = PVOX_CAPABILITIES.map((name) => byCapability.get(name));
|
|
4304
4307
|
if (orderedCapabilities.some((entry) => entry.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 capability evidence must bind the binaryClosureHash.");
|
|
4305
|
-
const capabilityEvidenceSetHash = requireSha256(
|
|
4308
|
+
const capabilityEvidenceSetHash = requireSha256(record2.capabilityEvidenceSetHash, "ModelProcessingManifestV2.capabilityEvidenceSetHash");
|
|
4306
4309
|
const capabilityEvidenceSetAttestation = createHashAttestation(
|
|
4307
|
-
|
|
4310
|
+
record2.capabilityEvidenceSetAttestation,
|
|
4308
4311
|
PVOX_CAPABILITY_SET_HASH_DOMAIN,
|
|
4309
4312
|
capabilityEvidenceSetHash,
|
|
4310
4313
|
"ModelProcessingManifestV2.capabilityEvidenceSetAttestation"
|
|
4311
4314
|
);
|
|
4312
|
-
const requiredCapabilityEntries = asDenseArray(
|
|
4315
|
+
const requiredCapabilityEntries = asDenseArray(record2.requiredCapabilities, "ModelProcessingManifestV2.requiredCapabilities", 1, PVOX_CAPABILITIES.length);
|
|
4313
4316
|
const requiredCapabilities = requiredCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `ModelProcessingManifestV2.requiredCapabilities[${index}]`));
|
|
4314
4317
|
if (new Set(requiredCapabilities).size !== requiredCapabilities.length) throw new Error("ModelProcessingManifestV2.requiredCapabilities must be unique.");
|
|
4315
4318
|
if (!requiredCapabilities.includes("rendering")) throw new Error("ModelProcessingManifestV2 rendering capability is always required.");
|
|
@@ -4317,11 +4320,11 @@ function createModelProcessingManifestV2(input) {
|
|
|
4317
4320
|
if (technicalProfile.hasCollision && collisionPages.every(({ spatialKey }) => spatialKey?.lodLevel !== 0)) {
|
|
4318
4321
|
throw new Error("ModelProcessingManifestV2 supported collision requires LOD0 collision-field evidence.");
|
|
4319
4322
|
}
|
|
4320
|
-
const physicalRegionInventory = createPvoxPhysicalRegionInventoryV1(
|
|
4323
|
+
const physicalRegionInventory = createPvoxPhysicalRegionInventoryV1(record2.physicalRegionInventory);
|
|
4321
4324
|
if (physicalRegionInventory.subjectContentHash !== binaryClosureHash || physicalRegionInventory.validationEvidenceHash !== pvox.validationEvidenceHash || physicalRegionInventory.entries.length !== technicalProfile.materialRegionCount || physicalRegionInventory.entries.some(({ physicalPaletteIndex }) => physicalPaletteIndex >= technicalProfile.physicalPaletteRecordCount)) {
|
|
4322
4325
|
throw new Error("ModelProcessingManifestV2 authoritative physical region inventory must bind the PVOX validator, closure, region count, and palette records.");
|
|
4323
4326
|
}
|
|
4324
|
-
const physicalEntries = asDenseArray(
|
|
4327
|
+
const physicalEntries = asDenseArray(record2.physicalProperties, "ModelProcessingManifestV2.physicalProperties", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
|
|
4325
4328
|
const physicalProperties = physicalEntries.map(createPhysicalPropertyEvidence);
|
|
4326
4329
|
const byRegionProperty = new Map(physicalProperties.map((entry) => [`${entry.regionId}\0${entry.materialId}\0${entry.property}`, entry]));
|
|
4327
4330
|
if (byRegionProperty.size !== physicalProperties.length) throw new Error("ModelProcessingManifestV2 physical properties must be unique per region, material, and property.");
|
|
@@ -4361,15 +4364,15 @@ function createModelProcessingManifestV2(input) {
|
|
|
4361
4364
|
if (physicalProperties.length !== technicalProfile.physicalEvidenceRecordCount) {
|
|
4362
4365
|
throw new Error("ModelProcessingManifestV2 authoritative material-region evidence count must match the PVOX PEVI record count.");
|
|
4363
4366
|
}
|
|
4364
|
-
const physicalEvidenceHash = requireSha256(
|
|
4367
|
+
const physicalEvidenceHash = requireSha256(record2.physicalEvidenceHash, "ModelProcessingManifestV2.physicalEvidenceHash");
|
|
4365
4368
|
const physicalEvidenceAttestation = createHashAttestation(
|
|
4366
|
-
|
|
4369
|
+
record2.physicalEvidenceAttestation,
|
|
4367
4370
|
PVOX_PHYSICAL_EVIDENCE_HASH_DOMAIN,
|
|
4368
4371
|
physicalEvidenceHash,
|
|
4369
4372
|
"ModelProcessingManifestV2.physicalEvidenceAttestation"
|
|
4370
4373
|
);
|
|
4371
4374
|
const massPropertiesEvidence = createReviewedStructuralEvidence(
|
|
4372
|
-
|
|
4375
|
+
record2.massPropertiesEvidence,
|
|
4373
4376
|
"mass-properties",
|
|
4374
4377
|
binaryClosureHash,
|
|
4375
4378
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4377,7 +4380,7 @@ function createModelProcessingManifestV2(input) {
|
|
|
4377
4380
|
"ModelProcessingManifestV2.massPropertiesEvidence"
|
|
4378
4381
|
);
|
|
4379
4382
|
const bondGraphEvidence = createReviewedStructuralEvidence(
|
|
4380
|
-
|
|
4383
|
+
record2.bondGraphEvidence,
|
|
4381
4384
|
"bond-graph",
|
|
4382
4385
|
binaryClosureHash,
|
|
4383
4386
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4385,7 +4388,7 @@ function createModelProcessingManifestV2(input) {
|
|
|
4385
4388
|
"ModelProcessingManifestV2.bondGraphEvidence"
|
|
4386
4389
|
);
|
|
4387
4390
|
const interiorLayerEvidence = createReviewedStructuralEvidence(
|
|
4388
|
-
|
|
4391
|
+
record2.interiorLayerEvidence,
|
|
4389
4392
|
"interior-layers",
|
|
4390
4393
|
binaryClosureHash,
|
|
4391
4394
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4395,9 +4398,9 @@ function createModelProcessingManifestV2(input) {
|
|
|
4395
4398
|
if (byCapability.get("destruction")?.status === "supported" && (technicalProfile.massPropertyRecordCount === 0 || technicalProfile.bondRecordCount === 0 || technicalProfile.interiorLayerRecordCount === 0)) {
|
|
4396
4399
|
throw new Error("ModelProcessingManifestV2 destruction capability requires reviewed mass, bond-graph, and interior-layer records.");
|
|
4397
4400
|
}
|
|
4398
|
-
const converter = createCompilerEvidence(
|
|
4401
|
+
const converter = createCompilerEvidence(record2.converter);
|
|
4399
4402
|
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.");
|
|
4400
|
-
const fidelity = createPvoxFidelityEvidence(
|
|
4403
|
+
const fidelity = createPvoxFidelityEvidence(record2.fidelity);
|
|
4401
4404
|
const diagonalMetres = technicalProfile.maximumPartitionDiagonalMetres;
|
|
4402
4405
|
if (fidelity.canonicalDocumentHash !== pvox.canonicalDocumentHash || fidelity.pvoxBinaryClosureHash !== binaryClosureHash || !approximatelyEqual(fidelity.evaluatedDiagonalMetres, diagonalMetres) || !approximatelyEqual(fidelity.lod0CellSizeMetres, pvox.lods[0].cellSizeMetres)) {
|
|
4403
4406
|
throw new Error("ModelProcessingManifestV2 fidelity evidence must bind the canonical document, PVOX closure, evaluated bounds, and LOD0 cell size.");
|
|
@@ -4406,22 +4409,22 @@ function createModelProcessingManifestV2(input) {
|
|
|
4406
4409
|
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)) {
|
|
4407
4410
|
throw new Error("ModelProcessingManifestV2 fidelity evidence must exactly match every governed PVOX LOD0 metric.");
|
|
4408
4411
|
}
|
|
4409
|
-
const childEntries = asDenseArray(
|
|
4412
|
+
const childEntries = asDenseArray(record2.children, "ModelProcessingManifestV2.children", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
|
|
4410
4413
|
const children = childEntries.map((entry, index) => createAssemblyChild2(entry, index, resolutionId, candidateId));
|
|
4411
|
-
const kind = requireEnum(
|
|
4414
|
+
const kind = requireEnum(record2.kind, ["leaf", "assembly"], "ModelProcessingManifestV2.kind");
|
|
4412
4415
|
if (kind === "leaf" && children.length !== 0) throw new Error("A leaf ModelProcessingManifestV2 cannot contain children.");
|
|
4413
4416
|
if (kind === "leaf" && technicalProfile.partitionCount !== 1) throw new Error("A leaf ModelProcessingManifestV2 must have one partition.");
|
|
4414
4417
|
if (kind === "leaf" && assemblyClosureHash === binaryClosureHash) throw new Error("A leaf ModelProcessingManifestV2 requires a distinct domain-separated empty-child assemblyClosureHash.");
|
|
4415
4418
|
if (kind === "assembly" && children.length === 0) throw new Error("An assembly ModelProcessingManifestV2 requires children.");
|
|
4416
4419
|
if (kind === "assembly") assertAssembly(children);
|
|
4417
|
-
const assemblyClosureAttestation = createHashAttestation(
|
|
4420
|
+
const assemblyClosureAttestation = createHashAttestation(record2.assemblyClosureAttestation, PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN, assemblyClosureHash, "ModelProcessingManifestV2.assemblyClosureAttestation");
|
|
4418
4421
|
const processingClosureAttestation = createHashAttestation(
|
|
4419
|
-
|
|
4422
|
+
record2.processingClosureAttestation,
|
|
4420
4423
|
PVOX_PROCESSING_CLOSURE_HASH_DOMAIN,
|
|
4421
4424
|
processingClosureHash,
|
|
4422
4425
|
"ModelProcessingManifestV2.processingClosureAttestation"
|
|
4423
4426
|
);
|
|
4424
|
-
const processedAt = requireTimestamp(
|
|
4427
|
+
const processedAt = requireTimestamp(record2.processedAt, "ModelProcessingManifestV2.processedAt");
|
|
4425
4428
|
const latestEvidence = Math.max(
|
|
4426
4429
|
Date.parse(converter.compiledAt),
|
|
4427
4430
|
Date.parse(fidelity.evaluatedAt),
|
|
@@ -4435,13 +4438,13 @@ function createModelProcessingManifestV2(input) {
|
|
|
4435
4438
|
return deepFreeze2({
|
|
4436
4439
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4437
4440
|
representation: "pvox",
|
|
4438
|
-
manifestId: requireToken(
|
|
4441
|
+
manifestId: requireToken(record2.manifestId, "ModelProcessingManifestV2.manifestId"),
|
|
4439
4442
|
resolutionId,
|
|
4440
4443
|
candidateId,
|
|
4441
4444
|
kind,
|
|
4442
|
-
requestProfileId: requireToken(
|
|
4445
|
+
requestProfileId: requireToken(record2.requestProfileId, "ModelProcessingManifestV2.requestProfileId"),
|
|
4443
4446
|
requestSemanticProfileHash,
|
|
4444
|
-
capabilityProfileId: requireEnum(
|
|
4447
|
+
capabilityProfileId: requireEnum(record2.capabilityProfileId, PVOX_RUNTIME_CAPABILITY_PROFILE_IDS, "ModelProcessingManifestV2.capabilityProfileId"),
|
|
4445
4448
|
contentHash,
|
|
4446
4449
|
binaryClosureHash,
|
|
4447
4450
|
processingClosureHash,
|
|
@@ -4471,76 +4474,76 @@ function createModelProcessingManifestV2(input) {
|
|
|
4471
4474
|
});
|
|
4472
4475
|
}
|
|
4473
4476
|
function createCandidateAssetRef2(value) {
|
|
4474
|
-
const
|
|
4475
|
-
if (
|
|
4476
|
-
exactKeys(
|
|
4477
|
-
assertDataOnlyTree(
|
|
4478
|
-
const asset = createModelAssetRef(
|
|
4479
|
-
if (
|
|
4477
|
+
const record2 = asRecord(value, "ModelCandidateV2.assetRef");
|
|
4478
|
+
if (record2.disposition === "existing") {
|
|
4479
|
+
exactKeys(record2, ["disposition", "kind", "contentHash", "asset"], "ModelCandidateV2.assetRef");
|
|
4480
|
+
assertDataOnlyTree(record2.asset, "ModelCandidateV2.assetRef.asset");
|
|
4481
|
+
const asset = createModelAssetRef(record2.asset);
|
|
4482
|
+
if (record2.kind !== asset.kind || record2.contentHash !== asset.contentHash) throw new Error("ModelCandidateV2 existing assetRef must match the immutable asset.");
|
|
4480
4483
|
return { disposition: "existing", kind: asset.kind, contentHash: asset.contentHash, asset };
|
|
4481
4484
|
}
|
|
4482
|
-
exactKeys(
|
|
4483
|
-
if (
|
|
4485
|
+
exactKeys(record2, ["disposition", "proposalId", "kind", "contentHash"], "ModelCandidateV2.assetRef");
|
|
4486
|
+
if (record2.disposition !== "proposed") throw new Error("ModelCandidateV2.assetRef disposition is unsupported.");
|
|
4484
4487
|
return {
|
|
4485
4488
|
disposition: "proposed",
|
|
4486
|
-
proposalId: requireToken(
|
|
4487
|
-
kind: requireEnum(
|
|
4488
|
-
contentHash: requireSha256(
|
|
4489
|
+
proposalId: requireToken(record2.proposalId, "ModelCandidateV2.assetRef.proposalId"),
|
|
4490
|
+
kind: requireEnum(record2.kind, ["leaf", "assembly"], "ModelCandidateV2.assetRef.kind"),
|
|
4491
|
+
contentHash: requireSha256(record2.contentHash, "ModelCandidateV2.assetRef.contentHash")
|
|
4489
4492
|
};
|
|
4490
4493
|
}
|
|
4491
4494
|
function createViews(value, resolutionId, candidateId) {
|
|
4492
4495
|
const entries = asDenseArray(value, "ModelCandidateV2.views", 4, 4);
|
|
4493
4496
|
const parsed = entries.map((entry, index) => {
|
|
4494
4497
|
const fieldName = `ModelCandidateV2.views[${index}]`;
|
|
4495
|
-
const
|
|
4496
|
-
exactKeys(
|
|
4497
|
-
const kind = requireEnum(
|
|
4498
|
+
const record2 = asRecord(entry, fieldName);
|
|
4499
|
+
exactKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
|
|
4500
|
+
const kind = requireEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`);
|
|
4498
4501
|
const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/${kind}.png`;
|
|
4499
|
-
if (
|
|
4500
|
-
if (
|
|
4501
|
-
return { kind, imageUri: expectedUri, sha256: requireSha256(
|
|
4502
|
+
if (record2.imageUri !== expectedUri) throw new Error(`${fieldName}.imageUri must match the candidate view identity.`);
|
|
4503
|
+
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.`);
|
|
4504
|
+
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 };
|
|
4502
4505
|
});
|
|
4503
4506
|
if (parsed.some((entry, index) => entry.kind !== MODEL_CONFIRMATION_VIEW_KINDS[index])) throw new Error("ModelCandidateV2.views must be ordered front, left, top, isometric.");
|
|
4504
4507
|
return parsed;
|
|
4505
4508
|
}
|
|
4506
4509
|
function createRenderEvidence2(value) {
|
|
4507
|
-
const
|
|
4508
|
-
exactKeys(
|
|
4509
|
-
if (
|
|
4510
|
+
const record2 = asRecord(value, "ModelCandidateV2.renderEvidence");
|
|
4511
|
+
exactKeys(record2, ["renderId", "representation", "lodLevel", "traversalBackend", "rendererId", "rendererVersion", "rendererQualificationHash", "settingsId", "settingsVersion", "cameraQualificationHash", "processingManifestId", "processingClosureHash", "pvoxContentHash", "pvoxRootHash", "pvoxDirectoryHash", "pvoxPageSetHash", "viewSha256s", "renderedAt", "evidenceHash", "evidenceHashAttestation"], "ModelCandidateV2.renderEvidence");
|
|
4512
|
+
if (record2.representation !== "pvox" || record2.lodLevel !== 0 || record2.traversalBackend !== "native-pvox") {
|
|
4510
4513
|
throw new Error("ModelCandidateV2 render evidence must use native PVOX LOD0 traversal.");
|
|
4511
4514
|
}
|
|
4512
|
-
if (
|
|
4515
|
+
if (record2.rendererId !== PVOX_NATIVE_RENDERER_ID || record2.settingsId !== PVOX_NATIVE_REVIEW_SETTINGS_ID) {
|
|
4513
4516
|
throw new Error("ModelCandidateV2 render evidence must use the governed native renderer and review settings.");
|
|
4514
4517
|
}
|
|
4515
|
-
const evidenceHash = requireSha256(
|
|
4518
|
+
const evidenceHash = requireSha256(record2.evidenceHash, "ModelCandidateV2.renderEvidence.evidenceHash");
|
|
4516
4519
|
return {
|
|
4517
|
-
renderId: requireToken(
|
|
4520
|
+
renderId: requireToken(record2.renderId, "ModelCandidateV2.renderEvidence.renderId"),
|
|
4518
4521
|
representation: "pvox",
|
|
4519
4522
|
lodLevel: 0,
|
|
4520
4523
|
traversalBackend: "native-pvox",
|
|
4521
4524
|
rendererId: PVOX_NATIVE_RENDERER_ID,
|
|
4522
|
-
rendererVersion: assertImmutableAssetVersion(
|
|
4523
|
-
rendererQualificationHash: requireSha256(
|
|
4525
|
+
rendererVersion: assertImmutableAssetVersion(record2.rendererVersion),
|
|
4526
|
+
rendererQualificationHash: requireSha256(record2.rendererQualificationHash, "ModelCandidateV2.renderEvidence.rendererQualificationHash"),
|
|
4524
4527
|
settingsId: PVOX_NATIVE_REVIEW_SETTINGS_ID,
|
|
4525
|
-
settingsVersion: assertImmutableAssetVersion(
|
|
4526
|
-
cameraQualificationHash: requireSha256(
|
|
4527
|
-
processingManifestId: requireToken(
|
|
4528
|
-
processingClosureHash: requireSha256(
|
|
4529
|
-
pvoxContentHash: requireSha256(
|
|
4530
|
-
pvoxRootHash: requireSha256(
|
|
4531
|
-
pvoxDirectoryHash: requireSha256(
|
|
4532
|
-
pvoxPageSetHash: requireSha256(
|
|
4533
|
-
viewSha256s: requireShaTuple(
|
|
4534
|
-
renderedAt: requireTimestamp(
|
|
4528
|
+
settingsVersion: assertImmutableAssetVersion(record2.settingsVersion),
|
|
4529
|
+
cameraQualificationHash: requireSha256(record2.cameraQualificationHash, "ModelCandidateV2.renderEvidence.cameraQualificationHash"),
|
|
4530
|
+
processingManifestId: requireToken(record2.processingManifestId, "ModelCandidateV2.renderEvidence.processingManifestId"),
|
|
4531
|
+
processingClosureHash: requireSha256(record2.processingClosureHash, "ModelCandidateV2.renderEvidence.processingClosureHash"),
|
|
4532
|
+
pvoxContentHash: requireSha256(record2.pvoxContentHash, "ModelCandidateV2.renderEvidence.pvoxContentHash"),
|
|
4533
|
+
pvoxRootHash: requireSha256(record2.pvoxRootHash, "ModelCandidateV2.renderEvidence.pvoxRootHash"),
|
|
4534
|
+
pvoxDirectoryHash: requireSha256(record2.pvoxDirectoryHash, "ModelCandidateV2.renderEvidence.pvoxDirectoryHash"),
|
|
4535
|
+
pvoxPageSetHash: requireSha256(record2.pvoxPageSetHash, "ModelCandidateV2.renderEvidence.pvoxPageSetHash"),
|
|
4536
|
+
viewSha256s: requireShaTuple(record2.viewSha256s, "ModelCandidateV2.renderEvidence.viewSha256s"),
|
|
4537
|
+
renderedAt: requireTimestamp(record2.renderedAt, "ModelCandidateV2.renderEvidence.renderedAt"),
|
|
4535
4538
|
evidenceHash,
|
|
4536
|
-
evidenceHashAttestation: createHashAttestation(
|
|
4539
|
+
evidenceHashAttestation: createHashAttestation(record2.evidenceHashAttestation, PVOX_RENDER_EVIDENCE_HASH_DOMAIN, evidenceHash, "ModelCandidateV2.renderEvidence.evidenceHashAttestation")
|
|
4537
4540
|
};
|
|
4538
4541
|
}
|
|
4539
4542
|
function createPvoxModelMatchAssessmentV2(input) {
|
|
4540
4543
|
const fieldName = "PvoxModelMatchAssessmentV2";
|
|
4541
|
-
const
|
|
4542
|
-
assertDataOnlyTree(
|
|
4543
|
-
exactKeys(
|
|
4544
|
+
const record2 = asRecord(input, fieldName);
|
|
4545
|
+
assertDataOnlyTree(record2, fieldName);
|
|
4546
|
+
exactKeys(record2, [
|
|
4544
4547
|
"contractVersion",
|
|
4545
4548
|
"score",
|
|
4546
4549
|
"assurance",
|
|
@@ -4557,65 +4560,65 @@ function createPvoxModelMatchAssessmentV2(input) {
|
|
|
4557
4560
|
"evidenceHash"
|
|
4558
4561
|
], fieldName);
|
|
4559
4562
|
const assessment = createModelMatchAssessment({
|
|
4560
|
-
...
|
|
4561
|
-
score:
|
|
4562
|
-
...
|
|
4563
|
-
hardConstraintPass:
|
|
4564
|
-
exactMatch:
|
|
4565
|
-
reasonCodes:
|
|
4566
|
-
ranker:
|
|
4567
|
-
fidelityWarnings:
|
|
4568
|
-
request:
|
|
4569
|
-
candidateId:
|
|
4570
|
-
candidateContentHash:
|
|
4563
|
+
...record2.contractVersion === void 0 ? {} : { contractVersion: record2.contractVersion },
|
|
4564
|
+
score: record2.score,
|
|
4565
|
+
...record2.assurance === void 0 ? {} : { assurance: record2.assurance },
|
|
4566
|
+
hardConstraintPass: record2.hardConstraintPass,
|
|
4567
|
+
exactMatch: record2.exactMatch,
|
|
4568
|
+
reasonCodes: record2.reasonCodes,
|
|
4569
|
+
ranker: record2.ranker,
|
|
4570
|
+
fidelityWarnings: record2.fidelityWarnings,
|
|
4571
|
+
request: record2.request,
|
|
4572
|
+
candidateId: record2.candidateId,
|
|
4573
|
+
candidateContentHash: record2.candidateContentHash
|
|
4571
4574
|
});
|
|
4572
4575
|
return deepFreeze2({
|
|
4573
4576
|
...assessment,
|
|
4574
|
-
requestSemanticProfileHash: requireSha256(
|
|
4575
|
-
requestSemanticProfileCanonical: requireString(
|
|
4576
|
-
evidenceHash: requireSha256(
|
|
4577
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, `${fieldName}.requestSemanticProfileHash`),
|
|
4578
|
+
requestSemanticProfileCanonical: requireString(record2.requestSemanticProfileCanonical, `${fieldName}.requestSemanticProfileCanonical`, 65536),
|
|
4579
|
+
evidenceHash: requireSha256(record2.evidenceHash, `${fieldName}.evidenceHash`)
|
|
4577
4580
|
});
|
|
4578
4581
|
}
|
|
4579
4582
|
function createConfirmationBinding(value) {
|
|
4580
|
-
const
|
|
4581
|
-
assertDataOnlyTree(
|
|
4582
|
-
exactKeys(
|
|
4583
|
-
const bindingHash = requireSha256(
|
|
4584
|
-
const hardGateEntries = asDenseArray(
|
|
4583
|
+
const record2 = asRecord(value, "ModelCandidateV2.confirmationBinding");
|
|
4584
|
+
assertDataOnlyTree(record2, "ModelCandidateV2.confirmationBinding");
|
|
4585
|
+
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");
|
|
4586
|
+
const bindingHash = requireSha256(record2.bindingHash, "ModelCandidateV2.confirmationBinding.bindingHash");
|
|
4587
|
+
const hardGateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.confirmationBinding.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
|
|
4585
4588
|
return {
|
|
4586
4589
|
bindingHash,
|
|
4587
|
-
bindingHashAttestation: createHashAttestation(
|
|
4588
|
-
resolutionId: requirePathSegment(
|
|
4589
|
-
requesterId: requireToken(
|
|
4590
|
-
request: createModelRequestSpecV2(
|
|
4591
|
-
requestSemanticProfileHash: requireSha256(
|
|
4592
|
-
requestRevision: requireInteger(
|
|
4593
|
-
candidateId: requirePathSegment(
|
|
4594
|
-
candidateContentHash: requireSha256(
|
|
4595
|
-
candidateAssetRef: createCandidateAssetRef2(
|
|
4596
|
-
match: createPvoxModelMatchAssessmentV2(
|
|
4597
|
-
provenance: createModelProvenance(
|
|
4598
|
-
rights: createModelRightsAssessment(
|
|
4599
|
-
sourceContentHash: requireSha256(
|
|
4600
|
-
binaryClosureHash: requireSha256(
|
|
4601
|
-
pvoxRootHash: requireSha256(
|
|
4602
|
-
pvoxDirectoryHash: requireSha256(
|
|
4603
|
-
pvoxPageSetHash: requireSha256(
|
|
4604
|
-
compilationInputHash: requireSha256(
|
|
4605
|
-
runtimeRequestProfileHash: requireSha256(
|
|
4606
|
-
processingClosureHash: requireSha256(
|
|
4607
|
-
assemblyClosureHash: requireSha256(
|
|
4608
|
-
evaluationClosureHash: requireSha256(
|
|
4609
|
-
capabilityEvidenceSetHash: requireSha256(
|
|
4590
|
+
bindingHashAttestation: createHashAttestation(record2.bindingHashAttestation, PVOX_CONFIRMATION_BINDING_HASH_DOMAIN, bindingHash, "ModelCandidateV2.confirmationBinding.bindingHashAttestation"),
|
|
4591
|
+
resolutionId: requirePathSegment(record2.resolutionId, "ModelCandidateV2.confirmationBinding.resolutionId"),
|
|
4592
|
+
requesterId: requireToken(record2.requesterId, "ModelCandidateV2.confirmationBinding.requesterId"),
|
|
4593
|
+
request: createModelRequestSpecV2(record2.request),
|
|
4594
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, "ModelCandidateV2.confirmationBinding.requestSemanticProfileHash"),
|
|
4595
|
+
requestRevision: requireInteger(record2.requestRevision, "ModelCandidateV2.confirmationBinding.requestRevision", 0, MODEL_REQUEST_MAX_REVISION),
|
|
4596
|
+
candidateId: requirePathSegment(record2.candidateId, "ModelCandidateV2.confirmationBinding.candidateId"),
|
|
4597
|
+
candidateContentHash: requireSha256(record2.candidateContentHash, "ModelCandidateV2.confirmationBinding.candidateContentHash"),
|
|
4598
|
+
candidateAssetRef: createCandidateAssetRef2(record2.candidateAssetRef),
|
|
4599
|
+
match: createPvoxModelMatchAssessmentV2(record2.match),
|
|
4600
|
+
provenance: createModelProvenance(record2.provenance),
|
|
4601
|
+
rights: createModelRightsAssessment(record2.rights),
|
|
4602
|
+
sourceContentHash: requireSha256(record2.sourceContentHash, "ModelCandidateV2.confirmationBinding.sourceContentHash"),
|
|
4603
|
+
binaryClosureHash: requireSha256(record2.binaryClosureHash, "ModelCandidateV2.confirmationBinding.binaryClosureHash"),
|
|
4604
|
+
pvoxRootHash: requireSha256(record2.pvoxRootHash, "ModelCandidateV2.confirmationBinding.pvoxRootHash"),
|
|
4605
|
+
pvoxDirectoryHash: requireSha256(record2.pvoxDirectoryHash, "ModelCandidateV2.confirmationBinding.pvoxDirectoryHash"),
|
|
4606
|
+
pvoxPageSetHash: requireSha256(record2.pvoxPageSetHash, "ModelCandidateV2.confirmationBinding.pvoxPageSetHash"),
|
|
4607
|
+
compilationInputHash: requireSha256(record2.compilationInputHash, "ModelCandidateV2.confirmationBinding.compilationInputHash"),
|
|
4608
|
+
runtimeRequestProfileHash: requireSha256(record2.runtimeRequestProfileHash, "ModelCandidateV2.confirmationBinding.runtimeRequestProfileHash"),
|
|
4609
|
+
processingClosureHash: requireSha256(record2.processingClosureHash, "ModelCandidateV2.confirmationBinding.processingClosureHash"),
|
|
4610
|
+
assemblyClosureHash: requireSha256(record2.assemblyClosureHash, "ModelCandidateV2.confirmationBinding.assemblyClosureHash"),
|
|
4611
|
+
evaluationClosureHash: requireSha256(record2.evaluationClosureHash, "ModelCandidateV2.confirmationBinding.evaluationClosureHash"),
|
|
4612
|
+
capabilityEvidenceSetHash: requireSha256(record2.capabilityEvidenceSetHash, "ModelCandidateV2.confirmationBinding.capabilityEvidenceSetHash"),
|
|
4610
4613
|
hardGates: hardGateEntries.map(createPvoxGateEvidence),
|
|
4611
|
-
viewSha256s: requireShaTuple(
|
|
4612
|
-
rightsDecisionId: requireToken(
|
|
4613
|
-
fidelityEvidenceHash: requireSha256(
|
|
4614
|
-
physicalEvidenceHash: requireSha256(
|
|
4615
|
-
rendererId: requireToken(
|
|
4616
|
-
rendererVersion: assertImmutableAssetVersion(
|
|
4617
|
-
rendererEvidenceHash: requireSha256(
|
|
4618
|
-
renderEvidence: createRenderEvidence2(
|
|
4614
|
+
viewSha256s: requireShaTuple(record2.viewSha256s, "ModelCandidateV2.confirmationBinding.viewSha256s"),
|
|
4615
|
+
rightsDecisionId: requireToken(record2.rightsDecisionId, "ModelCandidateV2.confirmationBinding.rightsDecisionId"),
|
|
4616
|
+
fidelityEvidenceHash: requireSha256(record2.fidelityEvidenceHash, "ModelCandidateV2.confirmationBinding.fidelityEvidenceHash"),
|
|
4617
|
+
physicalEvidenceHash: requireSha256(record2.physicalEvidenceHash, "ModelCandidateV2.confirmationBinding.physicalEvidenceHash"),
|
|
4618
|
+
rendererId: requireToken(record2.rendererId, "ModelCandidateV2.confirmationBinding.rendererId"),
|
|
4619
|
+
rendererVersion: assertImmutableAssetVersion(record2.rendererVersion),
|
|
4620
|
+
rendererEvidenceHash: requireSha256(record2.rendererEvidenceHash, "ModelCandidateV2.confirmationBinding.rendererEvidenceHash"),
|
|
4621
|
+
renderEvidence: createRenderEvidence2(record2.renderEvidence)
|
|
4619
4622
|
};
|
|
4620
4623
|
}
|
|
4621
4624
|
function assertRequestMatchesProcessing(request, processing) {
|
|
@@ -4650,27 +4653,27 @@ function assertRequestMatchesProcessing(request, processing) {
|
|
|
4650
4653
|
}
|
|
4651
4654
|
}
|
|
4652
4655
|
function createModelCandidateV2(input) {
|
|
4653
|
-
const
|
|
4654
|
-
assertDataOnlyTree(
|
|
4656
|
+
const record2 = asRecord(input, "ModelCandidateV2");
|
|
4657
|
+
assertDataOnlyTree(record2, "ModelCandidateV2");
|
|
4655
4658
|
const commonKeys = ["contractVersion", "resolutionId", "requesterId", "candidateId", "request", "admissionStatus", "assetRef", "match", "provenance", "rights", "processingManifest", "views", "renderEvidence", "evaluationClosureHash", "evaluationClosureAttestation", "hardGates", "confirmationRequired"];
|
|
4656
|
-
const admissionStatus = requireEnum(
|
|
4657
|
-
exactKeys(
|
|
4658
|
-
if (
|
|
4659
|
-
const resolutionId = requirePathSegment(
|
|
4660
|
-
const requesterId = requireToken(
|
|
4661
|
-
const candidateId = requirePathSegment(
|
|
4662
|
-
const request = createModelRequestSpecV2(
|
|
4663
|
-
const assetRef = createCandidateAssetRef2(
|
|
4664
|
-
assertDataOnlyTree(
|
|
4665
|
-
assertDataOnlyTree(
|
|
4666
|
-
assertDataOnlyTree(
|
|
4667
|
-
const match = createPvoxModelMatchAssessmentV2(
|
|
4659
|
+
const admissionStatus = requireEnum(record2.admissionStatus, ["confirmable", "diagnostic"], "ModelCandidateV2.admissionStatus");
|
|
4660
|
+
exactKeys(record2, admissionStatus === "confirmable" ? [...commonKeys, "confirmationBinding", "confirmationToken"] : [...commonKeys, "blockingReasonCodes"], "ModelCandidateV2");
|
|
4661
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelCandidateV2.contractVersion is unsupported.");
|
|
4662
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelCandidateV2.resolutionId");
|
|
4663
|
+
const requesterId = requireToken(record2.requesterId, "ModelCandidateV2.requesterId");
|
|
4664
|
+
const candidateId = requirePathSegment(record2.candidateId, "ModelCandidateV2.candidateId");
|
|
4665
|
+
const request = createModelRequestSpecV2(record2.request);
|
|
4666
|
+
const assetRef = createCandidateAssetRef2(record2.assetRef);
|
|
4667
|
+
assertDataOnlyTree(record2.match, "ModelCandidateV2.match");
|
|
4668
|
+
assertDataOnlyTree(record2.provenance, "ModelCandidateV2.provenance");
|
|
4669
|
+
assertDataOnlyTree(record2.rights, "ModelCandidateV2.rights");
|
|
4670
|
+
const match = createPvoxModelMatchAssessmentV2(record2.match);
|
|
4668
4671
|
assertImmutableAssetVersion(match.ranker.version);
|
|
4669
4672
|
assertImmutableAssetVersion(match.ranker.calibrationVersion);
|
|
4670
|
-
const provenance = createModelProvenance(
|
|
4671
|
-
const rights = createModelRightsAssessment(
|
|
4673
|
+
const provenance = createModelProvenance(record2.provenance);
|
|
4674
|
+
const rights = createModelRightsAssessment(record2.rights);
|
|
4672
4675
|
assertImmutableAssetVersion(rights.policyVersion);
|
|
4673
|
-
const processingManifest = createModelProcessingManifestV2(
|
|
4676
|
+
const processingManifest = createModelProcessingManifestV2(record2.processingManifest);
|
|
4674
4677
|
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.");
|
|
4675
4678
|
if (match.candidateId !== candidateId || match.candidateContentHash !== assetRef.contentHash) throw new Error("ModelCandidateV2 match must bind the exact candidate content.");
|
|
4676
4679
|
if (match.requestSemanticProfileHash !== request.requestSemanticProfileHash) throw new Error("ModelCandidateV2 semantic assessment must bind the complete normalized v2 request profile hash.");
|
|
@@ -4686,18 +4689,18 @@ function createModelCandidateV2(input) {
|
|
|
4686
4689
|
if (assetRef.disposition === "existing" && (resourceScope.kind !== "catalog" || resourceScope.assetId !== assetRef.asset.assetId || resourceScope.version !== assetRef.asset.version)) {
|
|
4687
4690
|
throw new Error("ModelCandidateV2 existing asset must prove the same immutable catalog PVOX representation.");
|
|
4688
4691
|
}
|
|
4689
|
-
const views = createViews(
|
|
4690
|
-
const renderEvidence = createRenderEvidence2(
|
|
4692
|
+
const views = createViews(record2.views, resolutionId, candidateId);
|
|
4693
|
+
const renderEvidence = createRenderEvidence2(record2.renderEvidence);
|
|
4691
4694
|
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.");
|
|
4692
4695
|
if (Date.parse(renderEvidence.renderedAt) < Date.parse(processingManifest.processedAt)) throw new Error("ModelCandidateV2 render evidence must follow processing.");
|
|
4693
|
-
const evaluationClosureHash = requireSha256(
|
|
4696
|
+
const evaluationClosureHash = requireSha256(record2.evaluationClosureHash, "ModelCandidateV2.evaluationClosureHash");
|
|
4694
4697
|
const evaluationClosureAttestation = createHashAttestation(
|
|
4695
|
-
|
|
4698
|
+
record2.evaluationClosureAttestation,
|
|
4696
4699
|
PVOX_EVALUATION_CLOSURE_HASH_DOMAIN,
|
|
4697
4700
|
evaluationClosureHash,
|
|
4698
4701
|
"ModelCandidateV2.evaluationClosureAttestation"
|
|
4699
4702
|
);
|
|
4700
|
-
const gateEntries = asDenseArray(
|
|
4703
|
+
const gateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
|
|
4701
4704
|
const gates = gateEntries.map(createPvoxGateEvidence);
|
|
4702
4705
|
const byGate = new Map(gates.map((entry) => [entry.kind, entry]));
|
|
4703
4706
|
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.");
|
|
@@ -4747,16 +4750,16 @@ function createModelCandidateV2(input) {
|
|
|
4747
4750
|
hardGates: orderedGates
|
|
4748
4751
|
};
|
|
4749
4752
|
if (admissionStatus === "diagnostic") {
|
|
4750
|
-
if (
|
|
4751
|
-
if (
|
|
4752
|
-
const suppliedEntries = asDenseArray(
|
|
4753
|
+
if (record2.confirmationRequired !== false) throw new Error("Diagnostic ModelCandidateV2 confirmationRequired must be false.");
|
|
4754
|
+
if (record2.blockingReasonCodes !== void 0) {
|
|
4755
|
+
const suppliedEntries = asDenseArray(record2.blockingReasonCodes, "ModelCandidateV2.blockingReasonCodes", 1, 256);
|
|
4753
4756
|
const suppliedBlockingReasons = suppliedEntries.map((entry, index) => requireReasonCode(entry, `ModelCandidateV2.blockingReasonCodes[${index}]`));
|
|
4754
4757
|
if (JSON.stringify(suppliedBlockingReasons) !== JSON.stringify(blockingReasonCodes)) throw new Error("Diagnostic ModelCandidateV2 blockingReasonCodes must be derived from its evidence.");
|
|
4755
4758
|
}
|
|
4756
4759
|
return deepFreeze2({ ...common, admissionStatus: "diagnostic", blockingReasonCodes, confirmationRequired: false });
|
|
4757
4760
|
}
|
|
4758
|
-
if (
|
|
4759
|
-
const confirmationBinding = createConfirmationBinding(
|
|
4761
|
+
if (record2.confirmationRequired !== true) throw new Error("Confirmable ModelCandidateV2 confirmationRequired must be true.");
|
|
4762
|
+
const confirmationBinding = createConfirmationBinding(record2.confirmationBinding);
|
|
4760
4763
|
const expectedBinding = {
|
|
4761
4764
|
resolutionId,
|
|
4762
4765
|
requesterId,
|
|
@@ -4793,7 +4796,7 @@ function createModelCandidateV2(input) {
|
|
|
4793
4796
|
if (JSON.stringify(confirmationBinding.renderEvidence) !== JSON.stringify(renderEvidence)) throw new Error("ModelCandidateV2 confirmationBinding must bind the complete native renderer evidence.");
|
|
4794
4797
|
if (JSON.stringify(confirmationBinding.hardGates) !== JSON.stringify(orderedGates)) throw new Error("ModelCandidateV2 confirmationBinding must contain the complete ordered hard-gate evidence set.");
|
|
4795
4798
|
if (confirmationBinding.viewSha256s.some((digest, index) => digest !== views[index].sha256)) throw new Error("ModelCandidateV2 confirmationBinding view hashes must match candidate views.");
|
|
4796
|
-
const confirmationToken = requireAttestation(
|
|
4799
|
+
const confirmationToken = requireAttestation(record2.confirmationToken, "ModelCandidateV2.confirmationToken");
|
|
4797
4800
|
if (confirmationBinding.bindingHashAttestation.attestationToken !== confirmationToken) throw new Error("ModelCandidateV2 confirmation token must attest the exact confirmation binding hash.");
|
|
4798
4801
|
return deepFreeze2({
|
|
4799
4802
|
...common,
|
|
@@ -4807,21 +4810,21 @@ function isModelCandidateV2Confirmable(candidate) {
|
|
|
4807
4810
|
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");
|
|
4808
4811
|
}
|
|
4809
4812
|
function createModelCandidateConfirmationV2(input, expectedCandidate, expectedResolutionId) {
|
|
4810
|
-
const
|
|
4811
|
-
exactKeys(
|
|
4812
|
-
if (
|
|
4813
|
+
const record2 = asRecord(input, "ModelCandidateConfirmationV2");
|
|
4814
|
+
exactKeys(record2, ["contractVersion", "confirmationId", "resolutionId", "requesterId", "candidateId", "confirmationToken", "confirmationBindingHash", "viewSha256s", "confirmedBy", "confirmedAt", "semanticRiskAccepted"], "ModelCandidateConfirmationV2");
|
|
4815
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelCandidateConfirmationV2.contractVersion is unsupported.");
|
|
4813
4816
|
const candidate = createModelCandidateV2(expectedCandidate);
|
|
4814
4817
|
if (candidate.admissionStatus !== "confirmable") throw new Error("ModelCandidateConfirmationV2 cannot confirm a diagnostic candidate.");
|
|
4815
4818
|
const resolutionId = requirePathSegment(expectedResolutionId, "ModelCandidateConfirmationV2 expected resolutionId");
|
|
4816
|
-
if (candidate.resolutionId !== resolutionId ||
|
|
4817
|
-
if (
|
|
4818
|
-
if (
|
|
4819
|
-
const viewSha256s = requireShaTuple(
|
|
4819
|
+
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.");
|
|
4820
|
+
if (record2.confirmationToken !== candidate.confirmationToken) throw new Error("ModelCandidateConfirmationV2 confirmationToken must match the candidate.");
|
|
4821
|
+
if (record2.confirmationBindingHash !== candidate.confirmationBinding.bindingHash) throw new Error("ModelCandidateConfirmationV2 confirmation binding must match the candidate.");
|
|
4822
|
+
const viewSha256s = requireShaTuple(record2.viewSha256s, "ModelCandidateConfirmationV2.viewSha256s");
|
|
4820
4823
|
if (viewSha256s.some((digest, index) => digest !== candidate.views[index].sha256)) throw new Error("ModelCandidateConfirmationV2 view hashes must match candidate evidence.");
|
|
4821
4824
|
if (!isModelCandidateV2Confirmable(candidate)) throw new Error("ModelCandidateConfirmationV2 candidate is not confirmable because a hard gate is blocked.");
|
|
4822
|
-
const semanticRiskAccepted = requireBoolean(
|
|
4825
|
+
const semanticRiskAccepted = requireBoolean(record2.semanticRiskAccepted, "ModelCandidateConfirmationV2.semanticRiskAccepted");
|
|
4823
4826
|
if (candidate.match.assurance === "low" !== semanticRiskAccepted) throw new Error("Only low semantic assurance requires and permits semantic risk acceptance.");
|
|
4824
|
-
const confirmedAt = requireTimestamp(
|
|
4827
|
+
const confirmedAt = requireTimestamp(record2.confirmedAt, "ModelCandidateConfirmationV2.confirmedAt");
|
|
4825
4828
|
const latestEvidence = Math.max(
|
|
4826
4829
|
Date.parse(candidate.provenance.capturedAt),
|
|
4827
4830
|
Date.parse(candidate.rights.reviewedAt),
|
|
@@ -4830,12 +4833,12 @@ function createModelCandidateConfirmationV2(input, expectedCandidate, expectedRe
|
|
|
4830
4833
|
...candidate.hardGates.map(({ evaluatedAt }) => Date.parse(evaluatedAt))
|
|
4831
4834
|
);
|
|
4832
4835
|
if (Date.parse(confirmedAt) < latestEvidence) throw new Error("ModelCandidateConfirmationV2 confirmation must follow all required evidence.");
|
|
4833
|
-
const confirmedBy = requireString(
|
|
4836
|
+
const confirmedBy = requireString(record2.confirmedBy, "ModelCandidateConfirmationV2.confirmedBy", 256);
|
|
4834
4837
|
if (DIRECT_URL.test(confirmedBy)) throw new Error("ModelCandidateConfirmationV2.confirmedBy must not expose a URL.");
|
|
4835
4838
|
if (confirmedBy !== candidate.requesterId) throw new Error("ModelCandidateConfirmationV2 confirmedBy must be the requester bound to the resolution.");
|
|
4836
4839
|
return deepFreeze2({
|
|
4837
4840
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4838
|
-
confirmationId: requireToken(
|
|
4841
|
+
confirmationId: requireToken(record2.confirmationId, "ModelCandidateConfirmationV2.confirmationId"),
|
|
4839
4842
|
resolutionId,
|
|
4840
4843
|
requesterId: candidate.requesterId,
|
|
4841
4844
|
candidateId: candidate.candidateId,
|
|
@@ -4859,8 +4862,8 @@ function candidatesEquivalent(left, right) {
|
|
|
4859
4862
|
}
|
|
4860
4863
|
function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmation, finalAssetRef) {
|
|
4861
4864
|
if (candidate.assetRef.disposition !== "proposed") throw new Error("ModelPromotionReceiptV2 is only valid for a proposed candidate.");
|
|
4862
|
-
const
|
|
4863
|
-
exactKeys(
|
|
4865
|
+
const record2 = asRecord(input, "ModelPromotionReceiptV2");
|
|
4866
|
+
exactKeys(record2, [
|
|
4864
4867
|
"contractVersion",
|
|
4865
4868
|
"promotionId",
|
|
4866
4869
|
"resolutionId",
|
|
@@ -4891,38 +4894,38 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4891
4894
|
"promotedAt",
|
|
4892
4895
|
"publicationToken"
|
|
4893
4896
|
], "ModelPromotionReceiptV2");
|
|
4894
|
-
if (
|
|
4897
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION || record2.publicationState !== "pointer-last-complete") {
|
|
4895
4898
|
throw new Error("ModelPromotionReceiptV2 must use the v2 contract and prove pointer-last completion.");
|
|
4896
4899
|
}
|
|
4897
|
-
if (
|
|
4900
|
+
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) {
|
|
4898
4901
|
throw new Error("ModelPromotionReceiptV2 must bind the exact proposal, confirmation, processing content, and PVOX assembly closure.");
|
|
4899
4902
|
}
|
|
4900
|
-
assertDataOnlyTree(
|
|
4901
|
-
const candidateAssetRef = createCandidateAssetRef2(
|
|
4903
|
+
assertDataOnlyTree(record2.candidateAssetRef, "ModelPromotionReceiptV2.candidateAssetRef");
|
|
4904
|
+
const candidateAssetRef = createCandidateAssetRef2(record2.candidateAssetRef);
|
|
4902
4905
|
if (JSON.stringify(candidateAssetRef) !== JSON.stringify(candidate.assetRef)) throw new Error("ModelPromotionReceiptV2 must bind the exact proposed candidate identity.");
|
|
4903
|
-
const childClosureEntries = asDenseArray(
|
|
4906
|
+
const childClosureEntries = asDenseArray(record2.assemblyChildClosureHashes, "ModelPromotionReceiptV2.assemblyChildClosureHashes", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
|
|
4904
4907
|
const assemblyChildClosureHashes = childClosureEntries.map((entry, index) => requireSha256(entry, `ModelPromotionReceiptV2.assemblyChildClosureHashes[${index}]`));
|
|
4905
4908
|
const expectedChildClosureHashes = candidate.processingManifest.children.map(({ assetRef }) => assetRef.binaryClosureHash);
|
|
4906
4909
|
if (JSON.stringify(assemblyChildClosureHashes) !== JSON.stringify(expectedChildClosureHashes)) throw new Error("ModelPromotionReceiptV2 child closure sequence must match the canonical assembly order.");
|
|
4907
|
-
assertDataOnlyTree(
|
|
4908
|
-
const receiptAssetRef = createModelAssetRef(
|
|
4910
|
+
assertDataOnlyTree(record2.finalAssetRef, "ModelPromotionReceiptV2.finalAssetRef");
|
|
4911
|
+
const receiptAssetRef = createModelAssetRef(record2.finalAssetRef);
|
|
4909
4912
|
if (JSON.stringify(receiptAssetRef) !== JSON.stringify(finalAssetRef)) throw new Error("ModelPromotionReceiptV2 finalAssetRef must match the completed resolution.");
|
|
4910
|
-
const creditsWrittenAt = requireTimestamp(
|
|
4911
|
-
const catalogRowWrittenAt = requireTimestamp(
|
|
4912
|
-
const indexSnapshotWrittenAt = requireTimestamp(
|
|
4913
|
-
const pointerPublishedAt = requireTimestamp(
|
|
4914
|
-
const promotedAt = requireTimestamp(
|
|
4913
|
+
const creditsWrittenAt = requireTimestamp(record2.creditsWrittenAt, "ModelPromotionReceiptV2.creditsWrittenAt");
|
|
4914
|
+
const catalogRowWrittenAt = requireTimestamp(record2.catalogRowWrittenAt, "ModelPromotionReceiptV2.catalogRowWrittenAt");
|
|
4915
|
+
const indexSnapshotWrittenAt = requireTimestamp(record2.indexSnapshotWrittenAt, "ModelPromotionReceiptV2.indexSnapshotWrittenAt");
|
|
4916
|
+
const pointerPublishedAt = requireTimestamp(record2.pointerPublishedAt, "ModelPromotionReceiptV2.pointerPublishedAt");
|
|
4917
|
+
const promotedAt = requireTimestamp(record2.promotedAt, "ModelPromotionReceiptV2.promotedAt");
|
|
4915
4918
|
const publicationSequence = [confirmation.confirmedAt, creditsWrittenAt, catalogRowWrittenAt, indexSnapshotWrittenAt, pointerPublishedAt, promotedAt].map(Date.parse);
|
|
4916
4919
|
if (publicationSequence.some((time, index) => index > 0 && time < publicationSequence[index - 1])) {
|
|
4917
4920
|
throw new Error("ModelPromotionReceiptV2 must prove credits, catalog row, index snapshot, and pointers were published in pointer-last order.");
|
|
4918
4921
|
}
|
|
4919
|
-
const publicationHash = requireSha256(
|
|
4920
|
-
const publicationHashAttestation = createHashAttestation(
|
|
4921
|
-
const publicationToken = requireAttestation(
|
|
4922
|
+
const publicationHash = requireSha256(record2.publicationHash, "ModelPromotionReceiptV2.publicationHash");
|
|
4923
|
+
const publicationHashAttestation = createHashAttestation(record2.publicationHashAttestation, PVOX_PUBLICATION_HASH_DOMAIN, publicationHash, "ModelPromotionReceiptV2.publicationHashAttestation");
|
|
4924
|
+
const publicationToken = requireAttestation(record2.publicationToken, "ModelPromotionReceiptV2.publicationToken");
|
|
4922
4925
|
if (publicationHashAttestation.attestationToken !== publicationToken) throw new Error("ModelPromotionReceiptV2 publication token must attest the exact publication hash.");
|
|
4923
4926
|
return deepFreeze2({
|
|
4924
4927
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4925
|
-
promotionId: requireToken(
|
|
4928
|
+
promotionId: requireToken(record2.promotionId, "ModelPromotionReceiptV2.promotionId"),
|
|
4926
4929
|
resolutionId,
|
|
4927
4930
|
requesterId: candidate.requesterId,
|
|
4928
4931
|
candidateId: candidate.candidateId,
|
|
@@ -4936,11 +4939,11 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4936
4939
|
assemblyClosureHash: candidate.processingManifest.assemblyClosureHash,
|
|
4937
4940
|
assemblyChildClosureHashes,
|
|
4938
4941
|
finalAssetRef: receiptAssetRef,
|
|
4939
|
-
creditsRecordHash: requireSha256(
|
|
4940
|
-
catalogRowHash: requireSha256(
|
|
4941
|
-
indexSnapshotHash: requireSha256(
|
|
4942
|
-
catalogPointerEtag: requireToken(
|
|
4943
|
-
indexPointerEtag: requireToken(
|
|
4942
|
+
creditsRecordHash: requireSha256(record2.creditsRecordHash, "ModelPromotionReceiptV2.creditsRecordHash"),
|
|
4943
|
+
catalogRowHash: requireSha256(record2.catalogRowHash, "ModelPromotionReceiptV2.catalogRowHash"),
|
|
4944
|
+
indexSnapshotHash: requireSha256(record2.indexSnapshotHash, "ModelPromotionReceiptV2.indexSnapshotHash"),
|
|
4945
|
+
catalogPointerEtag: requireToken(record2.catalogPointerEtag, "ModelPromotionReceiptV2.catalogPointerEtag"),
|
|
4946
|
+
indexPointerEtag: requireToken(record2.indexPointerEtag, "ModelPromotionReceiptV2.indexPointerEtag"),
|
|
4944
4947
|
publicationState: "pointer-last-complete",
|
|
4945
4948
|
creditsWrittenAt,
|
|
4946
4949
|
catalogRowWrittenAt,
|
|
@@ -4954,43 +4957,43 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4954
4957
|
}
|
|
4955
4958
|
function createModelRefinementQuestionV2(input, index) {
|
|
4956
4959
|
const fieldName = `ModelResolutionV2.refinementQuestions[${index}]`;
|
|
4957
|
-
const
|
|
4958
|
-
exactKeys(
|
|
4960
|
+
const record2 = asRecord(input, fieldName);
|
|
4961
|
+
exactKeys(record2, ["questionId", "prompt", "reasonCodes"], fieldName);
|
|
4959
4962
|
return deepFreeze2({
|
|
4960
|
-
questionId: requireToken(
|
|
4961
|
-
prompt: requireString(
|
|
4962
|
-
reasonCodes: requireReasonCodes(
|
|
4963
|
+
questionId: requireToken(record2.questionId, `${fieldName}.questionId`),
|
|
4964
|
+
prompt: requireString(record2.prompt, `${fieldName}.prompt`, 500),
|
|
4965
|
+
reasonCodes: requireReasonCodes(record2.reasonCodes, `${fieldName}.reasonCodes`)
|
|
4963
4966
|
});
|
|
4964
4967
|
}
|
|
4965
4968
|
function createModelResolutionV2(input) {
|
|
4966
|
-
const
|
|
4967
|
-
exactKeys(
|
|
4968
|
-
if (
|
|
4969
|
-
const resolutionId = requirePathSegment(
|
|
4970
|
-
const requesterId = requireToken(
|
|
4971
|
-
const request = createModelRequestSpecV2(
|
|
4972
|
-
const state = requireEnum(
|
|
4969
|
+
const record2 = asRecord(input, "ModelResolutionV2");
|
|
4970
|
+
exactKeys(record2, ["contractVersion", "resolutionId", "requesterId", "request", "attempts", "state", "compatibilityState", "candidates", "bestCandidate", "confirmation", "promotionReceipt", "refinementQuestions", "finalAssetRef", "stateReasonCode", "createdAt", "updatedAt"], "ModelResolutionV2");
|
|
4971
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelResolutionV2.contractVersion is unsupported.");
|
|
4972
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelResolutionV2.resolutionId");
|
|
4973
|
+
const requesterId = requireToken(record2.requesterId, "ModelResolutionV2.requesterId");
|
|
4974
|
+
const request = createModelRequestSpecV2(record2.request);
|
|
4975
|
+
const state = requireEnum(record2.state, MODEL_RESOLUTION_V2_STATES, "ModelResolutionV2.state");
|
|
4973
4976
|
const compatibilityState = toLegacyModelResolutionState(state);
|
|
4974
|
-
if (
|
|
4975
|
-
const candidateEntries = asDenseArray(
|
|
4977
|
+
if (record2.compatibilityState !== void 0 && record2.compatibilityState !== compatibilityState) throw new Error("ModelResolutionV2 compatibilityState must match its deterministic legacy projection.");
|
|
4978
|
+
const candidateEntries = asDenseArray(record2.candidates, "ModelResolutionV2.candidates", 0, 20);
|
|
4976
4979
|
const candidates = candidateEntries.map(createModelCandidateV2);
|
|
4977
4980
|
if (new Set(candidates.map(({ candidateId }) => candidateId)).size !== candidates.length) throw new Error("ModelResolutionV2 candidate IDs must be unique.");
|
|
4978
4981
|
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.");
|
|
4979
|
-
const bestCandidate =
|
|
4982
|
+
const bestCandidate = record2.bestCandidate === void 0 ? void 0 : createModelCandidateV2(record2.bestCandidate);
|
|
4980
4983
|
if (bestCandidate !== void 0 && !candidates.some((candidate) => candidatesEquivalent(candidate, bestCandidate))) throw new Error("ModelResolutionV2 bestCandidate must be one exact candidate in the resolution.");
|
|
4981
4984
|
if (state === "awaiting-confirmation" && (bestCandidate === void 0 || !isModelCandidateV2Confirmable(bestCandidate))) throw new Error("ModelResolutionV2 awaiting-confirmation requires one confirmable bestCandidate.");
|
|
4982
|
-
const refinementEntries = asDenseArray(
|
|
4985
|
+
const refinementEntries = asDenseArray(record2.refinementQuestions, "ModelResolutionV2.refinementQuestions", 0, 3);
|
|
4983
4986
|
const refinementQuestions = refinementEntries.map(createModelRefinementQuestionV2);
|
|
4984
4987
|
if (new Set(refinementQuestions.map(({ questionId }) => questionId)).size !== refinementQuestions.length) throw new Error("ModelResolutionV2 refinement question IDs must be unique.");
|
|
4985
4988
|
if (state === "awaiting-confirmation" && bestCandidate?.match.assurance === "low" && refinementQuestions.length === 0) throw new Error("Low-assurance ModelResolutionV2 requires refinement questions.");
|
|
4986
|
-
if (state === "completed" &&
|
|
4987
|
-
const confirmation =
|
|
4989
|
+
if (state === "completed" && record2.finalAssetRef === void 0) throw new Error("ModelResolutionV2 completed state requires finalAssetRef.");
|
|
4990
|
+
const confirmation = record2.confirmation === void 0 ? void 0 : bestCandidate === void 0 ? (() => {
|
|
4988
4991
|
throw new Error("ModelResolutionV2 confirmation requires bestCandidate.");
|
|
4989
|
-
})() : createModelCandidateConfirmationV2(
|
|
4992
|
+
})() : createModelCandidateConfirmationV2(record2.confirmation, bestCandidate, resolutionId);
|
|
4990
4993
|
if (["promoting", "completed"].includes(state) && confirmation === void 0) throw new Error(`ModelResolutionV2 ${state} requires confirmation.`);
|
|
4991
4994
|
if (!["promoting", "completed"].includes(state) && confirmation !== void 0) throw new Error("ModelResolutionV2 confirmation is only allowed while promoting or completed.");
|
|
4992
|
-
if (
|
|
4993
|
-
const finalAssetRef =
|
|
4995
|
+
if (record2.finalAssetRef !== void 0) assertDataOnlyTree(record2.finalAssetRef, "ModelResolutionV2.finalAssetRef");
|
|
4996
|
+
const finalAssetRef = record2.finalAssetRef === void 0 ? void 0 : createModelAssetRef(record2.finalAssetRef);
|
|
4994
4997
|
if (state === "completed" && finalAssetRef === void 0) throw new Error("ModelResolutionV2 completed state requires finalAssetRef.");
|
|
4995
4998
|
if (state !== "completed" && finalAssetRef !== void 0) throw new Error("ModelResolutionV2 finalAssetRef is only allowed for completed state.");
|
|
4996
4999
|
if (finalAssetRef !== void 0 && bestCandidate !== void 0) {
|
|
@@ -5005,21 +5008,21 @@ function createModelResolutionV2(input) {
|
|
|
5005
5008
|
}
|
|
5006
5009
|
}
|
|
5007
5010
|
let promotionReceipt;
|
|
5008
|
-
if (
|
|
5011
|
+
if (record2.promotionReceipt !== void 0) {
|
|
5009
5012
|
if (state !== "completed" || finalAssetRef === void 0 || confirmation === void 0 || bestCandidate?.admissionStatus !== "confirmable" || bestCandidate.assetRef.disposition !== "proposed") {
|
|
5010
5013
|
throw new Error("ModelResolutionV2 promotionReceipt is only allowed for a completed confirmed proposed candidate.");
|
|
5011
5014
|
}
|
|
5012
|
-
promotionReceipt = createModelPromotionReceiptV2(
|
|
5015
|
+
promotionReceipt = createModelPromotionReceiptV2(record2.promotionReceipt, resolutionId, bestCandidate, confirmation, finalAssetRef);
|
|
5013
5016
|
}
|
|
5014
5017
|
if (state === "completed" && bestCandidate?.assetRef.disposition === "proposed" && promotionReceipt === void 0) {
|
|
5015
5018
|
throw new Error("ModelResolutionV2 completed proposed candidates require a pointer-last promotionReceipt.");
|
|
5016
5019
|
}
|
|
5017
5020
|
if (bestCandidate?.assetRef.disposition === "existing" && promotionReceipt !== void 0) throw new Error("ModelResolutionV2 existing selections must not include a promotionReceipt.");
|
|
5018
|
-
const stateReasonCode =
|
|
5021
|
+
const stateReasonCode = record2.stateReasonCode === void 0 ? void 0 : requireReasonCode(record2.stateReasonCode, "ModelResolutionV2.stateReasonCode");
|
|
5019
5022
|
if (["unresolved", "failed", "cancelled"].includes(state) && stateReasonCode === void 0) throw new Error(`ModelResolutionV2 ${state} state requires stateReasonCode.`);
|
|
5020
5023
|
if (!["unresolved", "failed", "cancelled"].includes(state) && stateReasonCode !== void 0) throw new Error("ModelResolutionV2 stateReasonCode is reserved for terminal non-success states.");
|
|
5021
|
-
const createdAt = requireTimestamp(
|
|
5022
|
-
const updatedAt = requireTimestamp(
|
|
5024
|
+
const createdAt = requireTimestamp(record2.createdAt, "ModelResolutionV2.createdAt");
|
|
5025
|
+
const updatedAt = requireTimestamp(record2.updatedAt, "ModelResolutionV2.updatedAt");
|
|
5023
5026
|
if (Date.parse(updatedAt) < Date.parse(createdAt)) throw new Error("ModelResolutionV2.updatedAt must not precede createdAt.");
|
|
5024
5027
|
if (confirmation !== void 0 && Date.parse(confirmation.confirmedAt) > Date.parse(updatedAt)) {
|
|
5025
5028
|
throw new Error("ModelResolutionV2 confirmation confirmedAt must not exceed updatedAt.");
|
|
@@ -5032,7 +5035,7 @@ function createModelResolutionV2(input) {
|
|
|
5032
5035
|
resolutionId,
|
|
5033
5036
|
requesterId,
|
|
5034
5037
|
request,
|
|
5035
|
-
attempts: requireInteger(
|
|
5038
|
+
attempts: requireInteger(record2.attempts, "ModelResolutionV2.attempts", 1, 12),
|
|
5036
5039
|
state,
|
|
5037
5040
|
compatibilityState,
|
|
5038
5041
|
candidates,
|
|
@@ -5162,12 +5165,12 @@ function copyPvoxByteInput(value, fieldName, maximumByteLength) {
|
|
|
5162
5165
|
if (!(value instanceof Uint8Array)) throw new Error(`${fieldName} must be a Uint8Array.`);
|
|
5163
5166
|
let byteLength;
|
|
5164
5167
|
try {
|
|
5165
|
-
const
|
|
5166
|
-
const
|
|
5167
|
-
const
|
|
5168
|
-
if (
|
|
5169
|
-
byteLength =
|
|
5170
|
-
const buffer =
|
|
5168
|
+
const typedArrayPrototype2 = Object.getPrototypeOf(Uint8Array.prototype);
|
|
5169
|
+
const byteLengthGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "byteLength")?.get;
|
|
5170
|
+
const bufferGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "buffer")?.get;
|
|
5171
|
+
if (byteLengthGetter2 === void 0 || bufferGetter2 === void 0) throw new Error("Typed-array intrinsics are unavailable.");
|
|
5172
|
+
byteLength = byteLengthGetter2.call(value);
|
|
5173
|
+
const buffer = bufferGetter2.call(value);
|
|
5171
5174
|
if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
|
|
5172
5175
|
throw new Error(`${fieldName} must not use shared mutable storage.`);
|
|
5173
5176
|
}
|
|
@@ -5373,18 +5376,18 @@ function encodePvoxPageSetHashPreimageV1(records) {
|
|
|
5373
5376
|
preflightPvoxArrayCardinality(records, 1, PVOX_DEFAULT_LIMITS.maximumPages, "PVOX page-set hash input");
|
|
5374
5377
|
assertPvoxPreimageDataOnly(records, "PVOX page-set hash input");
|
|
5375
5378
|
let previousEnd = 0n;
|
|
5376
|
-
const encoded = records.map((
|
|
5377
|
-
assertExactPvoxPreimageKeys(
|
|
5378
|
-
if (
|
|
5379
|
-
const offset = asUnsignedBigInt(
|
|
5380
|
-
const byteLength = assertSafeIntegerInRange(
|
|
5379
|
+
const encoded = records.map((record2, position) => {
|
|
5380
|
+
assertExactPvoxPreimageKeys(record2, ["pageIndex", "byteOffset", "byteLength", "pageSha256"], `records[${position}]`);
|
|
5381
|
+
if (record2.pageIndex !== position) throw new Error("Page-set records must have contiguous page indexes starting at zero.");
|
|
5382
|
+
const offset = asUnsignedBigInt(record2.byteOffset, `records[${position}].byteOffset`);
|
|
5383
|
+
const byteLength = assertSafeIntegerInRange(record2.byteLength, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES, `records[${position}].byteLength`);
|
|
5381
5384
|
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.");
|
|
5382
5385
|
previousEnd = offset + BigInt(byteLength);
|
|
5383
5386
|
return concatenateBytes([
|
|
5384
|
-
encodePvoxU32LeV1(
|
|
5387
|
+
encodePvoxU32LeV1(record2.pageIndex),
|
|
5385
5388
|
encodePvoxU64LeV1(offset),
|
|
5386
5389
|
encodePvoxU32LeV1(byteLength),
|
|
5387
|
-
decodePvoxSha256HexV1(
|
|
5390
|
+
decodePvoxSha256HexV1(record2.pageSha256, `records[${position}].pageSha256`)
|
|
5388
5391
|
]);
|
|
5389
5392
|
});
|
|
5390
5393
|
return concatenateBytes([
|
|
@@ -5405,12 +5408,12 @@ function encodeCanonicalJsonRecords(values) {
|
|
|
5405
5408
|
const encoded = [];
|
|
5406
5409
|
let aggregateByteLength = 0;
|
|
5407
5410
|
for (const value of normalized) {
|
|
5408
|
-
const
|
|
5409
|
-
aggregateByteLength +=
|
|
5411
|
+
const record2 = encodeCanonicalJsonRecord(value);
|
|
5412
|
+
aggregateByteLength += record2.byteLength;
|
|
5410
5413
|
if (aggregateByteLength > PVOX_MAX_CANONICAL_HASH_UTF8_BYTES_V1) {
|
|
5411
5414
|
throw new Error("Canonical JSON records exceed the governed PVOX hash-input byte limit.");
|
|
5412
5415
|
}
|
|
5413
|
-
encoded.push(
|
|
5416
|
+
encoded.push(record2);
|
|
5414
5417
|
}
|
|
5415
5418
|
return encoded;
|
|
5416
5419
|
}
|
|
@@ -5477,15 +5480,15 @@ function encodePvoxPhysicalInventoryHashPreimageV1(input) {
|
|
|
5477
5480
|
assertPvoxPreimageDataOnly(input, "PVOX physical-inventory hash input");
|
|
5478
5481
|
assertExactPvoxPreimageKeys(input, ["inventoryVersion", "subjectBinaryClosureHash", "validationEvidenceHash", "entries"], "PVOX physical-inventory hash input");
|
|
5479
5482
|
if (input.entries.length < 1 || input.entries.length > PVOX_DEFAULT_LIMITS.maximumMaterialRegions) throw new Error("Physical inventory must fit the governed material-region limit.");
|
|
5480
|
-
const encoded = input.entries.map((
|
|
5481
|
-
assertExactPvoxPreimageKeys(
|
|
5482
|
-
const regionIndex = assertSafeIntegerInRange(
|
|
5483
|
+
const encoded = input.entries.map((record2, index) => {
|
|
5484
|
+
assertExactPvoxPreimageKeys(record2, ["regionIndex", "physicalPaletteIndex", "regionId", "materialId"], `entries[${index}]`);
|
|
5485
|
+
const regionIndex = assertSafeIntegerInRange(record2.regionIndex, 0, U32_MAXIMUM, `entries[${index}].regionIndex`);
|
|
5483
5486
|
if (regionIndex !== index) throw new Error("Physical inventory records must have contiguous canonical region indexes starting at zero.");
|
|
5484
5487
|
return concatenateBytes([
|
|
5485
5488
|
encodePvoxU32LeV1(regionIndex),
|
|
5486
|
-
encodePvoxU32LeV1(
|
|
5487
|
-
encodePvoxCanonicalUtf8V1(
|
|
5488
|
-
encodePvoxCanonicalUtf8V1(
|
|
5489
|
+
encodePvoxU32LeV1(record2.physicalPaletteIndex),
|
|
5490
|
+
encodePvoxCanonicalUtf8V1(record2.regionId),
|
|
5491
|
+
encodePvoxCanonicalUtf8V1(record2.materialId)
|
|
5489
5492
|
]);
|
|
5490
5493
|
});
|
|
5491
5494
|
return concatenateBytes([
|
|
@@ -5533,12 +5536,12 @@ function encodePvoxPhysicalEvidenceHashPreimageV1(input) {
|
|
|
5533
5536
|
let previousOrder = -1;
|
|
5534
5537
|
for (const [index, evidence] of normalizedEvidence.entries()) {
|
|
5535
5538
|
if (evidence === null || typeof evidence !== "object" || Array.isArray(evidence)) throw new Error(`Physical evidence[${index}] must be an object.`);
|
|
5536
|
-
const
|
|
5537
|
-
if (typeof
|
|
5539
|
+
const record2 = evidence;
|
|
5540
|
+
if (typeof record2.regionId !== "string" || typeof record2.materialId !== "string" || typeof record2.property !== "string") {
|
|
5538
5541
|
throw new Error(`Physical evidence[${index}] must declare regionId, materialId, and property.`);
|
|
5539
5542
|
}
|
|
5540
|
-
const regionIndex = regionOrder.get(`${
|
|
5541
|
-
const propertyIndex = PVOX_PHYSICAL_PROPERTIES.indexOf(
|
|
5543
|
+
const regionIndex = regionOrder.get(`${record2.regionId}\0${record2.materialId}`);
|
|
5544
|
+
const propertyIndex = PVOX_PHYSICAL_PROPERTIES.indexOf(record2.property);
|
|
5542
5545
|
if (regionIndex === void 0 || propertyIndex < 0) throw new Error(`Physical evidence[${index}] is absent from the governed inventory/property registry.`);
|
|
5543
5546
|
const order = regionIndex * PVOX_PHYSICAL_PROPERTIES.length + propertyIndex;
|
|
5544
5547
|
if (order <= previousOrder) throw new Error("Physical evidence must use canonical inventory-region and governed-property order.");
|
|
@@ -5670,33 +5673,33 @@ function encodePvoxEditOverlayRootHashPreimageV1(input) {
|
|
|
5670
5673
|
const pageIndexes = /* @__PURE__ */ new Set();
|
|
5671
5674
|
normalizedPatches.forEach((patch, index) => {
|
|
5672
5675
|
if (patch === null || typeof patch !== "object" || Array.isArray(patch)) throw new Error(`Edit-overlay patch[${index}] must be an object.`);
|
|
5673
|
-
const
|
|
5674
|
-
if (Object.keys(
|
|
5675
|
-
if (
|
|
5676
|
-
const lodLevel = assertSafeIntegerInRange(
|
|
5677
|
-
const partitionIndex = assertSafeIntegerInRange(
|
|
5678
|
-
const hierarchyDepth = assertSafeIntegerInRange(
|
|
5679
|
-
if (typeof
|
|
5680
|
-
const expectedPageIndex = assertSafeIntegerInRange(
|
|
5676
|
+
const record2 = patch;
|
|
5677
|
+
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.`);
|
|
5678
|
+
if (record2.fieldKind !== "render-field" && record2.fieldKind !== "collision-field") throw new Error(`Edit-overlay patch[${index}].fieldKind is invalid.`);
|
|
5679
|
+
const lodLevel = assertSafeIntegerInRange(record2.lodLevel, 0, PVOX_DEFAULT_LIMITS.maximumLodCount - 1, `orderedPatches[${index}].lodLevel`);
|
|
5680
|
+
const partitionIndex = assertSafeIntegerInRange(record2.partitionIndex, 0, PVOX_DEFAULT_LIMITS.maximumPartitions - 1, `orderedPatches[${index}].partitionIndex`);
|
|
5681
|
+
const hierarchyDepth = assertSafeIntegerInRange(record2.hierarchyDepth, 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth, `orderedPatches[${index}].hierarchyDepth`);
|
|
5682
|
+
if (typeof record2.mortonCode !== "string" || !/^[0-9a-f]{16}$/u.test(record2.mortonCode)) throw new Error(`Edit-overlay patch[${index}].mortonCode is invalid.`);
|
|
5683
|
+
const expectedPageIndex = assertSafeIntegerInRange(record2.expectedPageIndex, 0, PVOX_DEFAULT_LIMITS.maximumPages - 1, `orderedPatches[${index}].expectedPageIndex`);
|
|
5681
5684
|
if (pageIndexes.has(expectedPageIndex)) throw new Error("Edit-overlay permits only one transition per page.");
|
|
5682
5685
|
pageIndexes.add(expectedPageIndex);
|
|
5683
|
-
const expectedPageHash = decodePvoxSha256HexV1(
|
|
5684
|
-
const resultingPageHash = decodePvoxSha256HexV1(
|
|
5686
|
+
const expectedPageHash = decodePvoxSha256HexV1(record2.expectedPageHash, `orderedPatches[${index}].expectedPageHash`);
|
|
5687
|
+
const resultingPageHash = decodePvoxSha256HexV1(record2.resultingPageHash, `orderedPatches[${index}].resultingPageHash`);
|
|
5685
5688
|
if (expectedPageHash.every((value, offset) => value === resultingPageHash[offset])) throw new Error("Edit-overlay page transitions must change the page hash.");
|
|
5686
|
-
const expectedBrickHash =
|
|
5687
|
-
const resultingBrickHash =
|
|
5689
|
+
const expectedBrickHash = record2.expectedBrickHash;
|
|
5690
|
+
const resultingBrickHash = record2.resultingBrickHash;
|
|
5688
5691
|
decodePvoxSha256HexV1(expectedBrickHash, `orderedPatches[${index}].expectedBrickHash`);
|
|
5689
5692
|
decodePvoxSha256HexV1(resultingBrickHash, `orderedPatches[${index}].resultingBrickHash`);
|
|
5690
|
-
if (
|
|
5693
|
+
if (record2.operation === "insert") {
|
|
5691
5694
|
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.");
|
|
5692
|
-
} else if (
|
|
5695
|
+
} else if (record2.operation === "remove") {
|
|
5693
5696
|
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.");
|
|
5694
|
-
} else if (
|
|
5697
|
+
} else if (record2.operation === "replace") {
|
|
5695
5698
|
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.");
|
|
5696
5699
|
} else {
|
|
5697
5700
|
throw new Error(`Edit-overlay patch[${index}].operation is invalid.`);
|
|
5698
5701
|
}
|
|
5699
|
-
const address = [
|
|
5702
|
+
const address = [record2.fieldKind, String(lodLevel).padStart(2, "0"), String(partitionIndex).padStart(6, "0"), String(hierarchyDepth).padStart(2, "0"), record2.mortonCode].join("\0");
|
|
5700
5703
|
if (index > 0 && address <= previousAddress) throw new Error("Edit-overlay patches must be unique and canonically ordered by their complete spatial address.");
|
|
5701
5704
|
previousAddress = address;
|
|
5702
5705
|
});
|
|
@@ -5833,17 +5836,17 @@ function encodePvoxRootHashPreimageV1(exactHeader, orderedSectionHashes) {
|
|
|
5833
5836
|
const sectionCount = new DataView(header.buffer, header.byteOffset, header.byteLength).getUint16(PVOX_ROOT_HEADER_SECTION_COUNT_OFFSET, true);
|
|
5834
5837
|
if (orderedSectionHashes.length !== sectionCount) throw new Error("Root section hashes must match the header sectionCount.");
|
|
5835
5838
|
let previousKey = -1n;
|
|
5836
|
-
const sections = orderedSectionHashes.map((
|
|
5837
|
-
assertExactPvoxPreimageKeys(
|
|
5838
|
-
const sectionType = assertSafeIntegerInRange(
|
|
5839
|
-
const sectionVersion = assertSafeIntegerInRange(
|
|
5839
|
+
const sections = orderedSectionHashes.map((record2, index) => {
|
|
5840
|
+
assertExactPvoxPreimageKeys(record2, ["sectionType", "sectionVersion", "sectionHash"], `orderedSectionHashes[${index}]`);
|
|
5841
|
+
const sectionType = assertSafeIntegerInRange(record2.sectionType, 0, U32_MAXIMUM, `orderedSectionHashes[${index}].sectionType`);
|
|
5842
|
+
const sectionVersion = assertSafeIntegerInRange(record2.sectionVersion, 0, U16_MAXIMUM, `orderedSectionHashes[${index}].sectionVersion`);
|
|
5840
5843
|
const key = BigInt(sectionType) << 16n | BigInt(sectionVersion);
|
|
5841
5844
|
if (key <= previousKey) throw new Error("Root section hashes must be unique and strictly ordered by type then version.");
|
|
5842
5845
|
previousKey = key;
|
|
5843
5846
|
return concatenateBytes([
|
|
5844
5847
|
encodePvoxU32LeV1(sectionType),
|
|
5845
5848
|
encodePvoxU16LeV1(sectionVersion),
|
|
5846
|
-
decodePvoxSha256HexV1(
|
|
5849
|
+
decodePvoxSha256HexV1(record2.sectionHash, `orderedSectionHashes[${index}].sectionHash`)
|
|
5847
5850
|
]);
|
|
5848
5851
|
});
|
|
5849
5852
|
return concatenateBytes([TEXT_ENCODER.encode(PVOX_ROOT_HASH_DOMAIN), header, ...sections]);
|
|
@@ -6878,6 +6881,176 @@ var MODEL_RESOLUTION_V2_JSON_SCHEMAS = deepFreeze3({
|
|
|
6878
6881
|
modelResolution: standaloneSchema("modelResolution", "ModelResolutionV2")
|
|
6879
6882
|
});
|
|
6880
6883
|
|
|
6884
|
+
// src/model-resource-manifest.ts
|
|
6885
|
+
var import_gpu_shader3 = require("@plasius/gpu-shader");
|
|
6886
|
+
var MODEL_RESOURCE_MANIFEST_LIMITS = Object.freeze({
|
|
6887
|
+
files: 16384,
|
|
6888
|
+
fileBytes: 64 * 1024 * 1024,
|
|
6889
|
+
totalBytes: 128 * 1024 * 1024,
|
|
6890
|
+
metadataValues: 262144,
|
|
6891
|
+
metadataDepth: 16,
|
|
6892
|
+
timeoutMs: 6e4
|
|
6893
|
+
});
|
|
6894
|
+
var ModelResourceManifestError = class extends TypeError {
|
|
6895
|
+
constructor(code) {
|
|
6896
|
+
super(`Invalid model resource manifest: ${code}`);
|
|
6897
|
+
this.code = code;
|
|
6898
|
+
this.name = "ModelResourceManifestError";
|
|
6899
|
+
}
|
|
6900
|
+
code;
|
|
6901
|
+
};
|
|
6902
|
+
function fail(code = "invalid-input") {
|
|
6903
|
+
throw new ModelResourceManifestError(code);
|
|
6904
|
+
}
|
|
6905
|
+
function record(value) {
|
|
6906
|
+
if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail();
|
|
6907
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
6908
|
+
const keys = Reflect.ownKeys(value);
|
|
6909
|
+
if (keys.length > MODEL_RESOURCE_MANIFEST_LIMITS.files) fail("limit-exceeded");
|
|
6910
|
+
for (const key of keys) {
|
|
6911
|
+
if (typeof key !== "string" || ["__proto__", "constructor", "prototype"].includes(key)) fail();
|
|
6912
|
+
if (key.length > 128) fail("limit-exceeded");
|
|
6913
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
6914
|
+
if (!("value" in property) || !property.enumerable) fail();
|
|
6915
|
+
output[key] = property.value;
|
|
6916
|
+
}
|
|
6917
|
+
return output;
|
|
6918
|
+
}
|
|
6919
|
+
function list(value) {
|
|
6920
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail();
|
|
6921
|
+
if (value.length > MODEL_RESOURCE_MANIFEST_LIMITS.files) fail("limit-exceeded");
|
|
6922
|
+
if (Reflect.ownKeys(value).length !== value.length + 1) fail();
|
|
6923
|
+
const output = [];
|
|
6924
|
+
for (let i = 0; i < value.length; i++) {
|
|
6925
|
+
const property = Object.getOwnPropertyDescriptor(value, String(i));
|
|
6926
|
+
if (!property || !("value" in property) || !property.enumerable) fail();
|
|
6927
|
+
output.push(property.value);
|
|
6928
|
+
}
|
|
6929
|
+
return output;
|
|
6930
|
+
}
|
|
6931
|
+
function snapshot(value, budget, depth = 0) {
|
|
6932
|
+
if (--budget.remaining < 0 || depth > MODEL_RESOURCE_MANIFEST_LIMITS.metadataDepth) fail("limit-exceeded");
|
|
6933
|
+
if (value === null || value === void 0 || typeof value === "boolean") return value;
|
|
6934
|
+
if (typeof value === "string") {
|
|
6935
|
+
if (value.length > 2048) fail("limit-exceeded");
|
|
6936
|
+
return value;
|
|
6937
|
+
}
|
|
6938
|
+
if (typeof value === "number") {
|
|
6939
|
+
if (!Number.isFinite(value)) fail();
|
|
6940
|
+
return value;
|
|
6941
|
+
}
|
|
6942
|
+
if (Array.isArray(value)) return Object.freeze(list(value).map((item) => snapshot(item, budget, depth + 1)));
|
|
6943
|
+
const fields = record(value);
|
|
6944
|
+
for (const key of Object.keys(fields)) fields[key] = snapshot(fields[key], budget, depth + 1);
|
|
6945
|
+
return Object.freeze(fields);
|
|
6946
|
+
}
|
|
6947
|
+
function path(value) {
|
|
6948
|
+
if (typeof value !== "string" || value.length > 1024 || !value.length) fail();
|
|
6949
|
+
for (const part of value.split("/")) {
|
|
6950
|
+
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();
|
|
6951
|
+
}
|
|
6952
|
+
return value;
|
|
6953
|
+
}
|
|
6954
|
+
function contentType(value) {
|
|
6955
|
+
if (typeof value !== "string" || value.length > 128 || !/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+(?:; charset=utf-8)?$/u.test(value)) fail();
|
|
6956
|
+
return value;
|
|
6957
|
+
}
|
|
6958
|
+
function uniquePaths(paths) {
|
|
6959
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6960
|
+
for (const value of paths) {
|
|
6961
|
+
const normalized = path(value).toLowerCase();
|
|
6962
|
+
if (seen.has(normalized)) fail("path-conflict");
|
|
6963
|
+
seen.add(normalized);
|
|
6964
|
+
}
|
|
6965
|
+
for (const value of seen) {
|
|
6966
|
+
for (let offset = value.indexOf("/"); offset !== -1; offset = value.indexOf("/", offset + 1)) {
|
|
6967
|
+
if (seen.has(value.slice(0, offset))) fail("path-conflict");
|
|
6968
|
+
}
|
|
6969
|
+
}
|
|
6970
|
+
}
|
|
6971
|
+
var typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
|
|
6972
|
+
var byteLengthGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength").get;
|
|
6973
|
+
var bufferGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer").get;
|
|
6974
|
+
async function qualifyModelResourceManifest(input) {
|
|
6975
|
+
try {
|
|
6976
|
+
return await qualify(input);
|
|
6977
|
+
} catch (error) {
|
|
6978
|
+
if (error instanceof ModelResourceManifestError) throw error;
|
|
6979
|
+
return fail();
|
|
6980
|
+
}
|
|
6981
|
+
}
|
|
6982
|
+
async function qualify(input) {
|
|
6983
|
+
const started = performance.now();
|
|
6984
|
+
const root = record(input);
|
|
6985
|
+
if (Object.keys(root).some((key) => !["manifest", "packageManifest", "files", "signal", "timeoutMs"].includes(key))) fail();
|
|
6986
|
+
const timeout = root.timeoutMs ?? 1e4;
|
|
6987
|
+
if (typeof timeout !== "number" || !Number.isSafeInteger(timeout) || timeout < 1 || timeout > MODEL_RESOURCE_MANIFEST_LIMITS.timeoutMs) fail();
|
|
6988
|
+
const signal = root.signal;
|
|
6989
|
+
if (signal !== void 0 && !(signal instanceof AbortSignal)) fail();
|
|
6990
|
+
const check = () => {
|
|
6991
|
+
if (signal?.aborted) fail("aborted");
|
|
6992
|
+
if (performance.now() - started >= timeout) fail("deadline-exceeded");
|
|
6993
|
+
};
|
|
6994
|
+
check();
|
|
6995
|
+
const budget = { remaining: MODEL_RESOURCE_MANIFEST_LIMITS.metadataValues };
|
|
6996
|
+
const rawManifest = snapshot(root.manifest, budget);
|
|
6997
|
+
const inventory = record(snapshot(root.packageManifest, budget));
|
|
6998
|
+
if (Object.keys(inventory).some((key) => !["format", "entries"].includes(key)) || !["zip", "tar", "directory-manifest", "inline"].includes(inventory.format)) fail();
|
|
6999
|
+
let manifest;
|
|
7000
|
+
try {
|
|
7001
|
+
manifest = rawManifest.assetKind === "model" ? createModelAssetManifest(rawManifest) : createAssetManifest(rawManifest);
|
|
7002
|
+
} catch {
|
|
7003
|
+
return fail();
|
|
7004
|
+
}
|
|
7005
|
+
uniquePaths(manifest.files.map((file) => file.path));
|
|
7006
|
+
if (!manifest.files.some((file) => file.path === path(manifest.entrypoint))) fail("inventory-mismatch");
|
|
7007
|
+
const descriptors = new Map(manifest.files.map((file) => [file.path, file]));
|
|
7008
|
+
let total = 0;
|
|
7009
|
+
for (const file of manifest.files) {
|
|
7010
|
+
if (!Number.isSafeInteger(file.byteLength)) fail();
|
|
7011
|
+
if (file.byteLength > MODEL_RESOURCE_MANIFEST_LIMITS.fileBytes) fail("limit-exceeded");
|
|
7012
|
+
total += file.byteLength;
|
|
7013
|
+
if (total > MODEL_RESOURCE_MANIFEST_LIMITS.totalBytes) fail("limit-exceeded");
|
|
7014
|
+
contentType(file.contentType);
|
|
7015
|
+
}
|
|
7016
|
+
const entries = list(inventory.entries).map(record);
|
|
7017
|
+
uniquePaths(entries.map((entry) => path(entry.path)));
|
|
7018
|
+
const ids = /* @__PURE__ */ new Set();
|
|
7019
|
+
for (const entry of entries) {
|
|
7020
|
+
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();
|
|
7021
|
+
ids.add(entry.id);
|
|
7022
|
+
const descriptor = descriptors.get(entry.path);
|
|
7023
|
+
if (!descriptor) fail("inventory-mismatch");
|
|
7024
|
+
if (entry.contentType !== void 0 && contentType(entry.contentType) !== descriptor.contentType) fail("content-mismatch");
|
|
7025
|
+
}
|
|
7026
|
+
if (entries.length !== descriptors.size) fail("inventory-mismatch");
|
|
7027
|
+
const payloads = list(root.files).map((value) => {
|
|
7028
|
+
const file = record(value);
|
|
7029
|
+
if (Object.keys(file).length !== 3 || Object.keys(file).some((key) => !["path", "bytes", "contentType"].includes(key))) fail();
|
|
7030
|
+
const filePath = path(file.path);
|
|
7031
|
+
if (!(file.bytes instanceof Uint8Array) || Object.getPrototypeOf(file.bytes) !== Uint8Array.prototype || !(bufferGetter.call(file.bytes) instanceof ArrayBuffer)) fail();
|
|
7032
|
+
const descriptor = descriptors.get(filePath);
|
|
7033
|
+
if (!descriptor) fail("inventory-mismatch");
|
|
7034
|
+
if (byteLengthGetter.call(file.bytes) !== descriptor.byteLength || contentType(file.contentType) !== descriptor.contentType) fail("content-mismatch");
|
|
7035
|
+
return { path: filePath, bytes: file.bytes, descriptor };
|
|
7036
|
+
});
|
|
7037
|
+
uniquePaths(payloads.map((file) => file.path));
|
|
7038
|
+
if (payloads.length !== descriptors.size) fail("inventory-mismatch");
|
|
7039
|
+
const copies = payloads.map((file) => ({ ...file, bytes: new Uint8Array(file.bytes) }));
|
|
7040
|
+
for (const file of copies) {
|
|
7041
|
+
check();
|
|
7042
|
+
let hash;
|
|
7043
|
+
try {
|
|
7044
|
+
hash = await (0, import_gpu_shader3.computeSha256)(file.bytes);
|
|
7045
|
+
} catch {
|
|
7046
|
+
return fail("content-mismatch");
|
|
7047
|
+
}
|
|
7048
|
+
check();
|
|
7049
|
+
if (hash !== file.descriptor.sha256) fail("content-mismatch");
|
|
7050
|
+
}
|
|
7051
|
+
return Object.freeze({ ...manifest, files: Object.freeze([...manifest.files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) });
|
|
7052
|
+
}
|
|
7053
|
+
|
|
6881
7054
|
// src/index.ts
|
|
6882
7055
|
var ASSET_CONTRACTS_PACKAGE = "@plasius/asset-contracts";
|
|
6883
7056
|
var UNIFIED_ASSET_PIPELINE_FEATURE_FLAG_ID = "asset.pipeline.unified-ai-assets.enabled";
|
|
@@ -7058,7 +7231,7 @@ function createModelAssetManifest(input) {
|
|
|
7058
7231
|
"screenshot",
|
|
7059
7232
|
"metadata"
|
|
7060
7233
|
]);
|
|
7061
|
-
const descriptor = (0,
|
|
7234
|
+
const descriptor = (0, import_gpu_shader4.parseModelGpuCompatibilityDescriptor)({
|
|
7062
7235
|
modelId: base.assetId,
|
|
7063
7236
|
version: base.version,
|
|
7064
7237
|
gpuInterface: input.gpuInterface,
|
|
@@ -7088,7 +7261,7 @@ function createGpuInterfaceAssetManifest(input) {
|
|
|
7088
7261
|
);
|
|
7089
7262
|
assertAllowedFileRoles(base, ["gpu-interface-manifest"]);
|
|
7090
7263
|
assertRoleCount(base, "gpu-interface-manifest", 1);
|
|
7091
|
-
const gpuInterfaceManifest = (0,
|
|
7264
|
+
const gpuInterfaceManifest = (0, import_gpu_shader4.parseGpuInterfaceManifest)(input.gpuInterfaceManifest);
|
|
7092
7265
|
assertImmutableAssetVersion(gpuInterfaceManifest.interfaceVersion);
|
|
7093
7266
|
assertDomainIdentity(
|
|
7094
7267
|
base,
|
|
@@ -7111,7 +7284,7 @@ function createShaderAssetManifest(input) {
|
|
|
7111
7284
|
);
|
|
7112
7285
|
assertAllowedFileRoles(base, ["shader-manifest", "wgsl"]);
|
|
7113
7286
|
assertRoleCount(base, "shader-manifest", 1);
|
|
7114
|
-
const shaderManifest = (0,
|
|
7287
|
+
const shaderManifest = (0, import_gpu_shader4.parseShaderVersionManifest)(input.shaderManifest);
|
|
7115
7288
|
assertShaderManifestVersions(shaderManifest);
|
|
7116
7289
|
assertDomainIdentity(base, shaderManifest.shaderId, shaderManifest.version, "Shader");
|
|
7117
7290
|
assertShaderModuleFiles(base.files, shaderManifest);
|
|
@@ -7130,7 +7303,7 @@ function createShaderStyleProfileAssetManifest(input) {
|
|
|
7130
7303
|
);
|
|
7131
7304
|
assertAllowedFileRoles(base, ["shader-style-profile-manifest"]);
|
|
7132
7305
|
assertRoleCount(base, "shader-style-profile-manifest", 1);
|
|
7133
|
-
const styleProfileManifest = (0,
|
|
7306
|
+
const styleProfileManifest = (0, import_gpu_shader4.parseShaderStyleProfileManifest)(input.styleProfileManifest);
|
|
7134
7307
|
assertStyleProfileManifestVersions(styleProfileManifest);
|
|
7135
7308
|
assertDomainIdentity(base, styleProfileManifest.profileId, styleProfileManifest.version, "Style profile");
|
|
7136
7309
|
return Object.freeze({ ...base, styleProfileManifest });
|
|
@@ -7194,8 +7367,8 @@ function createShaderValidationEvidenceRef(input) {
|
|
|
7194
7367
|
sha256: assertSha256(attestation2.sha256, "Shader validation evidence attestation sha256")
|
|
7195
7368
|
})
|
|
7196
7369
|
};
|
|
7197
|
-
const supportedUniversal =
|
|
7198
|
-
const supportedAdditive =
|
|
7370
|
+
const supportedUniversal = import_gpu_shader4.SUPPORTED_STABLE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
|
|
7371
|
+
const supportedAdditive = import_gpu_shader4.SUPPORTED_ADDITIVE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
|
|
7199
7372
|
if (!supportedUniversal && !supportedAdditive) {
|
|
7200
7373
|
throw new Error("Shader validation evidence must target a supported WebGPU matrix policy.");
|
|
7201
7374
|
}
|
|
@@ -7206,7 +7379,7 @@ function createShaderValidationEvidenceRef(input) {
|
|
|
7206
7379
|
}
|
|
7207
7380
|
async function createGpuInterfaceRef(input) {
|
|
7208
7381
|
assertImmutableAssetVersion(input.manifest.interfaceVersion);
|
|
7209
|
-
const manifest = (0,
|
|
7382
|
+
const manifest = (0, import_gpu_shader4.parseGpuInterfaceManifest)(input.manifest);
|
|
7210
7383
|
assertAssetId(manifest.interfaceId);
|
|
7211
7384
|
assertImmutableAssetVersion(manifest.interfaceVersion);
|
|
7212
7385
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "GPU interface");
|
|
@@ -7221,7 +7394,7 @@ async function createGpuInterfaceRef(input) {
|
|
|
7221
7394
|
}
|
|
7222
7395
|
async function createShaderVersionRef(input) {
|
|
7223
7396
|
assertImmutableAssetVersion(input.manifest.version);
|
|
7224
|
-
const manifest = (0,
|
|
7397
|
+
const manifest = (0, import_gpu_shader4.parseShaderVersionManifest)(input.manifest);
|
|
7225
7398
|
assertAssetId(manifest.shaderId);
|
|
7226
7399
|
assertShaderManifestVersions(manifest);
|
|
7227
7400
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Shader");
|
|
@@ -7234,7 +7407,7 @@ async function createShaderVersionRef(input) {
|
|
|
7234
7407
|
}
|
|
7235
7408
|
async function createShaderStyleProfileRef(input) {
|
|
7236
7409
|
assertImmutableAssetVersion(input.manifest.version);
|
|
7237
|
-
const manifest = (0,
|
|
7410
|
+
const manifest = (0, import_gpu_shader4.parseShaderStyleProfileManifest)(input.manifest);
|
|
7238
7411
|
assertAssetId(manifest.profileId);
|
|
7239
7412
|
assertStyleProfileManifestVersions(manifest);
|
|
7240
7413
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Style profile");
|
|
@@ -7266,9 +7439,9 @@ async function validateGpuAssetFiles(input) {
|
|
|
7266
7439
|
if (input.files.size !== manifest.files.length) {
|
|
7267
7440
|
throw new Error("GPU asset validation requires exactly the declared file set.");
|
|
7268
7441
|
}
|
|
7269
|
-
for (const
|
|
7270
|
-
if (!manifest.files.some((file) => file.path ===
|
|
7271
|
-
throw new Error(`GPU asset validation received undeclared file ${
|
|
7442
|
+
for (const path2 of input.files.keys()) {
|
|
7443
|
+
if (!manifest.files.some((file) => file.path === path2)) {
|
|
7444
|
+
throw new Error(`GPU asset validation received undeclared file ${path2}.`);
|
|
7272
7445
|
}
|
|
7273
7446
|
}
|
|
7274
7447
|
await Promise.all(manifest.files.map(async (file) => {
|
|
@@ -7279,7 +7452,7 @@ async function validateGpuAssetFiles(input) {
|
|
|
7279
7452
|
if (bytes.byteLength !== file.byteLength) {
|
|
7280
7453
|
throw new Error(`GPU asset file ${file.path} byte length differs from its descriptor.`);
|
|
7281
7454
|
}
|
|
7282
|
-
if (await (0,
|
|
7455
|
+
if (await (0, import_gpu_shader4.computeSha256)(bytes) !== file.sha256) {
|
|
7283
7456
|
throw new Error(`GPU asset file ${file.path} digest differs from its descriptor.`);
|
|
7284
7457
|
}
|
|
7285
7458
|
if (file.contentType === ASSET_JSON_CONTENT_TYPE) {
|
|
@@ -7298,13 +7471,13 @@ async function validateGpuAssetFiles(input) {
|
|
|
7298
7471
|
}
|
|
7299
7472
|
const expectedDomainManifest = domainManifestForAsset(manifest);
|
|
7300
7473
|
if (expectedDomainManifest !== null) {
|
|
7301
|
-
const parsedEntrypoint = (0,
|
|
7302
|
-
if ((0,
|
|
7474
|
+
const parsedEntrypoint = (0, import_gpu_shader4.parseJsonBytes)(entrypointBytes, "GPU asset manifest entrypoint");
|
|
7475
|
+
if ((0, import_gpu_shader4.canonicalizeGpuContract)(parsedEntrypoint) !== (0, import_gpu_shader4.canonicalizeGpuContract)(expectedDomainManifest)) {
|
|
7303
7476
|
throw new Error("GPU asset entrypoint bytes differ from the validated domain manifest.");
|
|
7304
7477
|
}
|
|
7305
7478
|
} else if (manifest.assetKind === "shader-validation-evidence") {
|
|
7306
7479
|
assertEvidenceEntrypointIdentity(
|
|
7307
|
-
(0,
|
|
7480
|
+
(0, import_gpu_shader4.parseJsonBytes)(entrypointBytes, "Shader validation evidence entrypoint"),
|
|
7308
7481
|
manifest.validationEvidence
|
|
7309
7482
|
);
|
|
7310
7483
|
}
|
|
@@ -7528,7 +7701,7 @@ function assertTypedRuntimeManifestUri(manifest, value) {
|
|
|
7528
7701
|
}
|
|
7529
7702
|
function assertEvidenceEntrypointIdentity(value, ref2) {
|
|
7530
7703
|
const evidence = assertRecord2(value, "Shader validation evidence entrypoint");
|
|
7531
|
-
if (evidence.contractVersion !==
|
|
7704
|
+
if (evidence.contractVersion !== import_gpu_shader4.SHADER_VALIDATION_EVIDENCE_VERSION || evidence.evidenceId !== ref2.evidenceId || evidence.status !== "passed") {
|
|
7532
7705
|
throw new Error("Shader validation evidence bytes do not identify passing declared evidence.");
|
|
7533
7706
|
}
|
|
7534
7707
|
const matrixRef = assertRecord2(evidence.matrixRef, "Shader validation evidence matrixRef");
|
|
@@ -7544,9 +7717,9 @@ function assertEvidenceEntrypointIdentity(value, ref2) {
|
|
|
7544
7717
|
}
|
|
7545
7718
|
}
|
|
7546
7719
|
function assertCanonicalJsonBytes(bytes, fieldName) {
|
|
7547
|
-
const value = (0,
|
|
7720
|
+
const value = (0, import_gpu_shader4.parseJsonBytes)(bytes, fieldName);
|
|
7548
7721
|
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
7549
|
-
if ((0,
|
|
7722
|
+
if ((0, import_gpu_shader4.canonicalizeGpuContract)(value) !== text) {
|
|
7550
7723
|
throw new Error(`${fieldName} must use canonical JSON serialization.`);
|
|
7551
7724
|
}
|
|
7552
7725
|
}
|
|
@@ -7555,11 +7728,11 @@ async function bindDomainManifestBytes(bytes, manifest, fieldName) {
|
|
|
7555
7728
|
throw new Error(`${fieldName} manifest bytes must be a Uint8Array.`);
|
|
7556
7729
|
}
|
|
7557
7730
|
assertCanonicalJsonBytes(bytes, `${fieldName} manifest`);
|
|
7558
|
-
const parsed = (0,
|
|
7559
|
-
if ((0,
|
|
7731
|
+
const parsed = (0, import_gpu_shader4.parseJsonBytes)(bytes, `${fieldName} manifest`);
|
|
7732
|
+
if ((0, import_gpu_shader4.canonicalizeGpuContract)(parsed) !== (0, import_gpu_shader4.canonicalizeGpuContract)(manifest)) {
|
|
7560
7733
|
throw new Error(`${fieldName} manifest bytes differ from the supplied manifest.`);
|
|
7561
7734
|
}
|
|
7562
|
-
return (0,
|
|
7735
|
+
return (0, import_gpu_shader4.computeSha256)(bytes);
|
|
7563
7736
|
}
|
|
7564
7737
|
function assertRoleCount(manifest, role, expectedCount) {
|
|
7565
7738
|
if (manifest.files.filter((file) => file.role === role).length !== expectedCount) {
|
|
@@ -7679,8 +7852,10 @@ function assertRequiredString(value, fieldName) {
|
|
|
7679
7852
|
MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
7680
7853
|
MODEL_RESOLUTION_V2_JSON_SCHEMAS,
|
|
7681
7854
|
MODEL_RESOLUTION_V2_STATES,
|
|
7855
|
+
MODEL_RESOURCE_MANIFEST_LIMITS,
|
|
7682
7856
|
MODEL_RIGHTS_STATUSES,
|
|
7683
7857
|
MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
|
|
7858
|
+
ModelResourceManifestError,
|
|
7684
7859
|
PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
|
|
7685
7860
|
PVOX_ASSET_MANIFEST_VERSION,
|
|
7686
7861
|
PVOX_BINARY_CLOSURE_HASH_DOMAIN,
|
|
@@ -7871,6 +8046,7 @@ function assertRequiredString(value, fieldName) {
|
|
|
7871
8046
|
normalizeAssetId,
|
|
7872
8047
|
normalizePvoxRootHeaderForHashV1,
|
|
7873
8048
|
projectPvoxNamedJsonClosureV1,
|
|
8049
|
+
qualifyModelResourceManifest,
|
|
7874
8050
|
toLegacyModelResolutionState,
|
|
7875
8051
|
validateGpuAssetFiles
|
|
7876
8052
|
});
|