@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,422 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/attention.allocator.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * AttentionAllocator — manages a finite attention budget.
7
+ *
8
+ * Attention is a limited resource. Attending to entities consumes
9
+ * budget. Salient stimuli (from Perception) compete for allocation.
10
+ * Multiple simultaneous demands cause fragmentation (reduced depth).
11
+ *
12
+ * Receives modulation from:
13
+ * - EnergyRegulator (low energy → reduced capacity)
14
+ * - SleepPressureRegulator (fatigue → reduced capacity)
15
+ * - CircadianOscillator (time-of-day capacity variation)
16
+ *
17
+ * Part of Shard 0 (Regulatory Layer) — runs every tick, synchronous.
18
+ */
19
+
20
+ import type {
21
+ Duration,
22
+ Tick,
23
+ SimulationContext,
24
+ ReadonlySimulationState,
25
+ StateCommands,
26
+ SimulationEvent,
27
+ } from '#core/types'
28
+ import type { CognitiveEngine, SimulationEngine, EngineResult } from '#cognition/types'
29
+ import type { CognitiveEventSchema } from '#cognition/schema.registry'
30
+ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
31
+ import { GenerativeModel } from '#cognition/generative.model'
32
+ import { readEffectiveParams } from '#cognition/persona.prior'
33
+
34
+ export interface AttentionAllocatorConfig {
35
+ /** Maximum attention capacity (baseline, before modulation) */
36
+ maxCapacity?: number
37
+ /** Cost per tick to maintain attention on a single focus */
38
+ costPerFocus?: number
39
+ /** Maximum number of simultaneous attention foci */
40
+ maxFoci?: number
41
+ /** How quickly attention shifts between foci (lower = more sticky) */
42
+ shiftInertia?: number
43
+ bus?: CognitiveBus
44
+ }
45
+
46
+ interface AttentionFocus {
47
+ entityId: string
48
+ depth: number // 0-1: how deeply attended this focus is
49
+ duration: number // ticks this focus has been maintained
50
+ salienceAtCapture: number
51
+ }
52
+
53
+ // ── Voluntary attention effort (Option C) ─────────────────────
54
+ // `effort` is how much of the vitals-permitted capacity ceiling the mind chooses
55
+ // to engage. The executive sets it explicitly via its action vocabulary (a
56
+ // `focus` action mobilizes; `rest`/`sleep`/`wait`/`meditate` stand down); absent
57
+ // a choice it relaxes back to baseline, so focus/rest are transient unless
58
+ // renewed. Effort scales the ceiling, so vitals always win — you cannot focus
59
+ // past exhaustion (a collapsed ceiling leaves nothing to engage).
60
+ const EFFORT_BASELINE = 0.7 // homeostatic default utilization of the ceiling
61
+ const EFFORT_MIN = 0.4 // deepest voluntary stand-down (rest)
62
+ const EFFORT_MAX = 1.0 // full mobilization (focus)
63
+ const EFFORT_RELAX = 0.05 // fraction of the gap-to-baseline relaxed per tick
64
+
65
+ // ── Arousal-driven ceiling (Option A — involuntary upward lever) ──────────────
66
+ // Threat/reward arousal (the integrated `affect.arousal`) mobilizes capacity
67
+ // ABOVE baseline — fight-or-flight — but with a Yerkes–Dodson inverted-U: moderate
68
+ // arousal lifts the ceiling, while extreme arousal collapses it (fragmentation /
69
+ // tunnel vision). This is the involuntary counterpart to voluntary focus: it
70
+ // raises the ceiling effort utilizes, so a calm mind sits at baseline, a roused
71
+ // one can engage more, and a panicked one loses capacity it cannot will back.
72
+ const AROUSAL_REST = 0.3 // at/below this — calm; no mobilization (factor 1.0)
73
+ const AROUSAL_PEAK = 0.65 // optimal arousal — maximum mobilization
74
+ const AROUSAL_GAIN = 0.3 // peak upward mobilization (+30% ceiling at the peak)
75
+ const AROUSAL_OVERLOAD = 0.4 // impairment at maximum arousal (factor → 1 − 0.4)
76
+
77
+ export class AttentionAllocator implements SimulationEngine, CognitiveEngine {
78
+ readonly name = 'attention-allocator'
79
+
80
+ private _maxCapacity: number
81
+ private _costPerFocus: number
82
+ private _maxFoci: number
83
+ private _shiftInertia: number
84
+ private _activeFocus: AttentionFocus[] = []
85
+
86
+ private _energyLevel: number = 100
87
+ private _sleepPressure: number = 0
88
+
89
+ /** Voluntary effort set-point (0.4–1.0); see EFFORT_* constants. */
90
+ private _effort: number = EFFORT_BASELINE
91
+ /** A one-shot focus/rest request from the executive, applied next react(). */
92
+ private _effortRequest: number | null = null
93
+
94
+ private _bus: CognitiveBus | null = null
95
+
96
+ private readonly _model = new GenerativeModel()
97
+
98
+
99
+ constructor( config: AttentionAllocatorConfig = {} ){
100
+ this._bus = config.bus ?? null
101
+ this._maxCapacity = config.maxCapacity ?? 100
102
+ this._costPerFocus = config.costPerFocus ?? 5
103
+ this._maxFoci = config.maxFoci ?? 4
104
+ this._shiftInertia = config.shiftInertia ?? 0.7
105
+ }
106
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
107
+
108
+ // ── Engine interface ─────────────────────────────────────
109
+
110
+
111
+ subscribes(): string[] {
112
+ return [
113
+ 'energy.state.changed',
114
+ 'sleep.state.changed',
115
+ 'executive.prediction.formed',
116
+ 'attention.regulate', // voluntary focus/rest from the executive (Option C)
117
+ 'senses.*', // all sense percepts — salience feeds attention allocation
118
+ ]
119
+ }
120
+ publishes(): CognitiveEventSchema[] { return [] }
121
+
122
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
123
+ this._model.observe( e.type, e.salience )
124
+ switch( e.type ){
125
+ case 'energy.state.changed':
126
+ this._energyLevel = (e.payload as Record<string,number>)['level'] ?? this._energyLevel
127
+ break
128
+ case 'sleep.state.changed':
129
+ this._sleepPressure = (e.payload as Record<string,number>)['pressure'] ?? this._sleepPressure
130
+ break
131
+ case 'attention.regulate': {
132
+ // Voluntary focus/rest (Option C). Clamp the requested set-point; react()
133
+ // applies it next tick and then decays it back toward baseline.
134
+ const t = (e.payload as Record<string,number>)['effortTarget']
135
+ if( typeof t === 'number' )
136
+ this._effortRequest = Math.min( EFFORT_MAX, Math.max( EFFORT_MIN, t ) )
137
+ break
138
+ }
139
+ case 'executive.prediction.formed': {
140
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
141
+ if( p.predictedDomains.includes('attention') )
142
+ this._model.setPrecision( 'attention.usage', 1.0 + p.confidence * 0.5 )
143
+ break
144
+ }
145
+ default:
146
+ // senses.*.percept — use the event's pre-computed salience as an
147
+ // attention signal for the percept's source entity.
148
+ if( e.type.startsWith( 'senses.' ) ){
149
+ const percept = e.payload as { sourceEntityId?: string; salience?: number }
150
+ if( percept.sourceEntityId )
151
+ this._model.observe( `attention.entity.${percept.sourceEntityId}`, e.salience )
152
+ }
153
+ break
154
+ }
155
+ }
156
+
157
+ snapshot(): Record<string, unknown> {
158
+ return {
159
+ energyLevel: this._energyLevel,
160
+ sleepPressure: this._sleepPressure,
161
+ effort: this._effort,
162
+ }
163
+ }
164
+
165
+ async react(
166
+ delta: Duration,
167
+ _tick: Tick,
168
+ state: ReadonlySimulationState,
169
+ context: SimulationContext
170
+ ): Promise<EngineResult> {
171
+ this._readConfigFromState( state )
172
+
173
+ const
174
+ events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
175
+ commands: StateCommands = { metrics: [] }
176
+
177
+ // Voluntary effort (Option C): an explicit focus/rest request snaps the
178
+ // set-point; otherwise it relaxes toward the homeostatic baseline so focus/
179
+ // rest fade unless renewed each cycle.
180
+ if( this._effortRequest != null ){
181
+ this._effort = this._effortRequest
182
+ this._effortRequest = null
183
+ }
184
+ else
185
+ this._effort += ( EFFORT_BASELINE - this._effort ) * EFFORT_RELAX
186
+ this._effort = Math.min( EFFORT_MAX, Math.max( EFFORT_MIN, this._effort ) )
187
+
188
+ // Apply modulations to capacity — derived from regulatory metrics directly
189
+ // (modulation events are not routed as metrics, so we compute factors here).
190
+ // The CEILING (what the body permits) is set by the vitals AND arousal:
191
+ // energy/sleep can collapse it to forced rest/shutdown; arousal (fight-or-
192
+ // flight) can lift it above baseline, or — at the extreme — collapse it via
193
+ // fragmentation. Effort then scales how much of that ceiling is engaged.
194
+ const
195
+ energyLevel = this._energyLevel,
196
+ sleepPressure = this._sleepPressure,
197
+ arousal = state.metrics.get( 'affect.arousal' ) ?? AROUSAL_REST,
198
+ energyFactor = energyLevel < 30 ? 0.3 + ( energyLevel / 30 ) * 0.7 : 1.0,
199
+ sleepFactor = sleepPressure > 40
200
+ ? 1 - ( ( sleepPressure - 40 ) / 60 ) * 0.5
201
+ : 1.0,
202
+ arousalFactor = this._arousalFactor( arousal ),
203
+ ceiling = this._maxCapacity * energyFactor * sleepFactor * arousalFactor,
204
+ effectiveCapacity = ceiling * this._effort
205
+
206
+ // Get incoming salience signals (entities demanding attention)
207
+ const salienceMap = this._extractSalienceSignals( state )
208
+
209
+ // Decay existing focuses (attention fades if not reinforced)
210
+ this._decayFocuses( delta )
211
+
212
+ // Allocate attention: balance new salience vs. existing focus inertia
213
+ this._allocate( salienceMap, effectiveCapacity )
214
+
215
+ // Compute attention metrics
216
+ const
217
+ totalAllocated = this._activeFocus.reduce( ( s, f ) => s + f.depth * this._costPerFocus, 0 ),
218
+ attentionUsage = effectiveCapacity > 0 ? totalAllocated / effectiveCapacity : 0,
219
+ focusCount = this._activeFocus.length,
220
+ deepestFocus = this._activeFocus.reduce( ( best, f ) => f.depth > best.depth ? f : best, { depth: 0 } as AttentionFocus )
221
+
222
+ const
223
+ freeCapacity = Math.max( 0, effectiveCapacity - totalAllocated ),
224
+ // Normalized 0–1 spare-attention fraction vs the BASELINE capacity — the
225
+ // signal the FacetSupervisor budgets on. Normalizing by baseline (not the
226
+ // already-modulated effectiveCapacity) is what makes vital-reduced capacity
227
+ // (low energy / high sleep pressure) actually shrink the facet budget,
228
+ // instead of the raw 0–100 capacity inflating it ~100× so it never binds.
229
+ freeFraction = this._maxCapacity > 0 ? Math.max( 0, freeCapacity / this._maxCapacity ) : 0,
230
+ isIdle = attentionUsage < 0.2 ? 1 : 0
231
+
232
+ commands.metrics!.push(
233
+ [ 'attention.usage', attentionUsage ],
234
+ [ 'attention.focus_count', focusCount ],
235
+ [ 'attention.capacity', effectiveCapacity ],
236
+ [ 'attention.free_capacity', freeCapacity ],
237
+ [ 'attention.free_fraction', freeFraction ],
238
+ [ 'attention.effort', this._effort ],
239
+ [ 'attention.arousal_factor', arousalFactor ],
240
+ [ 'attention.idle', isIdle ],
241
+ )
242
+
243
+ // Collect all stale attention.focus entities to replace them cleanly
244
+ const staleIds: string[] = []
245
+ for( const [ id, entity ] of state.entities ){
246
+ if( entity.type === 'attention.focus')
247
+ staleIds.push( id )
248
+ }
249
+
250
+ // Persist current attention foci as entities (for other engines to read)
251
+ if( this._activeFocus.length > 0 ){
252
+ commands.set = this._activeFocus.map( f => ({
253
+ id: `attention-focus-${f.entityId}`,
254
+ type: 'attention.focus',
255
+ metadata: {
256
+ entityId: f.entityId,
257
+ depth: f.depth,
258
+ duration: f.duration,
259
+ },
260
+ }))
261
+
262
+ commands.delete = staleIds.filter( id => !this._activeFocus.some( f => `attention-focus-${f.entityId}` === id ) )
263
+ }
264
+ else {
265
+ commands.set = [{
266
+ id: 'attention-focus-none',
267
+ type: 'attention.focus',
268
+ metadata: { entityId: null, depth: 0, duration: 0 },
269
+ }]
270
+ commands.delete = staleIds.filter( id => id !== 'attention-focus-none')
271
+ }
272
+
273
+ // Event when attention shifts significantly
274
+ if( focusCount === 0 && this._activeFocus.length > 0 )
275
+ events.push({
276
+ type: 'attention.engaged',
277
+ source: this.name,
278
+ payload: { focus: deepestFocus.entityId, depth: deepestFocus.depth },
279
+ })
280
+
281
+ if( focusCount > 2 )
282
+ events.push({
283
+ type: 'attention.fragmented',
284
+ source: this.name,
285
+ payload: { focusCount, usage: attentionUsage },
286
+ })
287
+
288
+
289
+ // Phase C: publish cognitive event
290
+ const _bus = this._bus
291
+ if( _bus && attentionUsage > 0.8 )
292
+ _bus.publish({ type: 'working_memory.capacity.reached', version: 1, sourceEngine: this.name, salience: Math.min(1, attentionUsage), payload: { usage: attentionUsage, focusCount } })
293
+ // Phase D + F: rich state-change event — gated by prediction error on EITHER
294
+ // usage OR free fraction. Gating on free fraction too is what lets a voluntary
295
+ // effort change (focus/rest) reach the FacetSupervisor even when perceptual
296
+ // load — and thus usage — is steady; otherwise the budget would not move in a
297
+ // quiet mind.
298
+ if( _bus ){
299
+ const usageErr = this._model.observe( 'attention.usage', attentionUsage )
300
+ const freeErr = this._model.observe( 'attention.free_fraction', freeFraction )
301
+ if( !usageErr.gated || !freeErr.gated )
302
+ _bus.publish({ type: 'attention.state.changed', version: 1, sourceEngine: this.name, salience: Math.max( usageErr.salience, freeErr.salience ), payload: { usage: attentionUsage, focusCount, capacity: effectiveCapacity, freeFraction } })
303
+ }
304
+ return { events: events.length > 0 ? events : undefined, commands }
305
+ }
306
+
307
+ // ── Config hot-reload ────────────────────────────────────
308
+
309
+ private _readConfigFromState( state: ReadonlySimulationState ): void {
310
+ // Effective config = base engine-config-attention ⊕ persona-prior (single-source).
311
+ // The persona-consolidator lowers shiftInertia on belief-formation bias (edge 8)
312
+ // → attention shifts more readily, loosening the fixation that feeds the bias.
313
+ const p = readEffectiveParams( state, 'engine-config-attention' )
314
+ if( p.maxCapacity != null ) this._maxCapacity = p.maxCapacity
315
+ if( p.costPerFocus != null ) this._costPerFocus = p.costPerFocus
316
+ if( p.maxFoci != null ) this._maxFoci = p.maxFoci
317
+ if( p.shiftInertia != null ) this._shiftInertia = p.shiftInertia
318
+ }
319
+
320
+ // ── Internal ─────────────────────────────────────────────
321
+
322
+ /**
323
+ * Yerkes–Dodson arousal → ceiling factor. Calm (≤ AROUSAL_REST) → 1.0 (no
324
+ * boost); rises linearly to 1 + AROUSAL_GAIN at AROUSAL_PEAK (mobilization);
325
+ * then declines below 1 toward 1 − AROUSAL_OVERLOAD at maximum arousal
326
+ * (fragmentation / tunnel vision — capacity the mind cannot will back).
327
+ */
328
+ private _arousalFactor( arousal: number ): number {
329
+ const a = Math.min( 1, Math.max( 0, arousal ) )
330
+ if( a <= AROUSAL_REST ) return 1.0
331
+ if( a <= AROUSAL_PEAK )
332
+ return 1 + AROUSAL_GAIN * ( ( a - AROUSAL_REST ) / ( AROUSAL_PEAK - AROUSAL_REST ) )
333
+ return ( 1 + AROUSAL_GAIN ) - ( AROUSAL_GAIN + AROUSAL_OVERLOAD ) * ( ( a - AROUSAL_PEAK ) / ( 1 - AROUSAL_PEAK ) )
334
+ }
335
+
336
+ /**
337
+ * Extract salience signals from perceptual entities.
338
+ * Entities with higher salience demand more attention.
339
+ */
340
+ private _extractSalienceSignals( state: ReadonlySimulationState ): Map<string, number> {
341
+ const salienceMap = new Map<string, number>()
342
+
343
+ for( const [ id, entity ] of state.entities ){
344
+ if( entity.type === 'percept' && entity.metadata?.salience !== undefined )
345
+ salienceMap.set( id, entity.metadata.salience as number )
346
+
347
+ // Also check for explicit attention demands (urgent events)
348
+ if( entity.type === 'attention.demand')
349
+ salienceMap.set( id, ( entity.metadata?.urgency as number ) ?? 0.5 )
350
+ }
351
+
352
+ return salienceMap
353
+ }
354
+
355
+ /**
356
+ * Decay existing attention focuses over time.
357
+ * Sustained attention on a single focus resists decay (vigilance effect).
358
+ */
359
+ private _decayFocuses( delta: Duration ): void {
360
+ const decayRate = 0.01 * ( delta / 1000 )
361
+
362
+ this._activeFocus = this._activeFocus
363
+ .map( f => ({
364
+ ...f,
365
+ // Sustained focus decays slower (inertia)
366
+ depth: Math.max( 0, f.depth - decayRate * ( 1 - this._shiftInertia * Math.min( 1, f.duration / 100 ) ) ),
367
+ duration: f.duration + 1,
368
+ }))
369
+ .filter( f => f.depth > 0.01 ) // Prune negligible focuses
370
+ }
371
+
372
+ /**
373
+ * Allocate attention budget across competing salience signals.
374
+ * Balances capturing new salient stimuli against maintaining existing focus.
375
+ */
376
+ private _allocate( salienceMap: Map<string, number>, capacity: number ): void {
377
+ if( salienceMap.size === 0 ) return
378
+
379
+ // Sort by salience (highest first)
380
+ const sorted = Array.from( salienceMap.entries() )
381
+ .sort( ( a, b ) => b[1] - a[1] )
382
+
383
+ // Allocate to top-K salient entities (respecting max foci)
384
+ const allocated: AttentionFocus[] = []
385
+ let remainingCapacity = capacity
386
+
387
+ for( const [ entityId, salience ] of sorted ){
388
+ if( allocated.length >= this._maxFoci ) break
389
+
390
+ // Check if this entity already has attention
391
+ const existing = this._activeFocus.find( f => f.entityId === entityId )
392
+
393
+ if( existing ){
394
+ // Maintain existing focus — reinforce with salience
395
+ const reinforcedDepth = Math.min( 1, existing.depth + salience * 0.2 )
396
+ const cost = reinforcedDepth * this._costPerFocus
397
+
398
+ if( cost <= remainingCapacity ){
399
+ allocated.push({ ...existing, depth: reinforcedDepth })
400
+ remainingCapacity -= cost
401
+ }
402
+ }
403
+ else {
404
+ // New focus — cost of shifting attention
405
+ const shiftCost = this._costPerFocus * ( 1 + this._shiftInertia )
406
+ const initialDepth = Math.min( 0.5, salience )
407
+
408
+ if( shiftCost + initialDepth * this._costPerFocus <= remainingCapacity ){
409
+ allocated.push({
410
+ entityId,
411
+ depth: initialDepth,
412
+ duration: 0,
413
+ salienceAtCapture: salience,
414
+ })
415
+ remainingCapacity -= shiftCost + initialDepth * this._costPerFocus
416
+ }
417
+ }
418
+ }
419
+
420
+ this._activeFocus = allocated
421
+ }
422
+ }