@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,13 @@
1
+ import { type TmwSceneStateV1 } from "./scene-state.js";
2
+ /** 初始 Scene 只属于现有 metadata section;运行后唯一 authority 是 SAV.scene。 */
3
+ export declare function initialTmwSceneFromMetadata(metadata: Readonly<Record<string, unknown>>): TmwSceneStateV1 | null;
4
+ export interface TmwSceneSourceReferencesV1 {
5
+ readonly entityNodeIds: readonly string[];
6
+ readonly spatialWorld: null | Readonly<{
7
+ worldId: string;
8
+ revision: number;
9
+ spatialIds: readonly string[];
10
+ }>;
11
+ }
12
+ /** 引用校验消费作者源快照,不使用 project revision 代替地图自身 revision。 */
13
+ export declare function validateTmwSceneSourceReferencesV1(scene: TmwSceneStateV1 | null, source: TmwSceneSourceReferencesV1): void;
@@ -0,0 +1,25 @@
1
+ import { canonicalTmwSceneStateV1 } from "./scene-state.js";
2
+ /** 初始 Scene 只属于现有 metadata section;运行后唯一 authority 是 SAV.scene。 */
3
+ export function initialTmwSceneFromMetadata(metadata) {
4
+ const value = metadata.initial_scene;
5
+ return value === undefined || value === null ? null : canonicalTmwSceneStateV1(value);
6
+ }
7
+ /** 引用校验消费作者源快照,不使用 project revision 代替地图自身 revision。 */
8
+ export function validateTmwSceneSourceReferencesV1(scene, source) {
9
+ if (scene === null)
10
+ return;
11
+ const entities = new Set(source.entityNodeIds);
12
+ for (const id of scene.presentEntityNodeIds) {
13
+ if (!entities.has(id))
14
+ throw new Error(`tmw_scene_entity_missing:${id}`);
15
+ }
16
+ if (scene.spatialRef === null)
17
+ return;
18
+ const world = source.spatialWorld;
19
+ if (world === null || world.worldId !== scene.spatialRef.worldId)
20
+ throw new Error("tmw_scene_spatial_world_missing");
21
+ if (world.revision !== scene.spatialRef.sourceRevision)
22
+ throw new Error("tmw_scene_spatial_revision_mismatch");
23
+ if (!world.spatialIds.includes(scene.spatialRef.spatialId))
24
+ throw new Error("tmw_scene_spatial_id_missing");
25
+ }
@@ -0,0 +1,275 @@
1
+ import { TMW_SCENE_SEMANTICS_REVISION } from "./runtime-contract.js";
2
+ export declare const TMW_SCENE_SCHEMA_VERSION: 1;
3
+ export { TMW_SCENE_SEMANTICS_REVISION };
4
+ export declare const TMW_SCENE_MAX_PRESENT_ENTITIES: 4096;
5
+ export declare const TMW_SCENE_MAX_NOTES: 256;
6
+ export declare const TMW_SCENE_MAX_NOTE_TEXT_LENGTH: 32768;
7
+ /** Registry/UI/authoring consume this single closed schema authority. */
8
+ export declare const TMW_SCENE_CAPABILITY_ARGUMENT_SCHEMAS: Readonly<{
9
+ "scene.begin": Readonly<{
10
+ type: "object";
11
+ additionalProperties: false;
12
+ required: readonly string[];
13
+ properties: Readonly<{
14
+ sceneId: Readonly<{
15
+ type: "string";
16
+ minLength: 1;
17
+ maxLength: 512;
18
+ }>;
19
+ worldTime: Readonly<{
20
+ type: readonly string[];
21
+ }>;
22
+ presentEntityNodeIds: Readonly<{
23
+ type: "array";
24
+ maxItems: 4096;
25
+ uniqueItems: true;
26
+ items: Readonly<{
27
+ type: "string";
28
+ minLength: 1;
29
+ maxLength: 512;
30
+ }>;
31
+ }>;
32
+ spatialRef: Readonly<{
33
+ anyOf: readonly (Readonly<{
34
+ type: "null";
35
+ }> | Readonly<{
36
+ type: "object";
37
+ additionalProperties: false;
38
+ required: readonly string[];
39
+ properties: Readonly<{
40
+ worldId: Readonly<{
41
+ type: "string";
42
+ minLength: 1;
43
+ maxLength: 512;
44
+ }>;
45
+ sourceRevision: Readonly<{
46
+ type: "integer";
47
+ minimum: 0;
48
+ }>;
49
+ spatialId: Readonly<{
50
+ type: "string";
51
+ minLength: 1;
52
+ maxLength: 512;
53
+ }>;
54
+ }>;
55
+ }>)[];
56
+ }>;
57
+ }>;
58
+ }>;
59
+ "scene.end": Readonly<{
60
+ type: "object";
61
+ additionalProperties: false;
62
+ maxProperties: 0;
63
+ }>;
64
+ "scene.present.add": Readonly<{
65
+ type: "object";
66
+ additionalProperties: false;
67
+ required: readonly string[];
68
+ properties: Readonly<{
69
+ entityNodeId: Readonly<{
70
+ type: "string";
71
+ minLength: 1;
72
+ maxLength: 512;
73
+ }>;
74
+ }>;
75
+ }>;
76
+ "scene.present.remove": Readonly<{
77
+ type: "object";
78
+ additionalProperties: false;
79
+ required: readonly string[];
80
+ properties: Readonly<{
81
+ entityNodeId: Readonly<{
82
+ type: "string";
83
+ minLength: 1;
84
+ maxLength: 512;
85
+ }>;
86
+ }>;
87
+ }>;
88
+ "scene.present.replace": Readonly<{
89
+ type: "object";
90
+ additionalProperties: false;
91
+ required: readonly string[];
92
+ properties: Readonly<{
93
+ entityNodeIds: Readonly<{
94
+ type: "array";
95
+ maxItems: 4096;
96
+ uniqueItems: true;
97
+ items: Readonly<{
98
+ type: "string";
99
+ minLength: 1;
100
+ maxLength: 512;
101
+ }>;
102
+ }>;
103
+ }>;
104
+ }>;
105
+ "scene.time.set": Readonly<{
106
+ type: "object";
107
+ additionalProperties: false;
108
+ required: readonly string[];
109
+ properties: Readonly<{
110
+ worldTime: Readonly<{
111
+ type: readonly string[];
112
+ }>;
113
+ }>;
114
+ }>;
115
+ "scene.location.set": Readonly<{
116
+ type: "object";
117
+ additionalProperties: false;
118
+ required: readonly string[];
119
+ properties: Readonly<{
120
+ spatialRef: Readonly<{
121
+ anyOf: readonly (Readonly<{
122
+ type: "null";
123
+ }> | Readonly<{
124
+ type: "object";
125
+ additionalProperties: false;
126
+ required: readonly string[];
127
+ properties: Readonly<{
128
+ worldId: Readonly<{
129
+ type: "string";
130
+ minLength: 1;
131
+ maxLength: 512;
132
+ }>;
133
+ sourceRevision: Readonly<{
134
+ type: "integer";
135
+ minimum: 0;
136
+ }>;
137
+ spatialId: Readonly<{
138
+ type: "string";
139
+ minLength: 1;
140
+ maxLength: 512;
141
+ }>;
142
+ }>;
143
+ }>)[];
144
+ }>;
145
+ }>;
146
+ }>;
147
+ "scene.note.add": Readonly<{
148
+ type: "object";
149
+ additionalProperties: false;
150
+ required: readonly string[];
151
+ properties: Readonly<{
152
+ noteId: Readonly<{
153
+ type: "string";
154
+ minLength: 1;
155
+ maxLength: 512;
156
+ }>;
157
+ text: Readonly<{
158
+ type: "string";
159
+ maxLength: 32768;
160
+ }>;
161
+ lifetime: Readonly<{
162
+ enum: readonly string[];
163
+ }>;
164
+ }>;
165
+ }>;
166
+ "scene.note.remove": Readonly<{
167
+ type: "object";
168
+ additionalProperties: false;
169
+ required: readonly string[];
170
+ properties: Readonly<{
171
+ noteId: Readonly<{
172
+ type: "string";
173
+ minLength: 1;
174
+ maxLength: 512;
175
+ }>;
176
+ }>;
177
+ }>;
178
+ }>;
179
+ export type TmwSceneNoteLifetime = "once" | "scene_end";
180
+ export interface TmwSceneSpatialRefV1 {
181
+ readonly worldId: string;
182
+ readonly sourceRevision: number;
183
+ readonly spatialId: string;
184
+ }
185
+ export interface TmwSceneManuscriptNoteV1 {
186
+ readonly noteId: string;
187
+ /** Writer-facing manuscript, preserved verbatim (including whitespace). */
188
+ readonly text: string;
189
+ readonly lifetime: TmwSceneNoteLifetime;
190
+ readonly createdOperationId: string;
191
+ }
192
+ export interface TmwSceneStateV1 {
193
+ readonly schemaVersion: typeof TMW_SCENE_SCHEMA_VERSION;
194
+ readonly sceneId: string;
195
+ readonly revision: number;
196
+ readonly worldTime: number | null;
197
+ readonly presentEntityNodeIds: ReadonlyArray<string>;
198
+ readonly spatialRef: TmwSceneSpatialRefV1 | null;
199
+ readonly manuscriptNotes: ReadonlyArray<TmwSceneManuscriptNoteV1>;
200
+ }
201
+ export type TmwSceneOperationV1 = Readonly<{
202
+ kind: "scene.begin";
203
+ sceneId: string;
204
+ worldTime: number | null;
205
+ presentEntityNodeIds: ReadonlyArray<string>;
206
+ spatialRef: TmwSceneSpatialRefV1 | null;
207
+ }> | Readonly<{
208
+ kind: "scene.end";
209
+ }> | Readonly<{
210
+ kind: "scene.present.add";
211
+ entityNodeId: string;
212
+ }> | Readonly<{
213
+ kind: "scene.present.remove";
214
+ entityNodeId: string;
215
+ }> | Readonly<{
216
+ kind: "scene.present.replace";
217
+ entityNodeIds: ReadonlyArray<string>;
218
+ }> | Readonly<{
219
+ kind: "scene.time.set";
220
+ worldTime: number | null;
221
+ }> | Readonly<{
222
+ kind: "scene.location.set";
223
+ spatialRef: TmwSceneSpatialRefV1 | null;
224
+ }> | Readonly<{
225
+ kind: "scene.note.add";
226
+ noteId: string;
227
+ text: string;
228
+ lifetime: TmwSceneNoteLifetime;
229
+ }> | Readonly<{
230
+ kind: "scene.note.remove";
231
+ noteId: string;
232
+ }>;
233
+ export interface TmwCommittedWriterNoteConsumptionV1 {
234
+ readonly sceneRevision: number;
235
+ readonly noteIds: ReadonlyArray<string>;
236
+ readonly writerReceiptDigest: string;
237
+ }
238
+ /** Writer commit 与后续 authored event 之间的 crash-resume 描述;不预写未来 timeline binding。 */
239
+ export interface TmwPendingSceneClockCrossingV1 {
240
+ readonly eventId: string;
241
+ readonly eventType: string;
242
+ readonly logicalSeq: number;
243
+ readonly worldTime: number | null;
244
+ readonly crossingDirection: "forward" | "backward";
245
+ readonly payload: Readonly<Record<string, unknown>>;
246
+ readonly payloadHash: string;
247
+ readonly commitReceiptDigest: string;
248
+ /** 仅由 Runtime3 已接受 author lifecycle capability 写入;缺席即不允许 View 投递。 */
249
+ readonly authorViewProjection?: Readonly<{
250
+ readonly schema: "tmw3.author-view-event.v1";
251
+ readonly saveId: string;
252
+ readonly artifactDigest: string;
253
+ readonly sourceEventId: string;
254
+ readonly sourceCommitReceiptDigest: string;
255
+ readonly viewVisiblePayload: Readonly<Record<string, null | boolean | number | string>>;
256
+ }>;
257
+ }
258
+ export declare function canonicalTmwAuthorViewProjectionV1(value: unknown): TmwPendingSceneClockCrossingV1["authorViewProjection"];
259
+ export declare function canonicalTmwPendingSceneClockCrossingV1(value: TmwPendingSceneClockCrossingV1): TmwPendingSceneClockCrossingV1;
260
+ export declare function canonicalTmwSceneSpatialRefV1(value: TmwSceneSpatialRefV1): TmwSceneSpatialRefV1;
261
+ export declare function canonicalTmwSceneStateV1(value: TmwSceneStateV1): TmwSceneStateV1;
262
+ /** Converts a registered scene capability plus its arguments to the one reducer interface. */
263
+ export declare function tmwSceneOperationFromEffect(capabilityId: string, argumentsValue: unknown): TmwSceneOperationV1 | null;
264
+ /** Pure reducer. Persist its result only as part of the successful Runtime activation commit. */
265
+ export declare function reduceTmwSceneV1(current: TmwSceneStateV1 | null, operation: TmwSceneOperationV1, operationId: string): TmwSceneStateV1 | null;
266
+ /** Internal-only consume gate: callers must bind exact note ids to the successful writer receipt. */
267
+ export declare function consumeCommittedTmwSceneNotesV1(current: TmwSceneStateV1, consumption: TmwCommittedWriterNoteConsumptionV1, committedWriterReceiptDigest: string): TmwSceneStateV1;
268
+ export declare function reduceTmwSceneEffectsV1(current: TmwSceneStateV1 | null, effects: ReadonlyArray<Readonly<{
269
+ capabilityId: string;
270
+ input: unknown;
271
+ effectId: string;
272
+ }>>, operationId: string): TmwSceneStateV1 | null;
273
+ /** Single clock authority adapter: scheduler observes Scene; it never owns an independently writable world clock. */
274
+ export declare function projectTmwSceneWorldTimeV1(scene: TmwSceneStateV1 | null): number | null;
275
+ export declare function setTmwSceneWorldTimeV1(scene: TmwSceneStateV1 | null, worldTime: number | null): TmwSceneStateV1 | null;
@@ -0,0 +1,334 @@
1
+ import { canonicalTmwJsonValue, requireTmwIdentifier, requireTmwSafeInteger, requireTmwSha256, } from "./runtime-trace.js";
2
+ import { TMW_SCENE_SEMANTICS_REVISION } from "./runtime-contract.js";
3
+ export const TMW_SCENE_SCHEMA_VERSION = 1;
4
+ export { TMW_SCENE_SEMANTICS_REVISION };
5
+ export const TMW_SCENE_MAX_PRESENT_ENTITIES = 4096;
6
+ export const TMW_SCENE_MAX_NOTES = 256;
7
+ export const TMW_SCENE_MAX_NOTE_TEXT_LENGTH = 32768;
8
+ const IDENTIFIER_SCHEMA = Object.freeze({ type: "string", minLength: 1, maxLength: 512 });
9
+ const WORLD_TIME_SCHEMA = Object.freeze({ type: Object.freeze(["number", "null"]) });
10
+ const SPATIAL_REF_SCHEMA = Object.freeze({
11
+ anyOf: Object.freeze([
12
+ Object.freeze({ type: "null" }),
13
+ Object.freeze({
14
+ type: "object",
15
+ additionalProperties: false,
16
+ required: Object.freeze(["worldId", "sourceRevision", "spatialId"]),
17
+ properties: Object.freeze({
18
+ worldId: IDENTIFIER_SCHEMA,
19
+ sourceRevision: Object.freeze({ type: "integer", minimum: 0 }),
20
+ spatialId: IDENTIFIER_SCHEMA,
21
+ }),
22
+ }),
23
+ ]),
24
+ });
25
+ /** Registry/UI/authoring consume this single closed schema authority. */
26
+ export const TMW_SCENE_CAPABILITY_ARGUMENT_SCHEMAS = Object.freeze({
27
+ "scene.begin": Object.freeze({ type: "object", additionalProperties: false, required: Object.freeze(["sceneId", "worldTime", "presentEntityNodeIds", "spatialRef"]), properties: Object.freeze({ sceneId: IDENTIFIER_SCHEMA, worldTime: WORLD_TIME_SCHEMA, presentEntityNodeIds: Object.freeze({ type: "array", maxItems: TMW_SCENE_MAX_PRESENT_ENTITIES, uniqueItems: true, items: IDENTIFIER_SCHEMA }), spatialRef: SPATIAL_REF_SCHEMA }) }),
28
+ "scene.end": Object.freeze({ type: "object", additionalProperties: false, maxProperties: 0 }),
29
+ "scene.present.add": Object.freeze({ type: "object", additionalProperties: false, required: Object.freeze(["entityNodeId"]), properties: Object.freeze({ entityNodeId: IDENTIFIER_SCHEMA }) }),
30
+ "scene.present.remove": Object.freeze({ type: "object", additionalProperties: false, required: Object.freeze(["entityNodeId"]), properties: Object.freeze({ entityNodeId: IDENTIFIER_SCHEMA }) }),
31
+ "scene.present.replace": Object.freeze({ type: "object", additionalProperties: false, required: Object.freeze(["entityNodeIds"]), properties: Object.freeze({ entityNodeIds: Object.freeze({ type: "array", maxItems: TMW_SCENE_MAX_PRESENT_ENTITIES, uniqueItems: true, items: IDENTIFIER_SCHEMA }) }) }),
32
+ "scene.time.set": Object.freeze({ type: "object", additionalProperties: false, required: Object.freeze(["worldTime"]), properties: Object.freeze({ worldTime: WORLD_TIME_SCHEMA }) }),
33
+ "scene.location.set": Object.freeze({ type: "object", additionalProperties: false, required: Object.freeze(["spatialRef"]), properties: Object.freeze({ spatialRef: SPATIAL_REF_SCHEMA }) }),
34
+ "scene.note.add": Object.freeze({ type: "object", additionalProperties: false, required: Object.freeze(["noteId", "text", "lifetime"]), properties: Object.freeze({ noteId: IDENTIFIER_SCHEMA, text: Object.freeze({ type: "string", maxLength: TMW_SCENE_MAX_NOTE_TEXT_LENGTH }), lifetime: Object.freeze({ enum: Object.freeze(["once", "scene_end"]) }) }) }),
35
+ "scene.note.remove": Object.freeze({ type: "object", additionalProperties: false, required: Object.freeze(["noteId"]), properties: Object.freeze({ noteId: IDENTIFIER_SCHEMA }) }),
36
+ });
37
+ export function canonicalTmwAuthorViewProjectionV1(value) {
38
+ if (value === undefined)
39
+ return undefined;
40
+ if (typeof value !== "object" || value === null || Array.isArray(value)
41
+ || Object.keys(value).sort().join(",") !== "artifactDigest,saveId,schema,sourceCommitReceiptDigest,sourceEventId,viewVisiblePayload") {
42
+ throw new Error("tmw_scene_pending_crossing_author_view_projection_invalid");
43
+ }
44
+ const projection = record(value);
45
+ const { schema, saveId, artifactDigest, sourceEventId, sourceCommitReceiptDigest, viewVisiblePayload: rawViewVisiblePayload } = projection;
46
+ if (schema !== "tmw3.author-view-event.v1"
47
+ || typeof saveId !== "string"
48
+ || typeof artifactDigest !== "string"
49
+ || typeof sourceEventId !== "string"
50
+ || typeof sourceCommitReceiptDigest !== "string"
51
+ || typeof rawViewVisiblePayload !== "object"
52
+ || rawViewVisiblePayload === null
53
+ || Array.isArray(rawViewVisiblePayload)) {
54
+ throw new Error("tmw_scene_pending_crossing_author_view_projection_invalid");
55
+ }
56
+ const viewVisiblePayload = Object.create(null);
57
+ for (const [key, item] of Object.entries(rawViewVisiblePayload)) {
58
+ requireTmwIdentifier(key, "scene_pending_crossing_author_view_payload_key");
59
+ if (item !== null && typeof item !== "boolean" && typeof item !== "string" && (typeof item !== "number" || !Number.isFinite(item))) {
60
+ throw new Error("tmw_scene_pending_crossing_author_view_projection_invalid");
61
+ }
62
+ viewVisiblePayload[key] = item;
63
+ }
64
+ return Object.freeze({
65
+ schema: "tmw3.author-view-event.v1",
66
+ saveId: requireTmwIdentifier(saveId, "scene_pending_crossing_author_view_save_id"),
67
+ artifactDigest: requireTmwSha256(artifactDigest, "scene_pending_crossing_author_view_artifact_digest"),
68
+ sourceEventId: requireTmwIdentifier(sourceEventId, "scene_pending_crossing_author_view_event_id"),
69
+ sourceCommitReceiptDigest: requireTmwSha256(sourceCommitReceiptDigest, "scene_pending_crossing_author_view_receipt_digest"),
70
+ viewVisiblePayload: Object.freeze(viewVisiblePayload),
71
+ });
72
+ }
73
+ export function canonicalTmwPendingSceneClockCrossingV1(value) {
74
+ if (typeof value !== "object" || value === null || Array.isArray(value)
75
+ || !["commitReceiptDigest,crossingDirection,eventId,eventType,logicalSeq,payload,payloadHash,worldTime", "authorViewProjection,commitReceiptDigest,crossingDirection,eventId,eventType,logicalSeq,payload,payloadHash,worldTime"].includes(Object.keys(value).sort().join(","))
76
+ || (value.crossingDirection !== "forward" && value.crossingDirection !== "backward")) {
77
+ throw new Error("tmw_scene_pending_crossing_invalid");
78
+ }
79
+ const worldTime = canonicalSceneWorldTime(value.worldTime);
80
+ const authorViewProjection = value.authorViewProjection === undefined
81
+ ? undefined
82
+ : canonicalTmwAuthorViewProjectionV1(value.authorViewProjection);
83
+ return Object.freeze({
84
+ eventId: requireTmwIdentifier(value.eventId, "scene_pending_crossing_event_id"),
85
+ eventType: requireTmwIdentifier(value.eventType, "scene_pending_crossing_event_type"),
86
+ logicalSeq: requireTmwSafeInteger(value.logicalSeq, "scene_pending_crossing_logical_seq", 0),
87
+ worldTime,
88
+ crossingDirection: value.crossingDirection,
89
+ payload: record(value.payload),
90
+ payloadHash: requireTmwSha256(value.payloadHash, "scene_pending_crossing_payload_hash"),
91
+ commitReceiptDigest: requireTmwSha256(value.commitReceiptDigest, "scene_pending_crossing_receipt_digest"),
92
+ ...(authorViewProjection === undefined ? {} : { authorViewProjection }),
93
+ });
94
+ }
95
+ function canonicalIds(values, label) {
96
+ if (!Array.isArray(values))
97
+ throw new Error(`tmw_${label}_invalid`);
98
+ const ids = values.map((value) => requireTmwIdentifier(value, label));
99
+ if (new Set(ids).size !== ids.length)
100
+ throw new Error(`tmw_${label}_duplicate`);
101
+ if (ids.length > TMW_SCENE_MAX_PRESENT_ENTITIES)
102
+ throw new Error(`tmw_${label}_limit_exceeded`);
103
+ return Object.freeze([...ids].sort());
104
+ }
105
+ export function canonicalTmwSceneSpatialRefV1(value) {
106
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
107
+ throw new Error("tmw_scene_spatial_ref_invalid");
108
+ }
109
+ if (Object.keys(value).sort().join(",") !== "sourceRevision,spatialId,worldId") {
110
+ throw new Error("tmw_scene_spatial_ref_invalid");
111
+ }
112
+ return Object.freeze({
113
+ worldId: requireTmwIdentifier(value.worldId, "scene_world_id"),
114
+ sourceRevision: requireTmwSafeInteger(value.sourceRevision, "scene_spatial_source_revision", 0),
115
+ spatialId: requireTmwIdentifier(value.spatialId, "scene_spatial_id"),
116
+ });
117
+ }
118
+ export function canonicalTmwSceneStateV1(value) {
119
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
120
+ throw new Error("tmw_scene_invalid");
121
+ }
122
+ if (Object.keys(value).sort().join(",") !== "manuscriptNotes,presentEntityNodeIds,revision,sceneId,schemaVersion,spatialRef,worldTime"
123
+ || !Array.isArray(value.manuscriptNotes)) {
124
+ throw new Error("tmw_scene_invalid");
125
+ }
126
+ if (value.schemaVersion !== TMW_SCENE_SCHEMA_VERSION)
127
+ throw new Error("tmw_scene_schema_unsupported");
128
+ if (value.manuscriptNotes.length > TMW_SCENE_MAX_NOTES)
129
+ throw new Error("tmw_scene_note_limit_exceeded");
130
+ const notes = value.manuscriptNotes.map((note) => {
131
+ if (typeof note !== "object" || note === null || Array.isArray(note)
132
+ || Object.keys(note).sort().join(",") !== "createdOperationId,lifetime,noteId,text"
133
+ || typeof note.text !== "string"
134
+ || note.text.length > TMW_SCENE_MAX_NOTE_TEXT_LENGTH
135
+ || (note.lifetime !== "once" && note.lifetime !== "scene_end")) {
136
+ throw new Error("tmw_scene_note_invalid");
137
+ }
138
+ return Object.freeze({
139
+ noteId: requireTmwIdentifier(note.noteId, "scene_note_id"),
140
+ text: note.text,
141
+ lifetime: note.lifetime,
142
+ createdOperationId: requireTmwIdentifier(note.createdOperationId, "scene_note_operation_id"),
143
+ });
144
+ });
145
+ if (new Set(notes.map((note) => note.noteId)).size !== notes.length) {
146
+ throw new Error("tmw_scene_note_duplicate");
147
+ }
148
+ return Object.freeze({
149
+ schemaVersion: TMW_SCENE_SCHEMA_VERSION,
150
+ sceneId: requireTmwIdentifier(value.sceneId, "scene_id"),
151
+ revision: requireTmwSafeInteger(value.revision, "scene_revision", 0),
152
+ worldTime: canonicalSceneWorldTime(value.worldTime),
153
+ presentEntityNodeIds: canonicalIds(value.presentEntityNodeIds, "scene_present_entity_node_id"),
154
+ spatialRef: value.spatialRef === null ? null : canonicalTmwSceneSpatialRefV1(value.spatialRef),
155
+ manuscriptNotes: Object.freeze(notes),
156
+ });
157
+ }
158
+ function record(value) {
159
+ const canonical = canonicalTmwJsonValue(value, "scene_operation_arguments");
160
+ if (typeof canonical !== "object" || canonical === null || Array.isArray(canonical)) {
161
+ throw new Error("tmw_scene_operation_arguments_invalid");
162
+ }
163
+ return canonical;
164
+ }
165
+ function canonicalSceneWorldTime(value) {
166
+ if (value === null)
167
+ return null;
168
+ if (typeof value !== "number" || !Number.isFinite(value))
169
+ throw new Error("tmw_scene_world_time_invalid");
170
+ return value;
171
+ }
172
+ function requireExactKeys(value, keys) {
173
+ if (Object.keys(value).sort().join(",") !== [...keys].sort().join(",")) {
174
+ throw new Error("tmw_scene_operation_arguments_invalid");
175
+ }
176
+ }
177
+ /** Converts a registered scene capability plus its arguments to the one reducer interface. */
178
+ export function tmwSceneOperationFromEffect(capabilityId, argumentsValue) {
179
+ if (!capabilityId.startsWith("scene."))
180
+ return null;
181
+ const args = record(argumentsValue);
182
+ switch (capabilityId) {
183
+ case "scene.begin": {
184
+ requireExactKeys(args, ["sceneId", "worldTime", "presentEntityNodeIds", "spatialRef"]);
185
+ return Object.freeze({
186
+ kind: capabilityId,
187
+ sceneId: requireTmwIdentifier(args.sceneId, "scene_id"),
188
+ worldTime: canonicalSceneWorldTime(args.worldTime),
189
+ presentEntityNodeIds: canonicalIds(args.presentEntityNodeIds, "scene_present_entity_node_id"),
190
+ spatialRef: args.spatialRef === null ? null : canonicalTmwSceneSpatialRefV1(args.spatialRef),
191
+ });
192
+ }
193
+ case "scene.end": {
194
+ requireExactKeys(args, []);
195
+ return Object.freeze({ kind: capabilityId });
196
+ }
197
+ case "scene.present.add": {
198
+ requireExactKeys(args, ["entityNodeId"]);
199
+ return Object.freeze({ kind: capabilityId, entityNodeId: requireTmwIdentifier(args.entityNodeId, "scene_entity_node_id") });
200
+ }
201
+ case "scene.present.remove": {
202
+ requireExactKeys(args, ["entityNodeId"]);
203
+ return Object.freeze({ kind: capabilityId, entityNodeId: requireTmwIdentifier(args.entityNodeId, "scene_entity_node_id") });
204
+ }
205
+ case "scene.present.replace": {
206
+ requireExactKeys(args, ["entityNodeIds"]);
207
+ return Object.freeze({ kind: capabilityId, entityNodeIds: canonicalIds(args.entityNodeIds, "scene_present_entity_node_id") });
208
+ }
209
+ case "scene.time.set": {
210
+ requireExactKeys(args, ["worldTime"]);
211
+ return Object.freeze({ kind: capabilityId, worldTime: canonicalSceneWorldTime(args.worldTime) });
212
+ }
213
+ case "scene.location.set": {
214
+ requireExactKeys(args, ["spatialRef"]);
215
+ return Object.freeze({ kind: capabilityId, spatialRef: args.spatialRef === null ? null : canonicalTmwSceneSpatialRefV1(args.spatialRef) });
216
+ }
217
+ case "scene.note.add": {
218
+ requireExactKeys(args, ["noteId", "text", "lifetime"]);
219
+ if (typeof args.text !== "string" || args.text.length > TMW_SCENE_MAX_NOTE_TEXT_LENGTH
220
+ || (args.lifetime !== "once" && args.lifetime !== "scene_end"))
221
+ throw new Error("tmw_scene_note_invalid");
222
+ return Object.freeze({ kind: capabilityId, noteId: requireTmwIdentifier(args.noteId, "scene_note_id"), text: args.text, lifetime: args.lifetime });
223
+ }
224
+ case "scene.note.remove": {
225
+ requireExactKeys(args, ["noteId"]);
226
+ return Object.freeze({ kind: capabilityId, noteId: requireTmwIdentifier(args.noteId, "scene_note_id") });
227
+ }
228
+ default: throw new Error("tmw_scene_capability_unknown");
229
+ }
230
+ }
231
+ /** Pure reducer. Persist its result only as part of the successful Runtime activation commit. */
232
+ export function reduceTmwSceneV1(current, operation, operationId) {
233
+ const canonicalOperationId = requireTmwIdentifier(operationId, "scene_operation_id");
234
+ if (operation.kind === "scene.begin")
235
+ return canonicalTmwSceneStateV1({
236
+ schemaVersion: 1,
237
+ sceneId: operation.sceneId,
238
+ revision: 0,
239
+ worldTime: operation.worldTime,
240
+ presentEntityNodeIds: operation.presentEntityNodeIds,
241
+ spatialRef: operation.spatialRef,
242
+ manuscriptNotes: Object.freeze([]),
243
+ });
244
+ if (operation.kind === "scene.end")
245
+ return null;
246
+ if (current === null)
247
+ throw new Error("tmw_scene_not_active");
248
+ const base = canonicalTmwSceneStateV1(current);
249
+ let present = base.presentEntityNodeIds;
250
+ let spatialRef = base.spatialRef;
251
+ let worldTime = base.worldTime;
252
+ let notes = base.manuscriptNotes;
253
+ if (operation.kind === "scene.present.add")
254
+ present = canonicalIds([...present, operation.entityNodeId], "scene_present_entity_node_id");
255
+ else if (operation.kind === "scene.present.remove")
256
+ present = Object.freeze(present.filter((id) => id !== operation.entityNodeId));
257
+ else if (operation.kind === "scene.present.replace")
258
+ present = operation.entityNodeIds;
259
+ else if (operation.kind === "scene.time.set")
260
+ worldTime = operation.worldTime;
261
+ else if (operation.kind === "scene.location.set")
262
+ spatialRef = operation.spatialRef;
263
+ else if (operation.kind === "scene.note.add") {
264
+ if (notes.some((note) => note.noteId === operation.noteId))
265
+ throw new Error("tmw_scene_note_duplicate");
266
+ if (notes.length >= TMW_SCENE_MAX_NOTES)
267
+ throw new Error("tmw_scene_note_limit_exceeded");
268
+ notes = Object.freeze([...notes, Object.freeze({
269
+ noteId: operation.noteId,
270
+ text: operation.text,
271
+ lifetime: operation.lifetime,
272
+ createdOperationId: canonicalOperationId,
273
+ })]);
274
+ }
275
+ else {
276
+ const note = notes.find((candidate) => candidate.noteId === operation.noteId);
277
+ if (note === undefined)
278
+ throw new Error("tmw_scene_note_missing");
279
+ notes = Object.freeze(notes.filter((candidate) => candidate.noteId !== operation.noteId));
280
+ }
281
+ return canonicalTmwSceneStateV1({ ...base, revision: base.revision + 1, worldTime, presentEntityNodeIds: present, spatialRef, manuscriptNotes: notes });
282
+ }
283
+ /** Internal-only consume gate: callers must bind exact note ids to the successful writer receipt. */
284
+ export function consumeCommittedTmwSceneNotesV1(current, consumption, committedWriterReceiptDigest) {
285
+ const base = canonicalTmwSceneStateV1(current);
286
+ if (consumption.sceneRevision !== base.revision)
287
+ throw new Error("tmw_scene_note_consumption_revision_mismatch");
288
+ if (consumption.writerReceiptDigest !== committedWriterReceiptDigest) {
289
+ throw new Error("tmw_scene_note_consumption_receipt_mismatch");
290
+ }
291
+ const noteIds = canonicalIds(consumption.noteIds, "scene_consumed_note_id");
292
+ const byId = new Map(base.manuscriptNotes.map((note) => [note.noteId, note]));
293
+ for (const noteId of noteIds) {
294
+ const note = byId.get(noteId);
295
+ if (note === undefined)
296
+ throw new Error("tmw_scene_note_missing");
297
+ if (note.lifetime !== "once")
298
+ throw new Error("tmw_scene_note_not_once");
299
+ }
300
+ if (noteIds.length === 0)
301
+ return base;
302
+ const consumed = new Set(noteIds);
303
+ return canonicalTmwSceneStateV1({
304
+ ...base,
305
+ revision: base.revision + 1,
306
+ manuscriptNotes: Object.freeze(base.manuscriptNotes.filter((note) => !consumed.has(note.noteId))),
307
+ });
308
+ }
309
+ export function reduceTmwSceneEffectsV1(current, effects, operationId) {
310
+ return effects.reduce((scene, effect) => {
311
+ const operation = tmwSceneOperationFromEffect(effect.capabilityId, effect.input);
312
+ return operation === null ? scene : reduceTmwSceneV1(scene, operation, `${operationId}:${effect.effectId}`);
313
+ }, current);
314
+ }
315
+ /** Single clock authority adapter: scheduler observes Scene; it never owns an independently writable world clock. */
316
+ export function projectTmwSceneWorldTimeV1(scene) {
317
+ return scene?.worldTime ?? null;
318
+ }
319
+ export function setTmwSceneWorldTimeV1(scene, worldTime) {
320
+ const canonicalWorldTime = canonicalSceneWorldTime(worldTime);
321
+ if (scene === null) {
322
+ if (canonicalWorldTime !== null)
323
+ throw new Error("tmw_scene_not_active");
324
+ return null;
325
+ }
326
+ if (scene.worldTime === canonicalWorldTime)
327
+ return canonicalTmwSceneStateV1(scene);
328
+ const base = canonicalTmwSceneStateV1(scene);
329
+ return canonicalTmwSceneStateV1({
330
+ ...base,
331
+ revision: base.revision + 1,
332
+ worldTime: canonicalWorldTime,
333
+ });
334
+ }