@forgeax/engine-preview 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +75 -0
  3. package/assets/canonical-kit/cook-receipt.json +40 -0
  4. package/assets/canonical-kit/sky.hdr +0 -0
  5. package/assets/canonical-kit/sky.hdr.meta.json +19 -0
  6. package/dist/.tsbuildinfo +1 -0
  7. package/dist/__tests__/canonical-rig-owner.test-d.d.ts +1 -0
  8. package/dist/__tests__/consumer.smoke.test.d.ts +1 -0
  9. package/dist/__tests__/contract.test.d.ts +1 -0
  10. package/dist/__tests__/default-runtime.test.d.ts +1 -0
  11. package/dist/__tests__/host.test.d.ts +1 -0
  12. package/dist/__tests__/kit-receipt.test.d.ts +1 -0
  13. package/dist/__tests__/manifest.test.d.ts +1 -0
  14. package/dist/__tests__/material-oracle.test.d.ts +1 -0
  15. package/dist/__tests__/material.test.d.ts +1 -0
  16. package/dist/__tests__/mesh-oracle.test.d.ts +1 -0
  17. package/dist/__tests__/mesh-subject.test.d.ts +1 -0
  18. package/dist/__tests__/mesh.test.d.ts +1 -0
  19. package/dist/__tests__/oracle-status-owner.test-d.d.ts +1 -0
  20. package/dist/__tests__/owner-gate.test.d.ts +1 -0
  21. package/dist/__tests__/preview-contract.test.d.ts +1 -0
  22. package/dist/__tests__/texture-oracle.test.d.ts +1 -0
  23. package/dist/__tests__/texture-payload-class-owner.test-d.d.ts +1 -0
  24. package/dist/__tests__/texture.test.d.ts +1 -0
  25. package/dist/__tests__/vfx-oracle.test.d.ts +1 -0
  26. package/dist/__tests__/vfx-subject.test.d.ts +1 -0
  27. package/dist/__tests__/vfx.test.d.ts +1 -0
  28. package/dist/domains/material.d.ts +50 -0
  29. package/dist/domains/mesh.d.ts +53 -0
  30. package/dist/domains/subject.d.ts +28 -0
  31. package/dist/domains/texture.d.ts +63 -0
  32. package/dist/domains/vfx.d.ts +58 -0
  33. package/dist/evidence/errors.d.ts +14 -0
  34. package/dist/evidence/manifest.d.ts +10 -0
  35. package/dist/evidence/oracle.d.ts +104 -0
  36. package/dist/evidence/report.d.ts +15 -0
  37. package/dist/host/preview-host.d.ts +52 -0
  38. package/dist/host.d.ts +88 -0
  39. package/dist/index.d.ts +15 -0
  40. package/dist/index.mjs +1826 -0
  41. package/dist/index.mjs.map +1 -0
  42. package/dist/kit/canonical.d.ts +16 -0
  43. package/dist/kit/presentation.d.ts +33 -0
  44. package/dist/kit/receipt.d.ts +56 -0
  45. package/dist/kit/receipt.test-fixtures.d.ts +41 -0
  46. package/dist/material.d.ts +45 -0
  47. package/dist/material.mjs +469 -0
  48. package/dist/material.mjs.map +1 -0
  49. package/dist/mesh.d.ts +57 -0
  50. package/dist/mesh.mjs +529 -0
  51. package/dist/mesh.mjs.map +1 -0
  52. package/dist/primitive.d.ts +46 -0
  53. package/dist/texture.d.ts +57 -0
  54. package/dist/texture.mjs +526 -0
  55. package/dist/texture.mjs.map +1 -0
  56. package/dist/vfx.d.ts +55 -0
  57. package/dist/vfx.mjs +630 -0
  58. package/dist/vfx.mjs.map +1 -0
  59. package/package.json +83 -0
