@mindot/will 0.1.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 (178) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +777 -0
  3. package/dist/index.d.ts +8144 -0
  4. package/dist/index.js +26231 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +73 -0
  7. package/src/cognition/agency/access.grants.ts +59 -0
  8. package/src/cognition/agency/competence.codec.ts +89 -0
  9. package/src/cognition/agency/engines/action.selector.ts +462 -0
  10. package/src/cognition/agency/engines/affordance.synthesizer.ts +384 -0
  11. package/src/cognition/agency/engines/deliberation.engine.ts +241 -0
  12. package/src/cognition/agency/engines/instruction.intake.ts +54 -0
  13. package/src/cognition/agency/engines/motor.schema.executor.ts +532 -0
  14. package/src/cognition/agency/engines/reafference.engine.ts +241 -0
  15. package/src/cognition/agency/execution.primitives.ts +118 -0
  16. package/src/cognition/agency/index.ts +58 -0
  17. package/src/cognition/agency/proactive.communicator.ts +274 -0
  18. package/src/cognition/agency/reconcile.learning.ts +82 -0
  19. package/src/cognition/agency/schemas/external.ts +59 -0
  20. package/src/cognition/agency/schemas/innate.ts +111 -0
  21. package/src/cognition/agency/schemas/repertoire.ts +246 -0
  22. package/src/cognition/agency/selection.scoring.ts +191 -0
  23. package/src/cognition/agency/types.ts +165 -0
  24. package/src/cognition/bus.ts +391 -0
  25. package/src/cognition/completion.inbox.ts +86 -0
  26. package/src/cognition/config.mirror.entities.ts +475 -0
  27. package/src/cognition/conversation.memory.ts +81 -0
  28. package/src/cognition/event.log.ts +116 -0
  29. package/src/cognition/event.schemas.ts +568 -0
  30. package/src/cognition/faculties/aesthetic.evaluator.ts +313 -0
  31. package/src/cognition/faculties/affective.blender.ts +562 -0
  32. package/src/cognition/faculties/attachment.evaluator.ts +432 -0
  33. package/src/cognition/faculties/attention.allocator.ts +422 -0
  34. package/src/cognition/faculties/autobiographical.narrator.ts +300 -0
  35. package/src/cognition/faculties/bias.detector.ts +363 -0
  36. package/src/cognition/faculties/circadian.oscillator.ts +294 -0
  37. package/src/cognition/faculties/confidence.calibrator.ts +324 -0
  38. package/src/cognition/faculties/dream.simulator.ts +251 -0
  39. package/src/cognition/faculties/empathy.simulator.ts +253 -0
  40. package/src/cognition/faculties/energy.regulator.ts +320 -0
  41. package/src/cognition/faculties/episodic.consolidator.ts +728 -0
  42. package/src/cognition/faculties/executive.engine/commands.ts +459 -0
  43. package/src/cognition/faculties/executive.engine/config.ts +46 -0
  44. package/src/cognition/faculties/executive.engine/context.ts +605 -0
  45. package/src/cognition/faculties/executive.engine/deferred.effects.ts +104 -0
  46. package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +60 -0
  47. package/src/cognition/faculties/executive.engine/effort.gate.ts +114 -0
  48. package/src/cognition/faculties/executive.engine/engine.ts +1028 -0
  49. package/src/cognition/faculties/executive.engine/escalation.buffer.ts +90 -0
  50. package/src/cognition/faculties/executive.engine/facet.supervisor.ts +271 -0
  51. package/src/cognition/faculties/executive.engine/facet.ts +628 -0
  52. package/src/cognition/faculties/executive.engine/gating.ts +222 -0
  53. package/src/cognition/faculties/executive.engine/index.ts +6 -0
  54. package/src/cognition/faculties/executive.engine/messages.ts +102 -0
  55. package/src/cognition/faculties/executive.engine/parser.ts +380 -0
  56. package/src/cognition/faculties/executive.engine/prompt.factory.ts +1053 -0
  57. package/src/cognition/faculties/executive.engine/types.ts +323 -0
  58. package/src/cognition/faculties/exteroception.ts +338 -0
  59. package/src/cognition/faculties/forgetting.curve.ts +202 -0
  60. package/src/cognition/faculties/frustration.evaluator.ts +280 -0
  61. package/src/cognition/faculties/goal.manager.ts +1008 -0
  62. package/src/cognition/faculties/inhibition.controller.ts +351 -0
  63. package/src/cognition/faculties/interoception.ts +417 -0
  64. package/src/cognition/faculties/introspection.engine.ts +246 -0
  65. package/src/cognition/faculties/known.entity.tracker.ts +439 -0
  66. package/src/cognition/faculties/loss.evaluator.ts +224 -0
  67. package/src/cognition/faculties/moral.evaluator.ts +437 -0
  68. package/src/cognition/faculties/novelty.detector.ts +254 -0
  69. package/src/cognition/faculties/persona.consolidator.ts +845 -0
  70. package/src/cognition/faculties/planning.engine/engine.ts +859 -0
  71. package/src/cognition/faculties/planning.engine/plan.frontier.ts +83 -0
  72. package/src/cognition/faculties/planning.engine/plan.store.ts +174 -0
  73. package/src/cognition/faculties/planning.engine/plan.supervision.ts +494 -0
  74. package/src/cognition/faculties/planning.engine/types.ts +143 -0
  75. package/src/cognition/faculties/reputation.tracker.ts +294 -0
  76. package/src/cognition/faculties/reward.evaluator.ts +304 -0
  77. package/src/cognition/faculties/self.model.updater.ts +677 -0
  78. package/src/cognition/faculties/semantic.engine/clustering.ts +673 -0
  79. package/src/cognition/faculties/semantic.engine/index.ts +6 -0
  80. package/src/cognition/faculties/semantic.engine/integrator.ts +750 -0
  81. package/src/cognition/faculties/semantic.engine/types.ts +53 -0
  82. package/src/cognition/faculties/sleep.pressure.regulator.ts +262 -0
  83. package/src/cognition/faculties/social.perception.ts +333 -0
  84. package/src/cognition/faculties/spaced.repetition.ts +605 -0
  85. package/src/cognition/faculties/stress.regulator.ts +375 -0
  86. package/src/cognition/faculties/task.switcher.ts +239 -0
  87. package/src/cognition/faculties/theory.of.mind.ts +333 -0
  88. package/src/cognition/faculties/threat.evaluator.ts +312 -0
  89. package/src/cognition/faculties/working.memory.ts +406 -0
  90. package/src/cognition/generative.model.ts +344 -0
  91. package/src/cognition/heartbeat.ts +70 -0
  92. package/src/cognition/index.ts +291 -0
  93. package/src/cognition/instruction.handler.ts +146 -0
  94. package/src/cognition/memory/index.ts +35 -0
  95. package/src/cognition/memory/vector.adapter.ts +316 -0
  96. package/src/cognition/memory/vector.content.ts +47 -0
  97. package/src/cognition/memory/vector.embedder.ts +212 -0
  98. package/src/cognition/memory/vector.index.ts +459 -0
  99. package/src/cognition/memory/vector.types.ts +65 -0
  100. package/src/cognition/orchestrator.ts +142 -0
  101. package/src/cognition/persona.prior.ts +265 -0
  102. package/src/cognition/schema.registry.ts +145 -0
  103. package/src/cognition/senses/audition.engine/engine.ts +976 -0
  104. package/src/cognition/senses/audition.engine/salience.ts +59 -0
  105. package/src/cognition/senses/base.sense.engine.ts +117 -0
  106. package/src/cognition/senses/gustation.engine.ts +24 -0
  107. package/src/cognition/senses/index.ts +172 -0
  108. package/src/cognition/senses/olfaction.engine.ts +24 -0
  109. package/src/cognition/senses/somatosensation.engine.ts +23 -0
  110. package/src/cognition/senses/vision.engine.ts +23 -0
  111. package/src/cognition/types.ts +63 -0
  112. package/src/cognition/utilities/token.tracker.ts +492 -0
  113. package/src/core/abstracts.ts +60 -0
  114. package/src/core/async.engine.ts +539 -0
  115. package/src/core/clock.ts +196 -0
  116. package/src/core/completion.recorder.ts +122 -0
  117. package/src/core/conflict.detector.ts +140 -0
  118. package/src/core/distributed.ts +626 -0
  119. package/src/core/event.bus.ts +292 -0
  120. package/src/core/inbound.recorder.ts +100 -0
  121. package/src/core/index.ts +167 -0
  122. package/src/core/logger.ts +85 -0
  123. package/src/core/metrics.ts +107 -0
  124. package/src/core/orchestrator.ts +625 -0
  125. package/src/core/replay.ts +589 -0
  126. package/src/core/scenario.ts +120 -0
  127. package/src/core/serialization.ts +448 -0
  128. package/src/core/simulation.ts +197 -0
  129. package/src/core/snapshot.manager.ts +293 -0
  130. package/src/core/state.manager.ts +283 -0
  131. package/src/core/types.ts +274 -0
  132. package/src/core/utils.ts +82 -0
  133. package/src/core/wall.clock.ts +23 -0
  134. package/src/extensions/livestream.ext.ts +570 -0
  135. package/src/extensions/time.ext.ts +339 -0
  136. package/src/index.ts +91 -0
  137. package/src/llm/gate.ts +140 -0
  138. package/src/llm/index.ts +716 -0
  139. package/src/llm/summarizer.ts +170 -0
  140. package/src/llm/wire.contracts.ts +67 -0
  141. package/src/pma/eval.ts +651 -0
  142. package/src/pma/index.ts +1082 -0
  143. package/src/profiles/built-in.ts +7 -0
  144. package/src/profiles/companion.ts +28 -0
  145. package/src/profiles/company-brain.ts +54 -0
  146. package/src/profiles/customer-service.ts +30 -0
  147. package/src/profiles/game-npc.ts +20 -0
  148. package/src/profiles/index.ts +42 -0
  149. package/src/profiles/smart-home.ts +34 -0
  150. package/src/runners/coherence.runner.ts +49 -0
  151. package/src/runners/outreach.runner.ts +158 -0
  152. package/src/runners/social.runner.ts +182 -0
  153. package/src/runners/thin-shim.runner.ts +195 -0
  154. package/src/sdk/will.ts +334 -0
  155. package/src/stem/assembly.audit.ts +88 -0
  156. package/src/stem/distribution.ts +203 -0
  157. package/src/stem/guards/identity.coherence.ts +152 -0
  158. package/src/stem/guards/identity.guard.ts +227 -0
  159. package/src/stem/index.ts +1145 -0
  160. package/src/stem/mind.ts +1090 -0
  161. package/src/stem/tracts/ack.reconciler.ts +55 -0
  162. package/src/stem/tracts/biography.writer.ts +190 -0
  163. package/src/stem/tracts/effector.controller.ts +143 -0
  164. package/src/stem/tracts/health.reporter.ts +113 -0
  165. package/src/stem/tracts/inbound.queue.ts +54 -0
  166. package/src/stem/tracts/outbox.controller.ts +167 -0
  167. package/src/stem/tracts/outbox.writer.ts +133 -0
  168. package/src/stem/tracts/pma.controller.ts +109 -0
  169. package/src/stem/tracts/replay.controller.ts +177 -0
  170. package/src/stem/tracts/sensory.controller.ts +168 -0
  171. package/src/stem/tracts/session.logger.ts +157 -0
  172. package/src/stem/tracts/transport/index.ts +38 -0
  173. package/src/stem/tracts/transport/loopback.transport.ts +86 -0
  174. package/src/stem/tracts/transport/socketio.transport.ts +200 -0
  175. package/src/stem/tracts/transport/stream.transport.ts +152 -0
  176. package/src/stem/tracts/transport/types.ts +193 -0
  177. package/src/stem/tracts/transport.controller.ts +380 -0
  178. package/src/types.ts +116 -0
