@world-engines/tmw 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/LICENSE +46 -0
  2. package/dist/activation.d.ts +50 -0
  3. package/dist/activation.js +113 -0
  4. package/dist/audio-presentation-projection.d.ts +23 -0
  5. package/dist/audio-presentation-projection.js +39 -0
  6. package/dist/author-scenario-source-v3.d.ts +34 -0
  7. package/dist/author-scenario-source-v3.js +1 -0
  8. package/dist/authoring-runtime.d.ts +76 -0
  9. package/dist/authoring-runtime.js +282 -0
  10. package/dist/builder-diagnostics.d.ts +3 -0
  11. package/dist/builder-diagnostics.js +8 -0
  12. package/dist/builder-node.d.ts +178 -0
  13. package/dist/builder-node.js +212 -0
  14. package/dist/builder.d.ts +32 -0
  15. package/dist/builder.js +127 -0
  16. package/dist/capability-registry.d.ts +32 -0
  17. package/dist/capability-registry.js +33 -0
  18. package/dist/compile-target-profile.d.ts +16 -0
  19. package/dist/compile-target-profile.js +70 -0
  20. package/dist/compiled-plan.d.ts +44 -0
  21. package/dist/compiled-plan.js +1 -0
  22. package/dist/compiler-compatibility-gate.d.ts +96 -0
  23. package/dist/compiler-compatibility-gate.js +609 -0
  24. package/dist/compiler-release-authority.d.ts +212 -0
  25. package/dist/compiler-release-authority.js +676 -0
  26. package/dist/compiler-stage-cache.d.ts +42 -0
  27. package/dist/compiler-stage-cache.js +100 -0
  28. package/dist/compiler-stage-registry.d.ts +48 -0
  29. package/dist/compiler-stage-registry.js +126 -0
  30. package/dist/compiler.d.ts +7 -0
  31. package/dist/compiler.js +68 -0
  32. package/dist/condition-graph.d.ts +46 -0
  33. package/dist/condition-graph.js +135 -0
  34. package/dist/diagnostics.d.ts +20 -0
  35. package/dist/diagnostics.js +20 -0
  36. package/dist/durable-intent.d.ts +29 -0
  37. package/dist/durable-intent.js +60 -0
  38. package/dist/effect-flow.d.ts +69 -0
  39. package/dist/effect-flow.js +98 -0
  40. package/dist/event-fabric.d.ts +81 -0
  41. package/dist/event-fabric.js +227 -0
  42. package/dist/event-lifecycle.d.ts +59 -0
  43. package/dist/event-lifecycle.js +161 -0
  44. package/dist/evidence.d.ts +37 -0
  45. package/dist/evidence.js +92 -0
  46. package/dist/gameplay-state.d.ts +127 -0
  47. package/dist/gameplay-state.js +214 -0
  48. package/dist/index.d.ts +66 -0
  49. package/dist/index.js +52 -0
  50. package/dist/isolated-replay.d.ts +81 -0
  51. package/dist/isolated-replay.js +316 -0
  52. package/dist/music-candidate-set.d.ts +38 -0
  53. package/dist/music-candidate-set.js +102 -0
  54. package/dist/natural-language-chunking.d.ts +10 -0
  55. package/dist/natural-language-chunking.js +62 -0
  56. package/dist/natural-language-ingestion.d.ts +14 -0
  57. package/dist/natural-language-ingestion.js +128 -0
  58. package/dist/natural-language-structured-decode.d.ts +2 -0
  59. package/dist/natural-language-structured-decode.js +69 -0
  60. package/dist/natural-language-world-types.d.ts +142 -0
  61. package/dist/natural-language-world-types.js +2 -0
  62. package/dist/neutral-source-compiler.d.ts +135 -0
  63. package/dist/neutral-source-compiler.js +608 -0
  64. package/dist/presentation-intent.d.ts +42 -0
  65. package/dist/presentation-intent.js +137 -0
  66. package/dist/recall-query-envelope.d.ts +32 -0
  67. package/dist/recall-query-envelope.js +51 -0
  68. package/dist/rewrite-compensation.d.ts +28 -0
  69. package/dist/rewrite-compensation.js +85 -0
  70. package/dist/runtime-contract.d.ts +56 -0
  71. package/dist/runtime-contract.js +96 -0
  72. package/dist/runtime-domain-capabilities.d.ts +12 -0
  73. package/dist/runtime-domain-capabilities.js +20 -0
  74. package/dist/runtime-sav.d.ts +117 -0
  75. package/dist/runtime-sav.js +356 -0
  76. package/dist/runtime-trace.d.ts +44 -0
  77. package/dist/runtime-trace.js +119 -0
  78. package/dist/runtime.d.ts +238 -0
  79. package/dist/runtime.js +1887 -0
  80. package/dist/scene-author-source.d.ts +13 -0
  81. package/dist/scene-author-source.js +25 -0
  82. package/dist/scene-state.d.ts +275 -0
  83. package/dist/scene-state.js +334 -0
  84. package/dist/scheduler.d.ts +55 -0
  85. package/dist/scheduler.js +235 -0
  86. package/dist/selector-ast.d.ts +24 -0
  87. package/dist/selector-ast.js +18 -0
  88. package/dist/semantic-verdict.d.ts +48 -0
  89. package/dist/semantic-verdict.js +101 -0
  90. package/dist/simulator.d.ts +45 -0
  91. package/dist/simulator.js +58 -0
  92. package/dist/source-canonical.d.ts +4 -0
  93. package/dist/source-canonical.js +51 -0
  94. package/dist/source-crypto.d.ts +20 -0
  95. package/dist/source-crypto.js +17 -0
  96. package/dist/source-revision.d.ts +22 -0
  97. package/dist/source-revision.js +43 -0
  98. package/dist/spatial-impact-receipt.d.ts +38 -0
  99. package/dist/spatial-impact-receipt.js +67 -0
  100. package/dist/temporal-rewrite.d.ts +59 -0
  101. package/dist/temporal-rewrite.js +214 -0
  102. package/dist/time-domains.d.ts +37 -0
  103. package/dist/time-domains.js +142 -0
  104. package/dist/timeline-revision.d.ts +28 -0
  105. package/dist/timeline-revision.js +59 -0
  106. package/dist/trigger-document-v3.d.ts +99 -0
  107. package/dist/trigger-document-v3.js +334 -0
  108. package/dist/turn-commit.d.ts +56 -0
  109. package/dist/turn-commit.js +190 -0
  110. package/dist/validator.d.ts +7 -0
  111. package/dist/validator.js +142 -0
  112. package/dist/value-graph.d.ts +98 -0
  113. package/dist/value-graph.js +318 -0
  114. package/dist/view-event-delivery.d.ts +47 -0
  115. package/dist/view-event-delivery.js +197 -0
  116. package/dist/view-interaction-manifest-v3.d.ts +60 -0
  117. package/dist/view-interaction-manifest-v3.js +198 -0
  118. package/dist/view-payload-schema.d.ts +17 -0
  119. package/dist/view-payload-schema.js +84 -0
  120. package/package.json +62 -0
