@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.
- package/LICENSE +46 -0
- package/dist/activation.d.ts +50 -0
- package/dist/activation.js +113 -0
- package/dist/audio-presentation-projection.d.ts +23 -0
- package/dist/audio-presentation-projection.js +39 -0
- package/dist/author-scenario-source-v3.d.ts +34 -0
- package/dist/author-scenario-source-v3.js +1 -0
- package/dist/authoring-runtime.d.ts +76 -0
- package/dist/authoring-runtime.js +282 -0
- package/dist/builder-diagnostics.d.ts +3 -0
- package/dist/builder-diagnostics.js +8 -0
- package/dist/builder-node.d.ts +178 -0
- package/dist/builder-node.js +212 -0
- package/dist/builder.d.ts +32 -0
- package/dist/builder.js +127 -0
- package/dist/capability-registry.d.ts +32 -0
- package/dist/capability-registry.js +33 -0
- package/dist/compile-target-profile.d.ts +16 -0
- package/dist/compile-target-profile.js +70 -0
- package/dist/compiled-plan.d.ts +44 -0
- package/dist/compiled-plan.js +1 -0
- package/dist/compiler-compatibility-gate.d.ts +96 -0
- package/dist/compiler-compatibility-gate.js +609 -0
- package/dist/compiler-release-authority.d.ts +212 -0
- package/dist/compiler-release-authority.js +676 -0
- package/dist/compiler-stage-cache.d.ts +42 -0
- package/dist/compiler-stage-cache.js +100 -0
- package/dist/compiler-stage-registry.d.ts +48 -0
- package/dist/compiler-stage-registry.js +126 -0
- package/dist/compiler.d.ts +7 -0
- package/dist/compiler.js +68 -0
- package/dist/condition-graph.d.ts +46 -0
- package/dist/condition-graph.js +135 -0
- package/dist/diagnostics.d.ts +20 -0
- package/dist/diagnostics.js +20 -0
- package/dist/durable-intent.d.ts +29 -0
- package/dist/durable-intent.js +60 -0
- package/dist/effect-flow.d.ts +69 -0
- package/dist/effect-flow.js +98 -0
- package/dist/event-fabric.d.ts +81 -0
- package/dist/event-fabric.js +227 -0
- package/dist/event-lifecycle.d.ts +59 -0
- package/dist/event-lifecycle.js +161 -0
- package/dist/evidence.d.ts +37 -0
- package/dist/evidence.js +92 -0
- package/dist/gameplay-state.d.ts +127 -0
- package/dist/gameplay-state.js +214 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +52 -0
- package/dist/isolated-replay.d.ts +81 -0
- package/dist/isolated-replay.js +316 -0
- package/dist/music-candidate-set.d.ts +38 -0
- package/dist/music-candidate-set.js +102 -0
- package/dist/natural-language-chunking.d.ts +10 -0
- package/dist/natural-language-chunking.js +62 -0
- package/dist/natural-language-ingestion.d.ts +14 -0
- package/dist/natural-language-ingestion.js +128 -0
- package/dist/natural-language-structured-decode.d.ts +2 -0
- package/dist/natural-language-structured-decode.js +69 -0
- package/dist/natural-language-world-types.d.ts +142 -0
- package/dist/natural-language-world-types.js +2 -0
- package/dist/neutral-source-compiler.d.ts +135 -0
- package/dist/neutral-source-compiler.js +608 -0
- package/dist/presentation-intent.d.ts +42 -0
- package/dist/presentation-intent.js +137 -0
- package/dist/recall-query-envelope.d.ts +32 -0
- package/dist/recall-query-envelope.js +51 -0
- package/dist/rewrite-compensation.d.ts +28 -0
- package/dist/rewrite-compensation.js +85 -0
- package/dist/runtime-contract.d.ts +56 -0
- package/dist/runtime-contract.js +96 -0
- package/dist/runtime-domain-capabilities.d.ts +12 -0
- package/dist/runtime-domain-capabilities.js +20 -0
- package/dist/runtime-sav.d.ts +117 -0
- package/dist/runtime-sav.js +356 -0
- package/dist/runtime-trace.d.ts +44 -0
- package/dist/runtime-trace.js +119 -0
- package/dist/runtime.d.ts +238 -0
- package/dist/runtime.js +1887 -0
- package/dist/scene-author-source.d.ts +13 -0
- package/dist/scene-author-source.js +25 -0
- package/dist/scene-state.d.ts +275 -0
- package/dist/scene-state.js +334 -0
- package/dist/scheduler.d.ts +55 -0
- package/dist/scheduler.js +235 -0
- package/dist/selector-ast.d.ts +24 -0
- package/dist/selector-ast.js +18 -0
- package/dist/semantic-verdict.d.ts +48 -0
- package/dist/semantic-verdict.js +101 -0
- package/dist/simulator.d.ts +45 -0
- package/dist/simulator.js +58 -0
- package/dist/source-canonical.d.ts +4 -0
- package/dist/source-canonical.js +51 -0
- package/dist/source-crypto.d.ts +20 -0
- package/dist/source-crypto.js +17 -0
- package/dist/source-revision.d.ts +22 -0
- package/dist/source-revision.js +43 -0
- package/dist/spatial-impact-receipt.d.ts +38 -0
- package/dist/spatial-impact-receipt.js +67 -0
- package/dist/temporal-rewrite.d.ts +59 -0
- package/dist/temporal-rewrite.js +214 -0
- package/dist/time-domains.d.ts +37 -0
- package/dist/time-domains.js +142 -0
- package/dist/timeline-revision.d.ts +28 -0
- package/dist/timeline-revision.js +59 -0
- package/dist/trigger-document-v3.d.ts +99 -0
- package/dist/trigger-document-v3.js +334 -0
- package/dist/turn-commit.d.ts +56 -0
- package/dist/turn-commit.js +190 -0
- package/dist/validator.d.ts +7 -0
- package/dist/validator.js +142 -0
- package/dist/value-graph.d.ts +98 -0
- package/dist/value-graph.js +318 -0
- package/dist/view-event-delivery.d.ts +47 -0
- package/dist/view-event-delivery.js +197 -0
- package/dist/view-interaction-manifest-v3.d.ts +60 -0
- package/dist/view-interaction-manifest-v3.js +198 -0
- package/dist/view-payload-schema.d.ts +17 -0
- package/dist/view-payload-schema.js +84 -0
- package/package.json +62 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,1887 @@
|
|
|
1
|
+
import { TMW_RUNTIME_GENERATION, TMW_SEMANTIC_CONFIDENCE_THRESHOLD_MILLIONTHS, } from "./runtime-contract.js";
|
|
2
|
+
import { compareTmwValuesV1, readTmwSceneValueV1, resolveTmwValueGraphInputsV1, resolveTmwEffectValueArgumentsV1, tmwValueContextFromSavV1 } from "./value-graph.js";
|
|
3
|
+
import { emptyTmwGameplayStateV1, persistentTmwGameplayStateV1, prepareTmwGameplayEffectsV1 } from "./gameplay-state.js";
|
|
4
|
+
import { EventFabric, } from "./event-fabric.js";
|
|
5
|
+
import { planEffectFlow, } from "./effect-flow.js";
|
|
6
|
+
import { evaluateConditionGraph, } from "./condition-graph.js";
|
|
7
|
+
import { requireTmwIdentifier, requireTmwSafeInteger, requireTmwSha256 } from "./runtime-trace.js";
|
|
8
|
+
import { canonicalRuntimeSavPayload, encodeRuntimeSavSection, runtimeSavPayloadDigest, } from "./runtime-sav.js";
|
|
9
|
+
import { activateTmwCandidate, } from "./activation.js";
|
|
10
|
+
import { isTmwEventBroadcastSuspension, isTmwEventDefinitionActive, isTmwEventLifecycleCapability, reduceTmwEventLifecycle } from "./event-lifecycle.js";
|
|
11
|
+
import { canonicalTmwJson, canonicalTmwJsonValue, tmwSha256Hex } from "./runtime-trace.js";
|
|
12
|
+
import { crossingPolicyAllows, observeTmwEffectiveTime, tmwEffectiveTimeIsDue, } from "./time-domains.js";
|
|
13
|
+
import { canonicalSchedulerCoordinates, TmwScheduler, } from "./scheduler.js";
|
|
14
|
+
import { assertTemporalRewriteBinding, } from "./temporal-rewrite.js";
|
|
15
|
+
import { replayTemporalRewriteIsolated, } from "./isolated-replay.js";
|
|
16
|
+
import { canonicalTmwPendingSceneClockCrossingV1, consumeCommittedTmwSceneNotesV1, projectTmwSceneWorldTimeV1, reduceTmwSceneEffectsV1, setTmwSceneWorldTimeV1, } from "./scene-state.js";
|
|
17
|
+
/** effect 链先消费依赖,再读取本次执行已写入的局部变量;stable_id 只用于同级稳定排序。 */
|
|
18
|
+
function dependencyOrderedRuntimeNodes(plan) {
|
|
19
|
+
const byId = new Map(plan.nodes.map((node) => [node.stable_id, node]));
|
|
20
|
+
if (byId.size !== plan.nodes.length)
|
|
21
|
+
throw new Error("tmw_runtime_compiled_plan_dependencies_invalid");
|
|
22
|
+
const expectedDependencies = new Map(plan.nodes.map((node) => [node.stable_id, []]));
|
|
23
|
+
for (const edge of plan.edges) {
|
|
24
|
+
const dependencies = expectedDependencies.get(edge.to_stable_id);
|
|
25
|
+
if (!byId.has(edge.from_stable_id) || dependencies === undefined) {
|
|
26
|
+
throw new Error("tmw_runtime_compiled_plan_dependencies_invalid");
|
|
27
|
+
}
|
|
28
|
+
dependencies.push(edge.from_stable_id);
|
|
29
|
+
}
|
|
30
|
+
for (const node of plan.nodes) {
|
|
31
|
+
if (!Array.isArray(node.dependencies)
|
|
32
|
+
|| node.dependencies.some((dependency) => typeof dependency !== "string" || !byId.has(dependency))) {
|
|
33
|
+
throw new Error("tmw_runtime_compiled_plan_dependencies_invalid");
|
|
34
|
+
}
|
|
35
|
+
const actual = [...node.dependencies].sort();
|
|
36
|
+
const expected = [...expectedDependencies.get(node.stable_id)].sort();
|
|
37
|
+
if (actual.length !== expected.length || actual.some((dependency, index) => dependency !== expected[index])) {
|
|
38
|
+
throw new Error("tmw_runtime_compiled_plan_dependencies_invalid");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const ordered = [];
|
|
42
|
+
const visited = new Set();
|
|
43
|
+
const active = new Set();
|
|
44
|
+
const visit = (id) => {
|
|
45
|
+
if (visited.has(id))
|
|
46
|
+
return;
|
|
47
|
+
if (active.has(id))
|
|
48
|
+
throw new Error("tmw_runtime_effect_cycle");
|
|
49
|
+
const node = byId.get(id);
|
|
50
|
+
if (!node)
|
|
51
|
+
throw new Error("tmw_runtime_compiled_plan_dependencies_invalid");
|
|
52
|
+
active.add(id);
|
|
53
|
+
for (const dependency of node.dependencies)
|
|
54
|
+
visit(dependency);
|
|
55
|
+
active.delete(id);
|
|
56
|
+
visited.add(id);
|
|
57
|
+
ordered.push(node);
|
|
58
|
+
};
|
|
59
|
+
for (const node of plan.nodes)
|
|
60
|
+
visit(node.stable_id);
|
|
61
|
+
return Object.freeze(ordered);
|
|
62
|
+
}
|
|
63
|
+
function record(value, label) {
|
|
64
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
65
|
+
throw new Error(`tmw_runtime_${label}_object_required`);
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
function eventField(payload, path) {
|
|
70
|
+
let current = payload;
|
|
71
|
+
for (const part of path) {
|
|
72
|
+
if (typeof current !== "object" || current === null || Array.isArray(current))
|
|
73
|
+
return undefined;
|
|
74
|
+
current = current[part];
|
|
75
|
+
}
|
|
76
|
+
return current;
|
|
77
|
+
}
|
|
78
|
+
function comparePrimitive(actual, comparison, expected) {
|
|
79
|
+
if (actual === undefined)
|
|
80
|
+
return null;
|
|
81
|
+
if (comparison === "eq" || comparison === "neq") {
|
|
82
|
+
const equal = canonicalTmwJson(actual) === canonicalTmwJson(expected);
|
|
83
|
+
return comparison === "eq" ? equal : !equal;
|
|
84
|
+
}
|
|
85
|
+
if (comparison === "contains") {
|
|
86
|
+
if (typeof actual === "string" && typeof expected === "string")
|
|
87
|
+
return actual.includes(expected);
|
|
88
|
+
if (Array.isArray(actual)) {
|
|
89
|
+
return actual.some((entry) => canonicalTmwJson(entry) === canonicalTmwJson(expected));
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (typeof actual !== "number" || typeof expected !== "number")
|
|
94
|
+
return null;
|
|
95
|
+
if (comparison === "gt")
|
|
96
|
+
return actual > expected;
|
|
97
|
+
if (comparison === "gte")
|
|
98
|
+
return actual >= expected;
|
|
99
|
+
if (comparison === "lt")
|
|
100
|
+
return actual < expected;
|
|
101
|
+
return actual <= expected;
|
|
102
|
+
}
|
|
103
|
+
export function buildTmwConditionExpressionSnapshotV3(expression, eventPayload, prefix, positiveSemanticLatches = new Set(), valueContext = {}, valueGraph) {
|
|
104
|
+
const nodes = [];
|
|
105
|
+
const values = Object.create(null);
|
|
106
|
+
const semanticPredicates = [];
|
|
107
|
+
let sequence = 0;
|
|
108
|
+
const visit = (value) => {
|
|
109
|
+
const nodeId = `${prefix}:${sequence++}`;
|
|
110
|
+
if (value.op === "literal") {
|
|
111
|
+
const predicateKey = `${nodeId}:literal`;
|
|
112
|
+
values[predicateKey] = value.value ? "true" : "false";
|
|
113
|
+
nodes.push(Object.freeze({ nodeId, kind: "leaf", predicateKey }));
|
|
114
|
+
}
|
|
115
|
+
else if (value.op === "value_input" || value.op === "scene_field") {
|
|
116
|
+
const predicateKey = `${nodeId}:${value.op}`;
|
|
117
|
+
const resolved = value.op === "value_input" ? resolveTmwValueGraphInputsV1(valueGraph, prefix, valueContext)[value.port] : readTmwSceneValueV1(valueContext.scene, value.field);
|
|
118
|
+
const truth = resolved === undefined ? undefined : value.op === "value_input" ? resolved.value_type === "boolean" ? resolved.value : undefined : compareTmwValuesV1(resolved.value, value.value, value.comparison);
|
|
119
|
+
values[predicateKey] = truth === undefined ? "unknown" : truth ? "true" : "false";
|
|
120
|
+
nodes.push(Object.freeze({ nodeId, kind: "leaf", predicateKey }));
|
|
121
|
+
}
|
|
122
|
+
else if (value.op === "event_field") {
|
|
123
|
+
const predicateKey = `${nodeId}:event-field:${value.path.join(".")}`;
|
|
124
|
+
const comparison = comparePrimitive(eventField(eventPayload, value.path), value.comparison, value.value);
|
|
125
|
+
values[predicateKey] = comparison === null ? "unknown" : comparison ? "true" : "false";
|
|
126
|
+
nodes.push(Object.freeze({ nodeId, kind: "leaf", predicateKey }));
|
|
127
|
+
}
|
|
128
|
+
else if (value.op === "selector_exists") {
|
|
129
|
+
const predicateKey = `${nodeId}:selector`;
|
|
130
|
+
values[predicateKey] = "unknown";
|
|
131
|
+
nodes.push(Object.freeze({ nodeId, kind: "leaf", predicateKey }));
|
|
132
|
+
}
|
|
133
|
+
else if (value.op === "nl_condition") {
|
|
134
|
+
const predicateKey = `${nodeId}:nl:${value.stable_id}`;
|
|
135
|
+
const latched = positiveSemanticLatches.has(predicateKey);
|
|
136
|
+
values[predicateKey] = latched ? "true" : "unknown";
|
|
137
|
+
if (!latched)
|
|
138
|
+
semanticPredicates.push(Object.freeze({ predicateKey, condition: value }));
|
|
139
|
+
nodes.push(Object.freeze({ nodeId, kind: "leaf", predicateKey }));
|
|
140
|
+
}
|
|
141
|
+
else if (value.op === "not") {
|
|
142
|
+
nodes.push(Object.freeze({ nodeId, kind: "not", child: visit(value.value) }));
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
nodes.push(Object.freeze({
|
|
146
|
+
nodeId,
|
|
147
|
+
kind: value.op,
|
|
148
|
+
children: Object.freeze(value.values.map(visit)),
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
return nodeId;
|
|
152
|
+
};
|
|
153
|
+
const rootNodeId = visit(expression);
|
|
154
|
+
return Object.freeze({
|
|
155
|
+
graph: Object.freeze({ rootNodeId, nodes: Object.freeze(nodes) }),
|
|
156
|
+
values: Object.freeze(values),
|
|
157
|
+
semanticPredicates: Object.freeze(semanticPredicates),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const CONDITION_STATE_SCHEMA_V2 = "tmw3.condition-state.v2";
|
|
161
|
+
function positiveSemanticLatchesFromState(conditionState) {
|
|
162
|
+
if (typeof conditionState !== "object" || conditionState === null || Array.isArray(conditionState)) {
|
|
163
|
+
return new Map();
|
|
164
|
+
}
|
|
165
|
+
const state = conditionState;
|
|
166
|
+
if (state.schema !== CONDITION_STATE_SCHEMA_V2) {
|
|
167
|
+
return new Map();
|
|
168
|
+
}
|
|
169
|
+
if (Object.keys(state).sort().join("\0")
|
|
170
|
+
!== ["evaluations", "positiveSemanticLatches", "schema"].sort().join("\0")
|
|
171
|
+
|| !Array.isArray(state.positiveSemanticLatches)) {
|
|
172
|
+
throw new Error("tmw_condition_state_v2_invalid");
|
|
173
|
+
}
|
|
174
|
+
const latches = new Map();
|
|
175
|
+
for (const value of state.positiveSemanticLatches) {
|
|
176
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
177
|
+
throw new Error("tmw_positive_semantic_latch_invalid");
|
|
178
|
+
}
|
|
179
|
+
const latch = value;
|
|
180
|
+
if (Object.keys(latch).sort().join("\0")
|
|
181
|
+
!== ["evidenceSetDigest", "predicateKey", "sourceOperationId", "verdictDigest"].sort().join("\0")
|
|
182
|
+
|| typeof latch.predicateKey !== "string"
|
|
183
|
+
|| typeof latch.verdictDigest !== "string"
|
|
184
|
+
|| typeof latch.evidenceSetDigest !== "string"
|
|
185
|
+
|| typeof latch.sourceOperationId !== "string"
|
|
186
|
+
|| latches.has(latch.predicateKey)) {
|
|
187
|
+
throw new Error("tmw_positive_semantic_latch_invalid");
|
|
188
|
+
}
|
|
189
|
+
const predicateKey = requireTmwIdentifier(latch.predicateKey, "semantic_latch_predicate");
|
|
190
|
+
latches.set(predicateKey, Object.freeze({
|
|
191
|
+
predicateKey,
|
|
192
|
+
verdictDigest: requireTmwSha256(latch.verdictDigest, "semantic_latch_verdict"),
|
|
193
|
+
evidenceSetDigest: requireTmwSha256(latch.evidenceSetDigest, "semantic_latch_evidence"),
|
|
194
|
+
sourceOperationId: requireTmwIdentifier(latch.sourceOperationId, "semantic_latch_operation"),
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
return latches;
|
|
198
|
+
}
|
|
199
|
+
function appendPositiveSemanticLatches(target, receipt, values) {
|
|
200
|
+
if (receipt === null)
|
|
201
|
+
return;
|
|
202
|
+
for (const verdict of receipt.verdicts ?? []) {
|
|
203
|
+
if (values[verdict.predicateKey] !== "true")
|
|
204
|
+
continue;
|
|
205
|
+
target.set(verdict.predicateKey, Object.freeze({
|
|
206
|
+
predicateKey: verdict.predicateKey,
|
|
207
|
+
verdictDigest: receipt.verdictDigest,
|
|
208
|
+
evidenceSetDigest: verdict.evidenceSetDigest,
|
|
209
|
+
sourceOperationId: verdict.sourceOperationId,
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function materializeConditionState(latches, evaluations) {
|
|
214
|
+
return canonicalTmwJsonValue({
|
|
215
|
+
schema: CONDITION_STATE_SCHEMA_V2,
|
|
216
|
+
positiveSemanticLatches: Object.freeze([...latches.values()]
|
|
217
|
+
.sort((left, right) => left.predicateKey.localeCompare(right.predicateKey))),
|
|
218
|
+
evaluations,
|
|
219
|
+
}, "runtime_condition_state");
|
|
220
|
+
}
|
|
221
|
+
export function resolveTmwSemanticPredicateValues(predicates, receipt) {
|
|
222
|
+
const expected = new Map(predicates.map((predicate) => [predicate.predicateKey, predicate]));
|
|
223
|
+
const values = Object.create(null);
|
|
224
|
+
const seen = new Set();
|
|
225
|
+
for (const verdict of receipt.verdicts ?? []) {
|
|
226
|
+
const predicate = expected.get(verdict.predicateKey);
|
|
227
|
+
if (predicate === undefined || seen.has(verdict.predicateKey)) {
|
|
228
|
+
throw new Error("tmw_semantic_predicate_binding_mismatch");
|
|
229
|
+
}
|
|
230
|
+
seen.add(verdict.predicateKey);
|
|
231
|
+
if (verdict.sourceOperationId !== predicate.evidenceIdentity.sourceOperationId
|
|
232
|
+
|| verdict.evidenceSetDigest !== receipt.evidenceSetDigest) {
|
|
233
|
+
throw new Error("tmw_semantic_evidence_binding_mismatch");
|
|
234
|
+
}
|
|
235
|
+
if (!Number.isSafeInteger(verdict.confidenceMillionths)
|
|
236
|
+
|| verdict.confidenceMillionths < 0
|
|
237
|
+
|| verdict.confidenceMillionths > 1_000_000) {
|
|
238
|
+
throw new Error("tmw_semantic_confidence_invalid");
|
|
239
|
+
}
|
|
240
|
+
if (verdict.truth !== "true" && verdict.truth !== "false" && verdict.truth !== "unknown") {
|
|
241
|
+
throw new Error("tmw_semantic_truth_invalid");
|
|
242
|
+
}
|
|
243
|
+
values[verdict.predicateKey] = verdict.confidenceMillionths >= TMW_SEMANTIC_CONFIDENCE_THRESHOLD_MILLIONTHS
|
|
244
|
+
? verdict.truth
|
|
245
|
+
: "unknown";
|
|
246
|
+
}
|
|
247
|
+
for (const predicate of predicates) {
|
|
248
|
+
if (!seen.has(predicate.predicateKey))
|
|
249
|
+
values[predicate.predicateKey] = "unknown";
|
|
250
|
+
}
|
|
251
|
+
return Object.freeze(values);
|
|
252
|
+
}
|
|
253
|
+
function eventSchedulerCoordinates(coordinates) {
|
|
254
|
+
return canonicalSchedulerCoordinates(Object.freeze({
|
|
255
|
+
logicalSeq: coordinates.logicalSeq,
|
|
256
|
+
worldTime: coordinates.worldTime,
|
|
257
|
+
presentation: coordinates.presentation,
|
|
258
|
+
realTime: coordinates.realTime === null
|
|
259
|
+
? Object.freeze({})
|
|
260
|
+
: Object.freeze({ [coordinates.realTime.clock]: coordinates.realTime.value }),
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
function canonicalEventCoordinates(coordinates) {
|
|
264
|
+
const canonical = eventSchedulerCoordinates(coordinates);
|
|
265
|
+
return Object.freeze({
|
|
266
|
+
logicalSeq: canonical.logicalSeq,
|
|
267
|
+
worldTime: canonical.worldTime,
|
|
268
|
+
presentation: canonical.presentation,
|
|
269
|
+
realTime: coordinates.realTime === null
|
|
270
|
+
? null
|
|
271
|
+
: Object.freeze({
|
|
272
|
+
clock: coordinates.realTime.clock,
|
|
273
|
+
value: canonical.realTime[coordinates.realTime.clock],
|
|
274
|
+
}),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function schedulerObservedCoordinate(coordinates, effectiveTime) {
|
|
278
|
+
if (coordinates === null || coordinates === undefined)
|
|
279
|
+
return null;
|
|
280
|
+
if (effectiveTime.domain === "logical_seq")
|
|
281
|
+
return coordinates.logicalSeq;
|
|
282
|
+
if (effectiveTime.domain === "world_time")
|
|
283
|
+
return coordinates.worldTime;
|
|
284
|
+
if (effectiveTime.domain === "presentation_seq")
|
|
285
|
+
return coordinates.presentation?.eventSeq ?? null;
|
|
286
|
+
const clock = effectiveTime.real_time_clock;
|
|
287
|
+
return clock === null ? null : coordinates.realTime[clock] ?? null;
|
|
288
|
+
}
|
|
289
|
+
function observedCoordinate(coordinates, effectiveTime) {
|
|
290
|
+
if (effectiveTime.domain === "logical_seq")
|
|
291
|
+
return coordinates.logicalSeq;
|
|
292
|
+
if (effectiveTime.domain === "world_time")
|
|
293
|
+
return coordinates.worldTime;
|
|
294
|
+
if (effectiveTime.domain === "presentation_seq")
|
|
295
|
+
return coordinates.presentation?.eventSeq ?? null;
|
|
296
|
+
return coordinates.realTime?.clock === effectiveTime.real_time_clock
|
|
297
|
+
? coordinates.realTime.value
|
|
298
|
+
: null;
|
|
299
|
+
}
|
|
300
|
+
function dueForEvent(event, node) {
|
|
301
|
+
const observed = observedCoordinate(event.coordinates, node.effective_time);
|
|
302
|
+
if (observed === null)
|
|
303
|
+
return false;
|
|
304
|
+
return tmwEffectiveTimeIsDue(node.effective_time, observed, event.crossingDirection);
|
|
305
|
+
}
|
|
306
|
+
function downstreamNodeIds(plan, roots) {
|
|
307
|
+
const reachable = new Set(roots);
|
|
308
|
+
let changed = true;
|
|
309
|
+
while (changed) {
|
|
310
|
+
changed = false;
|
|
311
|
+
for (const edge of plan.edges) {
|
|
312
|
+
if (!reachable.has(edge.from_stable_id) || reachable.has(edge.to_stable_id))
|
|
313
|
+
continue;
|
|
314
|
+
reachable.add(edge.to_stable_id);
|
|
315
|
+
changed = true;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return reachable;
|
|
319
|
+
}
|
|
320
|
+
function combineTruth(values) {
|
|
321
|
+
if (values.includes("false"))
|
|
322
|
+
return "false";
|
|
323
|
+
return values.includes("unknown") ? "unknown" : "true";
|
|
324
|
+
}
|
|
325
|
+
function eventLedger(base, snapshot, rewriteEvent, eventId, schedulerDueReceipts = Object.freeze([])) {
|
|
326
|
+
const existing = record(base.eventLedger, "event_ledger");
|
|
327
|
+
const events = Array.isArray(existing.events) ? existing.events : [];
|
|
328
|
+
const rewriteEvents = Array.isArray(existing.rewriteEvents) ? existing.rewriteEvents : [];
|
|
329
|
+
const temporalBlockedEventIds = Array.isArray(existing.temporalBlockedEventIds)
|
|
330
|
+
? existing.temporalBlockedEventIds
|
|
331
|
+
: [];
|
|
332
|
+
const priorSchedulerDueReceipts = Array.isArray(existing.schedulerDueReceipts)
|
|
333
|
+
? existing.schedulerDueReceipts
|
|
334
|
+
: [];
|
|
335
|
+
return canonicalTmwJsonValue({
|
|
336
|
+
...existing,
|
|
337
|
+
events: [...events, ...snapshot.events],
|
|
338
|
+
rewriteEvents: rewriteEvent === null ? rewriteEvents : [...rewriteEvents, rewriteEvent],
|
|
339
|
+
temporalBlockedEventIds: rewriteEvent === null
|
|
340
|
+
? [...temporalBlockedEventIds, eventId]
|
|
341
|
+
: temporalBlockedEventIds,
|
|
342
|
+
schedulerDueReceipts: [...priorSchedulerDueReceipts, ...schedulerDueReceipts],
|
|
343
|
+
}, "runtime_event_ledger");
|
|
344
|
+
}
|
|
345
|
+
function verifiedAuthorViewEvent(base, binding, event, pending) {
|
|
346
|
+
const projection = pending?.authorViewProjection;
|
|
347
|
+
if (projection === undefined)
|
|
348
|
+
return Object.freeze([]);
|
|
349
|
+
if (projection.saveId !== event.scope.saveId || projection.artifactDigest !== binding.artifactDigest) {
|
|
350
|
+
throw new Error("tmw_runtime_author_view_projection_binding_mismatch");
|
|
351
|
+
}
|
|
352
|
+
const causationId = record(event.payload, "event_payload").causationId;
|
|
353
|
+
if (causationId !== projection.sourceEventId)
|
|
354
|
+
throw new Error("tmw_runtime_author_view_projection_source_mismatch");
|
|
355
|
+
const ledger = record(base.eventLedger, "event_ledger");
|
|
356
|
+
if (!Array.isArray(ledger.events))
|
|
357
|
+
throw new Error("tmw_runtime_author_view_projection_source_missing");
|
|
358
|
+
const source = ledger.events.filter((candidate) => {
|
|
359
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate))
|
|
360
|
+
return false;
|
|
361
|
+
const sourceEvent = candidate;
|
|
362
|
+
if (sourceEvent.eventId !== projection.sourceEventId
|
|
363
|
+
|| sourceEvent.artifactDigest !== projection.artifactDigest
|
|
364
|
+
|| sourceEvent.commitReceiptDigest !== projection.sourceCommitReceiptDigest)
|
|
365
|
+
return false;
|
|
366
|
+
const scope = sourceEvent.scope;
|
|
367
|
+
return typeof scope === "object" && scope !== null && !Array.isArray(scope)
|
|
368
|
+
&& scope.saveId === projection.saveId;
|
|
369
|
+
});
|
|
370
|
+
if (source.length !== 1)
|
|
371
|
+
throw new Error("tmw_runtime_author_view_projection_source_missing");
|
|
372
|
+
return Object.freeze([Object.freeze({
|
|
373
|
+
eventId: event.eventId,
|
|
374
|
+
eventType: event.eventType,
|
|
375
|
+
viewVisiblePayload: projection.viewVisiblePayload,
|
|
376
|
+
})]);
|
|
377
|
+
}
|
|
378
|
+
function writerTurnLedger(base, entry) {
|
|
379
|
+
const existing = record(base.eventLedger, "event_ledger");
|
|
380
|
+
const entries = Array.isArray(existing.writerSceneCommits) ? existing.writerSceneCommits : [];
|
|
381
|
+
return canonicalTmwJsonValue({ ...existing, writerSceneCommits: [...entries, entry] }, "runtime_event_ledger");
|
|
382
|
+
}
|
|
383
|
+
function writerTurnRequestDigestImage(input) {
|
|
384
|
+
return canonicalTmwJsonValue({
|
|
385
|
+
operationId: requireTmwIdentifier(input.operationId, "writer_operation_id"),
|
|
386
|
+
turnId: requireTmwIdentifier(input.turnId, "writer_turn_id"),
|
|
387
|
+
worldlineId: requireTmwIdentifier(input.worldlineId, "writer_worldline_id"),
|
|
388
|
+
logicalSeq: requireTmwSafeInteger(input.logicalSeq, "writer_logical_seq", 0),
|
|
389
|
+
worldTime: input.worldTime,
|
|
390
|
+
writerReceiptDigest: requireTmwSha256(input.writerReceiptDigest, "writer_receipt_digest"),
|
|
391
|
+
noteConsumption: input.noteConsumption,
|
|
392
|
+
pendingClockCrossings: input.pendingClockCrossings,
|
|
393
|
+
}, "writer_turn_request");
|
|
394
|
+
}
|
|
395
|
+
function committedWriterTurnEntry(base, operationId) {
|
|
396
|
+
const ledger = record(base.eventLedger, "event_ledger");
|
|
397
|
+
if (!Array.isArray(ledger.writerSceneCommits))
|
|
398
|
+
return null;
|
|
399
|
+
const matches = ledger.writerSceneCommits.filter((value) => {
|
|
400
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
401
|
+
return false;
|
|
402
|
+
return value.operationId === operationId;
|
|
403
|
+
});
|
|
404
|
+
if (matches.length !== 1)
|
|
405
|
+
throw new Error("tmw_runtime_writer_commit_ledger_invalid");
|
|
406
|
+
return matches[0];
|
|
407
|
+
}
|
|
408
|
+
function rewriteSourceEvents(base) {
|
|
409
|
+
const ledger = record(base.eventLedger, "event_ledger");
|
|
410
|
+
if (Array.isArray(ledger.temporalBlockedEventIds)
|
|
411
|
+
&& ledger.temporalBlockedEventIds.length > 0) {
|
|
412
|
+
throw new Error("tmw_rewrite_non_world_time_event_present");
|
|
413
|
+
}
|
|
414
|
+
const raw = ledger.rewriteEvents;
|
|
415
|
+
if (raw === undefined) {
|
|
416
|
+
const events = ledger.events;
|
|
417
|
+
if (Array.isArray(events) && events.length === 0)
|
|
418
|
+
return Object.freeze([]);
|
|
419
|
+
throw new Error("tmw_rewrite_source_metadata_missing");
|
|
420
|
+
}
|
|
421
|
+
if (!Array.isArray(raw))
|
|
422
|
+
throw new Error("tmw_rewrite_source_metadata_invalid");
|
|
423
|
+
return Object.freeze(raw);
|
|
424
|
+
}
|
|
425
|
+
function sourceEventLedgerDigestImage(events) {
|
|
426
|
+
return canonicalTmwJsonValue(events.map((event) => ({
|
|
427
|
+
eventId: event.eventId,
|
|
428
|
+
eventType: event.eventType,
|
|
429
|
+
effectiveWorldTime: event.effectiveWorldTime,
|
|
430
|
+
capabilityIds: event.capabilityIds,
|
|
431
|
+
payload: event.payload,
|
|
432
|
+
payloadHash: event.payloadHash,
|
|
433
|
+
replacementOf: event.replacementOf,
|
|
434
|
+
hardFact: event.hardFact,
|
|
435
|
+
externalIrreversible: event.externalIrreversible,
|
|
436
|
+
})), "rewrite_source_event_ledger");
|
|
437
|
+
}
|
|
438
|
+
function registryAdapter(registry) {
|
|
439
|
+
const byId = new Map(registry.capabilities.map((capability) => [capability.capability_id, capability]));
|
|
440
|
+
return Object.freeze({
|
|
441
|
+
resolve: (capabilityId) => {
|
|
442
|
+
const capability = byId.get(capabilityId);
|
|
443
|
+
if (capability === undefined)
|
|
444
|
+
return null;
|
|
445
|
+
return Object.freeze({
|
|
446
|
+
capabilityId: capability.capability_id,
|
|
447
|
+
profileIds: Object.freeze([registry.registry_version]),
|
|
448
|
+
executorKind: capability.policy === "presentation_only" ? "command" : "reconciler",
|
|
449
|
+
conflictKey: capability.capability_id,
|
|
450
|
+
mergePolicy: "reject",
|
|
451
|
+
allowsExternalSideEffect: capability.rewrite_safety === "external_irreversible",
|
|
452
|
+
selectorRequired: capability.selector_required,
|
|
453
|
+
});
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
function canonicalBinding(input) {
|
|
458
|
+
if (input.runtimeGeneration !== 3)
|
|
459
|
+
throw new Error("unsupported_runtime_generation");
|
|
460
|
+
return Object.freeze({
|
|
461
|
+
epoch: requireTmwSafeInteger(input.epoch, "runtime_epoch", 1),
|
|
462
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
463
|
+
runtimeContractHash: requireTmwSha256(input.runtimeContractHash, "runtime_contract_hash"),
|
|
464
|
+
artifactDigest: requireTmwSha256(input.artifactDigest, "runtime_artifact_digest"),
|
|
465
|
+
sourceRevision: requireTmwIdentifier(input.sourceRevision, "runtime_source_revision"),
|
|
466
|
+
timelineRevision: requireTmwIdentifier(input.timelineRevision, "runtime_timeline_revision"),
|
|
467
|
+
leaseId: requireTmwIdentifier(input.leaseId, "runtime_lease_id"),
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
export function createTmwRuntimeHost(input) {
|
|
471
|
+
const binding = canonicalBinding(input.binding);
|
|
472
|
+
if (input.compiledPlan.plan_version !== 3 || input.compiledPlan.runtime_generation !== 3) {
|
|
473
|
+
throw new Error("unsupported_runtime_generation");
|
|
474
|
+
}
|
|
475
|
+
if (input.compiledPlan.runtime_contract_hash !== binding.runtimeContractHash) {
|
|
476
|
+
throw new Error("runtime_contract_hash_mismatch");
|
|
477
|
+
}
|
|
478
|
+
if (input.compiledPlan.registry_version !== input.capabilityRegistry.registry_version) {
|
|
479
|
+
throw new Error("tmw_runtime_registry_version_mismatch");
|
|
480
|
+
}
|
|
481
|
+
const orderedRuntimeNodes = dependencyOrderedRuntimeNodes(input.compiledPlan);
|
|
482
|
+
let fabric = new EventFabric({
|
|
483
|
+
epoch: binding.epoch,
|
|
484
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
485
|
+
artifactDigest: binding.artifactDigest,
|
|
486
|
+
timelineRevision: binding.timelineRevision,
|
|
487
|
+
});
|
|
488
|
+
const capabilities = registryAdapter(input.capabilityRegistry);
|
|
489
|
+
const compiledEventHardFact = (eventTypeInput) => {
|
|
490
|
+
const eventType = requireTmwIdentifier(eventTypeInput, "runtime_event_type");
|
|
491
|
+
const matches = input.compiledPlan.nodes.flatMap((node) => {
|
|
492
|
+
if (node.kind !== "event")
|
|
493
|
+
return [];
|
|
494
|
+
const payload = record(node.canonical_payload, "compiled_event");
|
|
495
|
+
return payload.event_type === eventType ? [payload] : [];
|
|
496
|
+
});
|
|
497
|
+
if (matches.length !== 1 || typeof matches[0].hard_fact !== "boolean") {
|
|
498
|
+
throw new Error("tmw_runtime_event_node_ambiguous");
|
|
499
|
+
}
|
|
500
|
+
return matches[0].hard_fact;
|
|
501
|
+
};
|
|
502
|
+
const hasExternalIrreversibleCapability = (capabilityIds) => capabilityIds.some((capabilityId) => {
|
|
503
|
+
const capability = input.capabilityRegistry.capabilities.find((candidate) => candidate.capability_id === capabilityId);
|
|
504
|
+
if (capability === undefined)
|
|
505
|
+
throw new Error("tmw_runtime_capability_missing");
|
|
506
|
+
return capability.rewrite_safety === "external_irreversible";
|
|
507
|
+
});
|
|
508
|
+
let disposed = false;
|
|
509
|
+
let restoredSav = null;
|
|
510
|
+
let eventAccepted = false;
|
|
511
|
+
let executionTail = Promise.resolve();
|
|
512
|
+
const assertActive = () => {
|
|
513
|
+
if (disposed)
|
|
514
|
+
throw new Error("tmw_runtime_disposed");
|
|
515
|
+
};
|
|
516
|
+
return Object.freeze({
|
|
517
|
+
binding,
|
|
518
|
+
compiledPlan: input.compiledPlan,
|
|
519
|
+
ingestCommittedEvent: async (event) => {
|
|
520
|
+
assertActive();
|
|
521
|
+
eventAccepted = true;
|
|
522
|
+
return await fabric.appendCommitted(event);
|
|
523
|
+
},
|
|
524
|
+
executeCommittedEvent: async (event, ports) => {
|
|
525
|
+
let releaseExecution;
|
|
526
|
+
const previousExecution = executionTail;
|
|
527
|
+
executionTail = new Promise((resolve) => {
|
|
528
|
+
releaseExecution = resolve;
|
|
529
|
+
});
|
|
530
|
+
await previousExecution;
|
|
531
|
+
try {
|
|
532
|
+
assertActive();
|
|
533
|
+
const base = restoredSav;
|
|
534
|
+
if (base === null)
|
|
535
|
+
throw new Error("tmw_runtime_sav_state_missing");
|
|
536
|
+
const positiveSemanticLatches = new Map(positiveSemanticLatchesFromState(base.conditionState));
|
|
537
|
+
const positiveSemanticLatchKeys = new Set(positiveSemanticLatches.keys());
|
|
538
|
+
const operationId = `tmw3:event:${event.eventId}`;
|
|
539
|
+
if ((base.completedOperationIds ?? []).includes(operationId)) {
|
|
540
|
+
throw new Error("tmw_runtime_operation_already_completed");
|
|
541
|
+
}
|
|
542
|
+
if (event.timelineRevision !== base.activeTimelineRevision) {
|
|
543
|
+
throw new Error("tmw_event_timeline_revision_mismatch");
|
|
544
|
+
}
|
|
545
|
+
const pendingClockCrossing = base.pendingSceneClockCrossings.find((candidate) => candidate.eventId === event.eventId);
|
|
546
|
+
if (pendingClockCrossing !== undefined) {
|
|
547
|
+
if (pendingClockCrossing.eventType !== event.eventType
|
|
548
|
+
|| pendingClockCrossing.logicalSeq !== event.coordinates.logicalSeq
|
|
549
|
+
|| pendingClockCrossing.worldTime !== event.coordinates.worldTime
|
|
550
|
+
|| pendingClockCrossing.crossingDirection !== event.crossingDirection
|
|
551
|
+
|| pendingClockCrossing.payloadHash !== event.payloadHash
|
|
552
|
+
|| pendingClockCrossing.commitReceiptDigest !== event.commitReceiptDigest
|
|
553
|
+
|| canonicalTmwJson(pendingClockCrossing.payload) !== canonicalTmwJson(event.payload)) {
|
|
554
|
+
throw new Error("tmw_runtime_pending_scene_crossing_mismatch");
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
const authorViewEvents = verifiedAuthorViewEvent(base, binding, event, pendingClockCrossing);
|
|
558
|
+
if (await ports.activation.readByOperation(operationId) !== null) {
|
|
559
|
+
throw new Error("tmw_runtime_operation_already_activated");
|
|
560
|
+
}
|
|
561
|
+
eventAccepted = true;
|
|
562
|
+
const draftFabric = new EventFabric({
|
|
563
|
+
epoch: binding.epoch,
|
|
564
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
565
|
+
artifactDigest: binding.artifactDigest,
|
|
566
|
+
timelineRevision: base.activeTimelineRevision,
|
|
567
|
+
});
|
|
568
|
+
draftFabric.restoreLedgerHead(base.ledgerHead);
|
|
569
|
+
const eventReceipt = await draftFabric.appendCommitted(event);
|
|
570
|
+
const scheduler = new TmwScheduler(base.scheduler);
|
|
571
|
+
const schedulerReceipts = scheduler.advance(eventSchedulerCoordinates(event.coordinates), event.crossingDirection);
|
|
572
|
+
const dueSchedulerReceipts = Object.freeze(schedulerReceipts.filter((receipt) => receipt.status === "due"));
|
|
573
|
+
const consumedSuspensions = dueSchedulerReceipts.map((receipt) => {
|
|
574
|
+
const suspension = scheduler.consumeDue(receipt.suspensionId);
|
|
575
|
+
return Object.freeze({ receipt, suspension });
|
|
576
|
+
});
|
|
577
|
+
const dueSuspensions = new Map(consumedSuspensions.filter(({ suspension }) => !isTmwEventBroadcastSuspension(suspension)).map((entry) => [entry.suspension.resumeNodeId, entry]));
|
|
578
|
+
const eventRoots = new Set(input.compiledPlan.nodes.flatMap((node) => {
|
|
579
|
+
if (node.kind !== "event" || !dueForEvent(event, node))
|
|
580
|
+
return [];
|
|
581
|
+
if (!isTmwEventDefinitionActive(node.stable_id, base.durableIntents))
|
|
582
|
+
return [];
|
|
583
|
+
const payload = record(node.canonical_payload, "compiled_event");
|
|
584
|
+
return payload.event_type === event.eventType ? [node.stable_id] : [];
|
|
585
|
+
}));
|
|
586
|
+
if (event.hardFact !== compiledEventHardFact(event.eventType)) {
|
|
587
|
+
throw new Error("tmw_runtime_event_hard_fact_mismatch");
|
|
588
|
+
}
|
|
589
|
+
const reachable = new Set(downstreamNodeIds(input.compiledPlan, eventRoots));
|
|
590
|
+
for (const resumeNodeId of dueSuspensions.keys())
|
|
591
|
+
reachable.add(resumeNodeId);
|
|
592
|
+
const conditionResults = new Map();
|
|
593
|
+
const conditionValues = new Map();
|
|
594
|
+
const semanticPredicates = [];
|
|
595
|
+
const turnId = event.correlationId;
|
|
596
|
+
for (const node of input.compiledPlan.nodes) {
|
|
597
|
+
if (!reachable.has(node.stable_id) || node.kind !== "condition" || !dueForEvent(event, node)) {
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
const payload = record(node.canonical_payload, "compiled_condition");
|
|
601
|
+
const expression = payload.expression;
|
|
602
|
+
const expressionSnapshot = buildTmwConditionExpressionSnapshotV3(expression, record(event.payload, "event_payload"), node.stable_id, positiveSemanticLatchKeys, tmwValueContextFromSavV1(base), input.compiledPlan.value_graph);
|
|
603
|
+
const result = evaluateConditionGraph(expressionSnapshot.graph, {
|
|
604
|
+
snapshotHash: event.payloadHash,
|
|
605
|
+
values: expressionSnapshot.values,
|
|
606
|
+
evidenceRefs: Object.freeze(Object.fromEntries(Object.keys(expressionSnapshot.values).map((key) => [key, [event.eventId]]))),
|
|
607
|
+
frozenFacts: event.payload,
|
|
608
|
+
});
|
|
609
|
+
conditionResults.set(node.stable_id, result);
|
|
610
|
+
conditionValues.set(node.stable_id, expressionSnapshot.values);
|
|
611
|
+
semanticPredicates.push(...expressionSnapshot.semanticPredicates.map((predicate) => Object.freeze({
|
|
612
|
+
...predicate,
|
|
613
|
+
evidenceIdentity: Object.freeze({
|
|
614
|
+
eventId: event.eventId,
|
|
615
|
+
eventPayloadHash: event.payloadHash,
|
|
616
|
+
sourceOperationId: operationId,
|
|
617
|
+
}),
|
|
618
|
+
})));
|
|
619
|
+
}
|
|
620
|
+
const semanticPredicateKeys = Object.freeze(semanticPredicates.map((item) => item.predicateKey).sort());
|
|
621
|
+
const semanticIntentId = `tmw3:semantic:${event.eventId}`;
|
|
622
|
+
let semanticReceipt = null;
|
|
623
|
+
let semanticValues = Object.freeze({});
|
|
624
|
+
if (semanticPredicates.length > 0) {
|
|
625
|
+
try {
|
|
626
|
+
semanticReceipt = await ports.semanticJudge.judge({
|
|
627
|
+
event,
|
|
628
|
+
turnId,
|
|
629
|
+
operationId,
|
|
630
|
+
intentId: semanticIntentId,
|
|
631
|
+
idempotencyKey: operationId,
|
|
632
|
+
predicates: Object.freeze(semanticPredicates),
|
|
633
|
+
predicateKeys: semanticPredicateKeys,
|
|
634
|
+
});
|
|
635
|
+
semanticValues = resolveTmwSemanticPredicateValues(semanticPredicates, semanticReceipt);
|
|
636
|
+
appendPositiveSemanticLatches(positiveSemanticLatches, semanticReceipt, semanticValues);
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
semanticReceipt = null;
|
|
640
|
+
}
|
|
641
|
+
for (const [conditionId, values] of conditionValues) {
|
|
642
|
+
const merged = Object.freeze({ ...values, ...semanticValues });
|
|
643
|
+
const payload = record(input.compiledPlan.nodes.find((node) => node.stable_id === conditionId)
|
|
644
|
+
.canonical_payload, "compiled_condition");
|
|
645
|
+
const expressionSnapshot = buildTmwConditionExpressionSnapshotV3(payload.expression, record(event.payload, "event_payload"), conditionId, positiveSemanticLatchKeys, tmwValueContextFromSavV1(base), input.compiledPlan.value_graph);
|
|
646
|
+
conditionResults.set(conditionId, evaluateConditionGraph(expressionSnapshot.graph, {
|
|
647
|
+
snapshotHash: event.payloadHash,
|
|
648
|
+
values: merged,
|
|
649
|
+
evidenceRefs: Object.freeze(Object.fromEntries(Object.keys(merged).map((key) => [key, [event.eventId]]))),
|
|
650
|
+
frozenFacts: event.payload,
|
|
651
|
+
}));
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
const worldEffectNodeIds = Object.freeze(input.compiledPlan.nodes.flatMap((node) => {
|
|
655
|
+
if (!reachable.has(node.stable_id)
|
|
656
|
+
|| node.kind !== "effect"
|
|
657
|
+
|| (!dueSuspensions.has(node.stable_id) && !dueForEvent(event, node))) {
|
|
658
|
+
return [];
|
|
659
|
+
}
|
|
660
|
+
const payload = record(node.canonical_payload, "compiled_effect");
|
|
661
|
+
const capabilityId = String(payload.capability_id);
|
|
662
|
+
return capabilityId.startsWith("world.") ? [node.stable_id] : [];
|
|
663
|
+
}).sort());
|
|
664
|
+
const preparedWorldMutation = worldEffectNodeIds.length > 0
|
|
665
|
+
? await ports.effectCommit.prepareWorldMutation({
|
|
666
|
+
epoch: binding.epoch,
|
|
667
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
668
|
+
artifactDigest: binding.artifactDigest,
|
|
669
|
+
event,
|
|
670
|
+
turnId,
|
|
671
|
+
operationId,
|
|
672
|
+
worldEffectNodeIds,
|
|
673
|
+
})
|
|
674
|
+
: null;
|
|
675
|
+
const upstreamConditions = (nodeId) => {
|
|
676
|
+
const seen = new Set();
|
|
677
|
+
const pending = [nodeId];
|
|
678
|
+
const found = [];
|
|
679
|
+
while (pending.length > 0) {
|
|
680
|
+
const current = pending.pop();
|
|
681
|
+
for (const edge of input.compiledPlan.edges) {
|
|
682
|
+
if (edge.to_stable_id !== current || seen.has(edge.from_stable_id))
|
|
683
|
+
continue;
|
|
684
|
+
seen.add(edge.from_stable_id);
|
|
685
|
+
pending.push(edge.from_stable_id);
|
|
686
|
+
const condition = conditionResults.get(edge.from_stable_id);
|
|
687
|
+
if (condition !== undefined)
|
|
688
|
+
found.push(condition);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return found;
|
|
692
|
+
};
|
|
693
|
+
for (const node of input.compiledPlan.nodes) {
|
|
694
|
+
if (!reachable.has(node.stable_id)
|
|
695
|
+
|| dueSuspensions.has(node.stable_id)
|
|
696
|
+
|| node.kind !== "effect"
|
|
697
|
+
|| dueForEvent(event, node))
|
|
698
|
+
continue;
|
|
699
|
+
const observed = observedCoordinate(event.coordinates, node.effective_time);
|
|
700
|
+
if (observed === null
|
|
701
|
+
|| !crossingPolicyAllows(node.effective_time.crossing_policy, event.crossingDirection)) {
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
const upstream = upstreamConditions(node.stable_id);
|
|
705
|
+
const expectedConditionIds = new Set();
|
|
706
|
+
const pendingAncestors = [node.stable_id];
|
|
707
|
+
const seenAncestors = new Set();
|
|
708
|
+
while (pendingAncestors.length > 0) {
|
|
709
|
+
const current = pendingAncestors.pop();
|
|
710
|
+
for (const edge of input.compiledPlan.edges) {
|
|
711
|
+
if (edge.to_stable_id !== current || seenAncestors.has(edge.from_stable_id))
|
|
712
|
+
continue;
|
|
713
|
+
seenAncestors.add(edge.from_stable_id);
|
|
714
|
+
pendingAncestors.push(edge.from_stable_id);
|
|
715
|
+
if (input.compiledPlan.nodes.find((candidate) => candidate.stable_id === edge.from_stable_id)?.kind === "condition")
|
|
716
|
+
expectedConditionIds.add(edge.from_stable_id);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if ([...expectedConditionIds].some((id) => !conditionResults.has(id)))
|
|
720
|
+
continue;
|
|
721
|
+
const conditionTruth = combineTruth(upstream.map((condition) => condition.truth));
|
|
722
|
+
if (conditionTruth !== "true")
|
|
723
|
+
continue;
|
|
724
|
+
scheduler.schedule(Object.freeze({
|
|
725
|
+
suspensionId: `scheduled-effect:${event.eventId}:${node.stable_id}`,
|
|
726
|
+
epoch: binding.epoch,
|
|
727
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
728
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
729
|
+
artifactDigest: binding.artifactDigest,
|
|
730
|
+
sourceRevision: binding.sourceRevision,
|
|
731
|
+
timelineRevision: base.activeTimelineRevision,
|
|
732
|
+
scope: event.scope,
|
|
733
|
+
effectiveTime: observeTmwEffectiveTime(node.effective_time, observed),
|
|
734
|
+
resumeNodeId: node.stable_id,
|
|
735
|
+
correlationId: event.correlationId,
|
|
736
|
+
cancellationKey: null,
|
|
737
|
+
createdSnapshotHash: event.payloadHash,
|
|
738
|
+
durablePayload: canonicalTmwJsonValue({
|
|
739
|
+
schema: "tmw3.scheduled-effect.v1",
|
|
740
|
+
sourceEventId: event.eventId,
|
|
741
|
+
conditionTruth,
|
|
742
|
+
}, "runtime_scheduled_effect_payload"),
|
|
743
|
+
}));
|
|
744
|
+
}
|
|
745
|
+
const effectPlans = [];
|
|
746
|
+
let plannedGameplay = base.gameplayState ?? emptyTmwGameplayStateV1();
|
|
747
|
+
for (const node of orderedRuntimeNodes) {
|
|
748
|
+
if (!reachable.has(node.stable_id)
|
|
749
|
+
|| node.kind !== "effect"
|
|
750
|
+
|| (!dueSuspensions.has(node.stable_id) && !dueForEvent(event, node)))
|
|
751
|
+
continue;
|
|
752
|
+
const payload = record(node.canonical_payload, "compiled_effect");
|
|
753
|
+
const scheduled = dueSuspensions.get(node.stable_id);
|
|
754
|
+
const scheduledPayload = scheduled === undefined
|
|
755
|
+
? null
|
|
756
|
+
: record(scheduled.suspension.durablePayload, "scheduled_effect_payload");
|
|
757
|
+
if (scheduledPayload !== null
|
|
758
|
+
&& (scheduledPayload.schema !== "tmw3.scheduled-effect.v1"
|
|
759
|
+
|| scheduledPayload.conditionTruth !== "true")) {
|
|
760
|
+
throw new Error("tmw_runtime_scheduled_effect_payload_invalid");
|
|
761
|
+
}
|
|
762
|
+
const conditions = upstreamConditions(node.stable_id);
|
|
763
|
+
const valueInputsEnabled = scheduledPayload !== null || combineTruth(conditions.map((condition) => condition.truth)) === "true";
|
|
764
|
+
const plan = planEffectFlow({
|
|
765
|
+
epoch: binding.epoch,
|
|
766
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
767
|
+
artifactDigest: binding.artifactDigest,
|
|
768
|
+
sourceRevision: binding.sourceRevision,
|
|
769
|
+
timelineRevision: base.activeTimelineRevision,
|
|
770
|
+
turnId,
|
|
771
|
+
capabilityProfileId: input.capabilityRegistry.registry_version,
|
|
772
|
+
mode: "normal",
|
|
773
|
+
conditionTruth: scheduledPayload === null
|
|
774
|
+
? combineTruth(conditions.map((condition) => condition.truth))
|
|
775
|
+
: "true",
|
|
776
|
+
effects: Object.freeze([{
|
|
777
|
+
effectId: node.stable_id,
|
|
778
|
+
capabilityId: String(payload.capability_id),
|
|
779
|
+
selector: (payload.selector ?? null),
|
|
780
|
+
effectiveTime: node.effective_time,
|
|
781
|
+
input: valueInputsEnabled ? resolveTmwEffectValueArgumentsV1(input.compiledPlan.value_graph, node.stable_id, record(payload.arguments, "runtime_effect_arguments"), tmwValueContextFromSavV1({ ...base, gameplayState: plannedGameplay }), String(payload.capability_id)) : canonicalTmwJsonValue(payload.arguments, "runtime_effect_arguments"),
|
|
782
|
+
executionPolicy: node.execution_policy,
|
|
783
|
+
rewritePolicy: node.rewrite_policy,
|
|
784
|
+
priority: node.priority,
|
|
785
|
+
idempotencyKey: node.idempotency_key,
|
|
786
|
+
}]),
|
|
787
|
+
maxEffects: 256,
|
|
788
|
+
}, capabilities);
|
|
789
|
+
if (plan.status === "ready") {
|
|
790
|
+
const preparedGameplay = prepareTmwGameplayEffectsV1(plannedGameplay, plan.effects);
|
|
791
|
+
plannedGameplay = preparedGameplay.state;
|
|
792
|
+
effectPlans.push(Object.freeze({ ...plan, effects: preparedGameplay.effects }));
|
|
793
|
+
}
|
|
794
|
+
else
|
|
795
|
+
effectPlans.push(plan);
|
|
796
|
+
}
|
|
797
|
+
const durableIntents = semanticPredicateKeys.length === 0
|
|
798
|
+
? []
|
|
799
|
+
: [Object.freeze({
|
|
800
|
+
intentId: semanticIntentId,
|
|
801
|
+
epoch: binding.epoch,
|
|
802
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
803
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
804
|
+
artifactDigest: binding.artifactDigest,
|
|
805
|
+
sourceRevision: binding.sourceRevision,
|
|
806
|
+
timelineRevision: base.activeTimelineRevision,
|
|
807
|
+
turnId,
|
|
808
|
+
operationId,
|
|
809
|
+
capabilityId: "semantic.request_verdict",
|
|
810
|
+
idempotencyKey: operationId,
|
|
811
|
+
conflictKey: semanticIntentId,
|
|
812
|
+
priority: 0,
|
|
813
|
+
state: semanticReceipt === null
|
|
814
|
+
|| Object.values(semanticValues).includes("unknown")
|
|
815
|
+
? "blocked"
|
|
816
|
+
: "satisfied",
|
|
817
|
+
selectorSnapshotHash: event.payloadHash,
|
|
818
|
+
payload: Object.freeze({ predicateKeys: semanticPredicateKeys }),
|
|
819
|
+
lastReceiptDigest: semanticReceipt?.verdictDigest ?? null,
|
|
820
|
+
reasonCode: semanticReceipt === null ? "semantic_verdict_pending" : null,
|
|
821
|
+
})];
|
|
822
|
+
const gameplay = { state: plannedGameplay, effects: effectPlans.flatMap((plan) => plan.status === "ready" ? plan.effects : []) };
|
|
823
|
+
const acceptedGameplayEffects = new Set(gameplay.effects);
|
|
824
|
+
const lifecycle = await reduceTmwEventLifecycle({
|
|
825
|
+
plan: input.compiledPlan, event,
|
|
826
|
+
effects: effectPlans.flatMap((plan) => plan.status === "ready" ? plan.effects : []),
|
|
827
|
+
intents: [...base.durableIntents, ...durableIntents], scheduler,
|
|
828
|
+
dueBroadcasts: consumedSuspensions.map((entry) => entry.suspension).filter(isTmwEventBroadcastSuspension),
|
|
829
|
+
pending: base.pendingSceneClockCrossings,
|
|
830
|
+
}).catch(async (error) => {
|
|
831
|
+
await ports.effectCommit.rollbackWorldMutation({ operationId, preparedWorldMutation, reason: "activation_failed" });
|
|
832
|
+
throw error;
|
|
833
|
+
});
|
|
834
|
+
const effectReceipt = await ports.effectCommit.stage({
|
|
835
|
+
event,
|
|
836
|
+
turnId,
|
|
837
|
+
operationId,
|
|
838
|
+
preparedWorldMutation,
|
|
839
|
+
plans: Object.freeze(effectPlans.map((plan) => Object.freeze({ ...plan,
|
|
840
|
+
effects: Object.freeze(plan.effects.filter((effect) => acceptedGameplayEffects.has(effect) && !isTmwEventLifecycleCapability(effect.capabilityId))),
|
|
841
|
+
}))),
|
|
842
|
+
durableIntents: Object.freeze(durableIntents),
|
|
843
|
+
});
|
|
844
|
+
const terminalDigest = requireTmwSha256(effectReceipt.preActivationTerminalDigest, "runtime_effect_terminal_digest");
|
|
845
|
+
const presentationIntents = input.compiledPlan.nodes.flatMap((node, index) => {
|
|
846
|
+
if (!reachable.has(node.stable_id) || node.kind !== "presentation" || !dueForEvent(event, node)) {
|
|
847
|
+
return [];
|
|
848
|
+
}
|
|
849
|
+
const payload = record(node.canonical_payload, "compiled_presentation");
|
|
850
|
+
return [Object.freeze({
|
|
851
|
+
schema: "presentation-intent.v3",
|
|
852
|
+
generation: TMW_RUNTIME_GENERATION,
|
|
853
|
+
intent_id: `presentation:${node.stable_id}:${event.eventId}`,
|
|
854
|
+
epoch: binding.epoch,
|
|
855
|
+
artifact_digest: binding.artifactDigest,
|
|
856
|
+
timeline_revision: base.activeTimelineRevision,
|
|
857
|
+
activation_id: operationId,
|
|
858
|
+
dedupe_key: `presentation:${node.stable_id}:${event.eventId}`,
|
|
859
|
+
sequence: index,
|
|
860
|
+
payload: payload.payload,
|
|
861
|
+
metadata: Object.freeze({ sourceNodeId: node.stable_id }),
|
|
862
|
+
})];
|
|
863
|
+
});
|
|
864
|
+
const readyEffects = effectPlans.flatMap((plan) => plan.status === "ready" ? plan.effects : []);
|
|
865
|
+
const sceneAtObservedTime = base.scene === null || event.coordinates.worldTime === null
|
|
866
|
+
? base.scene
|
|
867
|
+
: setTmwSceneWorldTimeV1(base.scene, event.coordinates.worldTime);
|
|
868
|
+
const nextScene = reduceTmwSceneEffectsV1(sceneAtObservedTime, readyEffects, operationId);
|
|
869
|
+
const rewriteEvent = event.coordinates.worldTime === null
|
|
870
|
+
? null
|
|
871
|
+
: Object.freeze({
|
|
872
|
+
eventId: event.eventId,
|
|
873
|
+
eventType: event.eventType,
|
|
874
|
+
effectiveWorldTime: event.coordinates.worldTime,
|
|
875
|
+
capabilityIds: Object.freeze([...new Set(readyEffects.map((effect) => effect.capabilityId))].sort()),
|
|
876
|
+
payload: event.payload,
|
|
877
|
+
payloadHash: event.payloadHash,
|
|
878
|
+
replacementOf: event.replacementLineage,
|
|
879
|
+
hardFact: event.hardFact,
|
|
880
|
+
externalIrreversible: hasExternalIrreversibleCapability(readyEffects.map((effect) => effect.capabilityId)),
|
|
881
|
+
});
|
|
882
|
+
const ledger = eventLedger(base, draftFabric.snapshot(), rewriteEvent, event.eventId, dueSchedulerReceipts);
|
|
883
|
+
const dueReceiptDigests = await Promise.all(dueSchedulerReceipts.map(async (receipt) => await tmwSha256Hex(canonicalTmwJson(receipt))));
|
|
884
|
+
const [narrativeDigest, presentationIntentSetDigest] = await Promise.all([
|
|
885
|
+
tmwSha256Hex(canonicalTmwJson(base.narrative)),
|
|
886
|
+
tmwSha256Hex(canonicalTmwJson([...base.presentationIntents, ...presentationIntents])),
|
|
887
|
+
]);
|
|
888
|
+
const revisionId = `tmw3:revision:${await tmwSha256Hex(canonicalTmwJson({
|
|
889
|
+
operationId,
|
|
890
|
+
previous: base.activeTimelineRevision,
|
|
891
|
+
ledgerHead: draftFabric.snapshot().ledgerHead,
|
|
892
|
+
terminalDigest,
|
|
893
|
+
}))}`;
|
|
894
|
+
const nextDurableIntents = Object.freeze(lifecycle.intents.map((intent) => Object.freeze({ ...intent, timelineRevision: revisionId })));
|
|
895
|
+
const intentSetDigest = await tmwSha256Hex(canonicalTmwJson(nextDurableIntents));
|
|
896
|
+
const provisionalTimeline = Object.freeze({
|
|
897
|
+
revisionId,
|
|
898
|
+
parentRevisionId: base.activeTimelineRevision,
|
|
899
|
+
revisionSequence: base.activeTimeline.revisionSequence + 1,
|
|
900
|
+
epoch: binding.epoch,
|
|
901
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
902
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
903
|
+
artifactDigest: binding.artifactDigest,
|
|
904
|
+
sourceRevision: binding.sourceRevision,
|
|
905
|
+
worldlineId: event.scope.worldlineId,
|
|
906
|
+
operationId,
|
|
907
|
+
ledgerHead: draftFabric.snapshot().ledgerHead,
|
|
908
|
+
narrativeDigest,
|
|
909
|
+
savSectionDigest: "0".repeat(64),
|
|
910
|
+
intentSetDigest,
|
|
911
|
+
presentationIntentSetDigest,
|
|
912
|
+
createdFrom: "turn",
|
|
913
|
+
active: true,
|
|
914
|
+
});
|
|
915
|
+
const provisionalSav = Object.freeze({
|
|
916
|
+
...base,
|
|
917
|
+
activeTimelineRevision: revisionId,
|
|
918
|
+
activeTimeline: provisionalTimeline,
|
|
919
|
+
turnId,
|
|
920
|
+
preActivationTerminalDigest: terminalDigest,
|
|
921
|
+
ledgerHead: draftFabric.snapshot().ledgerHead,
|
|
922
|
+
eventLedger: ledger,
|
|
923
|
+
scene: nextScene,
|
|
924
|
+
gameplayState: persistentTmwGameplayStateV1(gameplay.state),
|
|
925
|
+
pendingSceneClockCrossings: lifecycle.pending,
|
|
926
|
+
scheduler: Object.freeze({
|
|
927
|
+
cursor: scheduler.snapshot().cursor === null
|
|
928
|
+
? null
|
|
929
|
+
: Object.freeze({
|
|
930
|
+
...scheduler.snapshot().cursor,
|
|
931
|
+
worldTime: projectTmwSceneWorldTimeV1(nextScene),
|
|
932
|
+
}),
|
|
933
|
+
suspensions: Object.freeze(scheduler.snapshot().suspensions.map((suspension) => Object.freeze({ ...suspension, timelineRevision: revisionId }))),
|
|
934
|
+
}),
|
|
935
|
+
conditionState: materializeConditionState(positiveSemanticLatches, Object.fromEntries([...conditionResults].map(([id, result]) => [
|
|
936
|
+
id,
|
|
937
|
+
canonicalTmwJsonValue(result, "runtime_condition_evaluation"),
|
|
938
|
+
]))),
|
|
939
|
+
durableIntents: nextDurableIntents,
|
|
940
|
+
presentationIntents: Object.freeze([
|
|
941
|
+
...base.presentationIntents,
|
|
942
|
+
...presentationIntents.map((intent) => canonicalTmwJsonValue(JSON.parse(canonicalTmwJson(intent)), "runtime_presentation_intent")),
|
|
943
|
+
]),
|
|
944
|
+
semanticVerdictRefs: Object.freeze([
|
|
945
|
+
...base.semanticVerdictRefs,
|
|
946
|
+
...(semanticReceipt === null ? [] : [semanticReceipt.verdictDigest]),
|
|
947
|
+
]),
|
|
948
|
+
operationReceiptDigests: Object.freeze([...new Set([
|
|
949
|
+
...base.operationReceiptDigests,
|
|
950
|
+
event.commitReceiptDigest,
|
|
951
|
+
terminalDigest,
|
|
952
|
+
...(semanticReceipt === null ? [] : [semanticReceipt.providerReceiptDigest]),
|
|
953
|
+
...dueReceiptDigests,
|
|
954
|
+
])]),
|
|
955
|
+
completedOperationIds: Object.freeze([
|
|
956
|
+
...(base.completedOperationIds ?? []),
|
|
957
|
+
operationId,
|
|
958
|
+
]),
|
|
959
|
+
worldMutationRecoveryOperations: Object.freeze([
|
|
960
|
+
...base.worldMutationRecoveryOperations,
|
|
961
|
+
...(effectReceipt.worldMutationRecoveryOperation === null
|
|
962
|
+
? []
|
|
963
|
+
: [effectReceipt.worldMutationRecoveryOperation]),
|
|
964
|
+
]),
|
|
965
|
+
});
|
|
966
|
+
const savSectionDigest = await runtimeSavPayloadDigest(provisionalSav);
|
|
967
|
+
const savPayload = Object.freeze({
|
|
968
|
+
...provisionalSav,
|
|
969
|
+
activeTimeline: Object.freeze({
|
|
970
|
+
...provisionalTimeline,
|
|
971
|
+
savSectionDigest,
|
|
972
|
+
}),
|
|
973
|
+
});
|
|
974
|
+
const savSection = await encodeRuntimeSavSection(savPayload);
|
|
975
|
+
const candidate = Object.freeze({
|
|
976
|
+
operationId,
|
|
977
|
+
epoch: binding.epoch,
|
|
978
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
979
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
980
|
+
artifactDigest: binding.artifactDigest,
|
|
981
|
+
sourceRevision: binding.sourceRevision,
|
|
982
|
+
expectedHead: Object.freeze({
|
|
983
|
+
revisionId: base.activeTimeline.revisionId,
|
|
984
|
+
revisionSequence: base.activeTimeline.revisionSequence,
|
|
985
|
+
ledgerHead: base.ledgerHead,
|
|
986
|
+
}),
|
|
987
|
+
timeline: savPayload.activeTimeline,
|
|
988
|
+
ledger,
|
|
989
|
+
ledgerHead: savPayload.ledgerHead,
|
|
990
|
+
narrative: savPayload.narrative,
|
|
991
|
+
narrativeDigest,
|
|
992
|
+
savSection,
|
|
993
|
+
savSectionDigest,
|
|
994
|
+
intents: savPayload.durableIntents,
|
|
995
|
+
intentSetDigest,
|
|
996
|
+
presentationIntents: savPayload.presentationIntents,
|
|
997
|
+
presentationIntentSetDigest,
|
|
998
|
+
preActivationTerminalDigest: terminalDigest,
|
|
999
|
+
});
|
|
1000
|
+
let authorViewOutboxStaged = false;
|
|
1001
|
+
const rollbackAuthorViewOutbox = async () => {
|
|
1002
|
+
if (!authorViewOutboxStaged || ports.verifiedAuthorViewOutbox === undefined)
|
|
1003
|
+
return;
|
|
1004
|
+
authorViewOutboxStaged = false;
|
|
1005
|
+
await ports.verifiedAuthorViewOutbox.rollback({ operationId });
|
|
1006
|
+
};
|
|
1007
|
+
if (authorViewEvents.length > 0 && ports.verifiedAuthorViewOutbox !== undefined) {
|
|
1008
|
+
// 仅把 verifiedAuthorViewEvent 的结果交给 outbox;不得将 event payload 或未验证 projection 旁路出去。
|
|
1009
|
+
authorViewOutboxStaged = true;
|
|
1010
|
+
try {
|
|
1011
|
+
await ports.verifiedAuthorViewOutbox.stage(Object.freeze({
|
|
1012
|
+
operationId,
|
|
1013
|
+
binding: Object.freeze({
|
|
1014
|
+
epoch: binding.epoch,
|
|
1015
|
+
artifactDigest: binding.artifactDigest,
|
|
1016
|
+
sourceRevision: binding.sourceRevision,
|
|
1017
|
+
timelineRevision: base.activeTimelineRevision,
|
|
1018
|
+
saveId: event.scope.saveId,
|
|
1019
|
+
worldlineId: event.scope.worldlineId,
|
|
1020
|
+
}),
|
|
1021
|
+
events: authorViewEvents,
|
|
1022
|
+
}));
|
|
1023
|
+
}
|
|
1024
|
+
catch (error) {
|
|
1025
|
+
await Promise.all([
|
|
1026
|
+
rollbackAuthorViewOutbox(),
|
|
1027
|
+
ports.effectCommit.rollbackWorldMutation({ operationId, preparedWorldMutation, reason: "activation_failed" }),
|
|
1028
|
+
]);
|
|
1029
|
+
throw error;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
let activation;
|
|
1033
|
+
try {
|
|
1034
|
+
activation = await activateTmwCandidate(candidate, ports.activation);
|
|
1035
|
+
}
|
|
1036
|
+
catch (error) {
|
|
1037
|
+
await Promise.all([
|
|
1038
|
+
rollbackAuthorViewOutbox(),
|
|
1039
|
+
ports.effectCommit.rollbackWorldMutation({ operationId, preparedWorldMutation, reason: "activation_failed" }),
|
|
1040
|
+
]);
|
|
1041
|
+
throw error;
|
|
1042
|
+
}
|
|
1043
|
+
if (activation.status !== "committed" && activation.status !== "already_committed") {
|
|
1044
|
+
await Promise.all([
|
|
1045
|
+
rollbackAuthorViewOutbox(),
|
|
1046
|
+
ports.effectCommit.rollbackWorldMutation({ operationId, preparedWorldMutation, reason: "activation_conflict" }),
|
|
1047
|
+
]);
|
|
1048
|
+
throw new Error("tmw_runtime_activation_conflict");
|
|
1049
|
+
}
|
|
1050
|
+
await ports.effectCommit.finalizeWorldMutation({
|
|
1051
|
+
operationId,
|
|
1052
|
+
preparedWorldMutation,
|
|
1053
|
+
});
|
|
1054
|
+
restoredSav = savPayload;
|
|
1055
|
+
fabric = new EventFabric({
|
|
1056
|
+
epoch: binding.epoch,
|
|
1057
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1058
|
+
artifactDigest: binding.artifactDigest,
|
|
1059
|
+
timelineRevision: revisionId,
|
|
1060
|
+
});
|
|
1061
|
+
fabric.restoreLedgerHead(savPayload.ledgerHead);
|
|
1062
|
+
return Object.freeze({ eventReceipt, activation, candidate, authorViewEvents });
|
|
1063
|
+
}
|
|
1064
|
+
finally {
|
|
1065
|
+
releaseExecution();
|
|
1066
|
+
}
|
|
1067
|
+
},
|
|
1068
|
+
commitSuccessfulWriterTurn: async (writerInput, activationPort) => {
|
|
1069
|
+
let releaseExecution;
|
|
1070
|
+
const previousExecution = executionTail;
|
|
1071
|
+
executionTail = new Promise((resolve) => { releaseExecution = resolve; });
|
|
1072
|
+
await previousExecution;
|
|
1073
|
+
try {
|
|
1074
|
+
assertActive();
|
|
1075
|
+
const base = restoredSav;
|
|
1076
|
+
if (base === null)
|
|
1077
|
+
throw new Error("tmw_runtime_sav_state_missing");
|
|
1078
|
+
const operationId = requireTmwIdentifier(writerInput.operationId, "writer_operation_id");
|
|
1079
|
+
const writerReceiptDigest = requireTmwSha256(writerInput.writerReceiptDigest, "writer_receipt_digest");
|
|
1080
|
+
const requestDigest = await tmwSha256Hex(canonicalTmwJson(writerTurnRequestDigestImage(writerInput)));
|
|
1081
|
+
if ((base.completedOperationIds ?? []).includes(operationId)) {
|
|
1082
|
+
const committed = committedWriterTurnEntry(base, operationId);
|
|
1083
|
+
if (committed === null || committed.requestDigest !== requestDigest
|
|
1084
|
+
|| committed.writerReceiptDigest !== writerReceiptDigest)
|
|
1085
|
+
throw new Error("tmw_runtime_writer_retry_request_mismatch");
|
|
1086
|
+
const existing = await activationPort.readByOperation(operationId);
|
|
1087
|
+
if (existing === null
|
|
1088
|
+
|| (existing.status !== "committed" && existing.status !== "already_committed")) {
|
|
1089
|
+
throw new Error("tmw_runtime_writer_retry_activation_missing");
|
|
1090
|
+
}
|
|
1091
|
+
return Object.freeze({ status: "already_committed", activation: existing, candidate: null });
|
|
1092
|
+
}
|
|
1093
|
+
const turnId = requireTmwIdentifier(writerInput.turnId, "writer_turn_id");
|
|
1094
|
+
const worldlineId = requireTmwIdentifier(writerInput.worldlineId, "writer_worldline_id");
|
|
1095
|
+
let pendingSceneClockCrossings = Object.freeze(writerInput.pendingClockCrossings.map(canonicalTmwPendingSceneClockCrossingV1));
|
|
1096
|
+
if (pendingSceneClockCrossings.some((entry) => entry.authorViewProjection !== undefined)) {
|
|
1097
|
+
throw new Error("tmw_runtime_writer_author_view_projection_forbidden");
|
|
1098
|
+
}
|
|
1099
|
+
const sceneAfterConsumption = base.scene === null || writerInput.noteConsumption === null
|
|
1100
|
+
? base.scene
|
|
1101
|
+
: consumeCommittedTmwSceneNotesV1(base.scene, {
|
|
1102
|
+
...writerInput.noteConsumption,
|
|
1103
|
+
writerReceiptDigest,
|
|
1104
|
+
}, writerReceiptDigest);
|
|
1105
|
+
const scene = sceneAfterConsumption === null
|
|
1106
|
+
? null
|
|
1107
|
+
: setTmwSceneWorldTimeV1(sceneAfterConsumption, writerInput.worldTime);
|
|
1108
|
+
const baseCursor = base.scheduler.cursor ?? null;
|
|
1109
|
+
const scheduler = Object.freeze({
|
|
1110
|
+
...base.scheduler,
|
|
1111
|
+
cursor: canonicalSchedulerCoordinates(Object.freeze({
|
|
1112
|
+
logicalSeq: requireTmwSafeInteger(writerInput.logicalSeq, "writer_logical_seq", 0),
|
|
1113
|
+
worldTime: projectTmwSceneWorldTimeV1(scene),
|
|
1114
|
+
presentation: baseCursor?.presentation ?? null,
|
|
1115
|
+
realTime: baseCursor?.realTime ?? Object.freeze({}),
|
|
1116
|
+
})),
|
|
1117
|
+
});
|
|
1118
|
+
const writerScheduler = new TmwScheduler(base.scheduler);
|
|
1119
|
+
const writerDue = writerScheduler.advance(scheduler.cursor, "forward");
|
|
1120
|
+
const dueBroadcasts = writerDue.filter((receipt) => receipt.status === "due").flatMap((receipt) => {
|
|
1121
|
+
const suspension = writerScheduler.snapshot().suspensions.find((entry) => entry.suspensionId === receipt.suspensionId);
|
|
1122
|
+
return isTmwEventBroadcastSuspension(suspension) ? [writerScheduler.consumeDue(receipt.suspensionId)] : [];
|
|
1123
|
+
});
|
|
1124
|
+
const writerLifecycle = dueBroadcasts.length === 0
|
|
1125
|
+
? { intents: base.durableIntents, pending: Object.freeze([...base.pendingSceneClockCrossings, ...pendingSceneClockCrossings]) }
|
|
1126
|
+
: await reduceTmwEventLifecycle({
|
|
1127
|
+
plan: input.compiledPlan,
|
|
1128
|
+
event: {
|
|
1129
|
+
...binding, eventId: operationId, eventType: "tmw.writer.committed", hardFact: true,
|
|
1130
|
+
timelineRevision: base.activeTimelineRevision,
|
|
1131
|
+
coordinates: { logicalSeq: scheduler.cursor.logicalSeq, worldTime: writerInput.worldTime, presentation: null, realTime: null },
|
|
1132
|
+
effectiveTime: { domain: "logical_seq", due_value: scheduler.cursor.logicalSeq, effective_value: scheduler.cursor.logicalSeq, crossing_policy: "forward_only", real_time_clock: null, wall_clock_policy: null },
|
|
1133
|
+
crossingDirection: "forward", scope: dueBroadcasts[0].scope,
|
|
1134
|
+
correlationId: turnId, causationId: null, replacementLineage: [], payload: {}, payloadHash: writerReceiptDigest, commitReceiptDigest: writerReceiptDigest,
|
|
1135
|
+
}, effects: [], intents: base.durableIntents, scheduler: writerScheduler, dueBroadcasts,
|
|
1136
|
+
pending: [...base.pendingSceneClockCrossings, ...pendingSceneClockCrossings],
|
|
1137
|
+
});
|
|
1138
|
+
pendingSceneClockCrossings = writerLifecycle.pending;
|
|
1139
|
+
const [narrativeDigest, presentationIntentSetDigest] = await Promise.all([
|
|
1140
|
+
tmwSha256Hex(canonicalTmwJson(base.narrative)),
|
|
1141
|
+
tmwSha256Hex(canonicalTmwJson(base.presentationIntents)),
|
|
1142
|
+
]);
|
|
1143
|
+
const revisionId = `tmw3:revision:${await tmwSha256Hex(canonicalTmwJson({ operationId, previous: base.activeTimelineRevision, writerReceiptDigest, scene }))}`;
|
|
1144
|
+
const writerIntents = Object.freeze(writerLifecycle.intents.map((intent) => Object.freeze({ ...intent, timelineRevision: revisionId })));
|
|
1145
|
+
const intentSetDigest = await tmwSha256Hex(canonicalTmwJson(writerIntents));
|
|
1146
|
+
const provisionalTimeline = Object.freeze({
|
|
1147
|
+
...base.activeTimeline,
|
|
1148
|
+
revisionId,
|
|
1149
|
+
parentRevisionId: base.activeTimelineRevision,
|
|
1150
|
+
revisionSequence: base.activeTimeline.revisionSequence + 1,
|
|
1151
|
+
operationId,
|
|
1152
|
+
worldlineId,
|
|
1153
|
+
narrativeDigest,
|
|
1154
|
+
intentSetDigest,
|
|
1155
|
+
presentationIntentSetDigest,
|
|
1156
|
+
savSectionDigest: "0".repeat(64),
|
|
1157
|
+
createdFrom: "turn",
|
|
1158
|
+
});
|
|
1159
|
+
const provisionalSav = Object.freeze({
|
|
1160
|
+
...base,
|
|
1161
|
+
activeTimelineRevision: revisionId,
|
|
1162
|
+
activeTimeline: provisionalTimeline,
|
|
1163
|
+
turnId,
|
|
1164
|
+
preActivationTerminalDigest: writerReceiptDigest,
|
|
1165
|
+
scene,
|
|
1166
|
+
pendingSceneClockCrossings,
|
|
1167
|
+
eventLedger: writerTurnLedger(base, Object.freeze({
|
|
1168
|
+
schema: "tmw3.writer-scene-commit.v1",
|
|
1169
|
+
operationId,
|
|
1170
|
+
requestDigest,
|
|
1171
|
+
writerReceiptDigest,
|
|
1172
|
+
})),
|
|
1173
|
+
scheduler: Object.freeze({ ...scheduler, suspensions: Object.freeze(writerScheduler.snapshot().suspensions.map((suspension) => Object.freeze({ ...suspension, timelineRevision: revisionId }))) }),
|
|
1174
|
+
durableIntents: writerIntents,
|
|
1175
|
+
operationReceiptDigests: Object.freeze([...new Set([...base.operationReceiptDigests, writerReceiptDigest])]),
|
|
1176
|
+
completedOperationIds: Object.freeze([...(base.completedOperationIds ?? []), operationId]),
|
|
1177
|
+
});
|
|
1178
|
+
const savSectionDigest = await runtimeSavPayloadDigest(provisionalSav);
|
|
1179
|
+
const savPayload = Object.freeze({
|
|
1180
|
+
...provisionalSav,
|
|
1181
|
+
activeTimeline: Object.freeze({ ...provisionalTimeline, savSectionDigest }),
|
|
1182
|
+
});
|
|
1183
|
+
const candidate = Object.freeze({
|
|
1184
|
+
operationId,
|
|
1185
|
+
epoch: binding.epoch,
|
|
1186
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
1187
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1188
|
+
artifactDigest: binding.artifactDigest,
|
|
1189
|
+
sourceRevision: binding.sourceRevision,
|
|
1190
|
+
expectedHead: Object.freeze({ revisionId: base.activeTimeline.revisionId, revisionSequence: base.activeTimeline.revisionSequence, ledgerHead: base.ledgerHead }),
|
|
1191
|
+
timeline: savPayload.activeTimeline,
|
|
1192
|
+
ledger: savPayload.eventLedger,
|
|
1193
|
+
ledgerHead: savPayload.ledgerHead,
|
|
1194
|
+
narrative: savPayload.narrative,
|
|
1195
|
+
narrativeDigest,
|
|
1196
|
+
savSection: await encodeRuntimeSavSection(savPayload),
|
|
1197
|
+
savSectionDigest,
|
|
1198
|
+
intents: savPayload.durableIntents,
|
|
1199
|
+
intentSetDigest,
|
|
1200
|
+
presentationIntents: savPayload.presentationIntents,
|
|
1201
|
+
presentationIntentSetDigest,
|
|
1202
|
+
preActivationTerminalDigest: writerReceiptDigest,
|
|
1203
|
+
});
|
|
1204
|
+
const activation = await activateTmwCandidate(candidate, activationPort);
|
|
1205
|
+
if (activation.status !== "committed" && activation.status !== "already_committed")
|
|
1206
|
+
throw new Error("tmw_runtime_activation_conflict");
|
|
1207
|
+
restoredSav = savPayload;
|
|
1208
|
+
fabric = new EventFabric({ epoch: binding.epoch, runtimeContractHash: binding.runtimeContractHash, artifactDigest: binding.artifactDigest, timelineRevision: revisionId });
|
|
1209
|
+
fabric.restoreLedgerHead(savPayload.ledgerHead);
|
|
1210
|
+
return Object.freeze({ status: activation.status === "already_committed" ? "already_committed" : "committed", activation, candidate });
|
|
1211
|
+
}
|
|
1212
|
+
finally {
|
|
1213
|
+
releaseExecution();
|
|
1214
|
+
}
|
|
1215
|
+
},
|
|
1216
|
+
rewriteTimeline: async (request, ports) => {
|
|
1217
|
+
let releaseExecution;
|
|
1218
|
+
const previousExecution = executionTail;
|
|
1219
|
+
executionTail = new Promise((resolve) => {
|
|
1220
|
+
releaseExecution = resolve;
|
|
1221
|
+
});
|
|
1222
|
+
await previousExecution;
|
|
1223
|
+
try {
|
|
1224
|
+
assertActive();
|
|
1225
|
+
const base = restoredSav;
|
|
1226
|
+
if (base === null)
|
|
1227
|
+
throw new Error("tmw_runtime_sav_state_missing");
|
|
1228
|
+
if ((base.completedOperationIds ?? []).includes(request.operationId)) {
|
|
1229
|
+
throw new Error("tmw_runtime_operation_already_completed");
|
|
1230
|
+
}
|
|
1231
|
+
const rewritePositiveSemanticLatches = new Map(positiveSemanticLatchesFromState(base.conditionState));
|
|
1232
|
+
const rewritePositiveSemanticLatchKeys = new Set(rewritePositiveSemanticLatches.keys());
|
|
1233
|
+
const sourceEvents = rewriteSourceEvents(base);
|
|
1234
|
+
const targetWorldTime = requireTmwSafeInteger(request.targetWorldTime, "rewrite_target_world_time");
|
|
1235
|
+
const toWorldTime = Math.max(targetWorldTime, ...sourceEvents.map((event) => event.effectiveWorldTime));
|
|
1236
|
+
const interval = Object.freeze({
|
|
1237
|
+
fromWorldTime: 0,
|
|
1238
|
+
toWorldTime,
|
|
1239
|
+
targetWorldTime,
|
|
1240
|
+
});
|
|
1241
|
+
const sourceEventLedgerDigest = await tmwSha256Hex(canonicalTmwJson(sourceEventLedgerDigestImage(sourceEvents)));
|
|
1242
|
+
const draft = await ports.draft.generate({
|
|
1243
|
+
epoch: binding.epoch,
|
|
1244
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
1245
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1246
|
+
artifactDigest: binding.artifactDigest,
|
|
1247
|
+
scenarioId: requireTmwIdentifier(request.scenarioId, "rewrite_scenario_id"),
|
|
1248
|
+
sourceRevision: binding.sourceRevision,
|
|
1249
|
+
timelineRevision: base.activeTimelineRevision,
|
|
1250
|
+
eventLedgerDigest: sourceEventLedgerDigest,
|
|
1251
|
+
interval,
|
|
1252
|
+
turnId: request.turnId,
|
|
1253
|
+
operationId: request.operationId,
|
|
1254
|
+
idempotencyKey: request.operationId,
|
|
1255
|
+
boundedContext: canonicalTmwJsonValue({
|
|
1256
|
+
sourceEvents,
|
|
1257
|
+
sourceEventLedgerDigest,
|
|
1258
|
+
narrative: base.narrative,
|
|
1259
|
+
activeTimeline: base.activeTimeline,
|
|
1260
|
+
}, "rewrite_bounded_context"),
|
|
1261
|
+
});
|
|
1262
|
+
await assertTemporalRewriteBinding(draft, {
|
|
1263
|
+
epoch: binding.epoch,
|
|
1264
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1265
|
+
artifactDigest: binding.artifactDigest,
|
|
1266
|
+
scenarioId: request.scenarioId,
|
|
1267
|
+
sourceRevision: binding.sourceRevision,
|
|
1268
|
+
timelineRevision: base.activeTimelineRevision,
|
|
1269
|
+
turnId: request.turnId,
|
|
1270
|
+
operationId: request.operationId,
|
|
1271
|
+
idempotencyKey: request.operationId,
|
|
1272
|
+
interval,
|
|
1273
|
+
});
|
|
1274
|
+
if (draft.conflictCodes.length !== 0)
|
|
1275
|
+
throw new Error("tmw_rewrite_conflict");
|
|
1276
|
+
const allowedCapabilityIds = input.capabilityRegistry.capabilities
|
|
1277
|
+
.map((capability) => capability.capability_id);
|
|
1278
|
+
const replay = await replayTemporalRewriteIsolated({
|
|
1279
|
+
draft,
|
|
1280
|
+
sourceEvents,
|
|
1281
|
+
sourceEventLedgerDigest,
|
|
1282
|
+
sourceRevisionSequence: base.activeTimeline.revisionSequence,
|
|
1283
|
+
worldlineId: base.activeTimeline.worldlineId,
|
|
1284
|
+
semanticVerdicts: Object.freeze([]),
|
|
1285
|
+
knownEvidenceRefs: Object.freeze(sourceEvents.map((event) => event.eventId)),
|
|
1286
|
+
allowedCapabilityIds: Object.freeze(allowedCapabilityIds),
|
|
1287
|
+
budget: Object.freeze({
|
|
1288
|
+
maxEvents: 4096,
|
|
1289
|
+
maxEffects: 4096,
|
|
1290
|
+
maxDepth: 64,
|
|
1291
|
+
deadlineEpochMs: requireTmwSafeInteger(request.deadlineEpochMs, "rewrite_deadline_epoch_ms", 1),
|
|
1292
|
+
}),
|
|
1293
|
+
nowEpochMs: Date.now(),
|
|
1294
|
+
});
|
|
1295
|
+
if (replay.status !== "passed"
|
|
1296
|
+
|| replay.projection === null
|
|
1297
|
+
|| replay.savCandidate === null
|
|
1298
|
+
|| replay.successorInputs === null) {
|
|
1299
|
+
throw new Error(`tmw_rewrite_rejected:${replay.reasonCodes.join(",")}`);
|
|
1300
|
+
}
|
|
1301
|
+
const projectedIds = new Set(replay.projectedEvents.map((event) => event.eventId));
|
|
1302
|
+
const sourceById = new Map(sourceEvents.map((event) => [event.eventId, event]));
|
|
1303
|
+
const existingLedger = record(base.eventLedger, "event_ledger");
|
|
1304
|
+
const committedEvents = Array.isArray(existingLedger.events)
|
|
1305
|
+
? existingLedger.events.filter((event) => {
|
|
1306
|
+
if (typeof event !== "object" || event === null || Array.isArray(event))
|
|
1307
|
+
return false;
|
|
1308
|
+
const eventId = event.eventId;
|
|
1309
|
+
return typeof eventId === "string" && projectedIds.has(eventId);
|
|
1310
|
+
})
|
|
1311
|
+
: [];
|
|
1312
|
+
const rewriteEvents = replay.projectedEvents.map((event) => {
|
|
1313
|
+
const source = sourceById.get(event.eventId);
|
|
1314
|
+
return Object.freeze({
|
|
1315
|
+
...event,
|
|
1316
|
+
hardFact: source?.hardFact ?? compiledEventHardFact(event.eventType),
|
|
1317
|
+
externalIrreversible: source?.externalIrreversible
|
|
1318
|
+
?? hasExternalIrreversibleCapability(event.capabilityIds),
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
const ledger = canonicalTmwJsonValue({
|
|
1322
|
+
events: committedEvents,
|
|
1323
|
+
rewriteEvents,
|
|
1324
|
+
rewriteProjection: replay.projection,
|
|
1325
|
+
}, "rewrite_runtime_event_ledger");
|
|
1326
|
+
const rewriteEffectPlans = [];
|
|
1327
|
+
let plannedRewriteGameplay = base.gameplayInitialState ?? emptyTmwGameplayStateV1();
|
|
1328
|
+
const rewriteConditionState = Object.create(null);
|
|
1329
|
+
const rewriteSemanticRefs = [];
|
|
1330
|
+
const rewriteProviderReceiptDigests = [];
|
|
1331
|
+
const pendingDurableIntents = [];
|
|
1332
|
+
const pendingSuspensions = [];
|
|
1333
|
+
const pendingPresentations = [];
|
|
1334
|
+
const rewriteRuntimeEvents = rewriteEvents.map((event, index) => Object.freeze({
|
|
1335
|
+
eventId: event.eventId,
|
|
1336
|
+
eventType: event.eventType,
|
|
1337
|
+
hardFact: event.hardFact,
|
|
1338
|
+
epoch: binding.epoch,
|
|
1339
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
1340
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1341
|
+
artifactDigest: binding.artifactDigest,
|
|
1342
|
+
sourceRevision: binding.sourceRevision,
|
|
1343
|
+
timelineRevision: base.activeTimelineRevision,
|
|
1344
|
+
scope: Object.freeze({
|
|
1345
|
+
scenarioId: request.scenarioId,
|
|
1346
|
+
saveId: binding.leaseId,
|
|
1347
|
+
worldlineId: base.activeTimeline.worldlineId,
|
|
1348
|
+
}),
|
|
1349
|
+
coordinates: Object.freeze({
|
|
1350
|
+
logicalSeq: index,
|
|
1351
|
+
worldTime: event.effectiveWorldTime,
|
|
1352
|
+
presentation: null,
|
|
1353
|
+
realTime: null,
|
|
1354
|
+
}),
|
|
1355
|
+
effectiveTime: Object.freeze({
|
|
1356
|
+
domain: "world_time",
|
|
1357
|
+
due_value: event.effectiveWorldTime,
|
|
1358
|
+
effective_value: event.effectiveWorldTime,
|
|
1359
|
+
crossing_policy: "both_directions",
|
|
1360
|
+
real_time_clock: null,
|
|
1361
|
+
wall_clock_policy: null,
|
|
1362
|
+
}),
|
|
1363
|
+
crossingDirection: "backward",
|
|
1364
|
+
correlationId: request.turnId,
|
|
1365
|
+
causationId: request.operationId,
|
|
1366
|
+
replacementLineage: event.replacementOf,
|
|
1367
|
+
payloadHash: event.payloadHash,
|
|
1368
|
+
payload: event.payload,
|
|
1369
|
+
commitReceiptDigest: event.payloadHash,
|
|
1370
|
+
}));
|
|
1371
|
+
for (const event of rewriteRuntimeEvents) {
|
|
1372
|
+
plannedRewriteGameplay = persistentTmwGameplayStateV1(plannedRewriteGameplay);
|
|
1373
|
+
const eventRoots = new Set(input.compiledPlan.nodes.flatMap((node) => {
|
|
1374
|
+
if (node.kind !== "event" || !dueForEvent(event, node))
|
|
1375
|
+
return [];
|
|
1376
|
+
const payload = record(node.canonical_payload, "compiled_event");
|
|
1377
|
+
return payload.event_type === event.eventType ? [node.stable_id] : [];
|
|
1378
|
+
}));
|
|
1379
|
+
const reachable = downstreamNodeIds(input.compiledPlan, eventRoots);
|
|
1380
|
+
const conditionResults = new Map();
|
|
1381
|
+
const conditionValues = new Map();
|
|
1382
|
+
const semanticPredicates = [];
|
|
1383
|
+
const semanticOperationId = `${request.operationId}:${event.eventId}`;
|
|
1384
|
+
for (const node of input.compiledPlan.nodes) {
|
|
1385
|
+
if (!reachable.has(node.stable_id)
|
|
1386
|
+
|| node.kind !== "condition"
|
|
1387
|
+
|| !dueForEvent(event, node))
|
|
1388
|
+
continue;
|
|
1389
|
+
const payload = record(node.canonical_payload, "compiled_condition");
|
|
1390
|
+
const expressionSnapshot = buildTmwConditionExpressionSnapshotV3(payload.expression, record(event.payload, "event_payload"), node.stable_id, rewritePositiveSemanticLatchKeys, tmwValueContextFromSavV1(base), input.compiledPlan.value_graph);
|
|
1391
|
+
const result = evaluateConditionGraph(expressionSnapshot.graph, {
|
|
1392
|
+
snapshotHash: event.payloadHash,
|
|
1393
|
+
values: expressionSnapshot.values,
|
|
1394
|
+
evidenceRefs: Object.freeze(Object.fromEntries(Object.keys(expressionSnapshot.values).map((key) => [key, [event.eventId]]))),
|
|
1395
|
+
frozenFacts: event.payload,
|
|
1396
|
+
});
|
|
1397
|
+
conditionResults.set(node.stable_id, result);
|
|
1398
|
+
conditionValues.set(node.stable_id, expressionSnapshot.values);
|
|
1399
|
+
semanticPredicates.push(...expressionSnapshot.semanticPredicates.map((predicate) => Object.freeze({
|
|
1400
|
+
...predicate,
|
|
1401
|
+
evidenceIdentity: Object.freeze({
|
|
1402
|
+
eventId: event.eventId,
|
|
1403
|
+
eventPayloadHash: event.payloadHash,
|
|
1404
|
+
sourceOperationId: semanticOperationId,
|
|
1405
|
+
}),
|
|
1406
|
+
})));
|
|
1407
|
+
}
|
|
1408
|
+
const semanticPredicateKeys = Object.freeze(semanticPredicates.map((item) => item.predicateKey).sort());
|
|
1409
|
+
let semanticReceipt = null;
|
|
1410
|
+
let semanticValues = Object.freeze({});
|
|
1411
|
+
if (semanticPredicates.length > 0) {
|
|
1412
|
+
try {
|
|
1413
|
+
semanticReceipt = await ports.semanticJudge.judge({
|
|
1414
|
+
event,
|
|
1415
|
+
turnId: request.turnId,
|
|
1416
|
+
operationId: semanticOperationId,
|
|
1417
|
+
intentId: `tmw3:rewrite-semantic:${event.eventId}`,
|
|
1418
|
+
idempotencyKey: semanticOperationId,
|
|
1419
|
+
predicates: Object.freeze(semanticPredicates),
|
|
1420
|
+
predicateKeys: semanticPredicateKeys,
|
|
1421
|
+
});
|
|
1422
|
+
semanticValues = resolveTmwSemanticPredicateValues(semanticPredicates, semanticReceipt);
|
|
1423
|
+
appendPositiveSemanticLatches(rewritePositiveSemanticLatches, semanticReceipt, semanticValues);
|
|
1424
|
+
for (const [predicateKey, truth] of Object.entries(semanticValues)) {
|
|
1425
|
+
if (truth === "true")
|
|
1426
|
+
rewritePositiveSemanticLatchKeys.add(predicateKey);
|
|
1427
|
+
}
|
|
1428
|
+
rewriteSemanticRefs.push(semanticReceipt.verdictDigest);
|
|
1429
|
+
rewriteProviderReceiptDigests.push(semanticReceipt.providerReceiptDigest);
|
|
1430
|
+
}
|
|
1431
|
+
catch {
|
|
1432
|
+
semanticReceipt = null;
|
|
1433
|
+
}
|
|
1434
|
+
for (const [conditionId, values] of conditionValues) {
|
|
1435
|
+
const merged = Object.freeze({ ...values, ...semanticValues });
|
|
1436
|
+
const node = input.compiledPlan.nodes.find((candidate) => candidate.stable_id === conditionId);
|
|
1437
|
+
const payload = record(node.canonical_payload, "compiled_condition");
|
|
1438
|
+
const expressionSnapshot = buildTmwConditionExpressionSnapshotV3(payload.expression, record(event.payload, "event_payload"), conditionId, rewritePositiveSemanticLatchKeys, tmwValueContextFromSavV1(base), input.compiledPlan.value_graph);
|
|
1439
|
+
conditionResults.set(conditionId, evaluateConditionGraph(expressionSnapshot.graph, {
|
|
1440
|
+
snapshotHash: event.payloadHash,
|
|
1441
|
+
values: merged,
|
|
1442
|
+
evidenceRefs: Object.freeze(Object.fromEntries(Object.keys(merged).map((key) => [key, [event.eventId]]))),
|
|
1443
|
+
frozenFacts: event.payload,
|
|
1444
|
+
}));
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
pendingDurableIntents.push(Object.freeze({
|
|
1448
|
+
event,
|
|
1449
|
+
predicateKeys: semanticPredicateKeys,
|
|
1450
|
+
receipt: semanticReceipt,
|
|
1451
|
+
semanticValues,
|
|
1452
|
+
}));
|
|
1453
|
+
const upstreamConditions = (nodeId) => {
|
|
1454
|
+
const seen = new Set();
|
|
1455
|
+
const pending = [nodeId];
|
|
1456
|
+
const found = [];
|
|
1457
|
+
while (pending.length > 0) {
|
|
1458
|
+
const current = pending.pop();
|
|
1459
|
+
for (const edge of input.compiledPlan.edges) {
|
|
1460
|
+
if (edge.to_stable_id !== current || seen.has(edge.from_stable_id))
|
|
1461
|
+
continue;
|
|
1462
|
+
seen.add(edge.from_stable_id);
|
|
1463
|
+
pending.push(edge.from_stable_id);
|
|
1464
|
+
const condition = conditionResults.get(edge.from_stable_id);
|
|
1465
|
+
if (condition !== undefined)
|
|
1466
|
+
found.push(condition);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return found;
|
|
1470
|
+
};
|
|
1471
|
+
for (const [conditionId, result] of conditionResults) {
|
|
1472
|
+
rewriteConditionState[`${event.eventId}:${conditionId}`] =
|
|
1473
|
+
canonicalTmwJsonValue(result, "rewrite_condition_result");
|
|
1474
|
+
}
|
|
1475
|
+
for (const node of orderedRuntimeNodes) {
|
|
1476
|
+
if (!reachable.has(node.stable_id) || !dueForEvent(event, node))
|
|
1477
|
+
continue;
|
|
1478
|
+
if (node.kind === "effect") {
|
|
1479
|
+
const payload = record(node.canonical_payload, "compiled_effect");
|
|
1480
|
+
const conditionTruth = combineTruth(upstreamConditions(node.stable_id).map((condition) => condition.truth));
|
|
1481
|
+
const plan = planEffectFlow({
|
|
1482
|
+
epoch: binding.epoch,
|
|
1483
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1484
|
+
artifactDigest: binding.artifactDigest,
|
|
1485
|
+
sourceRevision: binding.sourceRevision,
|
|
1486
|
+
timelineRevision: base.activeTimelineRevision,
|
|
1487
|
+
turnId: request.turnId,
|
|
1488
|
+
capabilityProfileId: input.capabilityRegistry.registry_version,
|
|
1489
|
+
mode: "rewrite",
|
|
1490
|
+
conditionTruth,
|
|
1491
|
+
effects: Object.freeze([{
|
|
1492
|
+
effectId: `rewrite:${event.eventId}:${node.stable_id}`,
|
|
1493
|
+
capabilityId: String(payload.capability_id),
|
|
1494
|
+
selector: (payload.selector ?? null),
|
|
1495
|
+
effectiveTime: node.effective_time,
|
|
1496
|
+
input: conditionTruth === "true" ? resolveTmwEffectValueArgumentsV1(input.compiledPlan.value_graph, node.stable_id, record(payload.arguments, "rewrite_effect_arguments"), tmwValueContextFromSavV1({ ...base, gameplayState: plannedRewriteGameplay }), String(payload.capability_id)) : canonicalTmwJsonValue(payload.arguments, "rewrite_effect_arguments"),
|
|
1497
|
+
executionPolicy: node.execution_policy,
|
|
1498
|
+
rewritePolicy: node.rewrite_policy,
|
|
1499
|
+
priority: node.priority,
|
|
1500
|
+
idempotencyKey: `rewrite:${request.operationId}:${event.eventId}:${node.stable_id}`,
|
|
1501
|
+
}]),
|
|
1502
|
+
maxEffects: 256,
|
|
1503
|
+
}, capabilities);
|
|
1504
|
+
if (plan.status === "ready") {
|
|
1505
|
+
const gameplay = prepareTmwGameplayEffectsV1(plannedRewriteGameplay, plan.effects);
|
|
1506
|
+
plannedRewriteGameplay = gameplay.state;
|
|
1507
|
+
rewriteEffectPlans.push(Object.freeze({ ...plan, effects: gameplay.effects }));
|
|
1508
|
+
}
|
|
1509
|
+
else
|
|
1510
|
+
rewriteEffectPlans.push(plan);
|
|
1511
|
+
if (plan.status === "suspended") {
|
|
1512
|
+
pendingSuspensions.push(Object.freeze({ event, node }));
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
else if (node.kind === "presentation") {
|
|
1516
|
+
pendingPresentations.push(Object.freeze({
|
|
1517
|
+
event,
|
|
1518
|
+
node,
|
|
1519
|
+
sequence: pendingPresentations.length,
|
|
1520
|
+
}));
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
const targetTimelineRevision = replay.successorInputs.revisionId;
|
|
1525
|
+
const durableIntents = pendingDurableIntents.flatMap(({ event, predicateKeys, receipt, semanticValues }) => predicateKeys.length === 0
|
|
1526
|
+
? []
|
|
1527
|
+
: [Object.freeze({
|
|
1528
|
+
intentId: `tmw3:rewrite-semantic:${event.eventId}`,
|
|
1529
|
+
epoch: binding.epoch,
|
|
1530
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
1531
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1532
|
+
artifactDigest: binding.artifactDigest,
|
|
1533
|
+
sourceRevision: binding.sourceRevision,
|
|
1534
|
+
timelineRevision: targetTimelineRevision,
|
|
1535
|
+
turnId: request.turnId,
|
|
1536
|
+
operationId: `${request.operationId}:${event.eventId}`,
|
|
1537
|
+
capabilityId: "semantic.request_verdict",
|
|
1538
|
+
idempotencyKey: `${request.operationId}:${event.eventId}`,
|
|
1539
|
+
conflictKey: `tmw3:rewrite-semantic:${event.eventId}`,
|
|
1540
|
+
priority: 0,
|
|
1541
|
+
state: receipt === null
|
|
1542
|
+
|| Object.values(semanticValues).includes("unknown")
|
|
1543
|
+
? "blocked"
|
|
1544
|
+
: "satisfied",
|
|
1545
|
+
selectorSnapshotHash: event.payloadHash,
|
|
1546
|
+
payload: Object.freeze({ predicateKeys }),
|
|
1547
|
+
lastReceiptDigest: receipt?.verdictDigest ?? null,
|
|
1548
|
+
reasonCode: receipt === null ? "semantic_verdict_pending" : null,
|
|
1549
|
+
})]);
|
|
1550
|
+
const presentationIntents = pendingPresentations.map(({ event, node, sequence }) => {
|
|
1551
|
+
const payload = record(node.canonical_payload, "compiled_presentation");
|
|
1552
|
+
return Object.freeze({
|
|
1553
|
+
schema: "presentation-intent.v3",
|
|
1554
|
+
generation: TMW_RUNTIME_GENERATION,
|
|
1555
|
+
intent_id: `rewrite:presentation:${node.stable_id}:${event.eventId}`,
|
|
1556
|
+
epoch: binding.epoch,
|
|
1557
|
+
artifact_digest: binding.artifactDigest,
|
|
1558
|
+
timeline_revision: targetTimelineRevision,
|
|
1559
|
+
activation_id: request.operationId,
|
|
1560
|
+
dedupe_key: `rewrite:presentation:${node.stable_id}:${event.eventId}`,
|
|
1561
|
+
sequence,
|
|
1562
|
+
payload: payload.payload,
|
|
1563
|
+
metadata: Object.freeze({
|
|
1564
|
+
sourceNodeId: node.stable_id,
|
|
1565
|
+
sourceEventId: event.eventId,
|
|
1566
|
+
}),
|
|
1567
|
+
});
|
|
1568
|
+
});
|
|
1569
|
+
const rewriteReadyEffects = rewriteEffectPlans.flatMap((plan) => plan.status === "ready" ? plan.effects : []);
|
|
1570
|
+
const sceneAtRewriteTime = base.scene === null
|
|
1571
|
+
? null
|
|
1572
|
+
: setTmwSceneWorldTimeV1(base.scene, targetWorldTime);
|
|
1573
|
+
const rewrittenScene = reduceTmwSceneEffectsV1(sceneAtRewriteTime, rewriteReadyEffects, request.operationId);
|
|
1574
|
+
const scheduler = Object.freeze({
|
|
1575
|
+
cursor: canonicalSchedulerCoordinates(Object.freeze({
|
|
1576
|
+
logicalSeq: replay.projectedEvents.length,
|
|
1577
|
+
worldTime: projectTmwSceneWorldTimeV1(rewrittenScene),
|
|
1578
|
+
presentation: null,
|
|
1579
|
+
realTime: Object.freeze({}),
|
|
1580
|
+
})),
|
|
1581
|
+
suspensions: Object.freeze(pendingSuspensions.map(({ event, node }) => Object.freeze({
|
|
1582
|
+
suspensionId: `rewrite:suspension:${event.eventId}:${node.stable_id}`,
|
|
1583
|
+
epoch: binding.epoch,
|
|
1584
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
1585
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1586
|
+
artifactDigest: binding.artifactDigest,
|
|
1587
|
+
sourceRevision: binding.sourceRevision,
|
|
1588
|
+
timelineRevision: targetTimelineRevision,
|
|
1589
|
+
scope: event.scope,
|
|
1590
|
+
effectiveTime: node.effective_time,
|
|
1591
|
+
resumeNodeId: node.stable_id,
|
|
1592
|
+
correlationId: request.turnId,
|
|
1593
|
+
cancellationKey: null,
|
|
1594
|
+
createdSnapshotHash: event.payloadHash,
|
|
1595
|
+
durablePayload: canonicalTmwJsonValue({
|
|
1596
|
+
eventId: event.eventId,
|
|
1597
|
+
operationId: request.operationId,
|
|
1598
|
+
}, "rewrite_scheduler_payload"),
|
|
1599
|
+
}))),
|
|
1600
|
+
});
|
|
1601
|
+
const aggregateRewriteEvent = Object.freeze({
|
|
1602
|
+
eventId: request.operationId,
|
|
1603
|
+
eventType: "tmw.temporal_rewrite",
|
|
1604
|
+
hardFact: false,
|
|
1605
|
+
epoch: binding.epoch,
|
|
1606
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
1607
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1608
|
+
artifactDigest: binding.artifactDigest,
|
|
1609
|
+
sourceRevision: binding.sourceRevision,
|
|
1610
|
+
timelineRevision: base.activeTimelineRevision,
|
|
1611
|
+
scope: Object.freeze({
|
|
1612
|
+
scenarioId: request.scenarioId,
|
|
1613
|
+
saveId: binding.leaseId,
|
|
1614
|
+
worldlineId: base.activeTimeline.worldlineId,
|
|
1615
|
+
}),
|
|
1616
|
+
coordinates: Object.freeze({
|
|
1617
|
+
logicalSeq: replay.projectedEvents.length,
|
|
1618
|
+
worldTime: targetWorldTime,
|
|
1619
|
+
presentation: null,
|
|
1620
|
+
realTime: null,
|
|
1621
|
+
}),
|
|
1622
|
+
effectiveTime: Object.freeze({
|
|
1623
|
+
domain: "world_time",
|
|
1624
|
+
due_value: targetWorldTime,
|
|
1625
|
+
effective_value: targetWorldTime,
|
|
1626
|
+
crossing_policy: "both_directions",
|
|
1627
|
+
real_time_clock: null,
|
|
1628
|
+
wall_clock_policy: null,
|
|
1629
|
+
}),
|
|
1630
|
+
crossingDirection: "backward",
|
|
1631
|
+
correlationId: request.turnId,
|
|
1632
|
+
causationId: null,
|
|
1633
|
+
replacementLineage: replay.affectedEventIds,
|
|
1634
|
+
payloadHash: replay.projection.ledgerDigest,
|
|
1635
|
+
payload: canonicalTmwJsonValue({
|
|
1636
|
+
draftDigest: draft.draftDigest,
|
|
1637
|
+
projectedEventSetDigest: replay.projectedEventSetDigest,
|
|
1638
|
+
successorInputDigest: replay.successorInputs.successorInputDigest,
|
|
1639
|
+
}, "rewrite_aggregate_event"),
|
|
1640
|
+
commitReceiptDigest: draft.draftDigest,
|
|
1641
|
+
});
|
|
1642
|
+
const rewriteGameplay = { state: plannedRewriteGameplay, effects: rewriteReadyEffects };
|
|
1643
|
+
const acceptedRewriteGameplayEffects = new Set(rewriteGameplay.effects);
|
|
1644
|
+
const worldEffectNodeIds = Object.freeze(rewriteEffectPlans.flatMap((plan) => plan.status === "ready"
|
|
1645
|
+
? plan.effects
|
|
1646
|
+
.filter((effect) => effect.capabilityId.startsWith("world."))
|
|
1647
|
+
.map((effect) => effect.effectId)
|
|
1648
|
+
: []));
|
|
1649
|
+
const preparedWorldMutation = worldEffectNodeIds.length === 0
|
|
1650
|
+
? null
|
|
1651
|
+
: await ports.effectCommit.prepareWorldMutation({
|
|
1652
|
+
epoch: binding.epoch,
|
|
1653
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1654
|
+
artifactDigest: binding.artifactDigest,
|
|
1655
|
+
event: aggregateRewriteEvent,
|
|
1656
|
+
turnId: request.turnId,
|
|
1657
|
+
operationId: request.operationId,
|
|
1658
|
+
worldEffectNodeIds,
|
|
1659
|
+
});
|
|
1660
|
+
const effectReceipt = await ports.effectCommit.stage({
|
|
1661
|
+
event: aggregateRewriteEvent,
|
|
1662
|
+
turnId: request.turnId,
|
|
1663
|
+
operationId: request.operationId,
|
|
1664
|
+
preparedWorldMutation,
|
|
1665
|
+
plans: Object.freeze(rewriteEffectPlans.map((plan) => Object.freeze({ ...plan, effects: Object.freeze(plan.effects.filter((effect) => acceptedRewriteGameplayEffects.has(effect))) }))),
|
|
1666
|
+
durableIntents: Object.freeze(durableIntents),
|
|
1667
|
+
});
|
|
1668
|
+
const terminalDigest = requireTmwSha256(effectReceipt.preActivationTerminalDigest, "rewrite_effect_terminal_digest");
|
|
1669
|
+
const [intentSetDigest, presentationIntentSetDigest] = await Promise.all([
|
|
1670
|
+
tmwSha256Hex(canonicalTmwJson(durableIntents)),
|
|
1671
|
+
tmwSha256Hex(canonicalTmwJson(presentationIntents)),
|
|
1672
|
+
]);
|
|
1673
|
+
const provisionalTimeline = Object.freeze({
|
|
1674
|
+
revisionId: replay.successorInputs.revisionId,
|
|
1675
|
+
parentRevisionId: base.activeTimelineRevision,
|
|
1676
|
+
revisionSequence: replay.successorInputs.revisionSequence,
|
|
1677
|
+
epoch: binding.epoch,
|
|
1678
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
1679
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1680
|
+
artifactDigest: binding.artifactDigest,
|
|
1681
|
+
sourceRevision: binding.sourceRevision,
|
|
1682
|
+
worldlineId: replay.successorInputs.worldlineId,
|
|
1683
|
+
operationId: request.operationId,
|
|
1684
|
+
ledgerHead: replay.projection.ledgerDigest,
|
|
1685
|
+
narrativeDigest: draft.narrativeDigest,
|
|
1686
|
+
savSectionDigest: "0".repeat(64),
|
|
1687
|
+
intentSetDigest,
|
|
1688
|
+
presentationIntentSetDigest,
|
|
1689
|
+
createdFrom: "temporal_rewrite",
|
|
1690
|
+
active: true,
|
|
1691
|
+
});
|
|
1692
|
+
const provisionalSav = Object.freeze({
|
|
1693
|
+
...base,
|
|
1694
|
+
activeTimelineRevision: provisionalTimeline.revisionId,
|
|
1695
|
+
activeTimeline: provisionalTimeline,
|
|
1696
|
+
turnId: request.turnId,
|
|
1697
|
+
preActivationTerminalDigest: terminalDigest,
|
|
1698
|
+
ledgerHead: replay.projection.ledgerDigest,
|
|
1699
|
+
eventLedger: ledger,
|
|
1700
|
+
narrative: draft.narrative,
|
|
1701
|
+
scene: rewrittenScene,
|
|
1702
|
+
gameplayState: persistentTmwGameplayStateV1(rewriteGameplay.state),
|
|
1703
|
+
scheduler,
|
|
1704
|
+
conditionState: materializeConditionState(rewritePositiveSemanticLatches, rewriteConditionState),
|
|
1705
|
+
durableIntents: Object.freeze(durableIntents),
|
|
1706
|
+
presentationIntents: Object.freeze(presentationIntents.map((intent) => canonicalTmwJsonValue(JSON.parse(canonicalTmwJson(intent)), "rewrite_presentation_intent"))),
|
|
1707
|
+
semanticVerdictRefs: Object.freeze([...new Set(rewriteSemanticRefs)]),
|
|
1708
|
+
candidateSetRevisions: base.candidateSetRevisions,
|
|
1709
|
+
operationReceiptDigests: Object.freeze([...new Set([
|
|
1710
|
+
...base.operationReceiptDigests,
|
|
1711
|
+
draft.draftDigest,
|
|
1712
|
+
replay.projection.ledgerDigest,
|
|
1713
|
+
replay.savCandidate.candidateDigest,
|
|
1714
|
+
replay.successorInputs.successorInputDigest,
|
|
1715
|
+
terminalDigest,
|
|
1716
|
+
...rewriteProviderReceiptDigests,
|
|
1717
|
+
...replay.savCandidate.compensationReceiptDigests,
|
|
1718
|
+
])]),
|
|
1719
|
+
completedOperationIds: Object.freeze([...new Set([
|
|
1720
|
+
...(base.completedOperationIds ?? []),
|
|
1721
|
+
request.operationId,
|
|
1722
|
+
])]),
|
|
1723
|
+
worldMutationRecoveryOperations: Object.freeze([
|
|
1724
|
+
...base.worldMutationRecoveryOperations,
|
|
1725
|
+
...(effectReceipt.worldMutationRecoveryOperation === null
|
|
1726
|
+
? []
|
|
1727
|
+
: [effectReceipt.worldMutationRecoveryOperation]),
|
|
1728
|
+
]),
|
|
1729
|
+
});
|
|
1730
|
+
const savSectionDigest = await runtimeSavPayloadDigest(provisionalSav);
|
|
1731
|
+
const savPayload = Object.freeze({
|
|
1732
|
+
...provisionalSav,
|
|
1733
|
+
activeTimeline: Object.freeze({
|
|
1734
|
+
...provisionalTimeline,
|
|
1735
|
+
savSectionDigest,
|
|
1736
|
+
}),
|
|
1737
|
+
});
|
|
1738
|
+
const savSection = await encodeRuntimeSavSection(savPayload);
|
|
1739
|
+
const candidate = Object.freeze({
|
|
1740
|
+
operationId: request.operationId,
|
|
1741
|
+
epoch: binding.epoch,
|
|
1742
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
1743
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1744
|
+
artifactDigest: binding.artifactDigest,
|
|
1745
|
+
sourceRevision: binding.sourceRevision,
|
|
1746
|
+
expectedHead: Object.freeze({
|
|
1747
|
+
revisionId: base.activeTimeline.revisionId,
|
|
1748
|
+
revisionSequence: base.activeTimeline.revisionSequence,
|
|
1749
|
+
ledgerHead: base.ledgerHead,
|
|
1750
|
+
}),
|
|
1751
|
+
timeline: savPayload.activeTimeline,
|
|
1752
|
+
ledger,
|
|
1753
|
+
ledgerHead: savPayload.ledgerHead,
|
|
1754
|
+
narrative: savPayload.narrative,
|
|
1755
|
+
narrativeDigest: draft.narrativeDigest,
|
|
1756
|
+
savSection,
|
|
1757
|
+
savSectionDigest,
|
|
1758
|
+
intents: savPayload.durableIntents,
|
|
1759
|
+
intentSetDigest,
|
|
1760
|
+
presentationIntents: savPayload.presentationIntents,
|
|
1761
|
+
presentationIntentSetDigest,
|
|
1762
|
+
preActivationTerminalDigest: terminalDigest,
|
|
1763
|
+
});
|
|
1764
|
+
let activation;
|
|
1765
|
+
try {
|
|
1766
|
+
activation = await activateTmwCandidate(candidate, ports.activation);
|
|
1767
|
+
}
|
|
1768
|
+
catch (error) {
|
|
1769
|
+
await ports.effectCommit.rollbackWorldMutation({
|
|
1770
|
+
operationId: request.operationId,
|
|
1771
|
+
preparedWorldMutation,
|
|
1772
|
+
reason: "activation_failed",
|
|
1773
|
+
});
|
|
1774
|
+
throw error;
|
|
1775
|
+
}
|
|
1776
|
+
if (activation.status !== "committed" && activation.status !== "already_committed") {
|
|
1777
|
+
await ports.effectCommit.rollbackWorldMutation({
|
|
1778
|
+
operationId: request.operationId,
|
|
1779
|
+
preparedWorldMutation,
|
|
1780
|
+
reason: "activation_conflict",
|
|
1781
|
+
});
|
|
1782
|
+
throw new Error("tmw_rewrite_activation_conflict");
|
|
1783
|
+
}
|
|
1784
|
+
await ports.effectCommit.finalizeWorldMutation({
|
|
1785
|
+
operationId: request.operationId,
|
|
1786
|
+
preparedWorldMutation,
|
|
1787
|
+
});
|
|
1788
|
+
restoredSav = savPayload;
|
|
1789
|
+
fabric = new EventFabric({
|
|
1790
|
+
epoch: binding.epoch,
|
|
1791
|
+
runtimeContractHash: binding.runtimeContractHash,
|
|
1792
|
+
artifactDigest: binding.artifactDigest,
|
|
1793
|
+
timelineRevision: savPayload.activeTimelineRevision,
|
|
1794
|
+
});
|
|
1795
|
+
fabric.restoreLedgerHead(savPayload.ledgerHead);
|
|
1796
|
+
return Object.freeze({ draft, activation, candidate });
|
|
1797
|
+
}
|
|
1798
|
+
finally {
|
|
1799
|
+
releaseExecution();
|
|
1800
|
+
}
|
|
1801
|
+
},
|
|
1802
|
+
restore: (payload) => {
|
|
1803
|
+
assertActive();
|
|
1804
|
+
if (eventAccepted || restoredSav !== null)
|
|
1805
|
+
throw new Error("tmw_runtime_restore_barrier_closed");
|
|
1806
|
+
const canonical = canonicalRuntimeSavPayload(payload);
|
|
1807
|
+
if (canonical.epoch !== binding.epoch)
|
|
1808
|
+
throw new Error("stale_content_epoch");
|
|
1809
|
+
if (canonical.runtimeContractHash !== binding.runtimeContractHash) {
|
|
1810
|
+
throw new Error("runtime_contract_hash_mismatch");
|
|
1811
|
+
}
|
|
1812
|
+
if (canonical.artifactDigest !== binding.artifactDigest
|
|
1813
|
+
|| canonical.sourceRevision !== binding.sourceRevision
|
|
1814
|
+
|| canonical.activeTimelineRevision !== binding.timelineRevision) {
|
|
1815
|
+
throw new Error("tmw_runtime_restore_binding_mismatch");
|
|
1816
|
+
}
|
|
1817
|
+
fabric.restoreLedgerHead(canonical.ledgerHead);
|
|
1818
|
+
restoredSav = canonical;
|
|
1819
|
+
},
|
|
1820
|
+
restoredSav: () => restoredSav,
|
|
1821
|
+
materializeSav: (savInput) => {
|
|
1822
|
+
assertActive();
|
|
1823
|
+
const base = restoredSav;
|
|
1824
|
+
if (base === null)
|
|
1825
|
+
throw new Error("tmw_runtime_sav_state_missing");
|
|
1826
|
+
return Object.freeze({
|
|
1827
|
+
...base,
|
|
1828
|
+
turnId: requireTmwIdentifier(savInput.turnId, "runtime_sav_turn_id"),
|
|
1829
|
+
preActivationTerminalDigest: requireTmwSha256(savInput.preActivationTerminalDigest, "runtime_sav_pre_activation_terminal_digest"),
|
|
1830
|
+
ledgerHead: fabric.snapshot().ledgerHead,
|
|
1831
|
+
});
|
|
1832
|
+
},
|
|
1833
|
+
viewEventCommitContext: (eventTypeInput, observation) => {
|
|
1834
|
+
assertActive();
|
|
1835
|
+
const eventType = requireTmwIdentifier(eventTypeInput, "runtime_view_event_type");
|
|
1836
|
+
const matches = input.compiledPlan.nodes.filter((node) => {
|
|
1837
|
+
if (node.kind !== "event")
|
|
1838
|
+
return false;
|
|
1839
|
+
return record(node.canonical_payload, "compiled_event").event_type === eventType;
|
|
1840
|
+
});
|
|
1841
|
+
if (matches.length !== 1)
|
|
1842
|
+
throw new Error("tmw_runtime_event_node_ambiguous");
|
|
1843
|
+
const node = matches[0];
|
|
1844
|
+
if (observation.crossingDirection !== "forward"
|
|
1845
|
+
&& observation.crossingDirection !== "backward") {
|
|
1846
|
+
throw new Error("tmw_runtime_clock_crossing_direction_invalid");
|
|
1847
|
+
}
|
|
1848
|
+
if (!crossingPolicyAllows(node.effective_time.crossing_policy, observation.crossingDirection)) {
|
|
1849
|
+
throw new Error("tmw_runtime_clock_crossing_direction_forbidden");
|
|
1850
|
+
}
|
|
1851
|
+
const coordinates = canonicalEventCoordinates(observation.coordinates);
|
|
1852
|
+
const observed = observedCoordinate(coordinates, node.effective_time);
|
|
1853
|
+
if (observed === null)
|
|
1854
|
+
throw new Error("tmw_runtime_observed_clock_missing");
|
|
1855
|
+
const previous = schedulerObservedCoordinate(restoredSav?.scheduler.cursor, node.effective_time);
|
|
1856
|
+
if (previous !== null
|
|
1857
|
+
&& ((observation.crossingDirection === "forward" && observed < previous)
|
|
1858
|
+
|| (observation.crossingDirection === "backward" && observed > previous))) {
|
|
1859
|
+
throw new Error("tmw_runtime_clock_crossing_direction_mismatch");
|
|
1860
|
+
}
|
|
1861
|
+
const effectiveTime = observeTmwEffectiveTime(node.effective_time, observed);
|
|
1862
|
+
return Object.freeze({
|
|
1863
|
+
timelineRevision: restoredSav?.activeTimelineRevision ?? binding.timelineRevision,
|
|
1864
|
+
hardFact: compiledEventHardFact(eventType),
|
|
1865
|
+
effectiveTime,
|
|
1866
|
+
coordinates,
|
|
1867
|
+
crossingDirection: observation.crossingDirection,
|
|
1868
|
+
});
|
|
1869
|
+
},
|
|
1870
|
+
evaluate: (evaluation) => {
|
|
1871
|
+
assertActive();
|
|
1872
|
+
const condition = evaluateConditionGraph(evaluation.graph, evaluation.snapshot);
|
|
1873
|
+
const effectPlan = planEffectFlow({
|
|
1874
|
+
...evaluation.effectFlow,
|
|
1875
|
+
conditionTruth: condition.truth,
|
|
1876
|
+
}, capabilities);
|
|
1877
|
+
return Object.freeze({ condition, effectPlan });
|
|
1878
|
+
},
|
|
1879
|
+
snapshot: () => {
|
|
1880
|
+
assertActive();
|
|
1881
|
+
return fabric.snapshot();
|
|
1882
|
+
},
|
|
1883
|
+
dispose: () => {
|
|
1884
|
+
disposed = true;
|
|
1885
|
+
},
|
|
1886
|
+
});
|
|
1887
|
+
}
|