package/dist/mesh.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ import { type SnapshotRef, type ToolContribution, type ToolSchema, type ToolSubjectRef } from '@forgeax/engine-tool-runtime';
2
+ import { type PreviewDomainRunner } from './host.js';
3
+ export interface MeshSubmeshBinding {
4
+ readonly id: string;
5
+ readonly vertexCount: number;
6
+ readonly indexCount: number;
7
+ }
8
+ export interface MeshAabb {
9
+ readonly min: readonly [number, number, number];
10
+ readonly max: readonly [number, number, number];
11
+ }
12
+ export interface MeshBinding {
13
+ readonly guid: string;
14
+ readonly vertexDigest: string;
15
+ readonly indexDigest: string;
16
+ readonly submeshes: readonly MeshSubmeshBinding[];
17
+ readonly aabb: MeshAabb;
18
+ }
19
+ export interface MeshPreviewRequest {
20
+ readonly subject: ToolSubjectRef;
21
+ readonly snapshot: SnapshotRef;
22
+ readonly binding: MeshBinding;
23
+ }
24
+ export interface MeshPreviewReport {
25
+ readonly kind: 'mesh';
26
+ readonly bindingGuid: string;
27
+ readonly submeshCount: number;
28
+ readonly expectedSubmeshCount: number;
29
+ readonly vertexCount: number;
30
+ readonly indexCount: number;
31
+ readonly framing: 'aabb' | 'inverted-aabb';
32
+ }
33
+ export declare const meshPreviewDescriptor: {
34
+ id: string;
35
+ title: string;
36
+ summary: string;
37
+ realm: "engine";
38
+ argsSchema: ToolSchema<MeshPreviewRequest>;
39
+ resultSchema: ToolSchema<unknown>;
40
+ evidence: readonly ["rhi-tape", "png", "profile-capture"];
41
+ preview: {
42
+ realm: "engine";
43
+ subject: {
44
+ kind: string;
45
+ guid: string;
46
+ };
47
+ snapshot: {
48
+ revision: number;
49
+ digest: string;
50
+ };
51
+ requiredEvidence: readonly ["rhi-tape", "png", "profile-capture"];
52
+ };
53
+ };
54
+ export declare function createMeshPreviewContribution(runner?: PreviewDomainRunner<MeshPreviewRequest, MeshPreviewReport>): ToolContribution<MeshPreviewRequest, unknown>;
55
+ export declare const meshPreview: ToolContribution<import("./index.js").ResourcePreviewArgs, unknown>;
56
+ export declare const meshPreviewPlugin: import("@forgeax/engine-plugin").ToolPlugin;
57
+ export default meshPreviewPlugin;
package/dist/mesh.mjs ADDED
@@ -0,0 +1,529 @@
1
+ import { defineToolCapability, defineTool } from '@forgeax/engine-tool-runtime';
2
+ import { builtinAssetKind } from '@forgeax/engine-runtime';
3
+ import { defineToolPlugin } from '@forgeax/engine-plugin';
4
+
5
+ // src/mesh.ts
6
+
7
+ // src/evidence/oracle.ts
8
+ function passedPreviewOracle(input) {
9
+ return {
10
+ status: "passed",
11
+ subjectBound: true,
12
+ drawCalls: input.drawCalls,
13
+ dispatches: input.dispatches ?? 0,
14
+ rendererHealthy: true,
15
+ detail: { ...input.detail }
16
+ };
17
+ }
18
+ function failedPreviewOracle(input) {
19
+ return {
20
+ status: "failed",
21
+ subjectBound: false,
22
+ drawCalls: input.drawCalls ?? 0,
23
+ dispatches: input.dispatches ?? 0,
24
+ rendererHealthy: input.rendererHealthy ?? false,
25
+ detail: { ...input.detail }
26
+ };
27
+ }
28
+ function oracleDetail(kind, requested, observed, mismatch) {
29
+ return {
30
+ kind,
31
+ mismatch: mismatch ?? "none",
32
+ requestedSubjectDigest: typeof requested.subjectDigest === "string" ? requested.subjectDigest : "unknown",
33
+ observedSubjectDigest: typeof observed.subjectDigest === "string" ? observed.subjectDigest : "unknown",
34
+ requestedOwnerDigest: JSON.stringify(requested),
35
+ observedOwnerDigest: JSON.stringify(observed)
36
+ };
37
+ }
38
+ function firstMismatch(requested, observed) {
39
+ for (const key of Object.keys(requested)) {
40
+ const requestedValue = requested[key];
41
+ const observedValue = observed[key];
42
+ if (Array.isArray(requestedValue) || Array.isArray(observedValue) || typeof requestedValue === "object" || typeof observedValue === "object") {
43
+ if (JSON.stringify(requestedValue) !== JSON.stringify(observedValue)) return key;
44
+ } else if (requestedValue !== observedValue) {
45
+ return key;
46
+ }
47
+ }
48
+ return void 0;
49
+ }
50
+ function evaluateMeshOracle(input) {
51
+ const requested = input.requested;
52
+ const observed = input.observed;
53
+ const mismatch = firstMismatch(requested, observed);
54
+ const healthy = input.observed.rendererHealthy && input.observed.drawCalls > 0;
55
+ const detail = oracleDetail("mesh", requested, observed, mismatch);
56
+ if (mismatch !== void 0 || !healthy) {
57
+ return failedPreviewOracle({
58
+ drawCalls: input.observed.drawCalls,
59
+ rendererHealthy: input.observed.rendererHealthy,
60
+ detail: { ...detail, rendererHealthy: input.observed.rendererHealthy }
61
+ });
62
+ }
63
+ return passedPreviewOracle({
64
+ drawCalls: input.observed.drawCalls,
65
+ detail: { ...detail, rendererHealthy: true }
66
+ });
67
+ }
68
+
69
+ // src/kit/canonical.ts
70
+ function createCanonicalPreviewRecipe(kind) {
71
+ const texture = kind === "texture";
72
+ return {
73
+ schemaVersion: "1.0.0",
74
+ rig: kind === "mesh" ? "asset-mesh" : texture ? "asset-quad" : "handle-sphere",
75
+ environment: texture ? "black" : "engine-canonical",
76
+ skybox: texture ? "none" : "engine-canonical",
77
+ skylight: texture ? "none" : "engine-canonical",
78
+ directionalLight: texture ? "none" : "engine-canonical",
79
+ stage: texture ? "texture-unlit-black" : "neutral-material-checker-unlit",
80
+ camera: texture ? "texture-orthographic" : "bounds-derived",
81
+ recipeDigest: `canonical:${kind}:${"bounds-derived"}`
82
+ };
83
+ }
84
+ function canonicalPresentation(kind) {
85
+ return {
86
+ kind: "lit-asset",
87
+ geometry: kind === "mesh" ? "asset-mesh" : "handle-sphere",
88
+ skylight: "engine-canonical",
89
+ directionalLight: "engine-canonical",
90
+ skybox: "engine-canonical"
91
+ };
92
+ }
93
+ var previewHostCapability = defineToolCapability("preview.host");
94
+
95
+ // src/domains/subject.ts
96
+ var RESOURCE_PREVIEW_DEFAULT_SIZE = 512;
97
+ var RESOURCE_PREVIEW_MIN_SIZE = 64;
98
+ var RESOURCE_PREVIEW_MAX_SIZE = 4096;
99
+ function isResourcePreviewSize(value) {
100
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= RESOURCE_PREVIEW_MIN_SIZE && value <= RESOURCE_PREVIEW_MAX_SIZE && (value & value - 1) === 0;
101
+ }
102
+ function isRecord(value) {
103
+ return typeof value === "object" && value !== null && !Array.isArray(value);
104
+ }
105
+ function isString(value) {
106
+ return typeof value === "string" && value.length > 0;
107
+ }
108
+ function isResourcePreviewResult(value) {
109
+ if (!isRecord(value)) return false;
110
+ const subject = value.subject;
111
+ if (!isRecord(subject) || !isString(subject.kind) || !isString(subject.guid) || !isString(subject.digest))
112
+ return false;
113
+ if (!isRecord(value.presentation) || !isString(value.presentation.kind)) return false;
114
+ if (!isRecord(value.recipe) || !isString(value.recipe.schemaVersion) || !isString(value.recipe.recipeDigest))
115
+ return false;
116
+ if (!isRecord(value.oracle) || !isString(value.oracle.status)) return false;
117
+ return Array.isArray(value.artifacts);
118
+ }
119
+ var resourcePreviewResultSchema = {
120
+ parse(value) {
121
+ return isResourcePreviewResult(value) ? { ok: true, value } : {
122
+ ok: false,
123
+ error: "resource preview result must include subject, presentation, recipe, oracle, and artifacts"
124
+ };
125
+ },
126
+ describe: '{"type":"object","required":["subject","presentation","recipe","oracle","artifacts"]}'
127
+ };
128
+ var resourcePreviewArgsSchema = {
129
+ parse(value) {
130
+ if (value === null || typeof value !== "object" || typeof Reflect.get(value, "guid") !== "string") {
131
+ return { ok: false, error: "expected { guid: string }" };
132
+ }
133
+ const guid = Reflect.get(value, "guid");
134
+ if (guid.length === 0) return { ok: false, error: "guid must not be empty" };
135
+ const size = Reflect.get(value, "size");
136
+ if (size === void 0) return { ok: true, value: { guid } };
137
+ return isResourcePreviewSize(size) ? { ok: true, value: { guid, size } } : {
138
+ ok: false,
139
+ error: `size must be a power of two between ${RESOURCE_PREVIEW_MIN_SIZE} and ${RESOURCE_PREVIEW_MAX_SIZE}`
140
+ };
141
+ },
142
+ describe: `{"type":"object","required":["guid"],"properties":{"guid":{"type":"string"},"size":{"type":"integer","minimum":${RESOURCE_PREVIEW_MIN_SIZE},"maximum":${RESOURCE_PREVIEW_MAX_SIZE},"description":"square power-of-two screenshot edge; defaults to ${RESOURCE_PREVIEW_DEFAULT_SIZE}"}}}`
143
+ };
144
+ function subjectDescriptor(kind) {
145
+ return {
146
+ id: `${kind}.preview`,
147
+ title: `Preview ${kind}`,
148
+ summary: `Preview one ${kind} asset in the Engine-owned resource host.`,
149
+ realm: "host",
150
+ argsSchema: resourcePreviewArgsSchema,
151
+ resultSchema: resourcePreviewResultSchema,
152
+ evidence: ["rhi-tape", "profile-capture", "png"]
153
+ };
154
+ }
155
+ function subjectFailure(code, expected, detail) {
156
+ return {
157
+ ok: false,
158
+ error: {
159
+ code,
160
+ expected,
161
+ hint: "Load the requested GUID through AssetRegistry.load(guid, kind) and repair the owner facts before retrying.",
162
+ detail
163
+ }
164
+ };
165
+ }
166
+ function assetLoadFailure(expected, runId, error) {
167
+ const owner = isRecord(error) ? error : void 0;
168
+ const ownerCode = typeof owner?.code === "string" ? owner.code : void 0;
169
+ const ownerExpected = typeof owner?.expected === "string" ? owner.expected : void 0;
170
+ const ownerHint = typeof owner?.hint === "string" ? owner.hint : void 0;
171
+ const ownerDetail = owner?.detail;
172
+ return subjectFailure("resource-preview-subject-invalid", expected, {
173
+ phase: "asset-load",
174
+ runId,
175
+ ...ownerCode === void 0 ? {} : { ownerCode },
176
+ ...ownerExpected === void 0 ? {} : { ownerExpected },
177
+ ...ownerHint === void 0 ? {} : { ownerHint },
178
+ ...ownerDetail === void 0 ? {} : { ownerDetail: toJsonValue(ownerDetail) }
179
+ });
180
+ }
181
+ function toJsonValue(value) {
182
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
183
+ if (typeof value === "number" && Number.isFinite(value)) return value;
184
+ if (Array.isArray(value)) return value.map((entry) => toJsonValue(entry));
185
+ if (isRecord(value)) {
186
+ return Object.fromEntries(
187
+ Object.entries(value).map(([key, entry]) => [key, toJsonValue(entry)])
188
+ );
189
+ }
190
+ return String(value);
191
+ }
192
+ function nativePreviewPlugin(kind, contribution) {
193
+ return defineToolPlugin({ name: `forgeax-preview-${kind}`, apply() {
194
+ } }, [
195
+ contribution
196
+ ]);
197
+ }
198
+
199
+ // src/domains/mesh.ts
200
+ function isRecord2(value) {
201
+ return typeof value === "object" && value !== null;
202
+ }
203
+ function digest(value) {
204
+ return typeof value === "string" && value.length > 0 ? value : void 0;
205
+ }
206
+ function observedAabb(observation) {
207
+ const value = observation.aabb;
208
+ if (!Array.isArray(value) || value.length !== 6 || value.some((entry) => typeof entry !== "number" || !Number.isFinite(entry)))
209
+ return void 0;
210
+ const [minX, minY, minZ, maxX, maxY, maxZ] = value;
211
+ if (minX > maxX || minY > maxY || minZ > maxZ) return void 0;
212
+ return [minX, minY, minZ, maxX, maxY, maxZ];
213
+ }
214
+ function inspectMeshSubject(input) {
215
+ if (!isRecord2(input.asset) || input.asset.kind !== "mesh") {
216
+ return subjectFailure("resource-preview-kind-mismatch", "a MeshAsset with kind mesh", {
217
+ phase: "subject",
218
+ guid: input.guid,
219
+ actualKind: isRecord2(input.asset) && typeof input.asset.kind === "string" ? input.asset.kind : "unknown"
220
+ });
221
+ }
222
+ const aabbValue = input.asset.aabb;
223
+ const aabb = aabbValue instanceof Float32Array ? [...aabbValue] : Array.isArray(aabbValue) ? aabbValue : [];
224
+ if (aabb.length !== 6 || aabb.some((value) => typeof value !== "number" || !Number.isFinite(value))) {
225
+ return subjectFailure("resource-preview-subject-invalid", "a finite six-value MeshAsset AABB", {
226
+ phase: "subject",
227
+ guid: input.guid,
228
+ field: "aabb"
229
+ });
230
+ }
231
+ const [minX, minY, minZ, maxX, maxY, maxZ] = aabb;
232
+ if (minX > maxX || minY > maxY || minZ > maxZ) {
233
+ return subjectFailure("resource-preview-subject-invalid", "a non-empty MeshAsset AABB", {
234
+ phase: "subject",
235
+ guid: input.guid,
236
+ field: "aabb"
237
+ });
238
+ }
239
+ const slots = input.asset.materialSlots;
240
+ const submeshes = input.asset.submeshes;
241
+ if (!Array.isArray(slots) || slots.length === 0) {
242
+ return subjectFailure(
243
+ "resource-preview-subject-invalid",
244
+ "every mesh submesh to have a material slot",
245
+ {
246
+ phase: "subject",
247
+ guid: input.guid,
248
+ field: "materialSlots"
249
+ }
250
+ );
251
+ }
252
+ if (!Array.isArray(submeshes) || submeshes.length === 0) {
253
+ return subjectFailure(
254
+ "resource-preview-subject-invalid",
255
+ "every MeshAsset to declare at least one submesh",
256
+ {
257
+ phase: "subject",
258
+ guid: input.guid,
259
+ field: "submeshes"
260
+ }
261
+ );
262
+ }
263
+ for (const submesh of submeshes) {
264
+ if (!isRecord2(submesh)) {
265
+ return subjectFailure(
266
+ "resource-preview-subject-invalid",
267
+ "each mesh submesh to be structured",
268
+ {
269
+ phase: "subject",
270
+ guid: input.guid,
271
+ field: "submeshes"
272
+ }
273
+ );
274
+ }
275
+ const materialSlot = submesh.materialSlot;
276
+ const indexOffset = submesh.indexOffset;
277
+ const indexCount = submesh.indexCount;
278
+ if (typeof materialSlot !== "number" || materialSlot < 0 || materialSlot >= slots.length || typeof indexOffset !== "number" || typeof indexCount !== "number" || indexOffset < 0 || indexCount <= 0) {
279
+ return subjectFailure(
280
+ "resource-preview-subject-invalid",
281
+ "every submesh range and material slot to be valid",
282
+ {
283
+ phase: "subject",
284
+ guid: input.guid,
285
+ field: "submeshes"
286
+ }
287
+ );
288
+ }
289
+ }
290
+ const owner = input.asset.ownerFacts;
291
+ const ownerFacts = isRecord2(owner) ? owner : input.ownerFacts;
292
+ const subjectDigest = digest(input.asset.digest ?? input.digest ?? ownerFacts?.subjectDigest);
293
+ const vertexDigest = digest(input.asset.vertexDigest ?? ownerFacts?.vertexDigest);
294
+ const indexDigest = digest(input.asset.indexDigest ?? ownerFacts?.indexDigest);
295
+ const submeshDigest = digest(input.asset.submeshDigest ?? ownerFacts?.submeshDigest);
296
+ const aabbDigest = digest(input.asset.aabbDigest ?? ownerFacts?.aabbDigest);
297
+ if (subjectDigest === void 0 || vertexDigest === void 0 || indexDigest === void 0 || submeshDigest === void 0 || aabbDigest === void 0) {
298
+ return subjectFailure(
299
+ "resource-preview-subject-invalid",
300
+ "the MeshAsset owner to publish vertex, index, submesh, AABB, and subject digests",
301
+ { phase: "subject", guid: input.guid, field: "ownerFacts" }
302
+ );
303
+ }
304
+ return {
305
+ ok: true,
306
+ value: {
307
+ subjectDigest,
308
+ vertexDigest,
309
+ indexDigest,
310
+ submeshDigest,
311
+ aabbDigest,
312
+ aabb: [minX, minY, minZ, maxX, maxY, maxZ],
313
+ submeshCount: submeshes.length,
314
+ materialSlotCount: slots.length
315
+ }
316
+ };
317
+ }
318
+ async function executeMeshPreview(args, input) {
319
+ if (input.assets === void 0)
320
+ return subjectFailure(
321
+ "resource-preview-subject-invalid",
322
+ "resource preview host to expose the existing AssetRegistry",
323
+ { phase: "asset-registry", runId: input.runId }
324
+ );
325
+ const loaded = await input.assets.load(args.guid, builtinAssetKind("mesh"));
326
+ if (!loaded.ok)
327
+ return assetLoadFailure(
328
+ "AssetRegistry.load(guid, meshAssetKind) to resolve the requested mesh",
329
+ input.runId,
330
+ loaded.error
331
+ );
332
+ const inspected = inspectMeshSubject({
333
+ guid: args.guid,
334
+ asset: loaded.value,
335
+ ...loaded.digest === void 0 ? {} : { digest: loaded.digest },
336
+ ...loaded.ownerFacts === void 0 ? {} : { ownerFacts: loaded.ownerFacts }
337
+ });
338
+ if (!inspected.ok) return inspected;
339
+ const renderer = input.renderer;
340
+ if (!input.rendererReady || !input.worldReady || renderer === void 0 || renderer.drawCalls <= 0 || renderer.nonBlackPixels <= 0 || renderer.observation === void 0)
341
+ return subjectFailure(
342
+ "resource-preview-oracle-failed",
343
+ "shared World and Renderer to be ready",
344
+ { phase: "renderer", runId: input.runId }
345
+ );
346
+ const aabb = observedAabb(renderer.observation);
347
+ const submeshCount = renderer.observation.submeshCount;
348
+ const materialSlotCount = renderer.observation.materialSlotCount;
349
+ if (aabb === void 0 || typeof submeshCount !== "number" || typeof materialSlotCount !== "number") {
350
+ return subjectFailure(
351
+ "resource-preview-oracle-failed",
352
+ "the rendered mesh observation to include AABB and material-slot facts",
353
+ { phase: "renderer-observation", runId: input.runId }
354
+ );
355
+ }
356
+ const observed = {
357
+ ...inspected.value,
358
+ subjectDigest: digest(renderer.observation.subjectDigest) ?? "",
359
+ vertexDigest: digest(renderer.observation.vertexDigest) ?? "",
360
+ indexDigest: digest(renderer.observation.indexDigest) ?? "",
361
+ submeshDigest: digest(renderer.observation.submeshDigest) ?? "",
362
+ aabbDigest: digest(renderer.observation.aabbDigest) ?? "",
363
+ aabb,
364
+ submeshCount,
365
+ materialSlotCount,
366
+ rendererHealthy: input.rendererReady && input.worldReady,
367
+ drawCalls: renderer.drawCalls,
368
+ nonBlackPixels: renderer.nonBlackPixels
369
+ };
370
+ const oracle = evaluateMeshOracle({ requested: inspected.value, observed });
371
+ if (oracle.status !== "passed") {
372
+ return subjectFailure(
373
+ "resource-preview-oracle-failed",
374
+ "the rendered mesh observation to match the loaded owner facts",
375
+ { phase: "oracle", runId: input.runId, ...oracle.detail }
376
+ );
377
+ }
378
+ return {
379
+ ok: true,
380
+ value: {
381
+ subject: { kind: "mesh", guid: args.guid, digest: inspected.value.subjectDigest },
382
+ presentation: canonicalPresentation("mesh"),
383
+ recipe: createCanonicalPreviewRecipe("mesh"),
384
+ oracle,
385
+ artifacts: input.artifacts ?? []
386
+ },
387
+ artifacts: input.artifacts ?? []
388
+ };
389
+ }
390
+
391
+ // src/host.ts
392
+ var REQUIRED_PREVIEW_EVIDENCE = [
393
+ "rhi-tape",
394
+ "png",
395
+ "profile-capture"
396
+ ];
397
+ function domainFailure(code, expected, hint, detail) {
398
+ return { ok: false, error: { code, expected, hint, detail } };
399
+ }
400
+ function previewRuntimeUnavailable(subject, snapshot, domain) {
401
+ return domainFailure(
402
+ "preview-runtime-unavailable",
403
+ "a Project GUID cold-load and an active PreviewHost/Browser/Dawn runner",
404
+ "Install the Project preview provider and retry; no synthetic artifact is published.",
405
+ {
406
+ domain,
407
+ subjectKind: subject.kind,
408
+ subjectGuid: subject.guid,
409
+ snapshotRevision: snapshot.revision,
410
+ snapshotDigest: snapshot.digest,
411
+ requiredEvidence: REQUIRED_PREVIEW_EVIDENCE
412
+ }
413
+ );
414
+ }
415
+ function validateDomainValue(value, request) {
416
+ if (value.subject.kind !== request.subject.kind || value.subject.guid !== request.subject.guid || value.snapshot.revision !== request.snapshot.revision || value.snapshot.digest !== request.snapshot.digest) {
417
+ return domainFailure(
418
+ "preview-subject-identity-mismatch",
419
+ "the terminal subject and snapshot to match the request",
420
+ "Repair the domain producer so evidence remains bound to the requested subject.",
421
+ {
422
+ requestGuid: request.subject.guid,
423
+ resultGuid: value.subject.guid,
424
+ requestSnapshot: request.snapshot.digest,
425
+ resultSnapshot: value.snapshot.digest
426
+ }
427
+ );
428
+ }
429
+ const kinds = new Set(value.artifacts.map((artifact) => artifact.kind));
430
+ const missing = REQUIRED_PREVIEW_EVIDENCE.filter((kind) => !kinds.has(kind));
431
+ if (missing.length > 0) {
432
+ return domainFailure(
433
+ "preview-evidence-incomplete",
434
+ "one subject-bound artifact for each required evidence kind",
435
+ "Repair the RHI, PNG, and profile producers before retrying.",
436
+ { missing }
437
+ );
438
+ }
439
+ return { ok: true, value };
440
+ }
441
+
442
+ // src/mesh.ts
443
+ var meshPreviewArgsSchema = {
444
+ parse(value) {
445
+ if (value === null || typeof value !== "object")
446
+ return { ok: false, error: "expected a mesh preview request object" };
447
+ const request = value;
448
+ const binding = request.binding;
449
+ if (request.subject?.kind !== "MeshAsset" || typeof request.subject.guid !== "string")
450
+ return { ok: false, error: "expected a MeshAsset subject" };
451
+ if (request.snapshot === void 0 || !Number.isSafeInteger(request.snapshot.revision) || typeof request.snapshot.digest !== "string")
452
+ return { ok: false, error: "expected a revisioned snapshot" };
453
+ if (binding === void 0 || binding.guid !== request.subject.guid || binding.vertexDigest.length === 0 || binding.indexDigest.length === 0 || binding.submeshes.length === 0 || binding.submeshes.some(
454
+ (submesh) => submesh.id.length === 0 || submesh.vertexCount <= 0 || submesh.indexCount <= 0
455
+ ) || binding.aabb.min.some((value2) => !Number.isFinite(value2)) || binding.aabb.max.some((value2) => !Number.isFinite(value2)) || binding.aabb.min.some((value2, index) => value2 >= (binding.aabb.max[index] ?? Number.NaN)))
456
+ return { ok: false, error: "expected complete mesh buffers and a valid AABB" };
457
+ return { ok: true, value: request };
458
+ },
459
+ describe: '{"type":"object","required":["subject","snapshot","binding"],"properties":{"subject":{"type":"object","required":["kind","guid"],"properties":{"kind":{"const":"MeshAsset"},"guid":{"type":"string"}}},"snapshot":{"type":"object","required":["revision","digest"]},"binding":{"type":"object","required":["guid","vertexDigest","indexDigest","submeshes","aabb"]}}}'
460
+ };
461
+ var meshPreviewResultSchema = {
462
+ parse: (value) => ({ ok: true, value }),
463
+ describe: '{"type":"object","required":["subject","snapshot","report","artifacts"]}'
464
+ };
465
+ var meshPreviewDescriptor = {
466
+ id: "mesh.preview",
467
+ title: "Preview mesh asset",
468
+ summary: "Draws every bound mesh submesh with neutral material and AABB framing.",
469
+ realm: "engine",
470
+ argsSchema: meshPreviewArgsSchema,
471
+ resultSchema: meshPreviewResultSchema,
472
+ evidence: ["rhi-tape", "png", "profile-capture"],
473
+ preview: {
474
+ realm: "engine",
475
+ subject: { kind: "MeshAsset", guid: "<request>" },
476
+ snapshot: { revision: 0, digest: "<request>" },
477
+ requiredEvidence: ["rhi-tape", "png", "profile-capture"]
478
+ }
479
+ };
480
+ function defaultMeshRunner(request) {
481
+ return Promise.resolve(previewRuntimeUnavailable(request.subject, request.snapshot, "mesh"));
482
+ }
483
+ function createMeshPreviewContribution(runner = async (request) => defaultMeshRunner(request)) {
484
+ return defineTool(meshPreviewDescriptor, async (request, context) => {
485
+ const result = await runner(request, context);
486
+ if (!result.ok) return result;
487
+ const report = result.value.report;
488
+ if (report.bindingGuid !== request.subject.guid || report.submeshCount !== report.expectedSubmeshCount || report.submeshCount !== request.binding.submeshes.length || report.vertexCount !== request.binding.submeshes.reduce((sum, submesh) => sum + submesh.vertexCount, 0) || report.indexCount !== request.binding.submeshes.reduce((sum, submesh) => sum + submesh.indexCount, 0) || report.framing !== "aabb") {
489
+ return domainFailure(
490
+ "preview-subject-falsified",
491
+ "all requested submeshes and the source AABB to drive the presentation",
492
+ "Reject partial geometry, fallback meshes, and inverted framing before publishing evidence.",
493
+ {
494
+ requestGuid: request.subject.guid,
495
+ reportBindingGuid: report.bindingGuid,
496
+ expectedSubmeshCount: request.binding.submeshes.length,
497
+ actualSubmeshCount: report.submeshCount,
498
+ framing: report.framing
499
+ }
500
+ );
501
+ }
502
+ const validated = validateDomainValue(result.value, request);
503
+ if (!validated.ok) return validated;
504
+ return { ok: true, value: validated.value };
505
+ });
506
+ }
507
+ var meshPreview = defineTool(
508
+ subjectDescriptor("mesh"),
509
+ (args, context) => (async () => {
510
+ const host = context.require(previewHostCapability);
511
+ if (!host.ok) return host;
512
+ return host.value.withSession(
513
+ async (mechanisms) => executeMeshPreview(args, {
514
+ ...mechanisms.assets === void 0 ? {} : { assets: mechanisms.assets },
515
+ ...mechanisms.renderer === void 0 ? {} : { renderer: mechanisms.renderer },
516
+ rendererReady: mechanisms.renderer?.rendererReady === true,
517
+ worldReady: mechanisms.renderer?.worldReady === true,
518
+ runId: mechanisms.runId,
519
+ ...mechanisms.artifacts === void 0 ? {} : { artifacts: mechanisms.artifacts }
520
+ })
521
+ );
522
+ })()
523
+ );
524
+ var meshPreviewPlugin = nativePreviewPlugin("mesh", meshPreview);
525
+ var mesh_default = meshPreviewPlugin;
526
+
527
+ export { createMeshPreviewContribution, mesh_default as default, meshPreview, meshPreviewDescriptor, meshPreviewPlugin };
528
+ //# sourceMappingURL=mesh.mjs.map
529
+ //# sourceMappingURL=mesh.mjs.map