@@ -0,0 +1,161 @@
1
+ import { canonicalTmwJson, canonicalTmwJsonValue, requireTmwIdentifier, requireTmwSafeInteger, tmwSha256Hex } from "./runtime-trace.js";
2
+ import { canonicalTmwAuthorViewProjectionV1 } from "./scene-state.js";
3
+ export const TMW_EVENT_MAX_BROADCAST_COUNT = 256;
4
+ export const TMW_EVENT_MAX_CAUSATION_DEPTH = 32;
5
+ export const TMW_EVENT_MAX_VIEW_VISIBLE_FIELDS = 64;
6
+ const ID = Object.freeze({ type: "string", minLength: 1, maxLength: 512 });
7
+ const TARGET = Object.freeze({ eventNodeId: ID });
8
+ const commandSchema = (properties, required) => Object.freeze({ type: "object", additionalProperties: false, properties, required });
9
+ export const TMW_EVENT_CAPABILITY_ARGUMENT_SCHEMAS = Object.freeze({
10
+ "event.create": commandSchema(TARGET, ["eventNodeId"]),
11
+ "event.delete": commandSchema(TARGET, ["eventNodeId"]),
12
+ "event.stop": commandSchema(TARGET, ["eventNodeId"]),
13
+ "event.start": commandSchema({ ...TARGET, count: { type: "integer", minimum: 1, maximum: TMW_EVENT_MAX_BROADCAST_COUNT }, delay: { type: "object", additionalProperties: false, required: ["domain", "value"], properties: { domain: { const: "world_time" }, value: { type: "number", minimum: 0 } } }, payload: { type: "object" }, viewVisiblePayload: { type: "object", maxProperties: TMW_EVENT_MAX_VIEW_VISIBLE_FIELDS, additionalProperties: { type: ["string", "number", "boolean", "null"] } } }, ["eventNodeId", "count", "delay", "payload"]),
14
+ "event.broadcast": commandSchema({ ...TARGET, count: { type: "integer", minimum: 1, maximum: TMW_EVENT_MAX_BROADCAST_COUNT }, payload: { type: "object" }, viewVisiblePayload: { type: "object", maxProperties: TMW_EVENT_MAX_VIEW_VISIBLE_FIELDS, additionalProperties: { type: ["string", "number", "boolean", "null"] } } }, ["eventNodeId", "count", "payload"]),
15
+ });
16
+ function primitiveRecord(value) {
17
+ if (value === undefined)
18
+ return null;
19
+ if (value === null || typeof value !== "object" || Array.isArray(value))
20
+ throw new Error("tmw_event_view_visible_payload_invalid");
21
+ const output = Object.create(null);
22
+ if (Object.keys(value).length > TMW_EVENT_MAX_VIEW_VISIBLE_FIELDS)
23
+ throw new Error("tmw_event_view_visible_payload_limit_exceeded");
24
+ for (const [key, item] of Object.entries(value)) {
25
+ requireTmwIdentifier(key, "event_view_visible_payload_key");
26
+ if (item !== null && typeof item !== "string" && typeof item !== "boolean" && (typeof item !== "number" || !Number.isFinite(item))) {
27
+ throw new Error("tmw_event_view_visible_payload_invalid");
28
+ }
29
+ output[key] = item;
30
+ }
31
+ return Object.freeze(output);
32
+ }
33
+ export function canonicalTmwEventViewVisiblePayload(value) {
34
+ return primitiveRecord(value);
35
+ }
36
+ export function isTmwEventLifecycleCapability(id) {
37
+ return Object.hasOwn(TMW_EVENT_CAPABILITY_ARGUMENT_SCHEMAS, id);
38
+ }
39
+ function object(value) {
40
+ if (value === null || typeof value !== "object" || Array.isArray(value))
41
+ throw new Error("tmw_event_lifecycle_object_required");
42
+ return value;
43
+ }
44
+ export function isTmwEventBroadcastSuspension(value) {
45
+ return object(value.durablePayload).schema === "tmw3.event-broadcast.v1";
46
+ }
47
+ export function isTmwEventDefinitionActive(nodeId, intents) {
48
+ const state = latestEventState(nodeId, intents);
49
+ return state === undefined || (object(state.payload).running === true && object(state.payload).deleted !== true);
50
+ }
51
+ function latestEventState(nodeId, intents) {
52
+ return intents.filter((intent) => isTmwEventLifecycleCapability(intent.capabilityId)
53
+ && object(intent.payload).schema === "tmw3.event-state.v1"
54
+ && object(intent.payload).eventNodeId === nodeId)
55
+ .sort((left, right) => Number(object(right.payload).generation) - Number(object(left.payload).generation))[0];
56
+ }
57
+ /** 事件定义始终来自已编译 world graph;这里只保存运行态与未来投递,不修改事实 ledger。 */
58
+ export async function reduceTmwEventLifecycle(input) {
59
+ const intents = new Map(input.intents.map((intent) => [intent.intentId, intent]));
60
+ let pending = input.pending.filter((entry) => entry.eventId !== input.event.eventId);
61
+ const cancellationKey = (nodeId) => `tmw3:event-flow:${nodeId}`;
62
+ const stopped = new Set();
63
+ const origin = () => Object.freeze({ correlationId: input.event.correlationId, causationId: input.event.eventId, depth: requireTmwSafeInteger((object(input.event.payload).runtimeEventDepth ?? 0), "event_causation_depth", 0) + 1 });
64
+ const enqueue = async (nodeId, eventId, payload, authorViewProjection, emissionOrigin = origin()) => {
65
+ const node = input.plan.nodes.find((candidate) => candidate.stable_id === nodeId && candidate.kind === "event");
66
+ if (node === undefined)
67
+ throw new Error("tmw_event_definition_no_match");
68
+ if (node.effective_time.domain !== "world_time" || input.event.coordinates.worldTime === null)
69
+ throw new Error("tmw_event_broadcast_world_clock_required");
70
+ if (pending.some((entry) => entry.eventId === eventId))
71
+ return;
72
+ const depth = requireTmwSafeInteger(emissionOrigin.depth, "event_causation_depth", 1);
73
+ if (depth > TMW_EVENT_MAX_CAUSATION_DEPTH)
74
+ throw new Error("tmw_event_causation_depth_exceeded");
75
+ const body = Object.freeze({ ...object(payload), runtimeEventNodeId: nodeId, runtimeEventDepth: depth, correlationId: emissionOrigin.correlationId, causationId: emissionOrigin.causationId });
76
+ pending.push(Object.freeze({ eventId, eventType: String(object(node.canonical_payload).event_type), logicalSeq: input.event.coordinates.logicalSeq, worldTime: input.event.coordinates.worldTime, crossingDirection: "forward", payload: body, payloadHash: await tmwSha256Hex(canonicalTmwJson(body)), commitReceiptDigest: input.event.commitReceiptDigest,
77
+ ...(authorViewProjection === undefined ? {} : { authorViewProjection }),
78
+ }));
79
+ };
80
+ for (const effect of input.effects) {
81
+ if (!isTmwEventLifecycleCapability(effect.capabilityId))
82
+ continue;
83
+ const args = object(effect.input);
84
+ const nodeId = requireTmwIdentifier(args.eventNodeId, "event_lifecycle_node_id");
85
+ if (!input.plan.nodes.some((node) => node.kind === "event" && node.stable_id === nodeId))
86
+ throw new Error("tmw_event_definition_no_match");
87
+ const key = `tmw3:event-state:${await tmwSha256Hex(canonicalTmwJson([input.event.eventId, effect.effectId]))}`;
88
+ const prior = latestEventState(nodeId, [...intents.values()]);
89
+ const deleted = prior !== undefined && object(prior.payload).deleted === true;
90
+ const saveState = (state, isDeleted) => intents.set(key, Object.freeze({
91
+ intentId: key, epoch: input.event.epoch, runtimeGeneration: 3, runtimeContractHash: input.event.runtimeContractHash, artifactDigest: input.event.artifactDigest, sourceRevision: input.event.sourceRevision, timelineRevision: input.event.timelineRevision,
92
+ turnId: input.event.correlationId, operationId: `tmw3:event:${input.event.eventId}`, capabilityId: effect.capabilityId, idempotencyKey: `${input.event.eventId}:${effect.effectId}`, conflictKey: cancellationKey(nodeId), priority: effect.priority, state: "satisfied", selectorSnapshotHash: input.event.payloadHash,
93
+ payload: Object.freeze({ schema: "tmw3.event-state.v1", eventNodeId: nodeId, deleted: isDeleted, running: state === "active", generation: prior === undefined ? 1 : Number(object(prior.payload).generation) + 1 }), lastReceiptDigest: input.event.commitReceiptDigest, reasonCode: null,
94
+ }));
95
+ if (effect.capabilityId === "event.create") {
96
+ saveState("active", false);
97
+ continue;
98
+ }
99
+ if (effect.capabilityId === "event.stop" || effect.capabilityId === "event.delete") {
100
+ input.scheduler.cancel(cancellationKey(nodeId));
101
+ stopped.add(nodeId);
102
+ pending = pending.filter((entry) => entry.payload.runtimeEventNodeId !== nodeId);
103
+ saveState("cancelled", effect.capabilityId === "event.delete" || deleted);
104
+ continue;
105
+ }
106
+ if (deleted)
107
+ throw new Error("tmw_event_definition_deleted");
108
+ const count = requireTmwSafeInteger(args.count, "event_broadcast_count", 1);
109
+ if (count > TMW_EVENT_MAX_BROADCAST_COUNT)
110
+ throw new Error("tmw_event_broadcast_count_exceeded");
111
+ const payload = canonicalTmwJsonValue(object(args.payload), "event_broadcast_payload");
112
+ const viewVisiblePayload = primitiveRecord(args.viewVisiblePayload);
113
+ const authorViewProjection = viewVisiblePayload === null ? undefined : Object.freeze({ schema: "tmw3.author-view-event.v1", saveId: input.event.scope.saveId, artifactDigest: input.event.artifactDigest, sourceEventId: input.event.eventId, sourceCommitReceiptDigest: input.event.commitReceiptDigest, viewVisiblePayload });
114
+ let delay = 0;
115
+ if (effect.capabilityId === "event.start") {
116
+ const temporal = object(args.delay);
117
+ if (temporal.domain !== "world_time")
118
+ throw new Error("tmw_event_delay_clock_not_produced");
119
+ if (typeof temporal.value !== "number" || !Number.isFinite(temporal.value) || temporal.value < 0)
120
+ throw new Error("tmw_event_delay_invalid");
121
+ delay = temporal.value;
122
+ }
123
+ if (input.event.coordinates.worldTime === null)
124
+ throw new Error("tmw_event_broadcast_world_clock_required");
125
+ for (let index = 0; index < count; index++) {
126
+ const eventId = `tmw3:broadcast:${await tmwSha256Hex(canonicalTmwJson([input.event.eventId, effect.effectId, index]))}`;
127
+ if (delay === 0) {
128
+ await enqueue(nodeId, eventId, payload, authorViewProjection);
129
+ continue;
130
+ }
131
+ const due = input.event.coordinates.worldTime + delay * (index + 1);
132
+ if (!Number.isFinite(due))
133
+ throw new Error("tmw_event_delay_invalid");
134
+ input.scheduler.schedule({ suspensionId: eventId, epoch: input.event.epoch, runtimeGeneration: 3, runtimeContractHash: input.event.runtimeContractHash, artifactDigest: input.event.artifactDigest, sourceRevision: input.event.sourceRevision, timelineRevision: input.event.timelineRevision, scope: input.event.scope,
135
+ effectiveTime: { domain: "world_time", effective_value: input.event.coordinates.worldTime, due_value: due, crossing_policy: "forward_only", real_time_clock: null, wall_clock_policy: null }, resumeNodeId: nodeId, correlationId: input.event.correlationId, cancellationKey: cancellationKey(nodeId), createdSnapshotHash: input.event.payloadHash,
136
+ durablePayload: { schema: "tmw3.event-broadcast.v1", payload, origin: origin(), ...(authorViewProjection === undefined ? {} : { authorViewProjection }) },
137
+ });
138
+ }
139
+ saveState("active", false);
140
+ }
141
+ for (const suspension of input.dueBroadcasts) {
142
+ if (stopped.has(suspension.resumeNodeId))
143
+ continue;
144
+ const durable = object(suspension.durablePayload);
145
+ const emissionOrigin = object(durable.origin);
146
+ const payload = durable.payload;
147
+ const { correlationId, causationId, depth } = emissionOrigin;
148
+ if (payload === undefined
149
+ || typeof correlationId !== "string"
150
+ || typeof causationId !== "string"
151
+ || typeof depth !== "number") {
152
+ throw new Error("tmw_event_broadcast_suspension_invalid");
153
+ }
154
+ await enqueue(suspension.resumeNodeId, suspension.suspensionId, payload, canonicalTmwAuthorViewProjectionV1(durable.authorViewProjection), {
155
+ correlationId: requireTmwIdentifier(correlationId, "event_origin_correlation_id"),
156
+ causationId: requireTmwIdentifier(causationId, "event_origin_causation_id"),
157
+ depth: requireTmwSafeInteger(depth, "event_causation_depth", 1),
158
+ });
159
+ }
160
+ return Object.freeze({ intents: Object.freeze([...intents.values()]), pending: Object.freeze(pending) });
161
+ }
@@ -0,0 +1,37 @@
1
+ import { TMW_RUNTIME_GENERATION } from "./runtime-contract.js";
2
+ import { type TmwJsonValue } from "./runtime-trace.js";
3
+ export type EvidenceKind = "committed_event" | "recall_readback" | "hard_fact";
4
+ export interface EvidenceItemV3 {
5
+ readonly evidenceRef: string;
6
+ readonly kind: EvidenceKind;
7
+ readonly sourceDigest: string;
8
+ readonly value: TmwJsonValue;
9
+ }
10
+ export interface EvidenceEnvelopeV3 {
11
+ readonly schemaVersion: 3;
12
+ readonly epoch: number;
13
+ readonly runtimeGeneration: typeof TMW_RUNTIME_GENERATION;
14
+ readonly runtimeContractHash: string;
15
+ readonly artifactDigest: string;
16
+ readonly scenarioId: string;
17
+ readonly sourceRevision: string;
18
+ readonly timelineRevision: string;
19
+ readonly turnId: string;
20
+ readonly operationId: string;
21
+ readonly intentId: string;
22
+ readonly idempotencyKey: string;
23
+ readonly budgetReceiptDigest: string;
24
+ readonly predicateKeys: ReadonlyArray<string>;
25
+ readonly items: ReadonlyArray<EvidenceItemV3>;
26
+ readonly evidenceSetDigest: string;
27
+ }
28
+ export interface EvidenceAssemblyInput extends Omit<EvidenceEnvelopeV3, "schemaVersion" | "runtimeGeneration" | "items" | "evidenceSetDigest"> {
29
+ readonly candidates: ReadonlyArray<EvidenceItemV3>;
30
+ readonly allowedEvidenceRefs: ReadonlyArray<string>;
31
+ readonly maxItems: number;
32
+ readonly maxCanonicalBytes: number;
33
+ readonly evidenceSetDigest: string;
34
+ }
35
+ export declare function assembleEvidenceEnvelope(input: EvidenceAssemblyInput): EvidenceEnvelopeV3;
36
+ export declare function verifyEvidenceEnvelopeDigest(envelope: EvidenceEnvelopeV3): Promise<EvidenceEnvelopeV3>;
37
+ export declare function createEvidenceEnvelope(input: Omit<EvidenceAssemblyInput, "evidenceSetDigest">): Promise<EvidenceEnvelopeV3>;
@@ -0,0 +1,92 @@
1
+ import { TMW_RUNTIME_GENERATION } from "./runtime-contract.js";
2
+ import { canonicalTmwJson, canonicalTmwJsonValue, requireTmwIdentifier, requireTmwSafeInteger, requireTmwSha256, tmwSha256Hex, } from "./runtime-trace.js";
3
+ function canonicalItem(input) {
4
+ if (input.kind !== "committed_event"
5
+ && input.kind !== "recall_readback"
6
+ && input.kind !== "hard_fact") {
7
+ throw new Error("tmw_evidence_kind_invalid");
8
+ }
9
+ return Object.freeze({
10
+ evidenceRef: requireTmwIdentifier(input.evidenceRef, "evidence_ref"),
11
+ kind: input.kind,
12
+ sourceDigest: requireTmwSha256(input.sourceDigest, "evidence_source_digest"),
13
+ value: canonicalTmwJsonValue(input.value, "evidence_value"),
14
+ });
15
+ }
16
+ export function assembleEvidenceEnvelope(input) {
17
+ const maxItems = requireTmwSafeInteger(input.maxItems, "evidence_max_items", 1);
18
+ const maxCanonicalBytes = requireTmwSafeInteger(input.maxCanonicalBytes, "evidence_max_bytes", 1);
19
+ const allowed = new Set(input.allowedEvidenceRefs.map((ref) => requireTmwIdentifier(ref, "evidence_allowed_ref")));
20
+ const byRef = new Map();
21
+ for (const candidateInput of input.candidates) {
22
+ const candidate = canonicalItem(candidateInput);
23
+ if (!allowed.has(candidate.evidenceRef))
24
+ continue;
25
+ const previous = byRef.get(candidate.evidenceRef);
26
+ if (previous !== undefined && (previous.sourceDigest !== candidate.sourceDigest
27
+ || canonicalTmwJson(previous.value) !== canonicalTmwJson(candidate.value))) {
28
+ throw new Error("tmw_evidence_ref_conflict");
29
+ }
30
+ byRef.set(candidate.evidenceRef, candidate);
31
+ }
32
+ const missing = [...allowed].filter((ref) => !byRef.has(ref));
33
+ if (missing.length > 0)
34
+ throw new Error("tmw_evidence_allowed_ref_missing");
35
+ const items = [...byRef.values()].sort((left, right) => left.evidenceRef < right.evidenceRef ? -1 : left.evidenceRef > right.evidenceRef ? 1 : 0);
36
+ if (items.length > maxItems)
37
+ throw new Error("tmw_evidence_item_budget_exhausted");
38
+ if (new TextEncoder().encode(canonicalTmwJson(items)).byteLength > maxCanonicalBytes) {
39
+ throw new Error("tmw_evidence_byte_budget_exhausted");
40
+ }
41
+ const predicateKeys = Object.freeze([...new Set(input.predicateKeys.map((key) => requireTmwIdentifier(key, "evidence_predicate_key")))].sort());
42
+ return Object.freeze({
43
+ schemaVersion: 3,
44
+ epoch: requireTmwSafeInteger(input.epoch, "evidence_epoch", 1),
45
+ runtimeGeneration: TMW_RUNTIME_GENERATION,
46
+ runtimeContractHash: requireTmwSha256(input.runtimeContractHash, "evidence_contract_hash"),
47
+ artifactDigest: requireTmwSha256(input.artifactDigest, "evidence_artifact_digest"),
48
+ scenarioId: requireTmwIdentifier(input.scenarioId, "evidence_scenario_id"),
49
+ sourceRevision: requireTmwIdentifier(input.sourceRevision, "evidence_source_revision"),
50
+ timelineRevision: requireTmwIdentifier(input.timelineRevision, "evidence_timeline_revision"),
51
+ turnId: requireTmwIdentifier(input.turnId, "evidence_turn_id"),
52
+ operationId: requireTmwIdentifier(input.operationId, "evidence_operation_id"),
53
+ intentId: requireTmwIdentifier(input.intentId, "evidence_intent_id"),
54
+ idempotencyKey: requireTmwIdentifier(input.idempotencyKey, "evidence_idempotency_key"),
55
+ budgetReceiptDigest: requireTmwSha256(input.budgetReceiptDigest, "evidence_budget_receipt_digest"),
56
+ predicateKeys,
57
+ items: Object.freeze(items),
58
+ evidenceSetDigest: requireTmwSha256(input.evidenceSetDigest, "evidence_set_digest"),
59
+ });
60
+ }
61
+ export async function verifyEvidenceEnvelopeDigest(envelope) {
62
+ const canonical = assembleEvidenceEnvelope({
63
+ epoch: envelope.epoch,
64
+ runtimeContractHash: envelope.runtimeContractHash,
65
+ artifactDigest: envelope.artifactDigest,
66
+ scenarioId: envelope.scenarioId,
67
+ sourceRevision: envelope.sourceRevision,
68
+ timelineRevision: envelope.timelineRevision,
69
+ turnId: envelope.turnId,
70
+ operationId: envelope.operationId,
71
+ intentId: envelope.intentId,
72
+ idempotencyKey: envelope.idempotencyKey,
73
+ budgetReceiptDigest: envelope.budgetReceiptDigest,
74
+ predicateKeys: envelope.predicateKeys,
75
+ candidates: envelope.items,
76
+ allowedEvidenceRefs: envelope.items.map((item) => item.evidenceRef),
77
+ maxItems: envelope.items.length,
78
+ maxCanonicalBytes: new TextEncoder().encode(canonicalTmwJson(envelope.items)).byteLength,
79
+ evidenceSetDigest: envelope.evidenceSetDigest,
80
+ });
81
+ const { evidenceSetDigest: _claimedDigest, ...digestImage } = canonical;
82
+ const computed = await tmwSha256Hex(canonicalTmwJson(digestImage));
83
+ if (computed !== canonical.evidenceSetDigest)
84
+ throw new Error("tmw_evidence_set_digest_mismatch");
85
+ return canonical;
86
+ }
87
+ export async function createEvidenceEnvelope(input) {
88
+ const provisional = assembleEvidenceEnvelope({ ...input, evidenceSetDigest: "0".repeat(64) });
89
+ const { evidenceSetDigest: _placeholder, ...digestImage } = provisional;
90
+ const evidenceSetDigest = await tmwSha256Hex(canonicalTmwJson(digestImage));
91
+ return Object.freeze({ ...provisional, evidenceSetDigest });
92
+ }
@@ -0,0 +1,127 @@
1
+ import { type TmwJsonValue } from "./runtime-trace.js";
2
+ import { type TmwValueTypeV1 } from "./value-graph.js";
3
+ export type TmwVariableScope = "save" | "execution";
4
+ export type TmwVariableType = TmwValueTypeV1 | "json";
5
+ export interface TmwVariableV1 {
6
+ readonly name: string;
7
+ readonly scope: TmwVariableScope;
8
+ readonly valueType: TmwVariableType;
9
+ readonly value: TmwJsonValue;
10
+ }
11
+ /** 对齐 WASM SaveSidecar.EntityStateEntry,不以 view_access 或 enabled 表示玩法解锁。 */
12
+ export interface TmwEntityStateV1 {
13
+ readonly active_state_id: string;
14
+ readonly kind: "available" | "unavailable" | "frozen";
15
+ readonly frozen_stack: readonly Readonly<{
16
+ source_trigger_id: string;
17
+ prev_state_id?: string;
18
+ prev_kind?: "available" | "unavailable";
19
+ }>[];
20
+ }
21
+ export interface TmwGameplayStateV1 {
22
+ readonly schemaVersion: 1;
23
+ readonly variables: readonly TmwVariableV1[];
24
+ readonly entity_state_stacks: Readonly<Record<string, TmwEntityStateV1>>;
25
+ }
26
+ /** 新统一算子只拥有自己的冻结来源;绝不清除旧算子留下的其它来源。 */
27
+ export declare const TMW_OBJECT_STATE_FREEZE_SOURCE_ID: "tmw.object_state";
28
+ export declare const TMW_GAMEPLAY_CAPABILITY_ARGUMENT_SCHEMAS: Readonly<{
29
+ "variable.create": {
30
+ type: string;
31
+ additionalProperties: boolean;
32
+ required: string[];
33
+ properties: Record<string, unknown>;
34
+ };
35
+ "variable.set": {
36
+ type: string;
37
+ additionalProperties: boolean;
38
+ required: string[];
39
+ properties: Record<string, unknown>;
40
+ };
41
+ "variable.delete": {
42
+ type: string;
43
+ additionalProperties: boolean;
44
+ required: string[];
45
+ properties: Record<string, unknown>;
46
+ };
47
+ "node.set_unlock": {
48
+ type: string;
49
+ additionalProperties: boolean;
50
+ required: string[];
51
+ properties: Record<string, unknown>;
52
+ };
53
+ "edge.set_unlock": {
54
+ type: string;
55
+ additionalProperties: boolean;
56
+ required: string[];
57
+ properties: Record<string, unknown>;
58
+ };
59
+ "node.freeze": {
60
+ type: string;
61
+ additionalProperties: boolean;
62
+ required: string[];
63
+ properties: Record<string, unknown>;
64
+ };
65
+ "node.unfreeze": {
66
+ type: string;
67
+ additionalProperties: boolean;
68
+ required: string[];
69
+ properties: Record<string, unknown>;
70
+ };
71
+ "edge.freeze": {
72
+ type: string;
73
+ additionalProperties: boolean;
74
+ required: string[];
75
+ properties: Record<string, unknown>;
76
+ };
77
+ "edge.unfreeze": {
78
+ type: string;
79
+ additionalProperties: boolean;
80
+ required: string[];
81
+ properties: Record<string, unknown>;
82
+ };
83
+ "object.set_frozen": {
84
+ type: string;
85
+ additionalProperties: boolean;
86
+ required: string[];
87
+ properties: Record<string, unknown>;
88
+ };
89
+ "object.set_unlock": {
90
+ type: string;
91
+ additionalProperties: boolean;
92
+ required: string[];
93
+ properties: Record<string, unknown>;
94
+ };
95
+ }>;
96
+ export declare function emptyTmwGameplayStateV1(): TmwGameplayStateV1;
97
+ /** 作者试玩世界的初始显隐投影;显式玩法状态优先,不从 UI 访问权限或蓝图 enabled 推断。 */
98
+ export declare function initialTmwGameplayStateFromWorldV1(world: Readonly<Record<string, unknown>>): TmwGameplayStateV1;
99
+ export declare function canonicalTmwGameplayStateV1(input: TmwGameplayStateV1): TmwGameplayStateV1;
100
+ export declare function readTmwVariableV1(state: TmwGameplayStateV1, name: string, scope: TmwVariableScope): TmwVariableV1;
101
+ export declare function readTmwEntityStateV1(state: TmwGameplayStateV1, target: "node" | "edge", id: string): TmwEntityStateV1;
102
+ export declare function readTmwUnlockV1(state: TmwGameplayStateV1, target: "node" | "edge", id: string): boolean;
103
+ export declare function persistentTmwGameplayStateV1(state: TmwGameplayStateV1): TmwGameplayStateV1;
104
+ /** 返回 null 表示非玩法状态 capability;冻结期间的写入是成功的 no-op,不延迟重放。 */
105
+ export declare function reduceTmwGameplayEffectV1(current: TmwGameplayStateV1, capabilityId: string, input: unknown): TmwGameplayStateV1 | null;
106
+ export declare function reduceTmwGameplayEffectsV1(current: TmwGameplayStateV1, effects: readonly Readonly<{
107
+ capabilityId: string;
108
+ input: unknown;
109
+ }>[]): TmwGameplayStateV1;
110
+ /** 旧属性写入口必须先经过这个门,不能通过属性路径旁路冻结。 */
111
+ export declare function tmwWorldAttributeVisibilityOperationV1(effect: Readonly<{
112
+ capabilityId: string;
113
+ input: unknown;
114
+ selector?: unknown;
115
+ }>): Readonly<{
116
+ nodeId: string;
117
+ unlocked: boolean;
118
+ }> | null;
119
+ /** 所有 effect 共享有序暂存态;返回实际执行集合,忽略的冻结写入不进入外部 WAL。 */
120
+ export declare function prepareTmwGameplayEffectsV1<T extends Readonly<{
121
+ capabilityId: string;
122
+ input: unknown;
123
+ selector?: unknown;
124
+ }>>(current: TmwGameplayStateV1, effects: readonly T[]): Readonly<{
125
+ state: TmwGameplayStateV1;
126
+ effects: readonly T[];
127
+ }>;
@@ -0,0 +1,214 @@
1
+ import { canonicalTmwJsonValue, requireTmwIdentifier } from "./runtime-trace.js";
2
+ import { isTmwObjectRefV1, isTmwTypedValueV1, TMW_VALUE_TYPES } from "./value-graph.js";
3
+ const idSchema = { type: "string", minLength: 1, maxLength: 512 };
4
+ const scopeSchema = { enum: ["save", "execution"] };
5
+ const schema = (properties) => ({ type: "object", additionalProperties: false, required: Object.keys(properties), properties });
6
+ const objectRefSchema = Object.freeze({ type: "object", tmw_value_type: "object_ref", additionalProperties: false, required: ["kind", "id"], properties: { kind: { enum: ["node", "edge"] }, id: idSchema } });
7
+ /** 新统一算子只拥有自己的冻结来源;绝不清除旧算子留下的其它来源。 */
8
+ export const TMW_OBJECT_STATE_FREEZE_SOURCE_ID = "tmw.object_state";
9
+ export const TMW_GAMEPLAY_CAPABILITY_ARGUMENT_SCHEMAS = Object.freeze({
10
+ "variable.create": schema({ name: idSchema, scope: scopeSchema, valueType: { enum: [...TMW_VALUE_TYPES, "json"] }, value: {} }),
11
+ "variable.set": schema({ name: idSchema, scope: scopeSchema, value: {} }),
12
+ "variable.delete": schema({ name: idSchema, scope: scopeSchema }),
13
+ "node.set_unlock": schema({ nodeId: idSchema, unlocked: { type: "boolean" } }),
14
+ "edge.set_unlock": schema({ edgeId: idSchema, unlocked: { type: "boolean" } }),
15
+ "node.freeze": schema({ nodeId: idSchema, sourceId: idSchema }),
16
+ "node.unfreeze": schema({ nodeId: idSchema, sourceId: idSchema }),
17
+ "edge.freeze": schema({ edgeId: idSchema, sourceId: idSchema }),
18
+ "edge.unfreeze": schema({ edgeId: idSchema, sourceId: idSchema }),
19
+ "object.set_frozen": schema({ object: objectRefSchema, frozen: { type: "boolean" } }),
20
+ "object.set_unlock": schema({ object: objectRefSchema, unlocked: { type: "boolean" } }),
21
+ });
22
+ function valueForType(type, value) {
23
+ if (type !== "json" && !TMW_VALUE_TYPES.includes(type))
24
+ throw new Error("tmw_variable_type_invalid");
25
+ const canonical = canonicalTmwJsonValue(value, "variable_value");
26
+ if (type !== "json" && !isTmwTypedValueV1(type, canonical))
27
+ throw new Error("tmw_variable_value_type_mismatch");
28
+ return canonical;
29
+ }
30
+ function scopeOf(value) {
31
+ if (value !== "save" && value !== "execution")
32
+ throw new Error("tmw_variable_scope_invalid");
33
+ return value;
34
+ }
35
+ export function emptyTmwGameplayStateV1() {
36
+ return Object.freeze({ schemaVersion: 1, variables: Object.freeze([]), entity_state_stacks: Object.freeze({}) });
37
+ }
38
+ /** 作者试玩世界的初始显隐投影;显式玩法状态优先,不从 UI 访问权限或蓝图 enabled 推断。 */
39
+ export function initialTmwGameplayStateFromWorldV1(world) {
40
+ if (world.gameplayState !== undefined)
41
+ return canonicalTmwGameplayStateV1(world.gameplayState);
42
+ let state = emptyTmwGameplayStateV1();
43
+ for (const [field, target] of [["entities", "node"], ["relations", "edge"]]) {
44
+ const records = world[field];
45
+ if (!records || typeof records !== "object" || Array.isArray(records))
46
+ continue;
47
+ for (const [id, raw] of Object.entries(records)) {
48
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
49
+ continue;
50
+ const record = raw;
51
+ const initial = record.unlocked ?? record.visible ?? (record.kind === "available" ? true : record.kind === "unavailable" ? false : undefined);
52
+ if (initial === undefined)
53
+ continue;
54
+ if (typeof initial !== "boolean")
55
+ throw new Error("tmw_initial_visibility_invalid");
56
+ state = reduceTmwGameplayEffectV1(state, `${target}.set_unlock`, { [target === "node" ? "nodeId" : "edgeId"]: id, unlocked: initial });
57
+ }
58
+ }
59
+ return state;
60
+ }
61
+ export function canonicalTmwGameplayStateV1(input) {
62
+ if (!input || input.schemaVersion !== 1 || !Array.isArray(input.variables)
63
+ || !input.entity_state_stacks || typeof input.entity_state_stacks !== "object" || Array.isArray(input.entity_state_stacks))
64
+ throw new Error("tmw_gameplay_state_invalid");
65
+ const variables = input.variables.map((variable) => Object.freeze({
66
+ name: requireTmwIdentifier(variable.name, "variable_name"), scope: scopeOf(variable.scope),
67
+ valueType: variable.valueType, value: valueForType(variable.valueType, variable.value),
68
+ })).sort((a, b) => `${a.scope}:${a.name}`.localeCompare(`${b.scope}:${b.name}`));
69
+ if (new Set(variables.map((v) => `${v.scope}:${v.name}`)).size !== variables.length)
70
+ throw new Error("tmw_variable_duplicate");
71
+ const entries = Object.entries(input.entity_state_stacks).sort(([a], [b]) => a.localeCompare(b)).map(([ref, state]) => {
72
+ if (!/^(node|edge):.+$/u.test(ref) || !state || !["available", "unavailable", "frozen"].includes(state.kind)
73
+ || typeof state.active_state_id !== "string" || !Array.isArray(state.frozen_stack)
74
+ || (state.kind === "frozen") !== (state.frozen_stack.length > 0))
75
+ throw new Error("tmw_entity_state_invalid");
76
+ const stack = state.frozen_stack.map((entry, index) => {
77
+ const source_trigger_id = requireTmwIdentifier(entry.source_trigger_id, "freeze_source");
78
+ if (index === 0 && (entry.prev_kind !== "available" && entry.prev_kind !== "unavailable"))
79
+ throw new Error("tmw_entity_freeze_preimage_missing");
80
+ return Object.freeze({ source_trigger_id, ...(entry.prev_state_id === undefined ? {} : { prev_state_id: entry.prev_state_id }), ...(entry.prev_kind === undefined ? {} : { prev_kind: entry.prev_kind }) });
81
+ });
82
+ if (new Set(stack.map((s) => s.source_trigger_id)).size !== stack.length)
83
+ throw new Error("tmw_freeze_source_duplicate");
84
+ return [ref, Object.freeze({ active_state_id: state.active_state_id, kind: state.kind, frozen_stack: Object.freeze(stack) })];
85
+ });
86
+ return Object.freeze({ schemaVersion: 1, variables: Object.freeze(variables), entity_state_stacks: Object.freeze(Object.fromEntries(entries)) });
87
+ }
88
+ export function readTmwVariableV1(state, name, scope) {
89
+ const result = state.variables.find((item) => item.name === name && item.scope === scopeOf(scope));
90
+ if (!result)
91
+ throw new Error("tmw_variable_missing");
92
+ return result;
93
+ }
94
+ export function readTmwEntityStateV1(state, target, id) {
95
+ requireTmwIdentifier(id, "entity_state_id");
96
+ return state.entity_state_stacks[`${target}:${id}`] ?? Object.freeze({ active_state_id: "", kind: "available", frozen_stack: Object.freeze([]) });
97
+ }
98
+ export function readTmwUnlockV1(state, target, id) {
99
+ const entry = readTmwEntityStateV1(state, target, id);
100
+ return (entry.kind === "frozen" ? entry.frozen_stack[0]?.prev_kind : entry.kind) === "available";
101
+ }
102
+ function objectRefFromGameplayEffect(value) {
103
+ if (!isTmwObjectRefV1(value))
104
+ throw new Error("tmw_object_ref_invalid");
105
+ return Object.freeze({ kind: value.kind, id: requireTmwIdentifier(value.id, "object_ref_id") });
106
+ }
107
+ export function persistentTmwGameplayStateV1(state) {
108
+ return canonicalTmwGameplayStateV1({ ...state, variables: state.variables.filter((v) => v.scope === "save") });
109
+ }
110
+ /** 返回 null 表示非玩法状态 capability;冻结期间的写入是成功的 no-op,不延迟重放。 */
111
+ export function reduceTmwGameplayEffectV1(current, capabilityId, input) {
112
+ if (!Object.hasOwn(TMW_GAMEPLAY_CAPABILITY_ARGUMENT_SCHEMAS, capabilityId))
113
+ return null;
114
+ const args = canonicalTmwJsonValue(input, "gameplay_effect");
115
+ if (!args || typeof args !== "object" || Array.isArray(args))
116
+ throw new Error("tmw_gameplay_arguments_invalid");
117
+ const expected = TMW_GAMEPLAY_CAPABILITY_ARGUMENT_SCHEMAS[capabilityId].required;
118
+ if (Object.keys(args).sort().join() !== [...expected].sort().join())
119
+ throw new Error("tmw_gameplay_arguments_invalid");
120
+ const state = canonicalTmwGameplayStateV1(current);
121
+ if (capabilityId.startsWith("variable.")) {
122
+ const name = requireTmwIdentifier(args.name, "variable_name");
123
+ const scope = scopeOf(args.scope);
124
+ const found = state.variables.find((v) => v.name === name && v.scope === scope);
125
+ const rest = state.variables.filter((v) => v !== found);
126
+ if (capabilityId === "variable.create") {
127
+ if (found)
128
+ throw new Error("tmw_variable_duplicate");
129
+ return canonicalTmwGameplayStateV1({ ...state, variables: [...rest, { name, scope, valueType: args.valueType, value: args.value }] });
130
+ }
131
+ if (!found)
132
+ throw new Error("tmw_variable_missing");
133
+ return canonicalTmwGameplayStateV1({ ...state, variables: capabilityId === "variable.delete" ? rest : [...rest, { ...found, value: valueForType(found.valueType, args.value) }] });
134
+ }
135
+ const objectRef = capabilityId.startsWith("object.") ? objectRefFromGameplayEffect(args.object) : null;
136
+ const target = objectRef?.kind ?? (capabilityId.startsWith("edge.") ? "edge" : "node");
137
+ const id = objectRef?.id ?? requireTmwIdentifier((target === "node" ? args.nodeId : args.edgeId), "entity_state_id");
138
+ const entry = readTmwEntityStateV1(state, target, id);
139
+ let next = entry;
140
+ if (capabilityId.endsWith("set_unlock")) {
141
+ if (typeof args.unlocked !== "boolean")
142
+ throw new Error("tmw_unlock_boolean_required");
143
+ if (entry.kind === "frozen")
144
+ return state;
145
+ next = { ...entry, kind: args.unlocked ? "available" : "unavailable" };
146
+ }
147
+ else {
148
+ const source = capabilityId === "object.set_frozen" ? TMW_OBJECT_STATE_FREEZE_SOURCE_ID : requireTmwIdentifier(args.sourceId, "freeze_source");
149
+ const freeze = capabilityId === "object.set_frozen" ? args.frozen : capabilityId === "node.freeze" || capabilityId === "edge.freeze";
150
+ if (typeof freeze !== "boolean")
151
+ throw new Error("tmw_freeze_boolean_required");
152
+ if (freeze) {
153
+ if (entry.frozen_stack.some((item) => item.source_trigger_id === source))
154
+ return state;
155
+ next = { ...entry, kind: "frozen", frozen_stack: [...entry.frozen_stack, { source_trigger_id: source, ...(entry.kind === "frozen" ? {} : { prev_kind: entry.kind, prev_state_id: entry.active_state_id }) }] };
156
+ }
157
+ else {
158
+ const index = entry.frozen_stack.findIndex((item) => item.source_trigger_id === source);
159
+ if (index < 0)
160
+ return state;
161
+ const removed = entry.frozen_stack[index];
162
+ const stack = entry.frozen_stack.filter((_, i) => i !== index);
163
+ if (stack.length === 0)
164
+ next = { ...entry, kind: removed.prev_kind, active_state_id: removed.prev_state_id ?? entry.active_state_id, frozen_stack: [] };
165
+ else {
166
+ if (index === 0)
167
+ stack[0] = { ...stack[0], prev_kind: removed.prev_kind, prev_state_id: removed.prev_state_id ?? "" };
168
+ next = { ...entry, frozen_stack: stack };
169
+ }
170
+ }
171
+ }
172
+ return canonicalTmwGameplayStateV1({ ...state, entity_state_stacks: { ...state.entity_state_stacks, [`${target}:${id}`]: next } });
173
+ }
174
+ export function reduceTmwGameplayEffectsV1(current, effects) {
175
+ return effects.reduce((state, effect) => reduceTmwGameplayEffectV1(state, effect.capabilityId, effect.input) ?? state, current);
176
+ }
177
+ /** 旧属性写入口必须先经过这个门,不能通过属性路径旁路冻结。 */
178
+ export function tmwWorldAttributeVisibilityOperationV1(effect) {
179
+ if (effect.capabilityId !== "world.set_attribute")
180
+ return null;
181
+ const args = effect.input;
182
+ const selector = effect.selector;
183
+ const path = Array.isArray(args?.path) ? args.path.join(".") : args?.path;
184
+ if (path !== "unlocked" && path !== "visible" && path !== "visibility" && path !== "kind")
185
+ return null;
186
+ if (selector?.op !== "entity_id")
187
+ throw new Error("tmw_visibility_entity_selector_required");
188
+ const nodeId = requireTmwIdentifier(selector.entity_id, "visibility_node_id");
189
+ if (path === "kind") {
190
+ if (args.value !== "available" && args.value !== "unavailable")
191
+ throw new Error("tmw_visibility_kind_invalid");
192
+ return { nodeId, unlocked: args.value === "available" };
193
+ }
194
+ if (typeof args.value !== "boolean")
195
+ throw new Error("tmw_visibility_boolean_required");
196
+ return { nodeId, unlocked: args.value };
197
+ }
198
+ /** 所有 effect 共享有序暂存态;返回实际执行集合,忽略的冻结写入不进入外部 WAL。 */
199
+ export function prepareTmwGameplayEffectsV1(current, effects) {
200
+ let state = current;
201
+ const accepted = [];
202
+ for (const effect of effects) {
203
+ const visibility = tmwWorldAttributeVisibilityOperationV1(effect);
204
+ if (visibility !== null) {
205
+ if (readTmwEntityStateV1(state, "node", visibility.nodeId).kind === "frozen")
206
+ continue;
207
+ state = reduceTmwGameplayEffectV1(state, "node.set_unlock", visibility);
208
+ }
209
+ else
210
+ state = reduceTmwGameplayEffectV1(state, effect.capabilityId, effect.input) ?? state;
211
+ accepted.push(effect);
212
+ }
213
+ return Object.freeze({ state, effects: Object.freeze(accepted) });
214
+ }