@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.
- package/LICENSE +202 -0
- package/README.md +777 -0
- package/dist/index.d.ts +8144 -0
- package/dist/index.js +26231 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
- package/src/cognition/agency/access.grants.ts +59 -0
- package/src/cognition/agency/competence.codec.ts +89 -0
- package/src/cognition/agency/engines/action.selector.ts +462 -0
- package/src/cognition/agency/engines/affordance.synthesizer.ts +384 -0
- package/src/cognition/agency/engines/deliberation.engine.ts +241 -0
- package/src/cognition/agency/engines/instruction.intake.ts +54 -0
- package/src/cognition/agency/engines/motor.schema.executor.ts +532 -0
- package/src/cognition/agency/engines/reafference.engine.ts +241 -0
- package/src/cognition/agency/execution.primitives.ts +118 -0
- package/src/cognition/agency/index.ts +58 -0
- package/src/cognition/agency/proactive.communicator.ts +274 -0
- package/src/cognition/agency/reconcile.learning.ts +82 -0
- package/src/cognition/agency/schemas/external.ts +59 -0
- package/src/cognition/agency/schemas/innate.ts +111 -0
- package/src/cognition/agency/schemas/repertoire.ts +246 -0
- package/src/cognition/agency/selection.scoring.ts +191 -0
- package/src/cognition/agency/types.ts +165 -0
- package/src/cognition/bus.ts +391 -0
- package/src/cognition/completion.inbox.ts +86 -0
- package/src/cognition/config.mirror.entities.ts +475 -0
- package/src/cognition/conversation.memory.ts +81 -0
- package/src/cognition/event.log.ts +116 -0
- package/src/cognition/event.schemas.ts +568 -0
- package/src/cognition/faculties/aesthetic.evaluator.ts +313 -0
- package/src/cognition/faculties/affective.blender.ts +562 -0
- package/src/cognition/faculties/attachment.evaluator.ts +432 -0
- package/src/cognition/faculties/attention.allocator.ts +422 -0
- package/src/cognition/faculties/autobiographical.narrator.ts +300 -0
- package/src/cognition/faculties/bias.detector.ts +363 -0
- package/src/cognition/faculties/circadian.oscillator.ts +294 -0
- package/src/cognition/faculties/confidence.calibrator.ts +324 -0
- package/src/cognition/faculties/dream.simulator.ts +251 -0
- package/src/cognition/faculties/empathy.simulator.ts +253 -0
- package/src/cognition/faculties/energy.regulator.ts +320 -0
- package/src/cognition/faculties/episodic.consolidator.ts +728 -0
- package/src/cognition/faculties/executive.engine/commands.ts +459 -0
- package/src/cognition/faculties/executive.engine/config.ts +46 -0
- package/src/cognition/faculties/executive.engine/context.ts +605 -0
- package/src/cognition/faculties/executive.engine/deferred.effects.ts +104 -0
- package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +60 -0
- package/src/cognition/faculties/executive.engine/effort.gate.ts +114 -0
- package/src/cognition/faculties/executive.engine/engine.ts +1028 -0
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +90 -0
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +271 -0
- package/src/cognition/faculties/executive.engine/facet.ts +628 -0
- package/src/cognition/faculties/executive.engine/gating.ts +222 -0
- package/src/cognition/faculties/executive.engine/index.ts +6 -0
- package/src/cognition/faculties/executive.engine/messages.ts +102 -0
- package/src/cognition/faculties/executive.engine/parser.ts +380 -0
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +1053 -0
- package/src/cognition/faculties/executive.engine/types.ts +323 -0
- package/src/cognition/faculties/exteroception.ts +338 -0
- package/src/cognition/faculties/forgetting.curve.ts +202 -0
- package/src/cognition/faculties/frustration.evaluator.ts +280 -0
- package/src/cognition/faculties/goal.manager.ts +1008 -0
- package/src/cognition/faculties/inhibition.controller.ts +351 -0
- package/src/cognition/faculties/interoception.ts +417 -0
- package/src/cognition/faculties/introspection.engine.ts +246 -0
- package/src/cognition/faculties/known.entity.tracker.ts +439 -0
- package/src/cognition/faculties/loss.evaluator.ts +224 -0
- package/src/cognition/faculties/moral.evaluator.ts +437 -0
- package/src/cognition/faculties/novelty.detector.ts +254 -0
- package/src/cognition/faculties/persona.consolidator.ts +845 -0
- package/src/cognition/faculties/planning.engine/engine.ts +859 -0
- package/src/cognition/faculties/planning.engine/plan.frontier.ts +83 -0
- package/src/cognition/faculties/planning.engine/plan.store.ts +174 -0
- package/src/cognition/faculties/planning.engine/plan.supervision.ts +494 -0
- package/src/cognition/faculties/planning.engine/types.ts +143 -0
- package/src/cognition/faculties/reputation.tracker.ts +294 -0
- package/src/cognition/faculties/reward.evaluator.ts +304 -0
- package/src/cognition/faculties/self.model.updater.ts +677 -0
- package/src/cognition/faculties/semantic.engine/clustering.ts +673 -0
- package/src/cognition/faculties/semantic.engine/index.ts +6 -0
- package/src/cognition/faculties/semantic.engine/integrator.ts +750 -0
- package/src/cognition/faculties/semantic.engine/types.ts +53 -0
- package/src/cognition/faculties/sleep.pressure.regulator.ts +262 -0
- package/src/cognition/faculties/social.perception.ts +333 -0
- package/src/cognition/faculties/spaced.repetition.ts +605 -0
- package/src/cognition/faculties/stress.regulator.ts +375 -0
- package/src/cognition/faculties/task.switcher.ts +239 -0
- package/src/cognition/faculties/theory.of.mind.ts +333 -0
- package/src/cognition/faculties/threat.evaluator.ts +312 -0
- package/src/cognition/faculties/working.memory.ts +406 -0
- package/src/cognition/generative.model.ts +344 -0
- package/src/cognition/heartbeat.ts +70 -0
- package/src/cognition/index.ts +291 -0
- package/src/cognition/instruction.handler.ts +146 -0
- package/src/cognition/memory/index.ts +35 -0
- package/src/cognition/memory/vector.adapter.ts +316 -0
- package/src/cognition/memory/vector.content.ts +47 -0
- package/src/cognition/memory/vector.embedder.ts +212 -0
- package/src/cognition/memory/vector.index.ts +459 -0
- package/src/cognition/memory/vector.types.ts +65 -0
- package/src/cognition/orchestrator.ts +142 -0
- package/src/cognition/persona.prior.ts +265 -0
- package/src/cognition/schema.registry.ts +145 -0
- package/src/cognition/senses/audition.engine/engine.ts +976 -0
- package/src/cognition/senses/audition.engine/salience.ts +59 -0
- package/src/cognition/senses/base.sense.engine.ts +117 -0
- package/src/cognition/senses/gustation.engine.ts +24 -0
- package/src/cognition/senses/index.ts +172 -0
- package/src/cognition/senses/olfaction.engine.ts +24 -0
- package/src/cognition/senses/somatosensation.engine.ts +23 -0
- package/src/cognition/senses/vision.engine.ts +23 -0
- package/src/cognition/types.ts +63 -0
- package/src/cognition/utilities/token.tracker.ts +492 -0
- package/src/core/abstracts.ts +60 -0
- package/src/core/async.engine.ts +539 -0
- package/src/core/clock.ts +196 -0
- package/src/core/completion.recorder.ts +122 -0
- package/src/core/conflict.detector.ts +140 -0
- package/src/core/distributed.ts +626 -0
- package/src/core/event.bus.ts +292 -0
- package/src/core/inbound.recorder.ts +100 -0
- package/src/core/index.ts +167 -0
- package/src/core/logger.ts +85 -0
- package/src/core/metrics.ts +107 -0
- package/src/core/orchestrator.ts +625 -0
- package/src/core/replay.ts +589 -0
- package/src/core/scenario.ts +120 -0
- package/src/core/serialization.ts +448 -0
- package/src/core/simulation.ts +197 -0
- package/src/core/snapshot.manager.ts +293 -0
- package/src/core/state.manager.ts +283 -0
- package/src/core/types.ts +274 -0
- package/src/core/utils.ts +82 -0
- package/src/core/wall.clock.ts +23 -0
- package/src/extensions/livestream.ext.ts +570 -0
- package/src/extensions/time.ext.ts +339 -0
- package/src/index.ts +91 -0
- package/src/llm/gate.ts +140 -0
- package/src/llm/index.ts +716 -0
- package/src/llm/summarizer.ts +170 -0
- package/src/llm/wire.contracts.ts +67 -0
- package/src/pma/eval.ts +651 -0
- package/src/pma/index.ts +1082 -0
- package/src/profiles/built-in.ts +7 -0
- package/src/profiles/companion.ts +28 -0
- package/src/profiles/company-brain.ts +54 -0
- package/src/profiles/customer-service.ts +30 -0
- package/src/profiles/game-npc.ts +20 -0
- package/src/profiles/index.ts +42 -0
- package/src/profiles/smart-home.ts +34 -0
- package/src/runners/coherence.runner.ts +49 -0
- package/src/runners/outreach.runner.ts +158 -0
- package/src/runners/social.runner.ts +182 -0
- package/src/runners/thin-shim.runner.ts +195 -0
- package/src/sdk/will.ts +334 -0
- package/src/stem/assembly.audit.ts +88 -0
- package/src/stem/distribution.ts +203 -0
- package/src/stem/guards/identity.coherence.ts +152 -0
- package/src/stem/guards/identity.guard.ts +227 -0
- package/src/stem/index.ts +1145 -0
- package/src/stem/mind.ts +1090 -0
- package/src/stem/tracts/ack.reconciler.ts +55 -0
- package/src/stem/tracts/biography.writer.ts +190 -0
- package/src/stem/tracts/effector.controller.ts +143 -0
- package/src/stem/tracts/health.reporter.ts +113 -0
- package/src/stem/tracts/inbound.queue.ts +54 -0
- package/src/stem/tracts/outbox.controller.ts +167 -0
- package/src/stem/tracts/outbox.writer.ts +133 -0
- package/src/stem/tracts/pma.controller.ts +109 -0
- package/src/stem/tracts/replay.controller.ts +177 -0
- package/src/stem/tracts/sensory.controller.ts +168 -0
- package/src/stem/tracts/session.logger.ts +157 -0
- package/src/stem/tracts/transport/index.ts +38 -0
- package/src/stem/tracts/transport/loopback.transport.ts +86 -0
- package/src/stem/tracts/transport/socketio.transport.ts +200 -0
- package/src/stem/tracts/transport/stream.transport.ts +152 -0
- package/src/stem/tracts/transport/types.ts +193 -0
- package/src/stem/tracts/transport.controller.ts +380 -0
- 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
|
+
}
|