@plasius/asset-contracts 0.7.0 → 0.8.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/dist/index.cjs CHANGED
@@ -43,8 +43,8 @@ __export(index_exports, {
43
43
  ASSET_SOURCE_ADAPTERS: () => ASSET_SOURCE_ADAPTERS,
44
44
  ASSET_WGSL_CONTENT_TYPE: () => ASSET_WGSL_CONTENT_TYPE,
45
45
  CANONICAL_MODEL_COORDINATE_SYSTEM: () => CANONICAL_MODEL_COORDINATE_SYSTEM,
46
- GPU_SHADER_STORE_FEATURE_FLAG: () => import_gpu_shader4.GPU_SHADER_STORE_FEATURE_FLAG,
47
- GPU_SHADER_STYLE_SELECTION_CAPABILITY: () => import_gpu_shader4.GPU_SHADER_STYLE_SELECTION_CAPABILITY,
46
+ GPU_SHADER_STORE_FEATURE_FLAG: () => import_gpu_shader5.GPU_SHADER_STORE_FEATURE_FLAG,
47
+ GPU_SHADER_STYLE_SELECTION_CAPABILITY: () => import_gpu_shader5.GPU_SHADER_STYLE_SELECTION_CAPABILITY,
48
48
  MODEL_ASSET_KINDS: () => MODEL_ASSET_KINDS,
49
49
  MODEL_CANDIDATE_HARD_GATE_KINDS: () => MODEL_CANDIDATE_HARD_GATE_KINDS,
50
50
  MODEL_CONFIRMATION_VIEW_KINDS: () => MODEL_CONFIRMATION_VIEW_KINDS,
@@ -64,11 +64,13 @@ __export(index_exports, {
64
64
  MODEL_RESOLUTION_V2_JSON_SCHEMAS: () => MODEL_RESOLUTION_V2_JSON_SCHEMAS,
65
65
  MODEL_RESOLUTION_V2_STATES: () => MODEL_RESOLUTION_V2_STATES,
66
66
  MODEL_RESOURCE_MANIFEST_LIMITS: () => MODEL_RESOURCE_MANIFEST_LIMITS,
67
+ MODEL_RESOURCE_PROMOTION_LIMITS: () => MODEL_RESOURCE_PROMOTION_LIMITS,
67
68
  MODEL_RIGHTS_STATUSES: () => MODEL_RIGHTS_STATUSES,
68
69
  MODEL_TEXTURE_RESOURCE_LIMITS: () => MODEL_TEXTURE_RESOURCE_LIMITS,
69
70
  MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE: () => MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
70
71
  ModelConversionDiagnosticsError: () => ModelConversionDiagnosticsError,
71
72
  ModelResourceManifestError: () => ModelResourceManifestError,
73
+ ModelResourcePromotionError: () => ModelResourcePromotionError,
72
74
  ModelTextureResourceError: () => ModelTextureResourceError,
73
75
  PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN: () => PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
74
76
  PVOX_ASSET_MANIFEST_VERSION: () => PVOX_ASSET_MANIFEST_VERSION,
@@ -204,6 +206,7 @@ __export(index_exports, {
204
206
  createModelRequestSpecV2: () => createModelRequestSpecV2,
205
207
  createModelResolution: () => createModelResolution,
206
208
  createModelResolutionV2: () => createModelResolutionV2,
209
+ createModelResourcePromotionRecord: () => createModelResourcePromotionRecord,
207
210
  createModelResourceRef: () => createModelResourceRef,
208
211
  createModelRightsAssessment: () => createModelRightsAssessment,
209
212
  createModelTechnicalProfile: () => createModelTechnicalProfile,
@@ -267,7 +270,7 @@ __export(index_exports, {
267
270
  validateGpuAssetFiles: () => validateGpuAssetFiles
268
271
  });
269
272
  module.exports = __toCommonJS(index_exports);
270
- var import_gpu_shader4 = require("@plasius/gpu-shader");
273
+ var import_gpu_shader5 = require("@plasius/gpu-shader");
271
274
 
272
275
  // src/asset-version.ts
273
276
  var ASSET_VERSION_PATTERN = /^[0-9A-Za-z][0-9A-Za-z._-]{0,127}$/u;
@@ -3052,7 +3055,7 @@ function requireEnum(value, allowed, fieldName) {
3052
3055
  }
3053
3056
  function requireReasonCodes(value, fieldName) {
3054
3057
  const entries = asDenseArray(value, fieldName, 0, 32);
3055
- const result = entries.map((entry, index) => requireReasonCode(entry, `${fieldName}[${index}]`));
3058
+ const result = entries.map((entry2, index) => requireReasonCode(entry2, `${fieldName}[${index}]`));
3056
3059
  if (new Set(result).size !== result.length) throw new Error(`${fieldName} must not contain duplicates.`);
3057
3060
  return result;
3058
3061
  }
@@ -3374,7 +3377,7 @@ function createPvoxRuntimeRequestProfileV1(value) {
3374
3377
  const record2 = asRecord(value, fieldName);
3375
3378
  exactKeys(record2, ["profileId", "fidelityProfileId", "capabilityProfileId", "geometryMode", "requiredCapabilities", "limits"], fieldName);
3376
3379
  const requestedCapabilityEntries = asDenseArray(record2.requiredCapabilities, `${fieldName}.requiredCapabilities`, 1, PVOX_CAPABILITIES.length);
3377
- const requiredCapabilities = requestedCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `${fieldName}.requiredCapabilities[${index}]`));
3380
+ const requiredCapabilities = requestedCapabilityEntries.map((entry2, index) => requireEnum(entry2, PVOX_CAPABILITIES, `${fieldName}.requiredCapabilities[${index}]`));
3378
3381
  if (new Set(requiredCapabilities).size !== requiredCapabilities.length) {
3379
3382
  throw new Error(`${fieldName}.requiredCapabilities must not contain duplicates.`);
3380
3383
  }
@@ -3537,7 +3540,7 @@ function createPvoxAssetManifestV1(input) {
3537
3540
  }
3538
3541
  if (expectedOffset !== artifact.byteLength) throw new Error("PvoxAssetManifestV1 pages must exactly cover the artifact bytes.");
3539
3542
  const lodEntries = asDenseArray(record2.lods, "PvoxAssetManifestV1.lods", 1, 4);
3540
- const lods = lodEntries.map((entry, index) => createLod(entry, index, pages.length));
3543
+ const lods = lodEntries.map((entry2, index) => createLod(entry2, index, pages.length));
3541
3544
  for (const [index, current] of lods.entries()) {
3542
3545
  if (current.level !== index) throw new Error("PvoxAssetManifestV1 LOD levels must be contiguous from LOD0.");
3543
3546
  const previous = lods[index - 1];
@@ -3773,7 +3776,7 @@ function createPhysicalPropertyEvidence(input) {
3773
3776
  const reviewedBy = record2.reviewedBy === void 0 ? void 0 : requireToken(record2.reviewedBy, "PhysicalPropertyEvidence.reviewedBy");
3774
3777
  const reviewedAt = record2.reviewedAt === void 0 ? void 0 : requireTimestamp(record2.reviewedAt, "PhysicalPropertyEvidence.reviewedAt");
3775
3778
  const reviewToken = record2.reviewToken === void 0 ? void 0 : requireAttestation(record2.reviewToken, "PhysicalPropertyEvidence.reviewToken");
3776
- 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.");
3779
+ if (new Set([reviewedBy, reviewedAt, reviewToken].map((entry2) => entry2 === void 0)).size !== 1) throw new Error("PhysicalPropertyEvidence signed review identity, timestamp, and token must be supplied together.");
3777
3780
  if (provenance === "inferred" && reviewedBy === void 0) throw new Error("Inferred PhysicalPropertyEvidence requires human review.");
3778
3781
  return deepFreeze2({
3779
3782
  property,
@@ -3796,9 +3799,9 @@ function createPvoxPhysicalRegionInventoryV1(input) {
3796
3799
  exactKeys(record2, ["inventoryVersion", "subjectContentHash", "inventoryHash", "inventoryHashAttestation", "validationEvidenceHash", "entries"], fieldName);
3797
3800
  if (record2.inventoryVersion !== PVOX_PHYSICAL_REGION_INVENTORY_VERSION) throw new Error(`${fieldName}.inventoryVersion is unsupported.`);
3798
3801
  const entriesInput = asDenseArray(record2.entries, `${fieldName}.entries`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
3799
- const entries = entriesInput.map((entry, index) => {
3802
+ const entries = entriesInput.map((entry2, index) => {
3800
3803
  const entryName = `${fieldName}.entries[${index}]`;
3801
- const item = asRecord(entry, entryName);
3804
+ const item = asRecord(entry2, entryName);
3802
3805
  exactKeys(item, ["regionIndex", "physicalPaletteIndex", "regionId", "materialId"], entryName);
3803
3806
  return {
3804
3807
  regionIndex: requireInteger(item.regionIndex, `${entryName}.regionIndex`, 0, PVOX_DEFAULT_LIMITS.maximumMaterialRegions - 1),
@@ -3807,7 +3810,7 @@ function createPvoxPhysicalRegionInventoryV1(input) {
3807
3810
  materialId: requireToken(item.materialId, `${entryName}.materialId`)
3808
3811
  };
3809
3812
  });
3810
- if (entries.some((entry, index) => entry.regionIndex !== index)) throw new Error(`${fieldName} regionIndex values must be contiguous and canonically ordered.`);
3813
+ if (entries.some((entry2, index) => entry2.regionIndex !== index)) throw new Error(`${fieldName} regionIndex values must be contiguous and canonically ordered.`);
3811
3814
  const pairs = entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
3812
3815
  if (new Set(pairs).size !== pairs.length) throw new Error(`${fieldName} region/material pairs must be unique.`);
3813
3816
  const inventoryHash = requireSha256(record2.inventoryHash, `${fieldName}.inventoryHash`);
@@ -3944,9 +3947,9 @@ function createPvoxEditJournalCurrentState(input, baseManifest) {
3944
3947
  const record2 = asRecord(input, fieldName);
3945
3948
  exactKeys(record2, ["baseContentHash", "basePageSetHash", "placementId", "gridVersion", "revision", "rootHash", "latestJournalHash", "pageHashes"], fieldName);
3946
3949
  const pageEntries = asDenseArray(record2.pageHashes, `${fieldName}.pageHashes`, baseManifest.pages.length, baseManifest.pages.length);
3947
- const pageHashes = pageEntries.map((entry, index) => {
3950
+ const pageHashes = pageEntries.map((entry2, index) => {
3948
3951
  const entryName = `${fieldName}.pageHashes[${index}]`;
3949
- const page = asRecord(entry, entryName);
3952
+ const page = asRecord(entry2, entryName);
3950
3953
  exactKeys(page, ["pageIndex", "sha256"], entryName);
3951
3954
  const pageIndex = requireInteger(page.pageIndex, `${entryName}.pageIndex`, 0, baseManifest.pages.length - 1);
3952
3955
  if (pageIndex !== index) throw new Error(`${fieldName}.pageHashes must be complete and ordered by pageIndex.`);
@@ -3997,9 +4000,9 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
3997
4000
  }
3998
4001
  }
3999
4002
  const patchEntries = asDenseArray(record2.patches, "PvoxEditJournal.patches", 1, PVOX_MAX_EDIT_PATCHES);
4000
- const patches = patchEntries.map((entry, index) => {
4003
+ const patches = patchEntries.map((entry2, index) => {
4001
4004
  const fieldName = `PvoxEditJournal.patches[${index}]`;
4002
- const patch = asRecord(entry, fieldName);
4005
+ const patch = asRecord(entry2, fieldName);
4003
4006
  exactKeys(patch, ["fieldKind", "lodLevel", "partitionIndex", "hierarchyDepth", "mortonCode", "expectedPageIndex", "expectedPageHash", "operation", "expectedBrickHash", "resultingBrickHash", "resultingPageHash"], fieldName);
4004
4007
  const mortonCode = requireString(patch.mortonCode, `${fieldName}.mortonCode`, 16);
4005
4008
  if (!MORTON_CODE.test(mortonCode)) throw new Error(`${fieldName}.mortonCode must be a canonical Morton code.`);
@@ -4076,7 +4079,7 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
4076
4079
  const journals = [];
4077
4080
  const operationIds = /* @__PURE__ */ new Set();
4078
4081
  const currentPageHashes = new Map(baseManifest.pages.map(({ pageIndex, sha256: sha2562 }) => [pageIndex, sha2562]));
4079
- for (const entry of entries) {
4082
+ for (const entry2 of entries) {
4080
4083
  const previous = journals.at(-1);
4081
4084
  const currentState = previous === void 0 ? void 0 : {
4082
4085
  baseContentHash: previous.baseContentHash,
@@ -4088,7 +4091,7 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
4088
4091
  latestJournalHash: previous.journalHash,
4089
4092
  pageHashes: baseManifest.pages.map(({ pageIndex }) => ({ pageIndex, sha256: currentPageHashes.get(pageIndex) }))
4090
4093
  };
4091
- const journal = createPvoxEditJournal(entry, baseManifest, currentState);
4094
+ const journal = createPvoxEditJournal(entry2, baseManifest, currentState);
4092
4095
  if (previous === void 0 && (journal.expectedRevision !== 0 || journal.previousJournalHash !== PVOX_EDIT_JOURNAL_GENESIS_HASH)) {
4093
4096
  throw new Error("PvoxEditJournalChain must begin at the canonical genesis revision.");
4094
4097
  }
@@ -4146,10 +4149,10 @@ function createTransform2(value, fieldName) {
4146
4149
  exactKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
4147
4150
  const translationMetres = requireVector(record2.translationMetres, `${fieldName}.translationMetres`);
4148
4151
  const rotationEntries = asDenseArray(record2.rotationQuaternion, `${fieldName}.rotationQuaternion`, 4, 4);
4149
- const rotationQuaternion = rotationEntries.map((entry, index) => requireNumber(entry, `${fieldName}.rotationQuaternion[${index}]`, -1, 1));
4152
+ const rotationQuaternion = rotationEntries.map((entry2, index) => requireNumber(entry2, `${fieldName}.rotationQuaternion[${index}]`, -1, 1));
4150
4153
  if (Math.abs(Math.hypot(...rotationQuaternion) - 1) > 1e-4) throw new Error(`${fieldName}.rotationQuaternion must be normalized.`);
4151
4154
  const scale = requireVector(record2.scale, `${fieldName}.scale`);
4152
- if (scale.some((entry) => entry <= 0)) throw new Error(`${fieldName}.scale values must be positive.`);
4155
+ if (scale.some((entry2) => entry2 <= 0)) throw new Error(`${fieldName}.scale values must be positive.`);
4153
4156
  return { translationMetres, rotationQuaternion, scale };
4154
4157
  }
4155
4158
  function createAssemblyChild2(value, index, resolutionId, candidateId) {
@@ -4317,10 +4320,10 @@ function createModelProcessingManifestV2(input) {
4317
4320
  }
4318
4321
  const capabilityEntries = asDenseArray(record2.capabilities, "ModelProcessingManifestV2.capabilities", PVOX_CAPABILITIES.length, PVOX_CAPABILITIES.length);
4319
4322
  const capabilities = capabilityEntries.map(createVoxelCapabilityAssessment);
4320
- const byCapability = new Map(capabilities.map((entry) => [entry.capability, entry]));
4323
+ const byCapability = new Map(capabilities.map((entry2) => [entry2.capability, entry2]));
4321
4324
  if (byCapability.size !== PVOX_CAPABILITIES.length || PVOX_CAPABILITIES.some((name) => !byCapability.has(name))) throw new Error("ModelProcessingManifestV2 must include every PVOX capability exactly once.");
4322
4325
  const orderedCapabilities = PVOX_CAPABILITIES.map((name) => byCapability.get(name));
4323
- if (orderedCapabilities.some((entry) => entry.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 capability evidence must bind the binaryClosureHash.");
4326
+ if (orderedCapabilities.some((entry2) => entry2.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 capability evidence must bind the binaryClosureHash.");
4324
4327
  const capabilityEvidenceSetHash = requireSha256(record2.capabilityEvidenceSetHash, "ModelProcessingManifestV2.capabilityEvidenceSetHash");
4325
4328
  const capabilityEvidenceSetAttestation = createHashAttestation(
4326
4329
  record2.capabilityEvidenceSetAttestation,
@@ -4329,7 +4332,7 @@ function createModelProcessingManifestV2(input) {
4329
4332
  "ModelProcessingManifestV2.capabilityEvidenceSetAttestation"
4330
4333
  );
4331
4334
  const requiredCapabilityEntries = asDenseArray(record2.requiredCapabilities, "ModelProcessingManifestV2.requiredCapabilities", 1, PVOX_CAPABILITIES.length);
4332
- const requiredCapabilities = requiredCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `ModelProcessingManifestV2.requiredCapabilities[${index}]`));
4335
+ const requiredCapabilities = requiredCapabilityEntries.map((entry2, index) => requireEnum(entry2, PVOX_CAPABILITIES, `ModelProcessingManifestV2.requiredCapabilities[${index}]`));
4333
4336
  if (new Set(requiredCapabilities).size !== requiredCapabilities.length) throw new Error("ModelProcessingManifestV2.requiredCapabilities must be unique.");
4334
4337
  if (!requiredCapabilities.includes("rendering")) throw new Error("ModelProcessingManifestV2 rendering capability is always required.");
4335
4338
  if (technicalProfile.hasCollision !== (byCapability.get("collision")?.status === "supported")) throw new Error("ModelProcessingManifestV2 collision capability must match technicalProfile.hasCollision.");
@@ -4342,9 +4345,9 @@ function createModelProcessingManifestV2(input) {
4342
4345
  }
4343
4346
  const physicalEntries = asDenseArray(record2.physicalProperties, "ModelProcessingManifestV2.physicalProperties", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
4344
4347
  const physicalProperties = physicalEntries.map(createPhysicalPropertyEvidence);
4345
- const byRegionProperty = new Map(physicalProperties.map((entry) => [`${entry.regionId}\0${entry.materialId}\0${entry.property}`, entry]));
4348
+ const byRegionProperty = new Map(physicalProperties.map((entry2) => [`${entry2.regionId}\0${entry2.materialId}\0${entry2.property}`, entry2]));
4346
4349
  if (byRegionProperty.size !== physicalProperties.length) throw new Error("ModelProcessingManifestV2 physical properties must be unique per region, material, and property.");
4347
- if (physicalProperties.some((entry) => entry.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 physical evidence must bind the PVOX binary closure subject.");
4350
+ if (physicalProperties.some((entry2) => entry2.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 physical evidence must bind the PVOX binary closure subject.");
4348
4351
  const authoritativePairs = physicalRegionInventory.entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
4349
4352
  const authoritativePairSet = new Set(authoritativePairs);
4350
4353
  if (physicalProperties.some(({ regionId, materialId }) => !authoritativePairSet.has(`${regionId}\0${materialId}`))) {
@@ -4426,7 +4429,7 @@ function createModelProcessingManifestV2(input) {
4426
4429
  throw new Error("ModelProcessingManifestV2 fidelity evidence must exactly match every governed PVOX LOD0 metric.");
4427
4430
  }
4428
4431
  const childEntries = asDenseArray(record2.children, "ModelProcessingManifestV2.children", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
4429
- const children = childEntries.map((entry, index) => createAssemblyChild2(entry, index, resolutionId, candidateId));
4432
+ const children = childEntries.map((entry2, index) => createAssemblyChild2(entry2, index, resolutionId, candidateId));
4430
4433
  const kind = requireEnum(record2.kind, ["leaf", "assembly"], "ModelProcessingManifestV2.kind");
4431
4434
  if (kind === "leaf" && children.length !== 0) throw new Error("A leaf ModelProcessingManifestV2 cannot contain children.");
4432
4435
  if (kind === "leaf" && technicalProfile.partitionCount !== 1) throw new Error("A leaf ModelProcessingManifestV2 must have one partition.");
@@ -4509,9 +4512,9 @@ function createCandidateAssetRef2(value) {
4509
4512
  }
4510
4513
  function createViews(value, resolutionId, candidateId) {
4511
4514
  const entries = asDenseArray(value, "ModelCandidateV2.views", 4, 4);
4512
- const parsed = entries.map((entry, index) => {
4515
+ const parsed = entries.map((entry2, index) => {
4513
4516
  const fieldName = `ModelCandidateV2.views[${index}]`;
4514
- const record2 = asRecord(entry, fieldName);
4517
+ const record2 = asRecord(entry2, fieldName);
4515
4518
  exactKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
4516
4519
  const kind = requireEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`);
4517
4520
  const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/${kind}.png`;
@@ -4519,7 +4522,7 @@ function createViews(value, resolutionId, candidateId) {
4519
4522
  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.`);
4520
4523
  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 };
4521
4524
  });
4522
- if (parsed.some((entry, index) => entry.kind !== MODEL_CONFIRMATION_VIEW_KINDS[index])) throw new Error("ModelCandidateV2.views must be ordered front, left, top, isometric.");
4525
+ if (parsed.some((entry2, index) => entry2.kind !== MODEL_CONFIRMATION_VIEW_KINDS[index])) throw new Error("ModelCandidateV2.views must be ordered front, left, top, isometric.");
4523
4526
  return parsed;
4524
4527
  }
4525
4528
  function createRenderEvidence2(value) {
@@ -4718,7 +4721,7 @@ function createModelCandidateV2(input) {
4718
4721
  );
4719
4722
  const gateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
4720
4723
  const gates = gateEntries.map(createPvoxGateEvidence);
4721
- const byGate = new Map(gates.map((entry) => [entry.kind, entry]));
4724
+ const byGate = new Map(gates.map((entry2) => [entry2.kind, entry2]));
4722
4725
  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.");
4723
4726
  const orderedGates = PVOX_NON_OVERRIDABLE_GATE_KINDS.map((kind) => byGate.get(kind));
4724
4727
  const concreteGateHashes = {
@@ -4741,7 +4744,7 @@ function createModelCandidateV2(input) {
4741
4744
  ...!match.hardConstraintPass ? ["semantic-hard-constraints-failed"] : [],
4742
4745
  ...!["allowed", "attribution-required"].includes(rights.status) ? [`rights:${rights.status}`] : [],
4743
4746
  ...processingManifest.requiredCapabilities.flatMap((capability) => {
4744
- const evidence = processingManifest.capabilities.find((entry) => entry.capability === capability);
4747
+ const evidence = processingManifest.capabilities.find((entry2) => entry2.capability === capability);
4745
4748
  return evidence.status === "supported" ? [] : [`capability:${capability}:${evidence.status}`];
4746
4749
  }),
4747
4750
  ...orderedGates.flatMap((gate) => gate.outcome === "passed" ? [] : gate.reasonCodes.map((reason) => `${gate.kind}:${reason}`))
@@ -4769,7 +4772,7 @@ function createModelCandidateV2(input) {
4769
4772
  if (record2.confirmationRequired !== false) throw new Error("Diagnostic ModelCandidateV2 confirmationRequired must be false.");
4770
4773
  if (record2.blockingReasonCodes !== void 0) {
4771
4774
  const suppliedEntries = asDenseArray(record2.blockingReasonCodes, "ModelCandidateV2.blockingReasonCodes", 1, 256);
4772
- const suppliedBlockingReasons = suppliedEntries.map((entry, index) => requireReasonCode(entry, `ModelCandidateV2.blockingReasonCodes[${index}]`));
4775
+ const suppliedBlockingReasons = suppliedEntries.map((entry2, index) => requireReasonCode(entry2, `ModelCandidateV2.blockingReasonCodes[${index}]`));
4773
4776
  if (JSON.stringify(suppliedBlockingReasons) !== JSON.stringify(blockingReasonCodes)) throw new Error("Diagnostic ModelCandidateV2 blockingReasonCodes must be derived from its evidence.");
4774
4777
  }
4775
4778
  return deepFreeze2({ ...common, admissionStatus: "diagnostic", blockingReasonCodes, confirmationRequired: false });
@@ -4823,7 +4826,7 @@ function createModelCandidateV2(input) {
4823
4826
  });
4824
4827
  }
4825
4828
  function isModelCandidateV2Confirmable(candidate) {
4826
- 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");
4829
+ 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((entry2) => entry2.capability === name)?.status === "supported");
4827
4830
  }
4828
4831
  function createModelCandidateConfirmationV2(input, expectedCandidate, expectedResolutionId) {
4829
4832
  const record2 = asRecord(input, "ModelCandidateConfirmationV2");
@@ -4920,7 +4923,7 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
4920
4923
  const candidateAssetRef = createCandidateAssetRef2(record2.candidateAssetRef);
4921
4924
  if (JSON.stringify(candidateAssetRef) !== JSON.stringify(candidate.assetRef)) throw new Error("ModelPromotionReceiptV2 must bind the exact proposed candidate identity.");
4922
4925
  const childClosureEntries = asDenseArray(record2.assemblyChildClosureHashes, "ModelPromotionReceiptV2.assemblyChildClosureHashes", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
4923
- const assemblyChildClosureHashes = childClosureEntries.map((entry, index) => requireSha256(entry, `ModelPromotionReceiptV2.assemblyChildClosureHashes[${index}]`));
4926
+ const assemblyChildClosureHashes = childClosureEntries.map((entry2, index) => requireSha256(entry2, `ModelPromotionReceiptV2.assemblyChildClosureHashes[${index}]`));
4924
4927
  const expectedChildClosureHashes = candidate.processingManifest.children.map(({ assetRef }) => assetRef.binaryClosureHash);
4925
4928
  if (JSON.stringify(assemblyChildClosureHashes) !== JSON.stringify(expectedChildClosureHashes)) throw new Error("ModelPromotionReceiptV2 child closure sequence must match the canonical assembly order.");
4926
4929
  assertDataOnlyTree(record2.finalAssetRef, "ModelPromotionReceiptV2.finalAssetRef");
@@ -5181,12 +5184,12 @@ function copyPvoxByteInput(value, fieldName, maximumByteLength) {
5181
5184
  if (!(value instanceof Uint8Array)) throw new Error(`${fieldName} must be a Uint8Array.`);
5182
5185
  let byteLength;
5183
5186
  try {
5184
- const typedArrayPrototype2 = Object.getPrototypeOf(Uint8Array.prototype);
5185
- const byteLengthGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "byteLength")?.get;
5186
- const bufferGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "buffer")?.get;
5187
- if (byteLengthGetter2 === void 0 || bufferGetter2 === void 0) throw new Error("Typed-array intrinsics are unavailable.");
5188
- byteLength = byteLengthGetter2.call(value);
5189
- const buffer = bufferGetter2.call(value);
5187
+ const typedArrayPrototype3 = Object.getPrototypeOf(Uint8Array.prototype);
5188
+ const byteLengthGetter3 = Object.getOwnPropertyDescriptor(typedArrayPrototype3, "byteLength")?.get;
5189
+ const bufferGetter3 = Object.getOwnPropertyDescriptor(typedArrayPrototype3, "buffer")?.get;
5190
+ if (byteLengthGetter3 === void 0 || bufferGetter3 === void 0) throw new Error("Typed-array intrinsics are unavailable.");
5191
+ byteLength = byteLengthGetter3.call(value);
5192
+ const buffer = bufferGetter3.call(value);
5190
5193
  if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
5191
5194
  throw new Error(`${fieldName} must not use shared mutable storage.`);
5192
5195
  }
@@ -6956,9 +6959,9 @@ function snapshot(value, budget, depth = 0) {
6956
6959
  return value;
6957
6960
  }
6958
6961
  if (Array.isArray(value)) return Object.freeze(list(value).map((item) => snapshot(item, budget, depth + 1)));
6959
- const fields3 = record(value);
6960
- for (const key of Object.keys(fields3)) fields3[key] = snapshot(fields3[key], budget, depth + 1);
6961
- return Object.freeze(fields3);
6962
+ const fields4 = record(value);
6963
+ for (const key of Object.keys(fields4)) fields4[key] = snapshot(fields4[key], budget, depth + 1);
6964
+ return Object.freeze(fields4);
6962
6965
  }
6963
6966
  function path(value) {
6964
6967
  if (typeof value !== "string" || value.length > 1024 || !value.length) fail();
@@ -7030,14 +7033,14 @@ async function qualify(input) {
7030
7033
  contentType(file.contentType);
7031
7034
  }
7032
7035
  const entries = list(inventory.entries).map(record);
7033
- uniquePaths(entries.map((entry) => path(entry.path)));
7036
+ uniquePaths(entries.map((entry2) => path(entry2.path)));
7034
7037
  const ids = /* @__PURE__ */ new Set();
7035
- for (const entry of entries) {
7036
- 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();
7037
- ids.add(entry.id);
7038
- const descriptor = descriptors.get(entry.path);
7038
+ for (const entry2 of entries) {
7039
+ if (Object.keys(entry2).some((key) => !["id", "path", "role", "contentType"].includes(key)) || typeof entry2.id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(entry2.id) || ids.has(entry2.id) || !["mesh", "material", "texture", "animation", "auxiliary"].includes(entry2.role)) fail();
7040
+ ids.add(entry2.id);
7041
+ const descriptor = descriptors.get(entry2.path);
7039
7042
  if (!descriptor) fail("inventory-mismatch");
7040
- if (entry.contentType !== void 0 && contentType(entry.contentType) !== descriptor.contentType) fail("content-mismatch");
7043
+ if (entry2.contentType !== void 0 && contentType(entry2.contentType) !== descriptor.contentType) fail("content-mismatch");
7041
7044
  }
7042
7045
  if (entries.length !== descriptors.size) fail("inventory-mismatch");
7043
7046
  const payloads = list(root.files).map((value) => {
@@ -7180,12 +7183,12 @@ async function qualifyModelTextureResources(input, options = {}) {
7180
7183
  ]);
7181
7184
  check();
7182
7185
  const files = new Map(manifest.files.map((file) => [file.path, file]));
7183
- const roles = new Map(inventory.entries.map((entry) => [entry.path, entry.role]));
7184
- for (const entry of inventory.entries) storageRole(entry.role, files.get(entry.path));
7186
+ const roles = new Map(inventory.entries.map((entry2) => [entry2.path, entry2.role]));
7187
+ for (const entry2 of inventory.entries) storageRole(entry2.role, files.get(entry2.path));
7185
7188
  const resources = new Map(graph.nodes.filter((node) => node.kind === "resource").map((node) => [node.id, node.value]));
7186
7189
  if (bindings.length !== resources.size || new Set(bindings.map((b) => b.resourceId)).size !== bindings.length) fail2("resource-mismatch");
7187
7190
  const resourceFiles = /* @__PURE__ */ new Set();
7188
- const qualified = bindings.map((binding) => {
7191
+ const qualified2 = bindings.map((binding) => {
7189
7192
  const value = resources.get(binding.resourceId);
7190
7193
  const file = files.get(binding.filePath);
7191
7194
  if (!value || !file) fail2("resource-mismatch");
@@ -7218,7 +7221,7 @@ async function qualifyModelTextureResources(input, options = {}) {
7218
7221
  contractVersion: 1,
7219
7222
  manifest,
7220
7223
  source: source.value,
7221
- resources: Object.freeze(qualified.sort((a, b) => compare(a.resourceId, b.resourceId))),
7224
+ resources: Object.freeze(qualified2.sort((a, b) => compare(a.resourceId, b.resourceId))),
7222
7225
  materials: Object.freeze(materials),
7223
7226
  textures: Object.freeze(textures),
7224
7227
  derived: Object.freeze(derived.sort((a, b) => compare(a.filePath, b.filePath)))
@@ -7237,6 +7240,9 @@ function tokenPath(value) {
7237
7240
  return value;
7238
7241
  }
7239
7242
 
7243
+ // src/model-resource-promotion.ts
7244
+ var import_gpu_shader4 = require("@plasius/gpu-shader");
7245
+
7240
7246
  // src/model-conversion-diagnostics.ts
7241
7247
  var MODEL_CONVERSION_DIAGNOSTIC_LIMITS = Object.freeze({ issues: 100, resources: 100, values: 16384, depth: 12, string: 2048, characters: 262144, timeoutMs: 6e4 });
7242
7248
  var ModelConversionDiagnosticsError = class extends TypeError {
@@ -7329,9 +7335,9 @@ async function qualifyModelConversionDiagnostics(input, options = {}) {
7329
7335
  only(binding, ["path", "resource"]);
7330
7336
  if (typeof binding.path !== "string" || !binding.path.startsWith("/") || binding.path === "/" || resources.has(binding.path)) fail3();
7331
7337
  const resource = createModelResourceRef(binding.resource);
7332
- const identity = JSON.stringify(resource);
7333
- if (identities.has(resource.uri) && identities.get(resource.uri) !== identity) fail3();
7334
- identities.set(resource.uri, identity);
7338
+ const identity2 = JSON.stringify(resource);
7339
+ if (identities.has(resource.uri) && identities.get(resource.uri) !== identity2) fail3();
7340
+ identities.set(resource.uri, identity2);
7335
7341
  resources.set(binding.path, resource);
7336
7342
  }
7337
7343
  check();
@@ -7384,6 +7390,193 @@ async function qualifyModelConversionDiagnostics(input, options = {}) {
7384
7390
  }
7385
7391
  }
7386
7392
 
7393
+ // src/model-resource-promotion.ts
7394
+ var MODEL_RESOURCE_PROMOTION_LIMITS = Object.freeze({ bindings: 1024, values: 262144, characters: 2097152, depth: 24, timeoutMs: 6e4 });
7395
+ var ModelResourcePromotionError = class extends TypeError {
7396
+ constructor(code) {
7397
+ super(`Invalid model resource promotion: ${code}`);
7398
+ this.code = code;
7399
+ this.name = "ModelResourcePromotionError";
7400
+ }
7401
+ code;
7402
+ };
7403
+ var qualified = /* @__PURE__ */ new WeakSet();
7404
+ function fail4(code = "invalid-input") {
7405
+ throw new ModelResourcePromotionError(code);
7406
+ }
7407
+ function fields3(value, allowed) {
7408
+ if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail4();
7409
+ const keys = Reflect.ownKeys(value);
7410
+ if (keys.length > 1024) fail4("limit-exceeded");
7411
+ const result = /* @__PURE__ */ Object.create(null);
7412
+ for (const key of keys) {
7413
+ if (typeof key !== "string" || key.length > 128 || ["__proto__", "constructor", "prototype"].includes(key) || allowed && !allowed.includes(key)) fail4();
7414
+ const property = Object.getOwnPropertyDescriptor(value, key);
7415
+ if (!("value" in property) || !property.enumerable) fail4();
7416
+ result[key] = property.value;
7417
+ }
7418
+ return result;
7419
+ }
7420
+ function array2(value, maximum) {
7421
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail4();
7422
+ if (value.length > maximum) fail4("limit-exceeded");
7423
+ if (Reflect.ownKeys(value).length !== value.length + 1) fail4();
7424
+ return Array.from({ length: value.length }, (_, i) => {
7425
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
7426
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail4();
7427
+ return descriptor.value;
7428
+ });
7429
+ }
7430
+ function snapshot4(value, budget, depth = 0) {
7431
+ if (--budget.values < 0 || depth > MODEL_RESOURCE_PROMOTION_LIMITS.depth) fail4("limit-exceeded");
7432
+ if (value === null || value === void 0 || typeof value === "boolean") return value;
7433
+ if (typeof value === "number" && Number.isFinite(value)) return value;
7434
+ if (typeof value === "string") {
7435
+ budget.characters -= value.length;
7436
+ if (value.length > 2048 || budget.characters < 0) fail4("limit-exceeded");
7437
+ return value;
7438
+ }
7439
+ if (Array.isArray(value)) return Object.freeze(array2(value, MODEL_RESOURCE_MANIFEST_LIMITS.files).map((v) => snapshot4(v, budget, depth + 1)));
7440
+ const result = fields3(value);
7441
+ for (const key of Object.keys(result)) {
7442
+ budget.characters -= key.length;
7443
+ if (budget.characters < 0) fail4("limit-exceeded");
7444
+ result[key] = snapshot4(result[key], budget, depth + 1);
7445
+ }
7446
+ return Object.freeze(result);
7447
+ }
7448
+ var typedArrayPrototype2 = Object.getPrototypeOf(Uint8Array.prototype);
7449
+ var byteLengthGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "byteLength").get;
7450
+ var bufferGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "buffer").get;
7451
+ function copyFiles(value) {
7452
+ let total = 0;
7453
+ const files = array2(value, MODEL_RESOURCE_MANIFEST_LIMITS.files).map((v) => {
7454
+ const file = fields3(v, ["path", "bytes", "contentType"]);
7455
+ const bytes = file.bytes;
7456
+ if (typeof file.path !== "string" || typeof file.contentType !== "string" || !(bytes instanceof Uint8Array) || Object.getPrototypeOf(bytes) !== Uint8Array.prototype || !(bufferGetter2.call(bytes) instanceof ArrayBuffer)) fail4();
7457
+ const length = byteLengthGetter2.call(bytes);
7458
+ total += length;
7459
+ if (length > MODEL_RESOURCE_MANIFEST_LIMITS.fileBytes || total > MODEL_RESOURCE_MANIFEST_LIMITS.totalBytes) fail4("limit-exceeded");
7460
+ return { path: file.path, bytes, contentType: file.contentType };
7461
+ });
7462
+ return files.map((f) => ({ ...f, bytes: new Uint8Array(f.bytes) }));
7463
+ }
7464
+ var same = (a, b) => (0, import_gpu_shader4.canonicalizeGpuContract)(a) === (0, import_gpu_shader4.canonicalizeGpuContract)(b);
7465
+ var entry = (m) => m.files.find((f) => f.path === m.entrypoint);
7466
+ var identity = (m) => `${m.assetId}/${m.version}`;
7467
+ async function createModelResourcePromotionRecord(input, options = {}) {
7468
+ let timer;
7469
+ let signal;
7470
+ let onAbort;
7471
+ try {
7472
+ const started = performance.now();
7473
+ const controls = fields3(options, ["signal", "timeoutMs"]);
7474
+ const timeoutMs = controls.timeoutMs === void 0 ? 1e4 : controls.timeoutMs;
7475
+ if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MODEL_RESOURCE_PROMOTION_LIMITS.timeoutMs) fail4();
7476
+ signal = controls.signal;
7477
+ const aborted = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted").get;
7478
+ const check = () => {
7479
+ if (signal !== void 0 && aborted.call(signal)) fail4("aborted");
7480
+ if (performance.now() - started >= timeoutMs) fail4("deadline-exceeded");
7481
+ };
7482
+ check();
7483
+ const root = fields3(input, ["promotion", "source", "output", "diagnostics", "resourceBindings", "candidate", "confirmation", "rollback"]);
7484
+ const budget = { values: MODEL_RESOURCE_PROMOTION_LIMITS.values, characters: MODEL_RESOURCE_PROMOTION_LIMITS.characters };
7485
+ const snap = (v) => snapshot4(v, budget);
7486
+ const promotionInput = snap(root.promotion);
7487
+ const sourceInput = fields3(root.source, ["manifest", "packageManifest", "files"]);
7488
+ const outputInput = fields3(root.output, ["manifest", "packageManifest", "files", "graph", "resources", "derived"]);
7489
+ const sourceMetadata = { manifest: snap(sourceInput.manifest), packageManifest: snap(sourceInput.packageManifest) };
7490
+ const outputMetadata = {
7491
+ manifest: snap(outputInput.manifest),
7492
+ packageManifest: snap(outputInput.packageManifest),
7493
+ graph: outputInput.graph,
7494
+ resources: snap(outputInput.resources),
7495
+ derived: snap(outputInput.derived)
7496
+ };
7497
+ const rawDiagnostics = snap(root.diagnostics);
7498
+ const candidate = createModelCandidate(snap(root.candidate));
7499
+ const confirmation = createModelCandidateConfirmation(snap(root.confirmation), candidate, candidate.resolutionId);
7500
+ const bindings = array2(snap(root.resourceBindings), MODEL_RESOURCE_PROMOTION_LIMITS.bindings).map((v) => {
7501
+ const b = fields3(v, ["resource", "package", "filePath"]);
7502
+ if (b.package !== "source" && b.package !== "output" || typeof b.filePath !== "string") fail4();
7503
+ return Object.freeze({ resource: createModelResourceRef(b.resource), package: b.package, filePath: b.filePath });
7504
+ }).sort((a, b) => a.resource.uri < b.resource.uri ? -1 : a.resource.uri > b.resource.uri ? 1 : 0);
7505
+ const history = root.rollback === void 0 ? void 0 : fields3(root.rollback, ["prior", "current"]);
7506
+ if (history && (!qualified.has(history.prior) || !qualified.has(history.current))) fail4("rollback-mismatch");
7507
+ const prior = history?.prior;
7508
+ const current = history?.current;
7509
+ const sourceFiles = copyFiles(sourceInput.files);
7510
+ const outputFiles = copyFiles(outputInput.files);
7511
+ check();
7512
+ const interrupted = new Promise((_resolve, reject) => {
7513
+ timer = setTimeout(() => reject(new ModelResourcePromotionError("deadline-exceeded")), Math.max(1, timeoutMs - (performance.now() - started)));
7514
+ onAbort = () => reject(new ModelResourcePromotionError("aborted"));
7515
+ if (signal) AbortSignal.prototype.addEventListener.call(signal, "abort", onAbort, { once: true });
7516
+ });
7517
+ const work = async () => {
7518
+ const source = await qualifyModelResourceManifest({ ...sourceMetadata, files: sourceFiles, signal, timeoutMs });
7519
+ check();
7520
+ const output = await qualifyModelTextureResources({ ...outputMetadata, files: outputFiles }, { signal, timeoutMs });
7521
+ check();
7522
+ const diagnostics = await qualifyModelConversionDiagnostics(rawDiagnostics, { signal, timeoutMs });
7523
+ check();
7524
+ assertImmutableAssetVersion(source.version);
7525
+ assertImmutableAssetVersion(output.manifest.version);
7526
+ if (identity(source) === identity(output.manifest) || !same(output.manifest, createModelAssetManifest({ ...promotionInput.manifest, files: [...promotionInput.manifest.files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0) })) || output.manifest.assetKind !== "model") fail4("package-mismatch");
7527
+ const converter = candidate.processingManifest.converter;
7528
+ if (!diagnostics.technicalAccepted || !same(diagnostics.source, candidate.provenance) || diagnostics.source.contentHash !== entry(source).sha256 || output.source.sourceContentHash !== entry(source).sha256 || converter.sourceContentHash !== entry(source).sha256 || converter.outputContentHash !== entry(output.manifest).sha256 || converter.id !== output.source.converterId || converter.version !== output.source.converterVersion || converter.sourceFormat !== output.source.sourceFormat || !same(diagnostics.converter.diagnostics, converter.diagnostics) || !same(diagnostics.converter.losses, converter.losses)) fail4("evidence-mismatch");
7529
+ const byUri = /* @__PURE__ */ new Map();
7530
+ for (const binding of bindings) {
7531
+ const file = (binding.package === "source" ? source : output.manifest).files.find((f) => f.path === binding.filePath);
7532
+ if (!file || byUri.has(binding.resource.uri) || file.sha256 !== binding.resource.sha256 || file.byteLength !== binding.resource.byteLength || file.contentType !== binding.resource.contentType) fail4("evidence-mismatch");
7533
+ byUri.set(binding.resource.uri, binding);
7534
+ }
7535
+ const requireResource = (resource, outputOnly = false) => {
7536
+ const binding = byUri.get(resource.uri);
7537
+ if (!binding || !same(binding.resource, resource) || outputOnly && binding.package !== "output") fail4("evidence-mismatch");
7538
+ return binding;
7539
+ };
7540
+ for (const d of diagnostics.diagnostics) if (d.resource) requireResource(d.resource);
7541
+ for (const lod of candidate.processingManifest.lods) requireResource(lod.resource, true);
7542
+ if (candidate.processingManifest.collision.resource) requireResource(candidate.processingManifest.collision.resource, true);
7543
+ for (const f of candidate.processingManifest.fidelityEvidence) if (f.evidenceResource) requireResource(f.evidenceResource, true);
7544
+ if (requireResource(candidate.processingManifest.lods[0].resource, true).filePath !== output.manifest.entrypoint) fail4("evidence-mismatch");
7545
+ if (candidate.assetRef.disposition === "existing" && (candidate.assetRef.asset.assetId !== output.manifest.assetId || candidate.assetRef.asset.version !== output.manifest.version)) fail4("evidence-mismatch");
7546
+ if (promotionInput.sourceAdapter !== output.manifest.sourceAdapter || promotionInput.reviewReport.passed !== true || promotionInput.reviewReport.findings.some((f) => f.severity === "blocking")) fail4("gate-blocked");
7547
+ const expectedSuffix = `/${output.manifest.assetId}/${output.manifest.version}/${output.manifest.entrypoint}`;
7548
+ const runtimeUri = new URL(promotionInput.runtimeManifestUri);
7549
+ if (!runtimeUri.pathname.endsWith(expectedSuffix)) fail4("evidence-mismatch");
7550
+ const promotion = await createGpuAssetPromotionRecord(promotionInput, new Map(outputFiles.map((f) => [f.path, f.bytes])));
7551
+ check();
7552
+ const closure = { contractVersion: 1, source, output, diagnostics, resourceBindings: Object.freeze(bindings), candidate, confirmation };
7553
+ const closureHash = await (0, import_gpu_shader4.computeSha256)(new TextEncoder().encode((0, import_gpu_shader4.canonicalizeGpuContract)({ contractVersion: 1, source, output, diagnostics, resourceBindings: bindings })));
7554
+ check();
7555
+ let rollback;
7556
+ if (promotion.outcome === "rolled-back") {
7557
+ if (!prior || !current || prior.promotion.assetId !== promotion.assetId || current.promotion.assetId !== promotion.assetId || prior.promotion.runtimeChannel !== promotion.runtimeChannel || current.promotion.runtimeChannel !== promotion.runtimeChannel || promotion.version !== prior.promotion.version || promotion.version === current.promotion.version || promotion.rollbackOfVersion !== current.promotion.version || closureHash !== prior.closureHash || !same(promotion.manifest, prior.promotion.manifest) || promotion.runtimeManifestUri !== prior.promotion.runtimeManifestUri) fail4("rollback-mismatch");
7558
+ rollback = Object.freeze({ fromVersion: current.promotion.version, fromClosureHash: current.closureHash, toVersion: prior.promotion.version, toClosureHash: prior.closureHash });
7559
+ } else if (history || promotion.rollbackOfVersion !== void 0) fail4("rollback-mismatch");
7560
+ const result = Object.freeze({ ...closure, promotion, closureHash, ...rollback ? { rollback } : {} });
7561
+ qualified.add(result);
7562
+ return result;
7563
+ };
7564
+ return await Promise.race([work(), interrupted]);
7565
+ } catch (error) {
7566
+ let code;
7567
+ try {
7568
+ code = Object.getOwnPropertyDescriptor(error, "code")?.value;
7569
+ } catch {
7570
+ return fail4();
7571
+ }
7572
+ if (["invalid-input", "limit-exceeded", "package-mismatch", "evidence-mismatch", "gate-blocked", "rollback-mismatch", "aborted", "deadline-exceeded"].includes(code)) return fail4(code);
7573
+ return fail4();
7574
+ } finally {
7575
+ if (timer !== void 0) clearTimeout(timer);
7576
+ if (signal && onAbort) AbortSignal.prototype.removeEventListener.call(signal, "abort", onAbort);
7577
+ }
7578
+ }
7579
+
7387
7580
  // src/index.ts
7388
7581
  var ASSET_CONTRACTS_PACKAGE = "@plasius/asset-contracts";
7389
7582
  var UNIFIED_ASSET_PIPELINE_FEATURE_FLAG_ID = "asset.pipeline.unified-ai-assets.enabled";
@@ -7564,7 +7757,7 @@ function createModelAssetManifest(input) {
7564
7757
  "screenshot",
7565
7758
  "metadata"
7566
7759
  ]);
7567
- const descriptor = (0, import_gpu_shader4.parseModelGpuCompatibilityDescriptor)({
7760
+ const descriptor = (0, import_gpu_shader5.parseModelGpuCompatibilityDescriptor)({
7568
7761
  modelId: base.assetId,
7569
7762
  version: base.version,
7570
7763
  gpuInterface: input.gpuInterface,
@@ -7594,7 +7787,7 @@ function createGpuInterfaceAssetManifest(input) {
7594
7787
  );
7595
7788
  assertAllowedFileRoles(base, ["gpu-interface-manifest"]);
7596
7789
  assertRoleCount(base, "gpu-interface-manifest", 1);
7597
- const gpuInterfaceManifest = (0, import_gpu_shader4.parseGpuInterfaceManifest)(input.gpuInterfaceManifest);
7790
+ const gpuInterfaceManifest = (0, import_gpu_shader5.parseGpuInterfaceManifest)(input.gpuInterfaceManifest);
7598
7791
  assertImmutableAssetVersion(gpuInterfaceManifest.interfaceVersion);
7599
7792
  assertDomainIdentity(
7600
7793
  base,
@@ -7617,7 +7810,7 @@ function createShaderAssetManifest(input) {
7617
7810
  );
7618
7811
  assertAllowedFileRoles(base, ["shader-manifest", "wgsl"]);
7619
7812
  assertRoleCount(base, "shader-manifest", 1);
7620
- const shaderManifest = (0, import_gpu_shader4.parseShaderVersionManifest)(input.shaderManifest);
7813
+ const shaderManifest = (0, import_gpu_shader5.parseShaderVersionManifest)(input.shaderManifest);
7621
7814
  assertShaderManifestVersions(shaderManifest);
7622
7815
  assertDomainIdentity(base, shaderManifest.shaderId, shaderManifest.version, "Shader");
7623
7816
  assertShaderModuleFiles(base.files, shaderManifest);
@@ -7636,7 +7829,7 @@ function createShaderStyleProfileAssetManifest(input) {
7636
7829
  );
7637
7830
  assertAllowedFileRoles(base, ["shader-style-profile-manifest"]);
7638
7831
  assertRoleCount(base, "shader-style-profile-manifest", 1);
7639
- const styleProfileManifest = (0, import_gpu_shader4.parseShaderStyleProfileManifest)(input.styleProfileManifest);
7832
+ const styleProfileManifest = (0, import_gpu_shader5.parseShaderStyleProfileManifest)(input.styleProfileManifest);
7640
7833
  assertStyleProfileManifestVersions(styleProfileManifest);
7641
7834
  assertDomainIdentity(base, styleProfileManifest.profileId, styleProfileManifest.version, "Style profile");
7642
7835
  return Object.freeze({ ...base, styleProfileManifest });
@@ -7700,8 +7893,8 @@ function createShaderValidationEvidenceRef(input) {
7700
7893
  sha256: assertSha256(attestation2.sha256, "Shader validation evidence attestation sha256")
7701
7894
  })
7702
7895
  };
7703
- const supportedUniversal = import_gpu_shader4.SUPPORTED_STABLE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
7704
- const supportedAdditive = import_gpu_shader4.SUPPORTED_ADDITIVE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
7896
+ const supportedUniversal = import_gpu_shader5.SUPPORTED_STABLE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
7897
+ const supportedAdditive = import_gpu_shader5.SUPPORTED_ADDITIVE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
7705
7898
  if (!supportedUniversal && !supportedAdditive) {
7706
7899
  throw new Error("Shader validation evidence must target a supported WebGPU matrix policy.");
7707
7900
  }
@@ -7712,7 +7905,7 @@ function createShaderValidationEvidenceRef(input) {
7712
7905
  }
7713
7906
  async function createGpuInterfaceRef(input) {
7714
7907
  assertImmutableAssetVersion(input.manifest.interfaceVersion);
7715
- const manifest = (0, import_gpu_shader4.parseGpuInterfaceManifest)(input.manifest);
7908
+ const manifest = (0, import_gpu_shader5.parseGpuInterfaceManifest)(input.manifest);
7716
7909
  assertAssetId(manifest.interfaceId);
7717
7910
  assertImmutableAssetVersion(manifest.interfaceVersion);
7718
7911
  const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "GPU interface");
@@ -7727,7 +7920,7 @@ async function createGpuInterfaceRef(input) {
7727
7920
  }
7728
7921
  async function createShaderVersionRef(input) {
7729
7922
  assertImmutableAssetVersion(input.manifest.version);
7730
- const manifest = (0, import_gpu_shader4.parseShaderVersionManifest)(input.manifest);
7923
+ const manifest = (0, import_gpu_shader5.parseShaderVersionManifest)(input.manifest);
7731
7924
  assertAssetId(manifest.shaderId);
7732
7925
  assertShaderManifestVersions(manifest);
7733
7926
  const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Shader");
@@ -7740,7 +7933,7 @@ async function createShaderVersionRef(input) {
7740
7933
  }
7741
7934
  async function createShaderStyleProfileRef(input) {
7742
7935
  assertImmutableAssetVersion(input.manifest.version);
7743
- const manifest = (0, import_gpu_shader4.parseShaderStyleProfileManifest)(input.manifest);
7936
+ const manifest = (0, import_gpu_shader5.parseShaderStyleProfileManifest)(input.manifest);
7744
7937
  assertAssetId(manifest.profileId);
7745
7938
  assertStyleProfileManifestVersions(manifest);
7746
7939
  const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Style profile");
@@ -7785,7 +7978,7 @@ async function validateGpuAssetFiles(input) {
7785
7978
  if (bytes.byteLength !== file.byteLength) {
7786
7979
  throw new Error(`GPU asset file ${file.path} byte length differs from its descriptor.`);
7787
7980
  }
7788
- if (await (0, import_gpu_shader4.computeSha256)(bytes) !== file.sha256) {
7981
+ if (await (0, import_gpu_shader5.computeSha256)(bytes) !== file.sha256) {
7789
7982
  throw new Error(`GPU asset file ${file.path} digest differs from its descriptor.`);
7790
7983
  }
7791
7984
  if (file.contentType === ASSET_JSON_CONTENT_TYPE) {
@@ -7804,13 +7997,13 @@ async function validateGpuAssetFiles(input) {
7804
7997
  }
7805
7998
  const expectedDomainManifest = domainManifestForAsset(manifest);
7806
7999
  if (expectedDomainManifest !== null) {
7807
- const parsedEntrypoint = (0, import_gpu_shader4.parseJsonBytes)(entrypointBytes, "GPU asset manifest entrypoint");
7808
- if ((0, import_gpu_shader4.canonicalizeGpuContract)(parsedEntrypoint) !== (0, import_gpu_shader4.canonicalizeGpuContract)(expectedDomainManifest)) {
8000
+ const parsedEntrypoint = (0, import_gpu_shader5.parseJsonBytes)(entrypointBytes, "GPU asset manifest entrypoint");
8001
+ if ((0, import_gpu_shader5.canonicalizeGpuContract)(parsedEntrypoint) !== (0, import_gpu_shader5.canonicalizeGpuContract)(expectedDomainManifest)) {
7809
8002
  throw new Error("GPU asset entrypoint bytes differ from the validated domain manifest.");
7810
8003
  }
7811
8004
  } else if (manifest.assetKind === "shader-validation-evidence") {
7812
8005
  assertEvidenceEntrypointIdentity(
7813
- (0, import_gpu_shader4.parseJsonBytes)(entrypointBytes, "Shader validation evidence entrypoint"),
8006
+ (0, import_gpu_shader5.parseJsonBytes)(entrypointBytes, "Shader validation evidence entrypoint"),
7814
8007
  manifest.validationEvidence
7815
8008
  );
7816
8009
  }
@@ -8034,7 +8227,7 @@ function assertTypedRuntimeManifestUri(manifest, value) {
8034
8227
  }
8035
8228
  function assertEvidenceEntrypointIdentity(value, ref2) {
8036
8229
  const evidence = assertRecord2(value, "Shader validation evidence entrypoint");
8037
- if (evidence.contractVersion !== import_gpu_shader4.SHADER_VALIDATION_EVIDENCE_VERSION || evidence.evidenceId !== ref2.evidenceId || evidence.status !== "passed") {
8230
+ if (evidence.contractVersion !== import_gpu_shader5.SHADER_VALIDATION_EVIDENCE_VERSION || evidence.evidenceId !== ref2.evidenceId || evidence.status !== "passed") {
8038
8231
  throw new Error("Shader validation evidence bytes do not identify passing declared evidence.");
8039
8232
  }
8040
8233
  const matrixRef = assertRecord2(evidence.matrixRef, "Shader validation evidence matrixRef");
@@ -8050,9 +8243,9 @@ function assertEvidenceEntrypointIdentity(value, ref2) {
8050
8243
  }
8051
8244
  }
8052
8245
  function assertCanonicalJsonBytes(bytes, fieldName) {
8053
- const value = (0, import_gpu_shader4.parseJsonBytes)(bytes, fieldName);
8246
+ const value = (0, import_gpu_shader5.parseJsonBytes)(bytes, fieldName);
8054
8247
  const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
8055
- if ((0, import_gpu_shader4.canonicalizeGpuContract)(value) !== text) {
8248
+ if ((0, import_gpu_shader5.canonicalizeGpuContract)(value) !== text) {
8056
8249
  throw new Error(`${fieldName} must use canonical JSON serialization.`);
8057
8250
  }
8058
8251
  }
@@ -8061,11 +8254,11 @@ async function bindDomainManifestBytes(bytes, manifest, fieldName) {
8061
8254
  throw new Error(`${fieldName} manifest bytes must be a Uint8Array.`);
8062
8255
  }
8063
8256
  assertCanonicalJsonBytes(bytes, `${fieldName} manifest`);
8064
- const parsed = (0, import_gpu_shader4.parseJsonBytes)(bytes, `${fieldName} manifest`);
8065
- if ((0, import_gpu_shader4.canonicalizeGpuContract)(parsed) !== (0, import_gpu_shader4.canonicalizeGpuContract)(manifest)) {
8257
+ const parsed = (0, import_gpu_shader5.parseJsonBytes)(bytes, `${fieldName} manifest`);
8258
+ if ((0, import_gpu_shader5.canonicalizeGpuContract)(parsed) !== (0, import_gpu_shader5.canonicalizeGpuContract)(manifest)) {
8066
8259
  throw new Error(`${fieldName} manifest bytes differ from the supplied manifest.`);
8067
8260
  }
8068
- return (0, import_gpu_shader4.computeSha256)(bytes);
8261
+ return (0, import_gpu_shader5.computeSha256)(bytes);
8069
8262
  }
8070
8263
  function assertRoleCount(manifest, role, expectedCount) {
8071
8264
  if (manifest.files.filter((file) => file.role === role).length !== expectedCount) {
@@ -8187,11 +8380,13 @@ function assertRequiredString(value, fieldName) {
8187
8380
  MODEL_RESOLUTION_V2_JSON_SCHEMAS,
8188
8381
  MODEL_RESOLUTION_V2_STATES,
8189
8382
  MODEL_RESOURCE_MANIFEST_LIMITS,
8383
+ MODEL_RESOURCE_PROMOTION_LIMITS,
8190
8384
  MODEL_RIGHTS_STATUSES,
8191
8385
  MODEL_TEXTURE_RESOURCE_LIMITS,
8192
8386
  MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
8193
8387
  ModelConversionDiagnosticsError,
8194
8388
  ModelResourceManifestError,
8389
+ ModelResourcePromotionError,
8195
8390
  ModelTextureResourceError,
8196
8391
  PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
8197
8392
  PVOX_ASSET_MANIFEST_VERSION,
@@ -8327,6 +8522,7 @@ function assertRequiredString(value, fieldName) {
8327
8522
  createModelRequestSpecV2,
8328
8523
  createModelResolution,
8329
8524
  createModelResolutionV2,
8525
+ createModelResourcePromotionRecord,
8330
8526
  createModelResourceRef,
8331
8527
  createModelRightsAssessment,
8332
8528
  createModelTechnicalProfile,