@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,344 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/generative.model.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * GenerativeModel — per-engine probabilistic prediction model.
7
+ *
8
+ * Implements a lightweight active-inference generative model for each engine:
9
+ * - Maintains EMA (exponential moving average) predictions per signal stream
10
+ * - Computes prediction error as |actual − predicted| / adaptive scale
11
+ * - Supports top-down anticipatory updates via anticipate()
12
+ * - Tracks prediction accuracy per stream (Welford online variance)
13
+ *
14
+ * Usage pattern:
15
+ * 1. Engines call predict(signal) at the start of react() to get the
16
+ * expected value for this tick.
17
+ * 2. After computing the actual value, call observe(signal, actual) to get
18
+ * the normalized prediction error and update the model.
19
+ * 3. If prediction error < GATE_THRESHOLD the engine may skip heavy
20
+ * computation (activation gating / energy saving).
21
+ * 4. ExecutiveEngine calls anticipate(signal, value, confidence) to inject
22
+ * top-down priors — the model blends the anticipation with the EMA.
23
+ *
24
+ * Design notes:
25
+ * - All parameters are per-stream; different signals can have different
26
+ * learning rates or value ranges.
27
+ * - Thread-safe in a single-threaded JS event loop.
28
+ * - No external dependencies.
29
+ */
30
+
31
+ // ── Defaults ─────────────────────────────────────────────────
32
+
33
+ const DEFAULT_ALPHA = 0.15 // EMA learning rate
34
+ const DEFAULT_RANGE = 0 // 0 ⇒ adaptive scale (learned per-stream from observed deviations)
35
+ const GATE_THRESHOLD = 0.05 // Normalized error below which processing can be gated
36
+ const ANTICIPATION_BLEND = 0.3 // How much top-down anticipation shifts the prior
37
+ // Adaptive normalisation: scale = SURPRISE_SCALE × mean-absolute-deviation (madEma).
38
+ // Paired with the default GATE_THRESHOLD this puts the gate at ~1 typical deviation,
39
+ // so a larger-than-usual error reads as surprising regardless of the signal's magnitude.
40
+ const SURPRISE_SCALE = 20
41
+ const SCALE_FLOOR = 1e-9 // guards against 0/0 on a perfectly constant signal
42
+
43
+ // ── Salience normalisation (merged from SalienceComputer) ────
44
+ // `salience` is a second view of the same prediction error, scaled against an
45
+ // exponentially-weighted variance and modulated by a top-down precision weight.
46
+ // Kept on a SEPARATE scale from gating (madEma×SURPRISE_SCALE) so neither
47
+ // behaviour changes — gating answers "should I compute?", salience answers "how
48
+ // much should this grab the Global Workspace?".
49
+ const NORMALISE_SCALE = 3 // |error| / (stdDev × NORMALISE_SCALE) → clamp [0,1]
50
+ const SALIENCE_EPSILON = 1e-6 // prevents div-by-zero on a zero-variance series
51
+ const PRECISION_MIN = 0.1 // precision weight bounds
52
+ const PRECISION_MAX = 3.0
53
+ const PRECISION_DECAY = 0.02 // mean-reversion of precision toward 1.0 per observation
54
+
55
+ // ── Types ─────────────────────────────────────────────────────
56
+
57
+ export interface PredictionError {
58
+ /** Raw absolute error |actual − predicted| */
59
+ error: number
60
+ /** Normalized error in [0, 1] relative to the signal's expected range (gating basis) */
61
+ normalized: number
62
+ /** Whether this signal was gated (error below threshold) */
63
+ gated: boolean
64
+ /**
65
+ * Precision-weighted surprise in [0, 1] for Global-Workspace ranking — the
66
+ * value the old SalienceComputer.observe() returned. Scaled against the
67
+ * stream's exponentially-weighted variance × its precision weight.
68
+ */
69
+ salience: number
70
+ }
71
+
72
+ export interface StreamConfig {
73
+ /** EMA learning rate (0–1) for the *prediction*. Higher = faster adaptation. Default 0.15. */
74
+ alpha?: number
75
+ /**
76
+ * EMA learning rate (0–1) for the learned *deviation scale* — the gating basis
77
+ * (madEma) and the salience variance basis (m2). Decoupled from `alpha` so the
78
+ * scale can track at a different rate than the prediction (e.g. a fast-adapting
79
+ * prediction over a slowly-drifting noise floor). Defaults to `alpha`, so an
80
+ * unset value preserves the original coupled behaviour exactly.
81
+ */
82
+ scaleAlpha?: number
83
+ /** Fixed full-scale range for normalization. Omit for adaptive scale (learned per-stream). */
84
+ range?: number
85
+ /** Custom gate threshold override. Default GATE_THRESHOLD (0.05). */
86
+ gateThreshold?: number
87
+ }
88
+
89
+ interface StreamState {
90
+ /** Current EMA prediction */
91
+ prediction: number
92
+ /** EMA of the normalized error in [0,1] — the running accuracy/surprise metric */
93
+ errorEma: number
94
+ /** EMA of the raw absolute deviation |actual − prediction| — the learned gating scale */
95
+ madEma: number
96
+ /** EMA of squared error — the adaptive variance basis for `salience` (merged from SalienceComputer) */
97
+ m2: number
98
+ /** EMA learning rate for the prediction */
99
+ alpha: number
100
+ /** EMA learning rate for the learned deviation scale (madEma + m2). Defaults to `alpha`. */
101
+ scaleAlpha: number
102
+ range: number
103
+ gateThreshold: number
104
+ /** Number of observations */
105
+ n: number
106
+ /** Tracks whether a top-down anticipation is active this tick */
107
+ anticipationWeight: number
108
+ }
109
+
110
+ /**
111
+ * Serializable, restorable snapshot of a GenerativeModel's full mutable state
112
+ * (FN9). Unlike snapshotAll() — which is a lossy human-readable summary — this
113
+ * captures every field needed to resume prediction identically after a restore.
114
+ * The Map is flattened to entry arrays so the payload is plain-JSON-safe.
115
+ */
116
+ export interface GenerativeModelSnapshot {
117
+ defaultAlpha: number
118
+ defaultRange: number
119
+ streams: Array<[ string, StreamState ]>
120
+ /** Per-stream top-down precision weights (merged from SalienceComputer). */
121
+ precision?: Array<[ string, number ]>
122
+ }
123
+
124
+ // ── GenerativeModel ───────────────────────────────────────────
125
+
126
+ export class GenerativeModel {
127
+ private _streams = new Map<string, StreamState>()
128
+ // Per-stream precision weights (top-down attention). Kept separate from
129
+ // StreamState so a precision set before the first observe() survives, matching
130
+ // the old SalienceComputer semantics.
131
+ private _precision = new Map<string, number>()
132
+ private _defaultAlpha: number
133
+ private _defaultRange: number
134
+
135
+ constructor( defaultAlpha = DEFAULT_ALPHA, defaultRange = DEFAULT_RANGE ){
136
+ this._defaultAlpha = defaultAlpha
137
+ this._defaultRange = defaultRange
138
+ }
139
+
140
+ /**
141
+ * Configure per-stream parameters. Call at engine construction time.
142
+ * Streams not configured use defaults.
143
+ */
144
+ configureStream( signal: string, config: StreamConfig ): void {
145
+ const existing = this._streams.get( signal )
146
+ if( existing ){
147
+ if( config.alpha != null ) existing.alpha = config.alpha
148
+ if( config.scaleAlpha != null ) existing.scaleAlpha = config.scaleAlpha
149
+ if( config.range != null ) existing.range = config.range
150
+ if( config.gateThreshold != null ) existing.gateThreshold = config.gateThreshold
151
+ }
152
+ else this._streams.set( signal, this._makeStream( config ) )
153
+ }
154
+
155
+ /**
156
+ * Returns the current prediction for a signal without observing anything.
157
+ * Returns 0 if the stream hasn't been seen yet.
158
+ */
159
+ predict( signal: string ): number {
160
+ return this._getOrCreate( signal ).prediction
161
+ }
162
+
163
+ /**
164
+ * Observe an actual value for a signal. Updates the EMA prediction and
165
+ * returns the prediction error for this tick.
166
+ */
167
+ observe( signal: string, actual: number ): PredictionError {
168
+ const s = this._getOrCreate( signal )
169
+
170
+ // Cold start: adopt the first observation as the prediction so the model
171
+ // doesn't report a spurious large error against a zero-initialised prior.
172
+ if( s.n === 0 ){
173
+ s.prediction = actual
174
+ s.n = 1
175
+
176
+ return { error: 0, normalized: 0, gated: true, salience: 0 }
177
+ }
178
+
179
+ const rawError = Math.abs( actual - s.prediction )
180
+
181
+ // ── Gating basis (madEma × SURPRISE_SCALE) ──
182
+ // Scale the error against the signal's own typical deviation (madEma) so
183
+ // surprise is meaningful regardless of magnitude. An explicitly configured
184
+ // range (>0) pins a fixed scale instead. Seed the basis from the first
185
+ // deviation so warm-up observations aren't all maximally surprising.
186
+ const scaleBasis = s.madEma > 0 ? s.madEma : rawError
187
+ const scale = s.range > 0 ? s.range : Math.max( SCALE_FLOOR, scaleBasis * SURPRISE_SCALE )
188
+
189
+ const normalized = Math.min( 1, rawError / scale )
190
+ const gated = normalized < s.gateThreshold
191
+
192
+ // ── Salience basis (EW-variance × precision, merged from SalienceComputer) ──
193
+ // A second, independent read of the same error: how much should this grab the
194
+ // Global Workspace? Normalised against the stream's exponentially-weighted
195
+ // variance and amplified by its top-down precision weight (which mean-reverts).
196
+ s.m2 = ( 1 - s.scaleAlpha ) * s.m2 + s.scaleAlpha * rawError * rawError
197
+ const variance = s.scaleAlpha * ( 1 - s.scaleAlpha ) * s.m2
198
+ const baseSalience = Math.min( 1, rawError / ( Math.sqrt( variance ) * NORMALISE_SCALE + SALIENCE_EPSILON ) )
199
+ const precision = this._precision.get( signal ) ?? 1.0
200
+ if( precision !== 1.0 )
201
+ this._precision.set( signal, Math.max( PRECISION_MIN, Math.min( PRECISION_MAX, precision + ( 1.0 - precision ) * PRECISION_DECAY ) ) )
202
+ const salience = Math.min( 1, baseSalience * precision )
203
+
204
+ // ── Update EMAs — blend top-down anticipation weight if present ──
205
+ const effectiveAlpha = s.anticipationWeight > 0 ? Math.min( 1, s.alpha + s.anticipationWeight * ANTICIPATION_BLEND ) : s.alpha
206
+
207
+ s.prediction = s.prediction + effectiveAlpha * ( actual - s.prediction )
208
+ s.madEma = s.madEma + s.scaleAlpha * ( rawError - s.madEma )
209
+ s.errorEma = s.errorEma + s.alpha * ( normalized - s.errorEma )
210
+ s.n++
211
+ s.anticipationWeight = 0 // consumed
212
+
213
+ return { error: rawError, normalized, gated, salience }
214
+ }
215
+
216
+ /**
217
+ * Set the top-down precision (attention) weight for a signal. Precision > 1
218
+ * amplifies that stream's `salience`; < 1 attenuates it. Mean-reverts toward 1.0
219
+ * on each observe(). (Merged from SalienceComputer.)
220
+ */
221
+ setPrecision( signal: string, weight: number ): void {
222
+ this._precision.set( signal, Math.max( PRECISION_MIN, Math.min( PRECISION_MAX, weight ) ) )
223
+ }
224
+
225
+ getPrecision( signal: string ): number {
226
+ return this._precision.get( signal ) ?? 1.0
227
+ }
228
+
229
+ /**
230
+ * Inject a top-down anticipation from a higher-level model (e.g. ExecutiveEngine).
231
+ * Blends the anticipation into the current prediction proportional to confidence.
232
+ * Effect decays after one observe() call.
233
+ */
234
+ anticipate( signal: string, predictedValue: number, confidence: number ): void {
235
+ const s = this._getOrCreate( signal )
236
+ const blendWeight = Math.max( 0, Math.min( 1, confidence ) )
237
+
238
+ s.prediction = s.prediction + blendWeight * ANTICIPATION_BLEND * ( predictedValue - s.prediction )
239
+ s.anticipationWeight = blendWeight
240
+ }
241
+
242
+ /**
243
+ * Returns the long-run mean normalized error for a signal (accuracy metric).
244
+ * 0 = perfect prediction, 1 = maximum surprise.
245
+ */
246
+ meanError( signal: string ): number {
247
+ return this._streams.get( signal )?.errorEma ?? 0
248
+ }
249
+
250
+ /**
251
+ * Returns true if the stream's mean error has stabilized below the gate threshold.
252
+ * Useful for deciding whether to skip full computation for a signal.
253
+ */
254
+ isStable( signal: string ): boolean {
255
+ const s = this._streams.get( signal )
256
+ if( !s || s.n < 5 ) return false // need at least 5 observations
257
+
258
+ return s.errorEma < s.gateThreshold
259
+ }
260
+
261
+ /**
262
+ * Returns a summary snapshot of all streams (for engine.snapshot() calls).
263
+ */
264
+ snapshotAll(): Record<string, { prediction: number; meanError: number; n: number }> {
265
+ const out: Record<string, { prediction: number; meanError: number; n: number }> = {}
266
+ for( const [ key, s ] of this._streams )
267
+ out[ key ] = { prediction: s.prediction, meanError: s.errorEma, n: s.n }
268
+
269
+ return out
270
+ }
271
+
272
+ /**
273
+ * Capture the full mutable state (FN9). Engines embed this in their own
274
+ * snapshot() so prediction baselines/accuracy survive a restore or replay
275
+ * instead of resetting to a freshly-zeroed model.
276
+ */
277
+ snapshot(): GenerativeModelSnapshot {
278
+ return {
279
+ defaultAlpha: this._defaultAlpha,
280
+ defaultRange: this._defaultRange,
281
+ streams: [ ...this._streams.entries() ].map( ([ k, v ]) => [ k, { ...v } ] ),
282
+ precision: [ ...this._precision.entries() ]
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Rehydrate from a snapshot() payload, replacing all current state. Defensive
288
+ * against malformed input so a partial/legacy snapshot can't corrupt the run.
289
+ */
290
+ restore( snap: GenerativeModelSnapshot | undefined | null ): void {
291
+ if( !snap ) return
292
+ if( typeof snap.defaultAlpha === 'number' ) this._defaultAlpha = snap.defaultAlpha
293
+ if( typeof snap.defaultRange === 'number' ) this._defaultRange = snap.defaultRange
294
+
295
+ this._streams = new Map(
296
+ ( snap.streams ?? [] ).map( ([ k, v ]) => [ k, {
297
+ prediction: v.prediction,
298
+ errorEma: v.errorEma,
299
+ madEma: v.madEma ?? 0,
300
+ m2: v.m2 ?? 0,
301
+ alpha: v.alpha,
302
+ // Legacy snapshots (pre-1e) coupled the scale to `alpha`; default to it.
303
+ scaleAlpha: v.scaleAlpha ?? v.alpha ?? this._defaultAlpha,
304
+ range: v.range,
305
+ gateThreshold: v.gateThreshold,
306
+ n: v.n,
307
+ anticipationWeight: v.anticipationWeight,
308
+ } ] )
309
+ )
310
+ this._precision = new Map( snap.precision ?? [] )
311
+ }
312
+
313
+ // ── Internal ─────────────────────────────────────────────────
314
+
315
+ private _getOrCreate( signal: string ): StreamState {
316
+ let s = this._streams.get( signal )
317
+ if( !s ){
318
+ s = this._makeStream({})
319
+ this._streams.set( signal, s )
320
+ }
321
+
322
+ return s
323
+ }
324
+
325
+ private _makeStream( config: StreamConfig ): StreamState {
326
+ return {
327
+ prediction: 0,
328
+ errorEma: 0,
329
+ madEma: 0,
330
+ m2: 0,
331
+ alpha: config.alpha ?? this._defaultAlpha,
332
+ // Defaults to the stream's own alpha → scale stays coupled to the prediction
333
+ // rate unless explicitly decoupled (behaviour-preserving).
334
+ scaleAlpha: config.scaleAlpha ?? config.alpha ?? this._defaultAlpha,
335
+ range: config.range ?? this._defaultRange,
336
+ gateThreshold: config.gateThreshold ?? GATE_THRESHOLD,
337
+ n: 0,
338
+ anticipationWeight: 0
339
+ }
340
+ }
341
+ }
342
+
343
+ // ── Gate threshold export for engines ────────────────────────
344
+ export { GATE_THRESHOLD }
@@ -0,0 +1,70 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/heartbeat.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * HeartbeatPublisher — demotes the tick from execution gate to clock signal.
7
+ *
8
+ * Plugs into the orchestrator's onAfterTick middleware. After every tick
9
+ * completes, it publishes a 'clock.tick' event to the CognitiveBus and
10
+ * calls bus.flush() so queued deliveries are dispatched synchronously.
11
+ *
12
+ * Engines that need time-based behavior (decay, deadlines, cooldowns)
13
+ * subscribe to 'clock.tick'. Engines that are purely event-driven ignore it.
14
+ *
15
+ * Adaptive interval support: the publisher tracks quiet vs active periods
16
+ * and the bus consumer can inspect the 'clock.tick' payload's `intervalHint`
17
+ * to decide whether to act this tick.
18
+ */
19
+
20
+ import type { Tick, ReadonlySimulationState } from '#core/types'
21
+ import type { CognitiveBus } from '#cognition/bus'
22
+ // Ensure SimulationContext augmentation (cognitiveBus?) is in scope
23
+ import '#cognition/bus'
24
+
25
+ const CLOCK_TICK_VERSION = 1
26
+
27
+ // When quiet, emit clock.tick only every N ticks (reduces bus traffic)
28
+ const QUIET_TICK_INTERVAL = 5
29
+
30
+ export class HeartbeatPublisher {
31
+ private _bus: CognitiveBus
32
+ private _sourceName = 'heartbeat-publisher'
33
+ private _quietTicks = 0
34
+
35
+ constructor( bus: CognitiveBus ){
36
+ this._bus = bus
37
+ }
38
+
39
+ /**
40
+ * Called directly from CognitiveOrchestrator._onAfterPhase1() after the
41
+ * EventBus flush. Publishes clock.tick to the CognitiveBus with adaptive
42
+ * quiet-period throttling.
43
+ */
44
+ publishTick( tick: Tick, state: ReadonlySimulationState ): void {
45
+ const
46
+ activeAgents = state.metrics.get('social.active_agents') ?? 0,
47
+ stressZone = state.metrics.get('stress.zone') ?? 1,
48
+ isActive = activeAgents > 0 || stressZone >= 2
49
+
50
+ if( isActive ){
51
+ this._quietTicks = 0
52
+ } else {
53
+ this._quietTicks++
54
+ if( this._quietTicks % QUIET_TICK_INTERVAL !== 0 ) return
55
+ }
56
+
57
+ this._bus.publish({
58
+ type: 'clock.tick',
59
+ version: CLOCK_TICK_VERSION,
60
+ sourceEngine: this._sourceName,
61
+ salience: 0,
62
+ payload: {
63
+ tick,
64
+ delta: state.metrics.get('system.last_delta') ?? 50,
65
+ isActive,
66
+ intervalHint: isActive ? 1 : Math.min( this._quietTicks, QUIET_TICK_INTERVAL ),
67
+ }
68
+ })
69
+ }
70
+ }