@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
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { TMW_RUNTIME_GENERATION } from "./runtime-contract.js";
|
|
2
|
+
import { canonicalTmwJsonValue, requireTmwIdentifier, requireTmwSafeInteger, requireTmwSha256, } from "./runtime-trace.js";
|
|
3
|
+
const ALLOWED_TRANSITIONS = Object.freeze({
|
|
4
|
+
active: new Set(["blocked", "applying", "cancelled", "superseded", "invalidated", "failed"]),
|
|
5
|
+
blocked: new Set(["active", "applying", "cancelled", "superseded", "invalidated", "failed"]),
|
|
6
|
+
applying: new Set(["blocked", "satisfied", "cancelled", "superseded", "invalidated", "failed"]),
|
|
7
|
+
satisfied: new Set(),
|
|
8
|
+
cancelled: new Set(),
|
|
9
|
+
superseded: new Set(),
|
|
10
|
+
invalidated: new Set(),
|
|
11
|
+
failed: new Set(),
|
|
12
|
+
});
|
|
13
|
+
export function canonicalDurableIntent(input) {
|
|
14
|
+
if (input.runtimeGeneration !== 3)
|
|
15
|
+
throw new Error("unsupported_runtime_generation");
|
|
16
|
+
if (!(input.state in ALLOWED_TRANSITIONS))
|
|
17
|
+
throw new Error("tmw_intent_state_invalid");
|
|
18
|
+
return Object.freeze({
|
|
19
|
+
intentId: requireTmwIdentifier(input.intentId, "intent_id"),
|
|
20
|
+
epoch: requireTmwSafeInteger(input.epoch, "intent_epoch", 1),
|
|
21
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
22
|
+
runtimeContractHash: requireTmwSha256(input.runtimeContractHash, "intent_contract_hash"),
|
|
23
|
+
artifactDigest: requireTmwSha256(input.artifactDigest, "intent_artifact_digest"),
|
|
24
|
+
sourceRevision: requireTmwIdentifier(input.sourceRevision, "intent_source_revision"),
|
|
25
|
+
timelineRevision: requireTmwIdentifier(input.timelineRevision, "intent_timeline_revision"),
|
|
26
|
+
turnId: requireTmwIdentifier(input.turnId, "intent_turn_id"),
|
|
27
|
+
operationId: requireTmwIdentifier(input.operationId, "intent_operation_id"),
|
|
28
|
+
capabilityId: requireTmwIdentifier(input.capabilityId, "intent_capability_id"),
|
|
29
|
+
idempotencyKey: requireTmwIdentifier(input.idempotencyKey, "intent_idempotency_key"),
|
|
30
|
+
conflictKey: requireTmwIdentifier(input.conflictKey, "intent_conflict_key"),
|
|
31
|
+
priority: requireTmwSafeInteger(input.priority, "intent_priority"),
|
|
32
|
+
state: input.state,
|
|
33
|
+
selectorSnapshotHash: requireTmwSha256(input.selectorSnapshotHash, "intent_selector_snapshot_hash"),
|
|
34
|
+
payload: canonicalTmwJsonValue(input.payload, "intent_payload"),
|
|
35
|
+
lastReceiptDigest: input.lastReceiptDigest === null
|
|
36
|
+
? null
|
|
37
|
+
: requireTmwSha256(input.lastReceiptDigest, "intent_receipt_digest"),
|
|
38
|
+
reasonCode: input.reasonCode === null
|
|
39
|
+
? null
|
|
40
|
+
: requireTmwIdentifier(input.reasonCode, "intent_reason_code"),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export function transitionDurableIntent(currentInput, next) {
|
|
44
|
+
const current = canonicalDurableIntent(currentInput);
|
|
45
|
+
if (next.state === current.state) {
|
|
46
|
+
if (next.lastReceiptDigest !== current.lastReceiptDigest || next.reasonCode !== current.reasonCode) {
|
|
47
|
+
throw new Error("tmw_intent_same_state_rewrite_forbidden");
|
|
48
|
+
}
|
|
49
|
+
return current;
|
|
50
|
+
}
|
|
51
|
+
if (!ALLOWED_TRANSITIONS[current.state].has(next.state)) {
|
|
52
|
+
throw new Error("tmw_intent_transition_forbidden");
|
|
53
|
+
}
|
|
54
|
+
return canonicalDurableIntent({
|
|
55
|
+
...current,
|
|
56
|
+
state: next.state,
|
|
57
|
+
lastReceiptDigest: next.lastReceiptDigest,
|
|
58
|
+
reasonCode: next.reasonCode,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type TmwJsonValue } from "./runtime-trace.js";
|
|
2
|
+
import type { SelectorAstV3 } from "./selector-ast.js";
|
|
3
|
+
import type { EffectEffectiveTime } from "./time-domains.js";
|
|
4
|
+
export type EffectExecutionPolicy = "attempt_once" | "ensure_until";
|
|
5
|
+
export type EffectRewritePolicy = "normal_only" | "rewrite_only" | "normal_and_rewrite";
|
|
6
|
+
export type EffectMergePolicy = "replace" | "max" | "min" | "union" | "intersection" | "reject";
|
|
7
|
+
export interface CapabilityDescriptorView {
|
|
8
|
+
readonly capabilityId: string;
|
|
9
|
+
readonly profileIds: ReadonlyArray<string>;
|
|
10
|
+
readonly executorKind: "command" | "reconciler";
|
|
11
|
+
readonly conflictKey: string;
|
|
12
|
+
readonly mergePolicy: EffectMergePolicy;
|
|
13
|
+
readonly allowsExternalSideEffect: boolean;
|
|
14
|
+
readonly selectorRequired: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface CapabilityRegistryView {
|
|
17
|
+
resolve(capabilityId: string): CapabilityDescriptorView | null;
|
|
18
|
+
}
|
|
19
|
+
export interface EffectInvocation {
|
|
20
|
+
readonly effectId: string;
|
|
21
|
+
readonly capabilityId: string;
|
|
22
|
+
readonly selector: SelectorAstV3 | null;
|
|
23
|
+
readonly effectiveTime: EffectEffectiveTime;
|
|
24
|
+
readonly input: TmwJsonValue;
|
|
25
|
+
readonly executionPolicy: EffectExecutionPolicy;
|
|
26
|
+
readonly rewritePolicy: EffectRewritePolicy;
|
|
27
|
+
readonly priority: number;
|
|
28
|
+
readonly idempotencyKey: string;
|
|
29
|
+
}
|
|
30
|
+
export interface EffectFlowInput {
|
|
31
|
+
readonly epoch: number;
|
|
32
|
+
readonly runtimeContractHash: string;
|
|
33
|
+
readonly artifactDigest: string;
|
|
34
|
+
readonly sourceRevision: string;
|
|
35
|
+
readonly timelineRevision: string;
|
|
36
|
+
readonly turnId: string;
|
|
37
|
+
readonly capabilityProfileId: string;
|
|
38
|
+
readonly mode: "normal" | "rewrite";
|
|
39
|
+
readonly conditionTruth: "true" | "false" | "unknown";
|
|
40
|
+
readonly effects: ReadonlyArray<EffectInvocation>;
|
|
41
|
+
readonly maxEffects: number;
|
|
42
|
+
}
|
|
43
|
+
export interface PlannedEffect {
|
|
44
|
+
readonly planIndex: number;
|
|
45
|
+
readonly effectId: string;
|
|
46
|
+
readonly capabilityId: string;
|
|
47
|
+
readonly selector: SelectorAstV3 | null;
|
|
48
|
+
readonly effectiveTime: EffectEffectiveTime;
|
|
49
|
+
readonly input: TmwJsonValue;
|
|
50
|
+
readonly executionPolicy: EffectExecutionPolicy;
|
|
51
|
+
readonly rewritePolicy: EffectRewritePolicy;
|
|
52
|
+
readonly priority: number;
|
|
53
|
+
readonly idempotencyKey: string;
|
|
54
|
+
readonly conflictKey: string;
|
|
55
|
+
readonly mergePolicy: EffectMergePolicy;
|
|
56
|
+
readonly externalSideEffect: boolean;
|
|
57
|
+
}
|
|
58
|
+
export interface EffectPlan {
|
|
59
|
+
readonly status: "ready" | "skipped" | "suspended";
|
|
60
|
+
readonly reason: "condition_true" | "condition_false" | "condition_unknown";
|
|
61
|
+
readonly epoch: number;
|
|
62
|
+
readonly runtimeContractHash: string;
|
|
63
|
+
readonly artifactDigest: string;
|
|
64
|
+
readonly sourceRevision: string;
|
|
65
|
+
readonly timelineRevision: string;
|
|
66
|
+
readonly turnId: string;
|
|
67
|
+
readonly effects: ReadonlyArray<PlannedEffect>;
|
|
68
|
+
}
|
|
69
|
+
export declare function planEffectFlow(input: EffectFlowInput, registry: CapabilityRegistryView): EffectPlan;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { canonicalTmwJsonValue, requireTmwIdentifier, requireTmwSafeInteger, requireTmwSha256, } from "./runtime-trace.js";
|
|
2
|
+
function policyAllows(policy, mode) {
|
|
3
|
+
return policy === "normal_and_rewrite"
|
|
4
|
+
|| (policy === "normal_only" && mode === "normal")
|
|
5
|
+
|| (policy === "rewrite_only" && mode === "rewrite");
|
|
6
|
+
}
|
|
7
|
+
function requireExecutionPolicy(value) {
|
|
8
|
+
if (value !== "attempt_once" && value !== "ensure_until") {
|
|
9
|
+
throw new Error("tmw_effect_execution_policy_invalid");
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function requireRewritePolicy(value) {
|
|
14
|
+
if (value !== "normal_only" && value !== "rewrite_only" && value !== "normal_and_rewrite") {
|
|
15
|
+
throw new Error("tmw_effect_rewrite_policy_invalid");
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
export function planEffectFlow(input, registry) {
|
|
20
|
+
const binding = {
|
|
21
|
+
epoch: requireTmwSafeInteger(input.epoch, "effect_epoch", 1),
|
|
22
|
+
runtimeContractHash: requireTmwSha256(input.runtimeContractHash, "effect_contract_hash"),
|
|
23
|
+
artifactDigest: requireTmwSha256(input.artifactDigest, "effect_artifact_digest"),
|
|
24
|
+
sourceRevision: requireTmwIdentifier(input.sourceRevision, "effect_source_revision"),
|
|
25
|
+
timelineRevision: requireTmwIdentifier(input.timelineRevision, "effect_timeline_revision"),
|
|
26
|
+
turnId: requireTmwIdentifier(input.turnId, "effect_turn_id"),
|
|
27
|
+
};
|
|
28
|
+
const maxEffects = requireTmwSafeInteger(input.maxEffects, "effect_max_effects", 1);
|
|
29
|
+
if (input.effects.length > maxEffects)
|
|
30
|
+
throw new Error("tmw_effect_budget_exhausted");
|
|
31
|
+
if (input.conditionTruth !== "true") {
|
|
32
|
+
return Object.freeze({
|
|
33
|
+
status: input.conditionTruth === "unknown" ? "suspended" : "skipped",
|
|
34
|
+
reason: input.conditionTruth === "unknown"
|
|
35
|
+
? "condition_unknown"
|
|
36
|
+
: "condition_false",
|
|
37
|
+
...binding,
|
|
38
|
+
effects: Object.freeze([]),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const profileId = requireTmwIdentifier(input.capabilityProfileId, "effect_profile_id");
|
|
42
|
+
const seenIds = new Set();
|
|
43
|
+
const seenIdempotency = new Set();
|
|
44
|
+
const planned = [];
|
|
45
|
+
for (const effect of input.effects) {
|
|
46
|
+
const effectId = requireTmwIdentifier(effect.effectId, "effect_id");
|
|
47
|
+
if (seenIds.has(effectId))
|
|
48
|
+
throw new Error("tmw_effect_id_duplicate");
|
|
49
|
+
seenIds.add(effectId);
|
|
50
|
+
const idempotencyKey = requireTmwIdentifier(effect.idempotencyKey, "effect_idempotency_key");
|
|
51
|
+
if (seenIdempotency.has(idempotencyKey))
|
|
52
|
+
throw new Error("tmw_effect_idempotency_duplicate");
|
|
53
|
+
seenIdempotency.add(idempotencyKey);
|
|
54
|
+
const executionPolicy = requireExecutionPolicy(effect.executionPolicy);
|
|
55
|
+
const rewritePolicy = requireRewritePolicy(effect.rewritePolicy);
|
|
56
|
+
const priority = requireTmwSafeInteger(effect.priority, "effect_priority");
|
|
57
|
+
const canonicalInput = canonicalTmwJsonValue(effect.input, "effect_input");
|
|
58
|
+
if (!policyAllows(rewritePolicy, input.mode))
|
|
59
|
+
continue;
|
|
60
|
+
const capabilityId = requireTmwIdentifier(effect.capabilityId, "effect_capability_id");
|
|
61
|
+
const capability = registry.resolve(capabilityId);
|
|
62
|
+
if (capability === null)
|
|
63
|
+
throw new Error("tmw_effect_capability_unknown");
|
|
64
|
+
if (capability.capabilityId !== capabilityId)
|
|
65
|
+
throw new Error("tmw_effect_capability_identity_mismatch");
|
|
66
|
+
if (!capability.profileIds.includes(profileId))
|
|
67
|
+
throw new Error("tmw_effect_capability_forbidden");
|
|
68
|
+
if (capability.selectorRequired && effect.selector === null) {
|
|
69
|
+
throw new Error("tmw_effect_selector_required");
|
|
70
|
+
}
|
|
71
|
+
if (executionPolicy === "ensure_until" && capability.executorKind !== "reconciler") {
|
|
72
|
+
throw new Error("tmw_effect_ensure_until_requires_reconciler");
|
|
73
|
+
}
|
|
74
|
+
planned.push(Object.freeze({
|
|
75
|
+
planIndex: planned.length,
|
|
76
|
+
effectId,
|
|
77
|
+
capabilityId,
|
|
78
|
+
selector: effect.selector,
|
|
79
|
+
effectiveTime: effect.effectiveTime,
|
|
80
|
+
input: canonicalInput,
|
|
81
|
+
executionPolicy,
|
|
82
|
+
rewritePolicy,
|
|
83
|
+
priority,
|
|
84
|
+
idempotencyKey,
|
|
85
|
+
conflictKey: requireTmwIdentifier(capability.conflictKey, "effect_conflict_key"),
|
|
86
|
+
mergePolicy: capability.mergePolicy,
|
|
87
|
+
externalSideEffect: capability.allowsExternalSideEffect,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
planned.sort((left, right) => right.priority - left.priority
|
|
91
|
+
|| (left.effectId < right.effectId ? -1 : left.effectId > right.effectId ? 1 : 0));
|
|
92
|
+
return Object.freeze({
|
|
93
|
+
status: "ready",
|
|
94
|
+
reason: "condition_true",
|
|
95
|
+
...binding,
|
|
96
|
+
effects: Object.freeze(planned.map((effect, index) => Object.freeze({ ...effect, planIndex: index }))),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { TMW_RUNTIME_GENERATION } from "./runtime-contract.js";
|
|
2
|
+
import { type RuntimeTraceSnapshot, type TmwJsonValue } from "./runtime-trace.js";
|
|
3
|
+
import { type CrossingDirection, type EventEffectiveTime, type RealTimeClock } from "./time-domains.js";
|
|
4
|
+
export { crossingPolicyAllows } from "./time-domains.js";
|
|
5
|
+
export type { CrossingDirection, CrossingPolicy, RealTimeClock, TmwClockDomain, WallClockPolicy, } from "./time-domains.js";
|
|
6
|
+
export interface EventScope {
|
|
7
|
+
readonly scenarioId: string;
|
|
8
|
+
readonly saveId: string;
|
|
9
|
+
readonly worldlineId: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PresentationCoordinate {
|
|
12
|
+
readonly planId: string;
|
|
13
|
+
readonly segmentId: string;
|
|
14
|
+
readonly cursor: number;
|
|
15
|
+
readonly eventSeq: number;
|
|
16
|
+
}
|
|
17
|
+
export interface RealTimeCoordinate {
|
|
18
|
+
readonly clock: RealTimeClock;
|
|
19
|
+
readonly value: number;
|
|
20
|
+
}
|
|
21
|
+
export interface EventTimeCoordinates {
|
|
22
|
+
readonly logicalSeq: number;
|
|
23
|
+
readonly worldTime: number | null;
|
|
24
|
+
readonly presentation: PresentationCoordinate | null;
|
|
25
|
+
readonly realTime: RealTimeCoordinate | null;
|
|
26
|
+
}
|
|
27
|
+
export interface CommittedEventEnvelope {
|
|
28
|
+
readonly eventId: string;
|
|
29
|
+
readonly eventType: string;
|
|
30
|
+
readonly hardFact: boolean;
|
|
31
|
+
readonly epoch: number;
|
|
32
|
+
readonly runtimeGeneration: typeof TMW_RUNTIME_GENERATION;
|
|
33
|
+
readonly runtimeContractHash: string;
|
|
34
|
+
readonly artifactDigest: string;
|
|
35
|
+
readonly sourceRevision: string;
|
|
36
|
+
readonly timelineRevision: string;
|
|
37
|
+
readonly scope: EventScope;
|
|
38
|
+
readonly coordinates: EventTimeCoordinates;
|
|
39
|
+
readonly effectiveTime: EventEffectiveTime;
|
|
40
|
+
readonly crossingDirection: CrossingDirection;
|
|
41
|
+
readonly correlationId: string;
|
|
42
|
+
readonly causationId: string | null;
|
|
43
|
+
readonly replacementLineage: ReadonlyArray<string>;
|
|
44
|
+
readonly payloadHash: string;
|
|
45
|
+
readonly payload: TmwJsonValue;
|
|
46
|
+
readonly commitReceiptDigest: string;
|
|
47
|
+
}
|
|
48
|
+
export interface EventFabricAppendReceipt {
|
|
49
|
+
readonly status: "appended" | "duplicate";
|
|
50
|
+
readonly ledgerSequence: number;
|
|
51
|
+
readonly eventId: string;
|
|
52
|
+
readonly dedupeKey: string;
|
|
53
|
+
readonly ledgerHead: string;
|
|
54
|
+
}
|
|
55
|
+
export declare const TMW_EMPTY_LEDGER_HEAD: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
56
|
+
export interface EventFabricSnapshot {
|
|
57
|
+
readonly epoch: number;
|
|
58
|
+
readonly runtimeContractHash: string;
|
|
59
|
+
readonly artifactDigest: string;
|
|
60
|
+
readonly timelineRevision: string;
|
|
61
|
+
readonly ledgerHead: string;
|
|
62
|
+
readonly events: ReadonlyArray<CommittedEventEnvelope>;
|
|
63
|
+
readonly trace: RuntimeTraceSnapshot;
|
|
64
|
+
}
|
|
65
|
+
export declare function canonicalCommittedEvent(input: CommittedEventEnvelope): CommittedEventEnvelope;
|
|
66
|
+
export declare function eventFabricDedupeKey(event: CommittedEventEnvelope): string;
|
|
67
|
+
export declare function compareCommittedEvents(left: CommittedEventEnvelope, right: CommittedEventEnvelope): number;
|
|
68
|
+
export declare class EventFabric {
|
|
69
|
+
#private;
|
|
70
|
+
constructor(input: Readonly<{
|
|
71
|
+
epoch: number;
|
|
72
|
+
runtimeContractHash: string;
|
|
73
|
+
artifactDigest: string;
|
|
74
|
+
timelineRevision: string;
|
|
75
|
+
traceLimit?: number;
|
|
76
|
+
}>);
|
|
77
|
+
appendCommitted(input: CommittedEventEnvelope): Promise<EventFabricAppendReceipt>;
|
|
78
|
+
restoreLedgerHead(ledgerHeadInput: string): void;
|
|
79
|
+
orderedEvents(): ReadonlyArray<CommittedEventEnvelope>;
|
|
80
|
+
snapshot(): EventFabricSnapshot;
|
|
81
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { TMW_RUNTIME_GENERATION } from "./runtime-contract.js";
|
|
2
|
+
import { BoundedRuntimeTrace, canonicalTmwJson, canonicalTmwJsonValue, requireTmwIdentifier, requireTmwSafeInteger, requireTmwSha256, tmwSha256Hex, } from "./runtime-trace.js";
|
|
3
|
+
import { canonicalTmwEffectiveTime, compareTmwEffectiveTime, crossingPolicyAllows, } from "./time-domains.js";
|
|
4
|
+
export { crossingPolicyAllows } from "./time-domains.js";
|
|
5
|
+
export const TMW_EMPTY_LEDGER_HEAD = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
6
|
+
function requireFiniteCoordinate(value, label) {
|
|
7
|
+
if (!Number.isFinite(value))
|
|
8
|
+
throw new Error(`tmw_event_${label}_invalid`);
|
|
9
|
+
return Object.is(value, -0) ? 0 : value;
|
|
10
|
+
}
|
|
11
|
+
function canonicalScope(scope) {
|
|
12
|
+
return Object.freeze({
|
|
13
|
+
scenarioId: requireTmwIdentifier(scope.scenarioId, "event_scenario_id"),
|
|
14
|
+
saveId: requireTmwIdentifier(scope.saveId, "event_save_id"),
|
|
15
|
+
worldlineId: requireTmwIdentifier(scope.worldlineId, "event_worldline_id"),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function canonicalPresentation(value) {
|
|
19
|
+
if (value === null)
|
|
20
|
+
return null;
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
planId: requireTmwIdentifier(value.planId, "event_presentation_plan_id"),
|
|
23
|
+
segmentId: requireTmwIdentifier(value.segmentId, "event_presentation_segment_id"),
|
|
24
|
+
cursor: requireTmwSafeInteger(value.cursor, "event_presentation_cursor"),
|
|
25
|
+
eventSeq: requireTmwSafeInteger(value.eventSeq, "event_presentation_event_seq"),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function canonicalRealTime(value) {
|
|
29
|
+
if (value === null)
|
|
30
|
+
return null;
|
|
31
|
+
if (!["monotonic_session", "active_play", "wall_clock"].includes(value.clock)) {
|
|
32
|
+
throw new Error("tmw_event_real_time_clock_invalid");
|
|
33
|
+
}
|
|
34
|
+
return Object.freeze({ clock: value.clock, value: requireFiniteCoordinate(value.value, "real_time") });
|
|
35
|
+
}
|
|
36
|
+
export function canonicalCommittedEvent(input) {
|
|
37
|
+
if (input.runtimeGeneration !== 3)
|
|
38
|
+
throw new Error("unsupported_runtime_generation");
|
|
39
|
+
if (input.crossingDirection !== "forward" && input.crossingDirection !== "backward") {
|
|
40
|
+
throw new Error("tmw_event_crossing_direction_invalid");
|
|
41
|
+
}
|
|
42
|
+
const effectiveTime = canonicalTmwEffectiveTime(input.effectiveTime);
|
|
43
|
+
if (!crossingPolicyAllows(effectiveTime.crossing_policy, input.crossingDirection)) {
|
|
44
|
+
throw new Error("tmw_event_crossing_direction_forbidden");
|
|
45
|
+
}
|
|
46
|
+
const replacementLineage = Object.freeze(input.replacementLineage.map((id) => requireTmwIdentifier(id, "event_replacement_lineage")));
|
|
47
|
+
if (new Set(replacementLineage).size !== replacementLineage.length) {
|
|
48
|
+
throw new Error("tmw_event_replacement_lineage_duplicate");
|
|
49
|
+
}
|
|
50
|
+
const payload = canonicalTmwJsonValue(input.payload, "event_payload");
|
|
51
|
+
const coordinates = Object.freeze({
|
|
52
|
+
logicalSeq: requireTmwSafeInteger(input.coordinates.logicalSeq, "event_logical_seq"),
|
|
53
|
+
worldTime: input.coordinates.worldTime === null
|
|
54
|
+
? null
|
|
55
|
+
: requireFiniteCoordinate(input.coordinates.worldTime, "world_time"),
|
|
56
|
+
presentation: canonicalPresentation(input.coordinates.presentation),
|
|
57
|
+
realTime: canonicalRealTime(input.coordinates.realTime),
|
|
58
|
+
});
|
|
59
|
+
const observedEffectiveValue = effectiveTime.domain === "logical_seq"
|
|
60
|
+
? coordinates.logicalSeq
|
|
61
|
+
: effectiveTime.domain === "world_time"
|
|
62
|
+
? coordinates.worldTime
|
|
63
|
+
: effectiveTime.domain === "presentation_seq"
|
|
64
|
+
? coordinates.presentation?.eventSeq ?? null
|
|
65
|
+
: coordinates.realTime?.clock === effectiveTime.real_time_clock
|
|
66
|
+
? coordinates.realTime.value
|
|
67
|
+
: null;
|
|
68
|
+
if (observedEffectiveValue === null || observedEffectiveValue !== effectiveTime.effective_value) {
|
|
69
|
+
throw new Error("tmw_event_effective_coordinate_mismatch");
|
|
70
|
+
}
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
eventId: requireTmwIdentifier(input.eventId, "event_id"),
|
|
73
|
+
eventType: requireTmwIdentifier(input.eventType, "event_type"),
|
|
74
|
+
hardFact: (() => {
|
|
75
|
+
if (typeof input.hardFact !== "boolean")
|
|
76
|
+
throw new Error("tmw_event_hard_fact_invalid");
|
|
77
|
+
return input.hardFact;
|
|
78
|
+
})(),
|
|
79
|
+
epoch: requireTmwSafeInteger(input.epoch, "event_epoch", 1),
|
|
80
|
+
runtimeGeneration: TMW_RUNTIME_GENERATION,
|
|
81
|
+
runtimeContractHash: requireTmwSha256(input.runtimeContractHash, "event_contract_hash"),
|
|
82
|
+
artifactDigest: requireTmwSha256(input.artifactDigest, "event_artifact_digest"),
|
|
83
|
+
sourceRevision: requireTmwIdentifier(input.sourceRevision, "event_source_revision"),
|
|
84
|
+
timelineRevision: requireTmwIdentifier(input.timelineRevision, "event_timeline_revision"),
|
|
85
|
+
scope: canonicalScope(input.scope),
|
|
86
|
+
coordinates,
|
|
87
|
+
effectiveTime,
|
|
88
|
+
crossingDirection: input.crossingDirection,
|
|
89
|
+
correlationId: requireTmwIdentifier(input.correlationId, "event_correlation_id"),
|
|
90
|
+
causationId: input.causationId === null
|
|
91
|
+
? null
|
|
92
|
+
: requireTmwIdentifier(input.causationId, "event_causation_id"),
|
|
93
|
+
replacementLineage,
|
|
94
|
+
payloadHash: requireTmwSha256(input.payloadHash, "event_payload_hash"),
|
|
95
|
+
payload,
|
|
96
|
+
commitReceiptDigest: requireTmwSha256(input.commitReceiptDigest, "event_commit_receipt_digest"),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export function eventFabricDedupeKey(event) {
|
|
100
|
+
return [
|
|
101
|
+
event.epoch,
|
|
102
|
+
event.timelineRevision,
|
|
103
|
+
event.sourceRevision,
|
|
104
|
+
event.eventId,
|
|
105
|
+
event.replacementLineage.join(","),
|
|
106
|
+
].join(":");
|
|
107
|
+
}
|
|
108
|
+
export function compareCommittedEvents(left, right) {
|
|
109
|
+
if (left.coordinates.logicalSeq !== right.coordinates.logicalSeq) {
|
|
110
|
+
return left.coordinates.logicalSeq - right.coordinates.logicalSeq;
|
|
111
|
+
}
|
|
112
|
+
const effectiveOrder = compareTmwEffectiveTime(left.effectiveTime, right.effectiveTime);
|
|
113
|
+
if (effectiveOrder !== 0)
|
|
114
|
+
return effectiveOrder;
|
|
115
|
+
if (left.eventId !== right.eventId)
|
|
116
|
+
return left.eventId < right.eventId ? -1 : 1;
|
|
117
|
+
if (left.payloadHash !== right.payloadHash)
|
|
118
|
+
return left.payloadHash < right.payloadHash ? -1 : 1;
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
async function nextLedgerHead(previous, event, sequence) {
|
|
122
|
+
return await tmwSha256Hex(canonicalTmwJson({
|
|
123
|
+
previous,
|
|
124
|
+
sequence,
|
|
125
|
+
eventId: event.eventId,
|
|
126
|
+
payloadHash: event.payloadHash,
|
|
127
|
+
commitReceiptDigest: event.commitReceiptDigest,
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
export class EventFabric {
|
|
131
|
+
#epoch;
|
|
132
|
+
#runtimeContractHash;
|
|
133
|
+
#artifactDigest;
|
|
134
|
+
#timelineRevision;
|
|
135
|
+
#events = [];
|
|
136
|
+
#dedupe = new Map();
|
|
137
|
+
#trace;
|
|
138
|
+
#ledgerHead = TMW_EMPTY_LEDGER_HEAD;
|
|
139
|
+
#appendTail = Promise.resolve();
|
|
140
|
+
constructor(input) {
|
|
141
|
+
this.#epoch = requireTmwSafeInteger(input.epoch, "fabric_epoch", 1);
|
|
142
|
+
this.#runtimeContractHash = requireTmwSha256(input.runtimeContractHash, "fabric_contract_hash");
|
|
143
|
+
this.#artifactDigest = requireTmwSha256(input.artifactDigest, "fabric_artifact_digest");
|
|
144
|
+
this.#timelineRevision = requireTmwIdentifier(input.timelineRevision, "fabric_timeline_revision");
|
|
145
|
+
this.#trace = new BoundedRuntimeTrace(input.traceLimit);
|
|
146
|
+
}
|
|
147
|
+
async appendCommitted(input) {
|
|
148
|
+
let releaseAppend;
|
|
149
|
+
const precedingAppend = this.#appendTail;
|
|
150
|
+
this.#appendTail = new Promise((resolve) => {
|
|
151
|
+
releaseAppend = resolve;
|
|
152
|
+
});
|
|
153
|
+
await precedingAppend;
|
|
154
|
+
try {
|
|
155
|
+
const event = canonicalCommittedEvent(input);
|
|
156
|
+
this.#assertBinding(event);
|
|
157
|
+
const dedupeKey = eventFabricDedupeKey(event);
|
|
158
|
+
const previous = this.#dedupe.get(dedupeKey);
|
|
159
|
+
if (previous !== undefined) {
|
|
160
|
+
const existing = this.#events[previous.ledgerSequence];
|
|
161
|
+
if (existing?.payloadHash !== event.payloadHash
|
|
162
|
+
|| existing.commitReceiptDigest !== event.commitReceiptDigest) {
|
|
163
|
+
throw new Error("tmw_event_dedupe_identity_conflict");
|
|
164
|
+
}
|
|
165
|
+
this.#trace.append({ kind: "event_duplicate", stableId: event.eventId, detail: { dedupeKey } });
|
|
166
|
+
return Object.freeze({ ...previous, status: "duplicate" });
|
|
167
|
+
}
|
|
168
|
+
const sequence = this.#events.length;
|
|
169
|
+
this.#ledgerHead = await nextLedgerHead(this.#ledgerHead, event, sequence);
|
|
170
|
+
this.#events.push(event);
|
|
171
|
+
const receipt = Object.freeze({
|
|
172
|
+
status: "appended",
|
|
173
|
+
ledgerSequence: sequence,
|
|
174
|
+
eventId: event.eventId,
|
|
175
|
+
dedupeKey,
|
|
176
|
+
ledgerHead: this.#ledgerHead,
|
|
177
|
+
});
|
|
178
|
+
this.#dedupe.set(dedupeKey, receipt);
|
|
179
|
+
this.#trace.append({
|
|
180
|
+
kind: "event_committed",
|
|
181
|
+
stableId: event.eventId,
|
|
182
|
+
detail: {
|
|
183
|
+
ledgerSequence: sequence,
|
|
184
|
+
clockDomain: event.effectiveTime.domain,
|
|
185
|
+
effectiveValue: event.effectiveTime.effective_value,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
return receipt;
|
|
189
|
+
}
|
|
190
|
+
finally {
|
|
191
|
+
releaseAppend();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
restoreLedgerHead(ledgerHeadInput) {
|
|
195
|
+
if (this.#events.length !== 0 || this.#dedupe.size !== 0
|
|
196
|
+
|| this.#ledgerHead !== TMW_EMPTY_LEDGER_HEAD) {
|
|
197
|
+
throw new Error("tmw_event_fabric_restore_barrier_closed");
|
|
198
|
+
}
|
|
199
|
+
this.#ledgerHead = requireTmwSha256(ledgerHeadInput, "fabric_ledger_head");
|
|
200
|
+
}
|
|
201
|
+
orderedEvents() {
|
|
202
|
+
return Object.freeze([...this.#events].sort(compareCommittedEvents));
|
|
203
|
+
}
|
|
204
|
+
snapshot() {
|
|
205
|
+
return Object.freeze({
|
|
206
|
+
epoch: this.#epoch,
|
|
207
|
+
runtimeContractHash: this.#runtimeContractHash,
|
|
208
|
+
artifactDigest: this.#artifactDigest,
|
|
209
|
+
timelineRevision: this.#timelineRevision,
|
|
210
|
+
ledgerHead: this.#ledgerHead,
|
|
211
|
+
events: Object.freeze([...this.#events]),
|
|
212
|
+
trace: this.#trace.snapshot(),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
#assertBinding(event) {
|
|
216
|
+
if (event.epoch !== this.#epoch)
|
|
217
|
+
throw new Error("stale_content_epoch");
|
|
218
|
+
if (event.runtimeContractHash !== this.#runtimeContractHash) {
|
|
219
|
+
throw new Error("runtime_contract_hash_mismatch");
|
|
220
|
+
}
|
|
221
|
+
if (event.artifactDigest !== this.#artifactDigest)
|
|
222
|
+
throw new Error("tmw_event_artifact_mismatch");
|
|
223
|
+
if (event.timelineRevision !== this.#timelineRevision) {
|
|
224
|
+
throw new Error("tmw_event_timeline_revision_mismatch");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { CompiledPlanV3 } from "./compiled-plan.js";
|
|
2
|
+
import type { DurableIntentV3 } from "./durable-intent.js";
|
|
3
|
+
import type { PlannedEffect } from "./effect-flow.js";
|
|
4
|
+
import type { CommittedEventEnvelope } from "./event-fabric.js";
|
|
5
|
+
import { type TmwJsonPrimitive, type TmwJsonValue } from "./runtime-trace.js";
|
|
6
|
+
import { type TmwPendingSceneClockCrossingV1 } from "./scene-state.js";
|
|
7
|
+
import { TmwScheduler, type SuspensionRecord } from "./scheduler.js";
|
|
8
|
+
export declare const TMW_EVENT_MAX_BROADCAST_COUNT = 256;
|
|
9
|
+
export declare const TMW_EVENT_MAX_CAUSATION_DEPTH = 32;
|
|
10
|
+
export declare const TMW_EVENT_MAX_VIEW_VISIBLE_FIELDS = 64;
|
|
11
|
+
export declare const TMW_EVENT_CAPABILITY_ARGUMENT_SCHEMAS: Readonly<{
|
|
12
|
+
"event.create": Readonly<{
|
|
13
|
+
type: "object";
|
|
14
|
+
additionalProperties: false;
|
|
15
|
+
properties: Record<string, unknown>;
|
|
16
|
+
required: string[];
|
|
17
|
+
}>;
|
|
18
|
+
"event.delete": Readonly<{
|
|
19
|
+
type: "object";
|
|
20
|
+
additionalProperties: false;
|
|
21
|
+
properties: Record<string, unknown>;
|
|
22
|
+
required: string[];
|
|
23
|
+
}>;
|
|
24
|
+
"event.stop": Readonly<{
|
|
25
|
+
type: "object";
|
|
26
|
+
additionalProperties: false;
|
|
27
|
+
properties: Record<string, unknown>;
|
|
28
|
+
required: string[];
|
|
29
|
+
}>;
|
|
30
|
+
"event.start": Readonly<{
|
|
31
|
+
type: "object";
|
|
32
|
+
additionalProperties: false;
|
|
33
|
+
properties: Record<string, unknown>;
|
|
34
|
+
required: string[];
|
|
35
|
+
}>;
|
|
36
|
+
"event.broadcast": Readonly<{
|
|
37
|
+
type: "object";
|
|
38
|
+
additionalProperties: false;
|
|
39
|
+
properties: Record<string, unknown>;
|
|
40
|
+
required: string[];
|
|
41
|
+
}>;
|
|
42
|
+
}>;
|
|
43
|
+
export declare function canonicalTmwEventViewVisiblePayload(value: TmwJsonValue | undefined): Readonly<Record<string, TmwJsonPrimitive>> | null;
|
|
44
|
+
export declare function isTmwEventLifecycleCapability(id: string): boolean;
|
|
45
|
+
export declare function isTmwEventBroadcastSuspension(value: SuspensionRecord): boolean;
|
|
46
|
+
export declare function isTmwEventDefinitionActive(nodeId: string, intents: ReadonlyArray<DurableIntentV3>): boolean;
|
|
47
|
+
/** 事件定义始终来自已编译 world graph;这里只保存运行态与未来投递,不修改事实 ledger。 */
|
|
48
|
+
export declare function reduceTmwEventLifecycle(input: Readonly<{
|
|
49
|
+
plan: CompiledPlanV3;
|
|
50
|
+
event: CommittedEventEnvelope;
|
|
51
|
+
effects: ReadonlyArray<PlannedEffect>;
|
|
52
|
+
intents: ReadonlyArray<DurableIntentV3>;
|
|
53
|
+
scheduler: TmwScheduler;
|
|
54
|
+
dueBroadcasts: ReadonlyArray<SuspensionRecord>;
|
|
55
|
+
pending: ReadonlyArray<TmwPendingSceneClockCrossingV1>;
|
|
56
|
+
}>): Promise<Readonly<{
|
|
57
|
+
intents: ReadonlyArray<DurableIntentV3>;
|
|
58
|
+
pending: ReadonlyArray<TmwPendingSceneClockCrossingV1>;
|
|
59
|
+
}>>;
|