@world-engines/tmw 0.1.0-alpha.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.
Files changed (120) hide show
  1. package/LICENSE +46 -0
  2. package/dist/activation.d.ts +50 -0
  3. package/dist/activation.js +113 -0
  4. package/dist/audio-presentation-projection.d.ts +23 -0
  5. package/dist/audio-presentation-projection.js +39 -0
  6. package/dist/author-scenario-source-v3.d.ts +34 -0
  7. package/dist/author-scenario-source-v3.js +1 -0
  8. package/dist/authoring-runtime.d.ts +76 -0
  9. package/dist/authoring-runtime.js +282 -0
  10. package/dist/builder-diagnostics.d.ts +3 -0
  11. package/dist/builder-diagnostics.js +8 -0
  12. package/dist/builder-node.d.ts +178 -0
  13. package/dist/builder-node.js +212 -0
  14. package/dist/builder.d.ts +32 -0
  15. package/dist/builder.js +127 -0
  16. package/dist/capability-registry.d.ts +32 -0
  17. package/dist/capability-registry.js +33 -0
  18. package/dist/compile-target-profile.d.ts +16 -0
  19. package/dist/compile-target-profile.js +70 -0
  20. package/dist/compiled-plan.d.ts +44 -0
  21. package/dist/compiled-plan.js +1 -0
  22. package/dist/compiler-compatibility-gate.d.ts +96 -0
  23. package/dist/compiler-compatibility-gate.js +609 -0
  24. package/dist/compiler-release-authority.d.ts +212 -0
  25. package/dist/compiler-release-authority.js +676 -0
  26. package/dist/compiler-stage-cache.d.ts +42 -0
  27. package/dist/compiler-stage-cache.js +100 -0
  28. package/dist/compiler-stage-registry.d.ts +48 -0
  29. package/dist/compiler-stage-registry.js +126 -0
  30. package/dist/compiler.d.ts +7 -0
  31. package/dist/compiler.js +68 -0
  32. package/dist/condition-graph.d.ts +46 -0
  33. package/dist/condition-graph.js +135 -0
  34. package/dist/diagnostics.d.ts +20 -0
  35. package/dist/diagnostics.js +20 -0
  36. package/dist/durable-intent.d.ts +29 -0
  37. package/dist/durable-intent.js +60 -0
  38. package/dist/effect-flow.d.ts +69 -0
  39. package/dist/effect-flow.js +98 -0
  40. package/dist/event-fabric.d.ts +81 -0
  41. package/dist/event-fabric.js +227 -0
  42. package/dist/event-lifecycle.d.ts +59 -0
  43. package/dist/event-lifecycle.js +161 -0
  44. package/dist/evidence.d.ts +37 -0
  45. package/dist/evidence.js +92 -0
  46. package/dist/gameplay-state.d.ts +127 -0
  47. package/dist/gameplay-state.js +214 -0
  48. package/dist/index.d.ts +66 -0
  49. package/dist/index.js +52 -0
  50. package/dist/isolated-replay.d.ts +81 -0
  51. package/dist/isolated-replay.js +316 -0
  52. package/dist/music-candidate-set.d.ts +38 -0
  53. package/dist/music-candidate-set.js +102 -0
  54. package/dist/natural-language-chunking.d.ts +10 -0
  55. package/dist/natural-language-chunking.js +62 -0
  56. package/dist/natural-language-ingestion.d.ts +14 -0
  57. package/dist/natural-language-ingestion.js +128 -0
  58. package/dist/natural-language-structured-decode.d.ts +2 -0
  59. package/dist/natural-language-structured-decode.js +69 -0
  60. package/dist/natural-language-world-types.d.ts +142 -0
  61. package/dist/natural-language-world-types.js +2 -0
  62. package/dist/neutral-source-compiler.d.ts +135 -0
  63. package/dist/neutral-source-compiler.js +608 -0
  64. package/dist/presentation-intent.d.ts +42 -0
  65. package/dist/presentation-intent.js +137 -0
  66. package/dist/recall-query-envelope.d.ts +32 -0
  67. package/dist/recall-query-envelope.js +51 -0
  68. package/dist/rewrite-compensation.d.ts +28 -0
  69. package/dist/rewrite-compensation.js +85 -0
  70. package/dist/runtime-contract.d.ts +56 -0
  71. package/dist/runtime-contract.js +96 -0
  72. package/dist/runtime-domain-capabilities.d.ts +12 -0
  73. package/dist/runtime-domain-capabilities.js +20 -0
  74. package/dist/runtime-sav.d.ts +117 -0
  75. package/dist/runtime-sav.js +356 -0
  76. package/dist/runtime-trace.d.ts +44 -0
  77. package/dist/runtime-trace.js +119 -0
  78. package/dist/runtime.d.ts +238 -0
  79. package/dist/runtime.js +1887 -0
  80. package/dist/scene-author-source.d.ts +13 -0
  81. package/dist/scene-author-source.js +25 -0
  82. package/dist/scene-state.d.ts +275 -0
  83. package/dist/scene-state.js +334 -0
  84. package/dist/scheduler.d.ts +55 -0
  85. package/dist/scheduler.js +235 -0
  86. package/dist/selector-ast.d.ts +24 -0
  87. package/dist/selector-ast.js +18 -0
  88. package/dist/semantic-verdict.d.ts +48 -0
  89. package/dist/semantic-verdict.js +101 -0
  90. package/dist/simulator.d.ts +45 -0
  91. package/dist/simulator.js +58 -0
  92. package/dist/source-canonical.d.ts +4 -0
  93. package/dist/source-canonical.js +51 -0
  94. package/dist/source-crypto.d.ts +20 -0
  95. package/dist/source-crypto.js +17 -0
  96. package/dist/source-revision.d.ts +22 -0
  97. package/dist/source-revision.js +43 -0
  98. package/dist/spatial-impact-receipt.d.ts +38 -0
  99. package/dist/spatial-impact-receipt.js +67 -0
  100. package/dist/temporal-rewrite.d.ts +59 -0
  101. package/dist/temporal-rewrite.js +214 -0
  102. package/dist/time-domains.d.ts +37 -0
  103. package/dist/time-domains.js +142 -0
  104. package/dist/timeline-revision.d.ts +28 -0
  105. package/dist/timeline-revision.js +59 -0
  106. package/dist/trigger-document-v3.d.ts +99 -0
  107. package/dist/trigger-document-v3.js +334 -0
  108. package/dist/turn-commit.d.ts +56 -0
  109. package/dist/turn-commit.js +190 -0
  110. package/dist/validator.d.ts +7 -0
  111. package/dist/validator.js +142 -0
  112. package/dist/value-graph.d.ts +98 -0
  113. package/dist/value-graph.js +318 -0
  114. package/dist/view-event-delivery.d.ts +47 -0
  115. package/dist/view-event-delivery.js +197 -0
  116. package/dist/view-interaction-manifest-v3.d.ts +60 -0
  117. package/dist/view-interaction-manifest-v3.js +198 -0
  118. package/dist/view-payload-schema.d.ts +17 -0
  119. package/dist/view-payload-schema.js +84 -0
  120. package/package.json +62 -0
