@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,406 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/working.memory.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * WorkingMemory — limited-capacity active buffer for current cognition.
7
+ *
8
+ * Holds:
9
+ * - Current percepts (from perceptual engines)
10
+ * - Active goals (from executive layer)
11
+ * - Retrieved episodic memories (from long-term store)
12
+ * - Current affective state
13
+ *
14
+ * Properties:
15
+ * - Capacity-limited (7 ± 2 chunks by default)
16
+ * - Rapid decay without rehearsal (items fade in ~2-10 seconds)
17
+ * - Attentional boost (attended items resist decay)
18
+ * - Chunking (related items can be grouped into single slots)
19
+ * - Recency effect (newest items displace oldest when at capacity)
20
+ *
21
+ * Receives modulation from:
22
+ * - SleepPressureRegulator (fatigue reduces capacity)
23
+ * - StressRegulator (overload reduces effective capacity)
24
+ * - CircadianOscillator (alertness modulates rehearsal strength)
25
+ *
26
+ * Part of Shard 0 — runs every tick, synchronous.
27
+ */
28
+
29
+ import type {
30
+ Duration,
31
+ Tick,
32
+ SimulationContext,
33
+ ReadonlySimulationState,
34
+ StateCommands,
35
+ SimulationEvent,
36
+ } from '#core/types'
37
+ import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
38
+ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
39
+ import type { CognitiveEventSchema } from '#cognition/schema.registry'
40
+ import { GenerativeModel } from '#cognition/generative.model'
41
+ import { readEffectiveParams } from '#cognition/persona.prior'
42
+
43
+ export interface WorkingMemoryConfig {
44
+ /** Maximum chunks in working memory (default 7, Miller's Law) */
45
+ maxChunks?: number
46
+ /** Base decay rate per second (0-1, fraction of activation lost) */
47
+ baseDecayRate?: number
48
+ /** How much attention focus slows decay (0-1, 0 = no protection) */
49
+ attentionProtection?: number
50
+ /** Threshold below which an item is dropped */
51
+ retrievalThreshold?: number
52
+ /** Whether to emit detailed WM events */
53
+ emitEvents?: boolean
54
+ bus?: CognitiveBus
55
+ }
56
+
57
+ interface WMItem {
58
+ id: string
59
+ type: string // 'percept', 'goal', 'memory', 'thought'
60
+ content: unknown
61
+ activation: number // 0-1: current activation strength
62
+ attendedAt: Tick[] // recent ticks where this item had attention
63
+ createdAt: Tick
64
+ sourceEntityId?: string
65
+ tags: string[]
66
+ }
67
+
68
+ export class WorkingMemory implements SimulationEngine, CognitiveEngine {
69
+ readonly name = 'working-memory'
70
+
71
+ private _maxChunks: number
72
+ private _baseDecayRate: number
73
+ private _attentionProtection: number
74
+ private _retrievalThreshold: number
75
+ private _emitEvents: boolean
76
+
77
+ private _items: WMItem[] = []
78
+ private _modulatedCapacity: number
79
+ private _currentFocusId: string | null = null
80
+ private _activeGoalCount = 0
81
+
82
+ /**
83
+ * Monotonic suffix counter for injected WM item ids. Replaces Math.random()
84
+ * AND the former `Date.now()` component so a run replays identically (R2).
85
+ * Unique per engine instance, which is sufficient for WM item ids.
86
+ */
87
+ private _idSeq = 0
88
+
89
+ private _bus: CognitiveBus | null = null
90
+ private readonly _model = new GenerativeModel()
91
+
92
+ constructor( config: WorkingMemoryConfig = {} ){
93
+ this._bus = config.bus ?? null
94
+ this._maxChunks = config.maxChunks ?? 7
95
+ this._baseDecayRate = config.baseDecayRate ?? 0.08
96
+ this._attentionProtection = config.attentionProtection ?? 0.6
97
+ this._retrievalThreshold = config.retrievalThreshold ?? 0.05
98
+ this._emitEvents = config.emitEvents ?? false
99
+ this._modulatedCapacity = this._maxChunks
100
+ }
101
+
102
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
103
+
104
+ // ── Engine interface ─────────────────────────────────────
105
+
106
+
107
+ subscribes(): string[] {
108
+ return [
109
+ 'attention.focus.changed',
110
+ 'goal.state.changed',
111
+ 'executive.prediction.formed',
112
+ ]
113
+ }
114
+ publishes(): CognitiveEventSchema[] { return [] }
115
+
116
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
117
+ this._model.observe( e.type, e.salience )
118
+ switch( e.type ){
119
+ case 'attention.focus.changed': {
120
+ const p = e.payload as Record<string, unknown>
121
+ const focusId = p['entityId'] as string | undefined
122
+ if( focusId ) this._currentFocusId = focusId
123
+ const capacity = p['capacity'] as number | undefined
124
+ if( capacity != null ) this._modulatedCapacity = Math.round( capacity * this._maxChunks )
125
+ break
126
+ }
127
+ case 'goal.state.changed': {
128
+ const p = e.payload as Record<string, number>
129
+ if( p['activeCount'] != null ) this._activeGoalCount = p['activeCount']
130
+ break
131
+ }
132
+ case 'executive.prediction.formed': {
133
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
134
+ if( p.predictedDomains.includes('memory') )
135
+ this._model.setPrecision( 'memory.working.load', 1.0 + p.confidence * 0.5 )
136
+ break
137
+ }
138
+ }
139
+ }
140
+
141
+ snapshot(): Record<string, unknown> {
142
+ return {
143
+ items: this._items.length,
144
+ modulatedCap: this._modulatedCapacity,
145
+ focusId: this._currentFocusId,
146
+ activeGoals: this._activeGoalCount,
147
+ }
148
+ }
149
+
150
+ // ── Public API ───────────────────────────────────────────
151
+
152
+ /** Returns all active WM items sorted by activation descending. */
153
+ getItems(): WMItem[] {
154
+ return [ ...this._items ].sort( ( a, b ) => b.activation - a.activation )
155
+ }
156
+
157
+ /** Returns all active WM items of a given type, sorted by activation descending. */
158
+ getItemsByType( type: string ): WMItem[] {
159
+ return this._items
160
+ .filter( i => i.type === type )
161
+ .sort( ( a, b ) => b.activation - a.activation )
162
+ }
163
+
164
+ /** Inject an item directly (used by integration tests and episodic retrieval). */
165
+ load( item: Omit<WMItem, 'id' | 'createdAt'> & { createdAt?: Tick } ): void {
166
+ const id = `wm-${( this._idSeq++ ).toString( 36 )}`
167
+ const createdAt = item.createdAt ?? 0
168
+ this._evictIfNeeded()
169
+ this._items.push({ ...item, id, createdAt })
170
+ }
171
+
172
+ /**
173
+ * Effective config = base engine-config-working-memory ⊕ persona-prior (single-source).
174
+ * No-op at boot: mirror params equal the constructor defaults (reconciled in #83).
175
+ */
176
+ private _readConfigFromState( state: ReadonlySimulationState ): void {
177
+ const p = readEffectiveParams( state, 'engine-config-working-memory' )
178
+ if( p.maxChunks != null ) this._maxChunks = p.maxChunks
179
+ if( p.baseDecayRate != null ) this._baseDecayRate = p.baseDecayRate
180
+ if( p.attentionProtection != null ) this._attentionProtection = p.attentionProtection
181
+ if( p.retrievalThreshold != null ) this._retrievalThreshold = p.retrievalThreshold
182
+ }
183
+
184
+ async react(
185
+ delta: Duration,
186
+ tick: Tick,
187
+ state: ReadonlySimulationState,
188
+ _context: SimulationContext
189
+ ): Promise<EngineResult> {
190
+ const commands: StateCommands = { set: [], delete: [], metrics: [] }
191
+ const events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = []
192
+
193
+ // Effective config = base engine-config-working-memory ⊕ persona-prior (single-source).
194
+ this._readConfigFromState( state )
195
+
196
+ // Effective capacity: modulated by fatigue / stress signals from state
197
+ const fatigue = state.metrics.get('modulation.sleep_fatigue') ?? 0
198
+ const stress = state.metrics.get('modulation.stress_overload') ?? 0
199
+ const alertness = 1 - Math.max( fatigue, stress ) * 0.4
200
+ this._modulatedCapacity = Math.max( 3, Math.round( this._maxChunks * alertness ) )
201
+
202
+ // Ingest new content from state
203
+ this._ingestPercepts( state, tick )
204
+ this._ingestGoals( state, tick )
205
+
206
+ // Apply attention focus — boosts attendedAt for the currently focused item
207
+ this._applyAttention( state, tick )
208
+
209
+ // Decay all items
210
+ const deltaSeconds = delta / 1000
211
+ for( const item of this._items ){
212
+ const isAttended = item.attendedAt.includes( tick - 1 )
213
+ const decayRate = isAttended
214
+ ? this._baseDecayRate * ( 1 - this._attentionProtection )
215
+ : this._baseDecayRate
216
+ item.activation = Math.max( 0, item.activation - decayRate * deltaSeconds )
217
+ }
218
+
219
+ // Active rehearsal — items with sustained attention resist decay
220
+ this._rehearseItems()
221
+
222
+ // Drop items below threshold
223
+ const before = this._items.length
224
+ this._items = this._items.filter( i => i.activation >= this._retrievalThreshold )
225
+ const dropped = before - this._items.length
226
+
227
+ // Persist items as state entities + clean up stale ones
228
+ this._persistItems( tick, state, commands )
229
+
230
+ // Metrics
231
+ const load = this._items.length / this._maxChunks
232
+ commands.metrics!.push(
233
+ [ 'memory.working.load', load ],
234
+ [ 'memory.working.capacity', this._modulatedCapacity ],
235
+ [ 'memory.working.items', this._items.length ],
236
+ )
237
+
238
+ // Bus events
239
+ if( this._bus ){
240
+ this._bus.publish({
241
+ type: 'memory.working.changed',
242
+ version: 1,
243
+ sourceEngine: this.name,
244
+ salience: this._model.observe( 'memory.working.load', load ).salience,
245
+ payload: { items: this._items.length, capacity: this._modulatedCapacity, load },
246
+ })
247
+
248
+ if( this._emitEvents && this._items.length >= this._modulatedCapacity ){
249
+ this._bus.publish({
250
+ type: 'working_memory.full',
251
+ version: 1,
252
+ sourceEngine: this.name,
253
+ salience: 0.7,
254
+ payload: { capacity: this._modulatedCapacity, items: this._items.length },
255
+ })
256
+ }
257
+ }
258
+
259
+ if( this._emitEvents && dropped > 0 ){
260
+ events.push({
261
+ type: 'working.memory.overflow',
262
+ source: this.name,
263
+ payload: { dropped, remaining: this._items.length },
264
+ })
265
+ }
266
+
267
+ return { events: events.length > 0 ? events : undefined, commands }
268
+ }
269
+
270
+ // ── Internal ─────────────────────────────────────────────
271
+
272
+ /**
273
+ * Read the latest percept entities from state and inject them as WM items.
274
+ * Skips generic placeholder summaries to avoid noise.
275
+ */
276
+ private _ingestPercepts( state: ReadonlySimulationState, tick: Tick ): void {
277
+ for( const entity of state.entities.values() ){
278
+ if( entity.type !== 'percept' ) continue
279
+ if( this._items.some( i => i.sourceEntityId === entity.id ) ) continue
280
+
281
+ const summary = ( entity.metadata?.summary ?? entity.metadata?.content ?? '' ) as string
282
+ if( !summary || summary.startsWith('New percept:') ) continue
283
+
284
+ this._evictIfNeeded()
285
+ this._items.push({
286
+ id: `wm-percept-${entity.id}`,
287
+ type: 'percept',
288
+ content: { summary, entityId: entity.id },
289
+ activation: 0.75,
290
+ attendedAt: [],
291
+ createdAt: tick,
292
+ sourceEntityId: entity.id,
293
+ tags: [ 'percept', ...(( entity.metadata?.tags as string[] ) ?? []) ],
294
+ })
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Read active goal entities from state and ensure each has a WM slot.
300
+ * Goal items start at 0.65 activation — lower than percepts, higher than background.
301
+ */
302
+ private _ingestGoals( state: ReadonlySimulationState, tick: Tick ): void {
303
+ for( const entity of state.entities.values() ){
304
+ if( entity.type !== 'goal' ) continue
305
+ if( entity.metadata?.status !== 'active' ) continue
306
+ if( this._items.some( i => i.sourceEntityId === entity.id ) ) continue
307
+
308
+ this._evictIfNeeded()
309
+ this._items.push({
310
+ id: `wm-goal-${entity.id}`,
311
+ type: 'goal',
312
+ content: {
313
+ description: entity.metadata?.description ?? entity.metadata?.name ?? entity.id,
314
+ priority: entity.metadata?.priority ?? 0.5,
315
+ },
316
+ activation: 0.65,
317
+ attendedAt: [],
318
+ createdAt: tick,
319
+ sourceEntityId: entity.id,
320
+ tags: [ 'goal', 'executive' ],
321
+ })
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Mark the currently focused entity's WM item as attended this tick.
327
+ * Also checks for attention.focus entities in state as a fallback.
328
+ */
329
+ private _applyAttention( state: ReadonlySimulationState, tick: Tick ): void {
330
+ // Bus-driven focus (preferred)
331
+ if( this._currentFocusId ){
332
+ const item = this._items.find( i => i.sourceEntityId === this._currentFocusId )
333
+ if( item && !item.attendedAt.includes( tick ) ) item.attendedAt.push( tick )
334
+ }
335
+
336
+ // State-entity fallback: attention.focus entities
337
+ for( const entity of state.entities.values() ){
338
+ if( entity.type !== 'attention.focus' ) continue
339
+ const targetId = entity.metadata?.targetEntityId as string | undefined
340
+ if( !targetId ) continue
341
+ const item = this._items.find( i => i.sourceEntityId === targetId )
342
+ if( item && !item.attendedAt.includes( tick ) ) item.attendedAt.push( tick )
343
+ }
344
+
345
+ // Trim attendedAt history to last 20 ticks to avoid unbounded growth
346
+ for( const item of this._items ){
347
+ if( item.attendedAt.length > 20 )
348
+ item.attendedAt = item.attendedAt.slice( -20 )
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Items that have been attended 3+ times receive a small activation boost
354
+ * — simulating active rehearsal keeping them in working memory.
355
+ */
356
+ private _rehearseItems(): void {
357
+ for( const item of this._items ){
358
+ if( item.attendedAt.length >= 3 )
359
+ item.activation = Math.min( 1, item.activation + 0.02 )
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Write each live WM item as a state entity so downstream engines can read them,
365
+ * and delete any stale WM entities that no longer correspond to live items.
366
+ */
367
+ private _persistItems( tick: Tick, state: ReadonlySimulationState, commands: StateCommands ): void {
368
+ const liveIds = new Set<string>()
369
+
370
+ for( const item of this._items ){
371
+ const entityId = `wm-item-${item.id}`
372
+ liveIds.add( entityId )
373
+ commands.set!.push({
374
+ id: entityId,
375
+ type: 'working_memory.item',
376
+ metadata: {
377
+ wmType: item.type,
378
+ content: item.content,
379
+ activation: item.activation,
380
+ attendedCount: item.attendedAt.length,
381
+ tags: item.tags,
382
+ tick
383
+ }
384
+ })
385
+ }
386
+
387
+ // Remove stale WM entities from state that are no longer in the live item set
388
+ // (entities written in a previous tick whose item has since decayed)
389
+ for( const entity of state.entities.values() ){
390
+ if( entity.type === 'working_memory.item' && !liveIds.has( entity.id ) ){
391
+ commands.delete!.push( entity.id )
392
+ }
393
+ }
394
+ }
395
+
396
+ private _evictIfNeeded(): void {
397
+ if( this._items.length < this._modulatedCapacity ) return
398
+ let minIdx = 0
399
+ for( let i = 1; i < this._items.length; i++ ){
400
+ const cur = this._items[i]
401
+ const best = this._items[minIdx]
402
+ if( cur !== undefined && best !== undefined && cur.activation < best.activation ) minIdx = i
403
+ }
404
+ this._items.splice( minIdx, 1 )
405
+ }
406
+ }