@@ -0,0 +1,274 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/core/types.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * Core simulation framework types — completely generic.
7
+ * No logistics, mobility, or domain concepts here.
8
+ */
9
+
10
+ export type Timestamp = number // milliseconds since epoch
11
+ export type Duration = number // milliseconds
12
+ export type Tick = number // discrete simulation step
13
+
14
+ export interface Coordinates {
15
+ x: number
16
+ y: number
17
+ z?: number
18
+ }
19
+
20
+ // ── PRNG ─────────────────────────────────────────────────────
21
+
22
+ /**
23
+ * Seeded pseudo-random number generator (Mulberry32).
24
+ * Provides deterministic randomness for simulation replay.
25
+ * All engines should draw from SimulationContext.prng rather
26
+ * than Math.random() to guarantee replay fidelity.
27
+ */
28
+
29
+ export interface SeededPRNG {
30
+ /** Returns a float in [0, 1) */
31
+ next(): number
32
+ /** Returns an integer in [min, max) */
33
+ nextInt( min: number, max: number ): number
34
+ /** Returns true with the given probability (default 0.5) */
35
+ nextBool( probability?: number ): boolean
36
+ /** Current internal seed state — useful for snapshotting */
37
+ readonly state: number
38
+ }
39
+
40
+ // ── Deep readonly utility ────────────────────────────────────
41
+
42
+ /**
43
+ * Recursively marks every property, Map, Set, and Array as readonly.
44
+ * Used to enforce the double-buffer contract: engines receive a frozen
45
+ * snapshot and may only produce changes via StateCommands.
46
+ */
47
+ export type ReadonlyDeep<T> =
48
+ T extends ( infer U )[]
49
+ ? ReadonlyArray<ReadonlyDeep<U>>
50
+ : T extends Map<infer K, infer V>
51
+ ? ReadonlyMap<K, ReadonlyDeep<V>>
52
+ : T extends Set<infer U>
53
+ ? ReadonlySet<ReadonlyDeep<U>>
54
+ : T extends object
55
+ ? { readonly [K in keyof T]: ReadonlyDeep<T[K]> }
56
+ : T
57
+
58
+ // ── Event type system (discriminated union) ──────────────────
59
+
60
+ /**
61
+ * Base interface that all simulation events extend.
62
+ * The 'type' field acts as a discriminated union key.
63
+ */
64
+ export interface SimulationEventBase {
65
+ id: string
66
+ type: string
67
+ timestamp: Timestamp
68
+ tick: Tick
69
+ source: string
70
+ }
71
+
72
+ /**
73
+ * Typed simulation event with payload and metadata.
74
+ * T = payload type, M = metadata type.
75
+ */
76
+ export interface SimulationEvent<T = unknown, M = Record<string, unknown>> extends SimulationEventBase {
77
+ payload: T
78
+ metadata?: M
79
+ }
80
+
81
+ /**
82
+ * Helper to extract payload type from a specific event type string.
83
+ * Usage: type MyPayload = EventPayload<'entity.created'>
84
+ */
85
+ export type EventPayload<
86
+ T extends string,
87
+ E extends SimulationEvent = SimulationEvent
88
+ > = E extends { type: T; payload: infer P } ? P : never
89
+
90
+ // ── Core domain types ────────────────────────────────────────
91
+
92
+ export interface SimulationContext {
93
+ simulationId: string
94
+ runId: string
95
+ tags: Record<string, string>
96
+ /**
97
+ * Seeded PRNG for deterministic randomness.
98
+ * Engines must use this instead of Math.random() to guarantee
99
+ * that a run is fully reproducible from the same seed.
100
+ */
101
+ prng: SeededPRNG
102
+ }
103
+
104
+ export interface SimulationEntity {
105
+ id: string
106
+ type: string
107
+ createdAt: Timestamp
108
+ updatedAt: Timestamp
109
+ /**
110
+ * Sim tick of the last write (stamped by StateManager.setEntity).
111
+ * Distinct from `updatedAt` (wall-clock epoch ms). ConflictDetector compares
112
+ * this tick-vs-tick against a footprint's `tickObserved`; comparing `updatedAt`
113
+ * (ms) to a tick would always register a conflict.
114
+ */
115
+ updatedAtTick?: Tick
116
+ metadata?: Record<string, unknown>
117
+ }
118
+
119
+ /**
120
+ * The write-side shape engines produce for an entity (via StateCommands.set or
121
+ * a direct setEntity call). `createdAt`/`updatedAt`/`updatedAtTick` are optional
122
+ * because StateManager.setEntity is the single authority that stamps them from
123
+ * the sim clock — engines must not supply wall-clock timestamps, which would
124
+ * break replay (R2). A full SimulationEntity is assignable to EntityInput, so
125
+ * existing callers that pass complete entities keep working.
126
+ */
127
+ export type EntityInput =
128
+ Omit<SimulationEntity, 'createdAt' | 'updatedAt' | 'updatedAtTick'>
129
+ & Partial<Pick<SimulationEntity, 'createdAt' | 'updatedAt' | 'updatedAtTick'>>
130
+
131
+ export interface SimulationState {
132
+ tick: Tick
133
+ time: Timestamp
134
+ entities: Map<string, SimulationEntity>
135
+ metrics: Map<string, number>
136
+ }
137
+
138
+ /**
139
+ * Configuration for partial state restore operations.
140
+ * Support selective rollback — entities only,
141
+ * metrics only, or clock state independently.
142
+ */
143
+ export interface RestoreOptions {
144
+ /** Restore entity state (default: true) */
145
+ entities?: boolean
146
+ /** Restore metric values (default: true) */
147
+ metrics?: boolean
148
+ /** Restore clock tick and time (default: true) */
149
+ clock?: boolean
150
+ }
151
+
152
+ /**
153
+ * The frozen view of SimulationState passed to engines each tick.
154
+ * Engines read from this; writes go through StateCommands only.
155
+ */
156
+ export type ReadonlySimulationState = ReadonlyDeep<SimulationState>
157
+
158
+ /**
159
+ * The write side of the double-buffer contract.
160
+ * Engines return this from react() instead of mutating state directly.
161
+ * All commands from all engines are applied atomically after every engine
162
+ * has finished reading the frozen snapshot.
163
+ *
164
+ * metrics entries are either:
165
+ * [key, number] — set the metric to an absolute value
166
+ * [key, { delta: number }] — increment by delta (positive or negative)
167
+ */
168
+ export interface StateCommands {
169
+ set?: EntityInput[]
170
+ delete?: string[]
171
+ metrics?: Array<[ string, number]>
172
+ }
173
+
174
+ // Used by orchestrator for step/run methods
175
+ export interface MinimalContext {
176
+ simulationId: string
177
+ runId: string
178
+ seed?: number
179
+ }
180
+
181
+ // ── Async Reasoning Types ─────────────────────────────────────
182
+
183
+ /**
184
+ * Captures what an async engine observed when it began reasoning.
185
+ * Used by ConflictDetector to validate that the world hasn't changed
186
+ * in ways that invalidate the engine's intended commands.
187
+ */
188
+ export interface ReasoningFootprint {
189
+ /** The tick when the engine read its snapshot */
190
+ readonly tickObserved: Tick
191
+ /** Entity IDs the engine read during reasoning */
192
+ readonly entitiesRead: ReadonlySet<string>
193
+ /** Metric keys the engine read during reasoning */
194
+ readonly metricsRead: ReadonlySet<string>
195
+ /** Entity IDs the engine intends to modify */
196
+ readonly entitiesModified: ReadonlySet<string>
197
+ /** The commands the engine wants to apply */
198
+ readonly intendedCommands: StateCommands
199
+ /** Optional: engine that produced this footprint (for debugging) */
200
+ readonly source?: string
201
+ }
202
+
203
+ export interface ConflictReport {
204
+ /** Whether any conflicts were detected */
205
+ readonly hasConflicts: boolean
206
+ /** Read conflicts: entities/metrics observed that have since changed */
207
+ readonly readConflicts: string[]
208
+ /** Write conflicts: entities the engine wants to modify that others modified */
209
+ readonly writeConflicts: string[]
210
+ /** The tick when conflict detection ran */
211
+ readonly detectedAtTick: Tick
212
+ /** The footprint that was checked */
213
+ readonly footprint: ReasoningFootprint
214
+ }
215
+
216
+ export type ConflictStrategy = 'REJECT' | 'MERGE' | 'FORCE'
217
+
218
+ export interface ConflictResolution {
219
+ /** The strategy used to resolve conflicts */
220
+ readonly strategy: ConflictStrategy
221
+ /** If MERGE, the subset of commands that passed validation */
222
+ readonly resolvedCommands: StateCommands | null
223
+ /** If REJECT, whether to re-run the engine with current state */
224
+ readonly shouldRerun: boolean
225
+ /** Human-readable explanation of what happened */
226
+ readonly reason: string
227
+ }
228
+
229
+ /**
230
+ * Configuration for async engine behavior within the orchestrator.
231
+ */
232
+ export interface AsyncEngineConfig {
233
+ /** Default conflict strategy for this engine */
234
+ readonly defaultStrategy?: ConflictStrategy
235
+ /** Maximum ticks a reasoning can be pending before being auto-rejected */
236
+ readonly maxPendingTicks?: number
237
+ /** Whether to log conflict details for debugging */
238
+ readonly logConflicts?: boolean
239
+ /** Re-run on rejection (if false, commands are silently dropped) */
240
+ readonly rerunOnRejection?: boolean
241
+ }
242
+
243
+ // ── Homeostatic Types ──────────────────────────────────────────
244
+
245
+ /**
246
+ * A homeostatic drive signal emitted by regulatory engines.
247
+ * Drives represent survival-relevant needs that demand attention.
248
+ */
249
+ export interface DriveSignal {
250
+ /** Drive name (e.g., 'energy', 'sleep', 'safety') */
251
+ readonly name: string
252
+ /** Current intensity 0-1 (0 = satisfied, 1 = critically deprived) */
253
+ readonly intensity: number
254
+ /** How quickly this is escalating 0-1 (0 = stable, 1 = rapidly worsening) */
255
+ readonly urgency: number
256
+ /** The engine that produced this signal */
257
+ readonly source: string
258
+ }
259
+
260
+ /**
261
+ * A modulation signal that adjusts parameters in other engines.
262
+ * Regulatory engines emit these to tune cognitive function based
263
+ * on homeostatic state (e.g., high sleep pressure degrades attention).
264
+ */
265
+ export interface ModulationSignal {
266
+ /** Target engine name (or '*' for all engines) */
267
+ readonly target: string
268
+ /** Parameter to modulate */
269
+ readonly parameter: string
270
+ /** Multiplier to apply (1.0 = no change, 0.5 = halved, 2.0 = doubled) */
271
+ readonly factor: number
272
+ /** The engine that produced this signal */
273
+ readonly source: string
274
+ }
@@ -0,0 +1,82 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/core/utils.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ import type { SeededPRNG, SimulationContext } from '#core/types'
6
+ import { wallClock } from '#core/wall.clock'
7
+
8
+ // ── PRNG ─────────────────────────────────────────────────────
9
+
10
+ /**
11
+ * Creates a Mulberry32 PRNG from the given seed.
12
+ * Mulberry32 is a fast, high-quality 32-bit generator
13
+ * with a full 2^32 period.
14
+ */
15
+ export function createPRNG( seed: number ): SeededPRNG {
16
+ // Ensure unsigned 32-bit start state
17
+ let s = seed >>> 0
18
+
19
+ function next(): number {
20
+ s += 0x6D2B79F5
21
+ let t = Math.imul( s ^ ( s >>> 15 ), 1 | s )
22
+ t = ( t + Math.imul( t ^ ( t >>> 7 ), 61 | t ) ) ^ t
23
+ return ( ( t ^ ( t >>> 14 ) ) >>> 0 ) / 4294967296
24
+ }
25
+
26
+ return {
27
+ get state(){ return s },
28
+ next,
29
+ nextInt: ( min, max ) => Math.floor( next() * ( max - min ) ) + min,
30
+ nextBool: ( probability = 0.5 ) => next() < probability,
31
+ }
32
+ }
33
+
34
+ // ── Immutability ─────────────────────────────────────────────
35
+
36
+ /**
37
+ * Recursively `Object.freeze` a value and everything reachable from it, then
38
+ * return the same reference. Primitives (and `null`) pass through untouched.
39
+ *
40
+ * This is the runtime half of the double-buffer contract (R3): the type system
41
+ * marks the per-tick state snapshot `ReadonlyDeep`, but that is erased at
42
+ * runtime — a careless `entity.foo = x` on a value an engine *read* would
43
+ * silently corrupt the shared state and break determinism invisibly. Freezing
44
+ * the value turns that mutation into a loud `TypeError` (all ESM is strict
45
+ * mode) the instant it happens, so the "read-only snapshot" guarantee is
46
+ * enforced, not merely conventional.
47
+ *
48
+ * Cycle-safe: the container is frozen *before* its children are visited, so a
49
+ * reference that points back into an already-frozen object short-circuits on
50
+ * the `isFrozen` guard rather than recursing forever. Idempotent — re-freezing
51
+ * an already-frozen graph is a cheap no-op.
52
+ */
53
+ export function deepFreeze<T>( value: T ): T {
54
+ // Only objects/arrays/functions are freezable; primitives are immutable already.
55
+ if( value === null || typeof value !== 'object' ) return value
56
+ if( Object.isFrozen( value ) ) return value
57
+
58
+ // Freeze the container first so a cyclic reference back to it hits the
59
+ // isFrozen guard above on re-entry (prevents infinite recursion).
60
+ Object.freeze( value )
61
+
62
+ for( const key of Object.keys( value as Record<string, unknown> ) )
63
+ deepFreeze( ( value as Record<string, unknown> )[ key ] )
64
+
65
+ return value
66
+ }
67
+
68
+ // ── Context helpers ──────────────────────────────────────────
69
+
70
+ /**
71
+ * Creates a minimal SimulationContext with required prng.
72
+ * Use for standalone operations (tests, step calls) where a full
73
+ * simulation is not running.
74
+ */
75
+ export function createContext( simulationId: string, runId: string, seed?: number ): SimulationContext {
76
+ return {
77
+ simulationId,
78
+ runId,
79
+ tags: {},
80
+ prng: createPRNG( seed ?? wallClock() )
81
+ }
82
+ }
@@ -0,0 +1,23 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/core/wall.clock.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * The single sanctioned wall-clock boundary (R2).
7
+ *
8
+ * The determinism guard (tests/unit/determinism.guard.test.ts) bans bare
9
+ * `Date.now()` / `new Date` in `src/core` and `src/cognition`, because anything
10
+ * that feeds replayable state must read the *sim* clock (`SimulationState.time`
11
+ * or the tick) so a run reproduces byte-for-byte from the same seed.
12
+ *
13
+ * Some reads, though, are intentionally NON-replay-affecting — perf timing,
14
+ * idle detection, network/RPC metadata, snapshot ids, seed defaults. Those go
15
+ * through `wallClock()` so the intent is explicit and greppable, and so the one
16
+ * real `Date.now()` call lives in exactly one auditable place.
17
+ *
18
+ * Rule of thumb: if the value can ever land in the event log, a snapshot, or an
19
+ * entity field, it must NOT come from here — use the sim clock instead.
20
+ */
21
+ export function wallClock(): number {
22
+ return Date.now() // determinism-ok: this function is the wall-clock boundary
23
+ }