@@ -0,0 +1,608 @@
1
+ import { compileTargetProfileDigest, parseCompileTargetProfile, } from "./compile-target-profile.js";
2
+ import { compilerStageContentCacheKey, compilerStageCacheKey, } from "./compiler-stage-cache.js";
3
+ import { assertRegistryMatchesTargetProfile, buildCompilerStageRegistry, createPassThroughCompilerStage, } from "./compiler-stage-registry.js";
4
+ import { orderTmwDiagnostics, tmwDiagnostic, } from "./diagnostics.js";
5
+ import { getCanonicalTmwCapabilityRegistryV3, } from "./capability-registry.js";
6
+ import { compileTmw3 } from "./compiler.js";
7
+ import { initialTmwSceneFromMetadata } from "./scene-author-source.js";
8
+ import { parseTriggerDocumentV3, } from "./trigger-document-v3.js";
9
+ import { canonicalTmwBytes, isSha256Hex, runtimeContractHashV3, TMW_RUNTIME_GENERATION, TRIGGER_DOCUMENT_V3_VERSION, tmwSha256Hex, } from "./runtime-contract.js";
10
+ import { SpatialTopologyV1_Kind, SpatialWorldSourceV1, } from "@world-engines/protocol-ts/v1/scenario_part_source";
11
+ import { validatePlaneGeneration, } from "@world-engines/spatial-authoring/plane-generation";
12
+ import { validateProceduralWorld, } from "@world-engines/spatial-authoring/procedural-city";
13
+ import { validateRoadGeneration, } from "@world-engines/spatial-authoring/road-generation";
14
+ import { validateSpatialTemplateConfiguration, } from "@world-engines/spatial-authoring/spatial-templates";
15
+ export const NEUTRAL_SOURCE_COMPILER_RECEIPT_SCHEMA_VERSION = 1;
16
+ export const TMW_TRIGGER_SEMANTICS_STAGE_VERSION = "3";
17
+ export const NEUTRAL_SPATIAL_WORLD_STAGE_VERSION = "1";
18
+ export const NEUTRAL_SPATIAL_WORLD_STAGE_ID = "neutral.spatial-world";
19
+ export const CURRENT_NEUTRAL_COMPILER_STAGE_VERSIONS = Object.freeze([
20
+ Object.freeze({ stage_id: "neutral.metadata", stage_version: "1" }),
21
+ Object.freeze({ stage_id: "neutral.logical-graph", stage_version: "1" }),
22
+ Object.freeze({
23
+ stage_id: "tmw.trigger-semantics",
24
+ stage_version: TMW_TRIGGER_SEMANTICS_STAGE_VERSION,
25
+ }),
26
+ Object.freeze({ stage_id: "neutral.layout", stage_version: "1" }),
27
+ Object.freeze({ stage_id: "neutral.view-project", stage_version: "1" }),
28
+ Object.freeze({ stage_id: "neutral.resource-refs", stage_version: "1" }),
29
+ Object.freeze({ stage_id: "neutral.prompt-retrieval", stage_version: "1" }),
30
+ ]);
31
+ /** 空间发布 profile 必须显式绑定新 stage,旧七阶段 profile 保持可重放。 */
32
+ export const SPATIAL_NEUTRAL_COMPILER_STAGE_VERSIONS = Object.freeze([
33
+ ...CURRENT_NEUTRAL_COMPILER_STAGE_VERSIONS,
34
+ Object.freeze({ stage_id: NEUTRAL_SPATIAL_WORLD_STAGE_ID, stage_version: NEUTRAL_SPATIAL_WORLD_STAGE_VERSION }),
35
+ ]);
36
+ const REQUIRED_SECTION_KEYS = Object.freeze([
37
+ "metadata",
38
+ "logical_graph",
39
+ "trigger_semantics",
40
+ "layout",
41
+ "view_project",
42
+ "resource_refs",
43
+ "prompt_retrieval",
44
+ ]);
45
+ const SECTION_KEY_PATTERN = /^[a-z][a-z0-9_.-]{0,127}$/u;
46
+ const HEX_PATTERN = /^[0-9a-f]{64}$/u;
47
+ const strictDecoder = new TextDecoder("utf-8", { fatal: true });
48
+ function compareAscii(left, right) {
49
+ return left < right ? -1 : left > right ? 1 : 0;
50
+ }
51
+ function bytesEqual(left, right) {
52
+ if (left.byteLength !== right.byteLength)
53
+ return false;
54
+ let difference = 0;
55
+ for (let index = 0; index < left.byteLength; index += 1) {
56
+ difference |= (left[index] ?? 0) ^ (right[index] ?? 0);
57
+ }
58
+ return difference === 0;
59
+ }
60
+ const NEUTRAL_TRIGGER_KEYS = Object.freeze([
61
+ "conditions",
62
+ "edges",
63
+ "effects",
64
+ "events",
65
+ "presentations",
66
+ ]);
67
+ const EMPTY_NEUTRAL_TRIGGER_SEMANTICS = Object.freeze({
68
+ events: Object.freeze([]),
69
+ conditions: Object.freeze([]),
70
+ effects: Object.freeze([]),
71
+ presentations: Object.freeze([]),
72
+ edges: Object.freeze([]),
73
+ });
74
+ function plainObject(value, code) {
75
+ if (typeof value !== "object" || value === null || Array.isArray(value))
76
+ throw new Error(code);
77
+ return value;
78
+ }
79
+ function parseSpatialTopologySettings(bytes) {
80
+ if (bytes.byteLength === 0)
81
+ return Object.freeze({});
82
+ let value;
83
+ try {
84
+ value = JSON.parse(strictDecoder.decode(bytes));
85
+ }
86
+ catch {
87
+ throw new Error("spatial_world_topology_settings_json_invalid");
88
+ }
89
+ return plainObject(value, "spatial_world_topology_settings_object_required");
90
+ }
91
+ function decodeSpatialWorldSection(section) {
92
+ if (section.media_type !== "application/x-protobuf"
93
+ || section.canonical_encoding !== "chat.v1.SpatialWorldSourceV1")
94
+ throw new Error("spatial_world_encoding_invalid");
95
+ let source;
96
+ try {
97
+ source = SpatialWorldSourceV1.decode(section.payload_bytes);
98
+ }
99
+ catch {
100
+ throw new Error("spatial_world_protobuf_invalid");
101
+ }
102
+ if (source.schema_version !== 1)
103
+ throw new Error("spatial_world_schema_version_unsupported");
104
+ const settings = source.topology === undefined
105
+ ? Object.freeze({})
106
+ : parseSpatialTopologySettings(source.topology.settings);
107
+ return Object.freeze({ source, settings });
108
+ }
109
+ function unsupportedSpatialWorld(reason) {
110
+ return Object.freeze({ player_programmatic_supported: false, reason });
111
+ }
112
+ export function createNeutralSpatialWorldCompilerStage() {
113
+ return {
114
+ descriptor: {
115
+ stage_id: NEUTRAL_SPATIAL_WORLD_STAGE_ID,
116
+ stage_version: NEUTRAL_SPATIAL_WORLD_STAGE_VERSION,
117
+ section_keys: ["spatial_world", "logical_graph"],
118
+ depends_on: [],
119
+ },
120
+ async execute(input) {
121
+ const section = input.sections.find(section => section.section_key === "spatial_world");
122
+ if (section === undefined || section.payload_bytes.byteLength === 0) {
123
+ throw new Error("spatial_world_section_missing");
124
+ }
125
+ let decoded;
126
+ try {
127
+ decoded = decodeSpatialWorldSection(section);
128
+ }
129
+ catch (error) {
130
+ const message = error instanceof Error ? error.message : "spatial_world_invalid";
131
+ return Object.freeze({
132
+ ok: false,
133
+ diagnostics: Object.freeze([tmwDiagnostic(message.split(":")[0] || "spatial_world_invalid", "$/sections/spatial_world", message)]),
134
+ });
135
+ }
136
+ const unsupported = (reason) => Object.freeze({
137
+ ok: true,
138
+ bytes: canonicalTmwBytes(unsupportedSpatialWorld(reason)),
139
+ diagnostics: Object.freeze([tmwDiagnostic("spatial_world_player_programmatic_unsupported", "$/sections/spatial_world", `spatial world is preserved but not exported to Player: ${reason}`, [], "warning")]),
140
+ });
141
+ if (decoded.source.topology === undefined)
142
+ return unsupported("topology_missing");
143
+ if (decoded.source.topology.kind !== SpatialTopologyV1_Kind.KIND_PLANE) {
144
+ return unsupported("topology_not_plane");
145
+ }
146
+ if (decoded.settings.generation === undefined)
147
+ return unsupported("generation_missing");
148
+ if (decoded.settings.procedural === undefined)
149
+ return unsupported("procedural_missing");
150
+ try {
151
+ const generation = validatePlaneGeneration(decoded.settings.generation);
152
+ if (generation.profile !== "plane-perlin-hydrology-v1")
153
+ throw new Error("spatial_world_plane_profile_required");
154
+ if (decoded.settings.width !== generation.widthMeters
155
+ || decoded.settings.height !== generation.heightMeters)
156
+ throw new Error("spatial_world_generation_extent_mismatch");
157
+ const procedural = validateProceduralWorld(decoded.settings.procedural, {
158
+ widthMeters: generation.widthMeters,
159
+ heightMeters: generation.heightMeters,
160
+ });
161
+ if (procedural.worldSeed !== generation.seed) {
162
+ throw new Error("spatial_world_procedural_seed_mismatch");
163
+ }
164
+ const roadGeneration = decoded.settings.roadGeneration === undefined
165
+ ? undefined
166
+ : validateRoadGeneration(decoded.settings.roadGeneration);
167
+ const templates = decoded.settings.templates === undefined
168
+ ? undefined
169
+ : validateSpatialTemplateConfiguration(decoded.settings.templates);
170
+ const controlledActorId = decoded.settings.controlledActorId;
171
+ if (controlledActorId != null) {
172
+ if (typeof controlledActorId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(controlledActorId))
173
+ throw new Error("spatial_world_controlled_actor_invalid");
174
+ const graphSection = input.sections.find(section => section.section_key === "logical_graph");
175
+ if (!graphSection)
176
+ throw new Error("spatial_world_controlled_actor_graph_missing");
177
+ const graph = plainObject(JSON.parse(strictDecoder.decode(graphSection.payload_bytes)), "spatial_world_graph_invalid");
178
+ const entities = graph.entities;
179
+ if (!Array.isArray(entities) || !entities.some(entity => {
180
+ const item = plainObject(entity, "spatial_world_graph_entity_invalid");
181
+ return (item.entity_id ?? item.id) === controlledActorId;
182
+ }))
183
+ throw new Error("spatial_world_controlled_actor_not_found");
184
+ }
185
+ const digestInput = Object.freeze({
186
+ generation,
187
+ procedural,
188
+ ...(roadGeneration === undefined ? {} : { roadGeneration }),
189
+ ...(templates === undefined ? {} : { templates }),
190
+ ...(controlledActorId == null ? {} : { controlledActorId }),
191
+ });
192
+ const compiled = Object.freeze({
193
+ ...digestInput,
194
+ baseDigest: await tmwSha256Hex(canonicalTmwBytes(digestInput)),
195
+ });
196
+ return Object.freeze({
197
+ ok: true,
198
+ bytes: canonicalTmwBytes(compiled),
199
+ diagnostics: Object.freeze([]),
200
+ });
201
+ }
202
+ catch (error) {
203
+ const message = error instanceof Error ? error.message : "spatial_world_semantics_invalid";
204
+ return Object.freeze({
205
+ ok: false,
206
+ diagnostics: Object.freeze([tmwDiagnostic(message.split(":")[0] || "spatial_world_semantics_invalid", "$/sections/spatial_world/topology/settings", message)]),
207
+ });
208
+ }
209
+ },
210
+ };
211
+ }
212
+ export function neutralTriggerSemanticsFromDocument(document) {
213
+ const parsed = parseTriggerDocumentV3(document);
214
+ if (!parsed.ok)
215
+ throw new Error("trigger_semantics_target_document_invalid");
216
+ return Object.freeze({
217
+ events: parsed.value.events,
218
+ conditions: parsed.value.conditions,
219
+ effects: parsed.value.effects,
220
+ presentations: parsed.value.presentations,
221
+ edges: parsed.value.edges,
222
+ ...(parsed.value.value_graph === undefined ? {} : { value_graph: parsed.value.value_graph }),
223
+ });
224
+ }
225
+ export async function materializeTmw3TriggerDocument(value) {
226
+ const source = plainObject(value, "trigger_semantics_object_required");
227
+ const actualKeys = Object.keys(source).sort();
228
+ const expectedKeys = source.value_graph === undefined ? NEUTRAL_TRIGGER_KEYS : [...NEUTRAL_TRIGGER_KEYS, "value_graph"].sort();
229
+ if (actualKeys.length !== expectedKeys.length
230
+ || actualKeys.some((key, index) => key !== expectedKeys[index])
231
+ || !Array.isArray(source.events)
232
+ || !Array.isArray(source.conditions)
233
+ || !Array.isArray(source.effects)
234
+ || !Array.isArray(source.presentations)
235
+ || !Array.isArray(source.edges)) {
236
+ throw new Error("trigger_semantics_fields_mismatch");
237
+ }
238
+ const registry = getCanonicalTmwCapabilityRegistryV3();
239
+ const document = {
240
+ version: TRIGGER_DOCUMENT_V3_VERSION,
241
+ runtime_generation: TMW_RUNTIME_GENERATION,
242
+ runtime_contract_hash: await runtimeContractHashV3(),
243
+ registry_version: registry.registry_version,
244
+ events: source.events,
245
+ conditions: source.conditions,
246
+ effects: source.effects,
247
+ presentations: source.presentations,
248
+ edges: source.edges,
249
+ ...(source.value_graph === undefined ? {} : { value_graph: source.value_graph }),
250
+ };
251
+ const parsed = parseTriggerDocumentV3(document);
252
+ if (!parsed.ok) {
253
+ throw new Error(`trigger_semantics_invalid:${parsed.diagnostics.map((item) => item.code).join(",")}`);
254
+ }
255
+ return parsed.value;
256
+ }
257
+ function requireString(value, code) {
258
+ if (typeof value !== "string"
259
+ || value.length === 0
260
+ || value !== value.trim().normalize("NFC"))
261
+ throw new Error(code);
262
+ return value;
263
+ }
264
+ function requireSha256(value, code) {
265
+ if (typeof value !== "string" || !HEX_PATTERN.test(value))
266
+ throw new Error(code);
267
+ return value;
268
+ }
269
+ function sha256BytesToHex(value, code) {
270
+ if (!(value instanceof Uint8Array) || value.byteLength !== 32)
271
+ throw new Error(code);
272
+ return Array.from(value, (byte) => byte.toString(16).padStart(2, "0")).join("");
273
+ }
274
+ function normalizeCompileInput(value) {
275
+ const root = plainObject(value, "neutral_compile_input_object_required");
276
+ const witness = plainObject(root.witness, "neutral_compile_input_witness_required");
277
+ const manifest = plainObject(root.manifest, "neutral_compile_input_manifest_required");
278
+ const scenarioId = requireString(witness.scenario_id, "neutral_compile_scenario_id_invalid");
279
+ if (manifest.scenario_id !== scenarioId)
280
+ throw new Error("neutral_compile_scenario_mismatch");
281
+ const manifestDigest = requireSha256(witness.manifest_digest, "neutral_compile_manifest_digest_invalid");
282
+ if (sha256BytesToHex(manifest.manifest_sha256, "neutral_compile_manifest_sha256_invalid") !== manifestDigest)
283
+ throw new Error("neutral_compile_manifest_digest_mismatch");
284
+ if (!Array.isArray(root.sections) || !Array.isArray(manifest.sections)) {
285
+ throw new Error("neutral_compile_sections_required");
286
+ }
287
+ const identities = new Map();
288
+ for (const [index, value] of manifest.sections.entries()) {
289
+ const identity = plainObject(value, `neutral_compile_section_identity_invalid:${index}`);
290
+ const sectionKey = requireString(identity.section_key, `neutral_compile_section_key_invalid:${index}`);
291
+ if (!SECTION_KEY_PATTERN.test(sectionKey)) {
292
+ throw new Error(`neutral_compile_section_key_invalid:${index}`);
293
+ }
294
+ if (identities.has(sectionKey))
295
+ throw new Error("neutral_compile_section_key_duplicate");
296
+ identities.set(sectionKey, identity);
297
+ }
298
+ const sections = root.sections.map((value, index) => {
299
+ const section = plainObject(value, `neutral_compile_section_invalid:${index}`);
300
+ const sectionKey = requireString(section.section_key, `neutral_compile_section_key_invalid:${index}`);
301
+ const identity = identities.get(sectionKey);
302
+ if (identity === undefined)
303
+ throw new Error("neutral_compile_section_identity_missing");
304
+ const mediaType = requireString(section.media_type, `neutral_compile_section_media_type_invalid:${sectionKey}`);
305
+ const canonicalEncoding = requireString(section.canonical_encoding, `neutral_compile_section_encoding_invalid:${sectionKey}`);
306
+ if (identity.media_type !== mediaType
307
+ || identity.canonical_encoding !== canonicalEncoding)
308
+ throw new Error(`neutral_compile_section_descriptor_mismatch:${sectionKey}`);
309
+ if (!(section.payload instanceof Uint8Array)) {
310
+ throw new Error(`neutral_compile_section_payload_invalid:${sectionKey}`);
311
+ }
312
+ if (typeof identity.byte_length !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(identity.byte_length)) {
313
+ throw new Error(`neutral_compile_section_byte_length_invalid:${sectionKey}`);
314
+ }
315
+ const byteLength = Number(identity.byte_length);
316
+ if (!Number.isSafeInteger(byteLength)) {
317
+ throw new Error(`neutral_compile_section_byte_length_invalid:${sectionKey}`);
318
+ }
319
+ return Object.freeze({
320
+ section_key: sectionKey,
321
+ media_type: mediaType,
322
+ canonical_encoding: canonicalEncoding,
323
+ byte_length: byteLength,
324
+ sha256: sha256BytesToHex(identity.sha256, `neutral_compile_section_sha256_invalid:${sectionKey}`),
325
+ payload_bytes: section.payload.slice(),
326
+ });
327
+ }).sort((left, right) => compareAscii(left.section_key, right.section_key));
328
+ if (sections.length !== identities.size
329
+ || new Set(sections.map((section) => section.section_key)).size !== sections.length)
330
+ throw new Error("neutral_compile_section_set_mismatch");
331
+ for (const required of REQUIRED_SECTION_KEYS) {
332
+ if (!identities.has(required))
333
+ throw new Error(`neutral_compile_required_section_missing:${required}`);
334
+ }
335
+ return Object.freeze({
336
+ scenario_id: scenarioId,
337
+ source_manifest_sha256: manifestDigest,
338
+ sections: Object.freeze(sections),
339
+ });
340
+ }
341
+ function cloneSection(section) {
342
+ return Object.freeze({ ...section, payload_bytes: section.payload_bytes.slice() });
343
+ }
344
+ function cloneDependency(output) {
345
+ return Object.freeze({ ...output, bytes: output.bytes.slice() });
346
+ }
347
+ async function readValidCacheEntry(cache, cacheKey) {
348
+ const entry = await cache.read(cacheKey);
349
+ if (entry === null)
350
+ return null;
351
+ if (entry.cache_key !== cacheKey
352
+ || !isSha256Hex(entry.output_sha256)
353
+ || await tmwSha256Hex(entry.bytes) !== entry.output_sha256
354
+ || entry.diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
355
+ await cache.delete(cacheKey);
356
+ return null;
357
+ }
358
+ return entry;
359
+ }
360
+ function compilerFailure(error) {
361
+ const message = error instanceof Error ? error.message : "neutral_source_compile_failed";
362
+ const code = message.split(":")[0] || "neutral_source_compile_failed";
363
+ return Object.freeze({
364
+ ok: false,
365
+ diagnostics: Object.freeze([tmwDiagnostic(code, "$", message)]),
366
+ });
367
+ }
368
+ export async function compileNeutralScenarioSource(input, targetProfile, registry, cache) {
369
+ try {
370
+ const source = normalizeCompileInput(input);
371
+ // 初始 Scene 属于 metadata 原始 authority;在缓存读取前验证,输出保持逐字手稿。
372
+ const metadata = source.sections.find((section) => section.section_key === "metadata");
373
+ if (metadata !== undefined && metadata.payload_bytes.byteLength > 0
374
+ && metadata.media_type === "application/json") {
375
+ const metadataModel = plainObject(JSON.parse(strictDecoder.decode(metadata.payload_bytes)), "metadata_object_required");
376
+ if (metadataModel.metadata === null) {
377
+ if (metadataModel.initial_scene !== undefined && metadataModel.initial_scene !== null)
378
+ throw new Error("metadata_object_required");
379
+ initialTmwSceneFromMetadata(Object.freeze({}));
380
+ }
381
+ else {
382
+ initialTmwSceneFromMetadata(metadataModel.metadata === undefined
383
+ ? metadataModel : plainObject(metadataModel.metadata, "metadata_object_required"));
384
+ }
385
+ }
386
+ const profile = parseCompileTargetProfile(targetProfile);
387
+ assertRegistryMatchesTargetProfile(registry, profile);
388
+ const targetProfileSha256 = await compileTargetProfileDigest(profile);
389
+ const derivedObjects = [];
390
+ const stageReceipts = [];
391
+ const diagnostics = [];
392
+ const outputByStage = new Map();
393
+ const spatialSection = source.sections.find((section) => section.section_key === "spatial_world");
394
+ if (spatialSection !== undefined && spatialSection.payload_bytes.byteLength > 0
395
+ && !registry.stages.some(stage => stage.descriptor.stage_id === NEUTRAL_SPATIAL_WORLD_STAGE_ID)) {
396
+ throw new Error("spatial_world_compiler_profile_required");
397
+ }
398
+ for (const stage of registry.stages) {
399
+ const stageSections = stage.descriptor.section_keys.map((sectionKey) => {
400
+ const section = source.sections.find((candidate) => candidate.section_key === sectionKey);
401
+ if (section === undefined)
402
+ throw new Error(`compiler_stage_section_missing:${sectionKey}`);
403
+ return section;
404
+ });
405
+ const dependencies = stage.descriptor.depends_on.map((stageId) => {
406
+ const output = outputByStage.get(stageId);
407
+ if (output === undefined)
408
+ throw new Error(`compiler_stage_dependency_output_missing:${stageId}`);
409
+ return output;
410
+ });
411
+ const cacheIdentity = {
412
+ scenario_id: source.scenario_id,
413
+ source_manifest_sha256: source.source_manifest_sha256,
414
+ stage_id: stage.descriptor.stage_id,
415
+ stage_version: stage.descriptor.stage_version,
416
+ target_profile_sha256: targetProfileSha256,
417
+ section_sha256s: stageSections.map((section) => ({
418
+ section_key: section.section_key,
419
+ sha256: section.sha256,
420
+ })),
421
+ dependency_sha256s: dependencies.map((dependency) => ({
422
+ stage_id: dependency.stage_id,
423
+ sha256: dependency.sha256,
424
+ })),
425
+ };
426
+ const cacheKey = await compilerStageCacheKey(cacheIdentity);
427
+ const contentCacheKey = await compilerStageContentCacheKey(cacheIdentity);
428
+ let entry = await readValidCacheEntry(cache, contentCacheKey);
429
+ if (entry === null) {
430
+ const result = await stage.execute(Object.freeze({
431
+ scenario_id: source.scenario_id,
432
+ target_profile: profile,
433
+ target_profile_sha256: targetProfileSha256,
434
+ sections: Object.freeze(stageSections.map(cloneSection)),
435
+ dependency_outputs: Object.freeze(dependencies.map(cloneDependency)),
436
+ }));
437
+ diagnostics.push(...result.diagnostics);
438
+ if (!result.ok || result.diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
439
+ return Object.freeze({
440
+ ok: false,
441
+ diagnostics: orderTmwDiagnostics(diagnostics),
442
+ });
443
+ }
444
+ if (!(result.bytes instanceof Uint8Array))
445
+ throw new Error("compiler_stage_output_bytes_invalid");
446
+ entry = Object.freeze({
447
+ cache_key: contentCacheKey,
448
+ bytes: result.bytes.slice(),
449
+ output_sha256: await tmwSha256Hex(result.bytes),
450
+ diagnostics: orderTmwDiagnostics(result.diagnostics),
451
+ });
452
+ await cache.writeOnce(entry);
453
+ }
454
+ else {
455
+ diagnostics.push(...entry.diagnostics);
456
+ }
457
+ const derivedObject = Object.freeze({
458
+ stage_id: stage.descriptor.stage_id,
459
+ stage_version: stage.descriptor.stage_version,
460
+ media_type: "application/octet-stream",
461
+ bytes: entry.bytes.slice(),
462
+ byte_length: entry.bytes.byteLength,
463
+ sha256: entry.output_sha256,
464
+ });
465
+ derivedObjects.push(derivedObject);
466
+ outputByStage.set(stage.descriptor.stage_id, Object.freeze({
467
+ stage_id: stage.descriptor.stage_id,
468
+ stage_version: stage.descriptor.stage_version,
469
+ bytes: entry.bytes.slice(),
470
+ sha256: entry.output_sha256,
471
+ }));
472
+ stageReceipts.push(Object.freeze({
473
+ stage_id: stage.descriptor.stage_id,
474
+ stage_version: stage.descriptor.stage_version,
475
+ cache_key: cacheKey,
476
+ content_cache_key: contentCacheKey,
477
+ input_sections: Object.freeze(stageSections.map((section) => Object.freeze({
478
+ section_key: section.section_key,
479
+ sha256: section.sha256,
480
+ }))),
481
+ dependency_outputs: Object.freeze(dependencies.map((dependency) => Object.freeze({
482
+ stage_id: dependency.stage_id,
483
+ sha256: dependency.sha256,
484
+ }))),
485
+ output_byte_length: entry.bytes.byteLength,
486
+ output_sha256: entry.output_sha256,
487
+ }));
488
+ }
489
+ const derivedObjectsSha256 = await tmwSha256Hex(canonicalTmwBytes(derivedObjects.map((object) => ({
490
+ stage_id: object.stage_id,
491
+ stage_version: object.stage_version,
492
+ media_type: object.media_type,
493
+ byte_length: object.byte_length,
494
+ sha256: object.sha256,
495
+ }))));
496
+ const receiptCore = Object.freeze({
497
+ receipt_schema_version: NEUTRAL_SOURCE_COMPILER_RECEIPT_SCHEMA_VERSION,
498
+ scenario_id: source.scenario_id,
499
+ source_manifest_sha256: source.source_manifest_sha256,
500
+ compiler_release: profile.compiler_release,
501
+ target_profile_sha256: targetProfileSha256,
502
+ stages: Object.freeze(stageReceipts),
503
+ derived_objects_sha256: derivedObjectsSha256,
504
+ });
505
+ const receipt = Object.freeze({
506
+ ...receiptCore,
507
+ receipt_sha256: await tmwSha256Hex(canonicalTmwBytes(receiptCore)),
508
+ });
509
+ return Object.freeze({
510
+ ok: true,
511
+ derived_objects: Object.freeze(derivedObjects),
512
+ receipt,
513
+ diagnostics: orderTmwDiagnostics(diagnostics),
514
+ });
515
+ }
516
+ catch (error) {
517
+ return compilerFailure(error);
518
+ }
519
+ }
520
+ export function canonicalNeutralSourceCompilerReceiptBytes(receipt) {
521
+ const { receipt_sha256: _digest, ...core } = receipt;
522
+ return canonicalTmwBytes(core);
523
+ }
524
+ function createTmw3TriggerCompilerStage() {
525
+ return {
526
+ descriptor: {
527
+ stage_id: "tmw.trigger-semantics",
528
+ stage_version: TMW_TRIGGER_SEMANTICS_STAGE_VERSION,
529
+ section_keys: ["trigger_semantics"],
530
+ depends_on: [],
531
+ },
532
+ async execute(input) {
533
+ const section = input.sections[0];
534
+ if (section === undefined)
535
+ throw new Error("trigger_semantics_section_missing");
536
+ const acceptedEmpty = section.payload_bytes.byteLength === 0
537
+ && section.media_type === "application/octet-stream"
538
+ && section.canonical_encoding === "opaque-bytes-v1";
539
+ if (!acceptedEmpty
540
+ && (section.media_type !== "application/json"
541
+ || section.canonical_encoding !== "canonical-json-v1")) {
542
+ return Object.freeze({
543
+ ok: false,
544
+ diagnostics: Object.freeze([tmwDiagnostic("trigger_semantics_encoding_invalid", "$/sections/trigger_semantics", "trigger semantics must use canonical-json-v1 application/json")]),
545
+ });
546
+ }
547
+ let semantics;
548
+ if (acceptedEmpty) {
549
+ semantics = EMPTY_NEUTRAL_TRIGGER_SEMANTICS;
550
+ }
551
+ else {
552
+ try {
553
+ semantics = JSON.parse(strictDecoder.decode(section.payload_bytes));
554
+ }
555
+ catch {
556
+ return Object.freeze({
557
+ ok: false,
558
+ diagnostics: Object.freeze([tmwDiagnostic("trigger_semantics_json_invalid", "$/sections/trigger_semantics", "trigger semantics is not valid UTF-8 JSON")]),
559
+ });
560
+ }
561
+ if (!bytesEqual(section.payload_bytes, canonicalTmwBytes(semantics))) {
562
+ return Object.freeze({
563
+ ok: false,
564
+ diagnostics: Object.freeze([tmwDiagnostic("trigger_semantics_canonical_bytes_invalid", "$/sections/trigger_semantics", "trigger semantics bytes do not match canonical-json-v1")]),
565
+ });
566
+ }
567
+ }
568
+ let document;
569
+ try {
570
+ document = await materializeTmw3TriggerDocument(semantics);
571
+ }
572
+ catch (error) {
573
+ const message = error instanceof Error ? error.message : "trigger_semantics_invalid";
574
+ return Object.freeze({
575
+ ok: false,
576
+ diagnostics: Object.freeze([tmwDiagnostic(message.split(":")[0] || "trigger_semantics_invalid", "$/sections/trigger_semantics", message)]),
577
+ });
578
+ }
579
+ const compiled = await compileTmw3(document, getCanonicalTmwCapabilityRegistryV3());
580
+ if (!compiled.ok)
581
+ return compiled;
582
+ return Object.freeze({
583
+ ok: true,
584
+ bytes: canonicalTmwBytes(compiled.plan),
585
+ diagnostics: compiled.diagnostics,
586
+ });
587
+ },
588
+ };
589
+ }
590
+ export function buildCurrentTmw3NeutralCompilerStageRegistry(profile) {
591
+ const passThrough = (stageId, sectionKey) => createPassThroughCompilerStage({
592
+ stage_id: stageId,
593
+ stage_version: "1",
594
+ section_keys: [sectionKey],
595
+ depends_on: [],
596
+ });
597
+ return buildCompilerStageRegistry([
598
+ passThrough("neutral.metadata", "metadata"),
599
+ passThrough("neutral.logical-graph", "logical_graph"),
600
+ createTmw3TriggerCompilerStage(),
601
+ passThrough("neutral.layout", "layout"),
602
+ passThrough("neutral.view-project", "view_project"),
603
+ passThrough("neutral.resource-refs", "resource_refs"),
604
+ passThrough("neutral.prompt-retrieval", "prompt_retrieval"),
605
+ ...(profile?.stage_versions.some(stage => stage.stage_id === NEUTRAL_SPATIAL_WORLD_STAGE_ID)
606
+ ? [createNeutralSpatialWorldCompilerStage()] : []),
607
+ ]);
608
+ }
@@ -0,0 +1,42 @@
1
+ import type { TMW_RUNTIME_GENERATION } from "./runtime-contract.js";
2
+ import type { TmwJsonValue } from "./runtime-contract.js";
3
+ import type { TmwActivationCandidateV3, TmwActivationReceiptV3 } from "./activation.js";
4
+ export type PresentationIntentKind = "view" | "atmosphere" | "music";
5
+ export interface ViewPresentationPayloadV3 {
6
+ readonly kind: "view";
7
+ readonly overlay_id: string;
8
+ readonly visible: boolean;
9
+ }
10
+ export interface AtmospherePresentationPayloadV3 {
11
+ readonly kind: "atmosphere";
12
+ readonly atmosphere_id: string | null;
13
+ readonly intensity: number;
14
+ }
15
+ export interface MusicPresentationPayloadV3 {
16
+ readonly kind: "music";
17
+ readonly candidate_set_id: string;
18
+ readonly candidate_set_revision: number;
19
+ readonly preferred_resource_id: string | null;
20
+ }
21
+ export type PresentationPayloadV3 = ViewPresentationPayloadV3 | AtmospherePresentationPayloadV3 | MusicPresentationPayloadV3;
22
+ export interface PresentationIntentV3 {
23
+ readonly schema: "presentation-intent.v3";
24
+ readonly generation: typeof TMW_RUNTIME_GENERATION;
25
+ readonly intent_id: string;
26
+ readonly epoch: number;
27
+ readonly artifact_digest: string;
28
+ readonly timeline_revision: string;
29
+ readonly activation_id: string;
30
+ readonly dedupe_key: string;
31
+ readonly sequence: number;
32
+ readonly payload: PresentationPayloadV3;
33
+ readonly metadata: Readonly<Record<string, TmwJsonValue>>;
34
+ }
35
+ export interface CommittedPresentationActivationV3 {
36
+ readonly candidate: TmwActivationCandidateV3;
37
+ readonly receipt: TmwActivationReceiptV3;
38
+ }
39
+ export declare const parsePresentationPayloadV3: (value: unknown) => PresentationPayloadV3 | null;
40
+ export declare const parsePresentationIntentV3: (value: unknown) => PresentationIntentV3 | null;
41
+ export declare const committedPresentationActivationError: (activation: CommittedPresentationActivationV3) => string | null;
42
+ export declare const presentationIntentError: (intent: PresentationIntentV3, activation: CommittedPresentationActivationV3) => string | null;