@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,251 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/dream.simulator.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * DreamSimulator — reactivates and recombines memory traces during rest.
7
+ *
8
+ * Functions:
9
+ * - Memory reactivation: replays recent episodic memories
10
+ * - Emotional processing: dampens emotional charge of memories
11
+ * - Creative recombination: randomly associates related memories
12
+ * - Consolidation boost: strengthens reactivated memories
13
+ *
14
+ * Only active during sleep/rest states.
15
+ * Models the hippocampal-neocortical dialogue during slow-wave sleep
16
+ * and the emotional processing of REM sleep.
17
+ *
18
+ * Part of Shard 2 (Memory Layer) — runs every tick when sleeping.
19
+ */
20
+
21
+ import type {
22
+ Duration,
23
+ Tick,
24
+ SimulationContext,
25
+ SeededPRNG,
26
+ ReadonlySimulationState,
27
+ StateCommands,
28
+ SimulationEvent,
29
+ } from '#core/types'
30
+ import type { EpisodicConsolidator, EpisodicMemory } from '#faculties/episodic.consolidator'
31
+ import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
32
+ import type { CognitiveEventSchema } from '#cognition/schema.registry'
33
+ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
34
+ import { GenerativeModel } from '#cognition/generative.model'
35
+
36
+ export interface DreamSimulatorConfig {
37
+ /** Maximum memories to reactivate per tick */
38
+ maxReactivationsPerTick?: number
39
+ /** How much emotional dampening per reactivation */
40
+ emotionalDampeningRate?: number
41
+ /** Probability of creative recombination between two memories */
42
+ recombinationProbability?: number
43
+ bus?: CognitiveBus
44
+ }
45
+
46
+ export class DreamSimulator implements SimulationEngine, CognitiveEngine {
47
+ readonly name = 'dream-simulator'
48
+
49
+ private _maxReactivationsPerTick: number
50
+ private _emotionalDampeningRate: number
51
+ private _recombinationProbability: number
52
+
53
+ private _episodicConsolidator: EpisodicConsolidator | null = null
54
+ private _lastReactivationIndex = 0
55
+ private _isSleeping: boolean = false
56
+
57
+ private _bus: CognitiveBus | null = null
58
+
59
+ private readonly _model = new GenerativeModel()
60
+
61
+
62
+ constructor( config: DreamSimulatorConfig = {} ){
63
+ this._bus = config.bus ?? null
64
+ this._maxReactivationsPerTick = config.maxReactivationsPerTick ?? 5
65
+ this._emotionalDampeningRate = config.emotionalDampeningRate ?? 0.05
66
+ this._recombinationProbability = config.recombinationProbability ?? 0.1
67
+ }
68
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
69
+
70
+ // ── Reference to episodic consolidator ───────────────────
71
+
72
+ attachConsolidator( consolidator: EpisodicConsolidator ): void {
73
+ this._episodicConsolidator = consolidator
74
+ }
75
+
76
+ // ── Engine interface ─────────────────────────────────────
77
+
78
+ subscribes(): string[] {
79
+ return [
80
+ 'executive.prediction.formed',
81
+ 'sleep.begun',
82
+ 'sleep.ended',
83
+ ]
84
+ }
85
+
86
+ publishes(): CognitiveEventSchema[] { return [] }
87
+
88
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
89
+ this._model.observe( e.type, e.salience )
90
+ if( e.type === 'executive.prediction.formed' ){
91
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
92
+ if( p.predictedDomains.includes('memory') )
93
+ this._model.setPrecision( 'dream.reactivation', 1.0 + p.confidence * 0.5 )
94
+ }
95
+ if( e.type === 'sleep.begun' ) this._isSleeping = true
96
+ if( e.type === 'sleep.ended' ) this._isSleeping = false
97
+ }
98
+
99
+ snapshot(): Record<string, unknown> { return {} }
100
+
101
+ async react(
102
+ _delta: Duration,
103
+ tick: Tick,
104
+ _state: ReadonlySimulationState,
105
+ context: SimulationContext
106
+ ): Promise<EngineResult> {
107
+ const
108
+ events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
109
+ commands: StateCommands = { metrics: [] }
110
+
111
+ if( !this._isSleeping || !this._episodicConsolidator )
112
+ return { commands }
113
+
114
+ const allEpisodes = this._episodicConsolidator.getAllEpisodes()
115
+ if( allEpisodes.length === 0 )
116
+ return { commands }
117
+
118
+ let reactivated = 0
119
+ let recombinations = 0
120
+
121
+ // Working copies of only the episodes we touch this tick. We never mutate the
122
+ // shared objects getAllEpisodes() handed us (they may be held by other engines
123
+ // this tick) — instead we mutate drafts and commit them through the owning
124
+ // consolidator as an immutable replace (mirrors ForgettingCurve.applyDecay).
125
+ type Draft = { activationStrength: number; emotionalTags: Record<string, number>; tags: string[] }
126
+ const drafts = new Map<string, Draft>()
127
+ const draftOf = ( ep: EpisodicMemory ): Draft => {
128
+ let d = drafts.get( ep.id )
129
+ if( !d ){
130
+ d = { activationStrength: ep.activationStrength, emotionalTags: { ...ep.emotionalTags }, tags: [ ...ep.tags ] }
131
+ drafts.set( ep.id, d )
132
+ }
133
+ return d
134
+ }
135
+
136
+ // 1. Select memories for reactivation (biased toward recent + emotional)
137
+ const candidates = this._selectForReactivation( allEpisodes )
138
+
139
+ for( let i = 0; i < Math.min( this._maxReactivationsPerTick, candidates.length ); i++ ){
140
+ const episode = candidates[i]!
141
+ if( !episode ) continue
142
+
143
+ const d = draftOf( episode )
144
+
145
+ // Reactivate: boost strength slightly
146
+ d.activationStrength = Math.min( 1, d.activationStrength + 0.03 )
147
+
148
+ // Emotional dampening: reduce emotional charge (processing)
149
+ for( const emotion of Object.keys( d.emotionalTags ) ){
150
+ const current = d.emotionalTags[ emotion ] ?? 0
151
+ if( Math.abs( current ) > 0.05 )
152
+ d.emotionalTags[ emotion ] = current * ( 1 - this._emotionalDampeningRate )
153
+ }
154
+
155
+ reactivated++
156
+
157
+ // 2. Creative recombination: randomly pair with another memory.
158
+ // Draws from the seeded PRNG (not Math.random) so a run replays identically.
159
+ if( context.prng.nextBool( this._recombinationProbability ) ){
160
+ const other = this._pickRandomMemory( allEpisodes, episode, context.prng )
161
+ if( other ){
162
+ // Create a novel association — boost both memories slightly
163
+ const od = draftOf( other )
164
+ d.activationStrength = Math.min( 1, d.activationStrength + 0.02 )
165
+ od.activationStrength = Math.min( 1, od.activationStrength + 0.02 )
166
+
167
+ // Add each other's tags (cross-pollination)
168
+ for( const tag of od.tags ){
169
+ if( !d.tags.includes( tag ) )
170
+ d.tags.push( tag )
171
+ }
172
+
173
+ recombinations++
174
+ }
175
+ }
176
+ }
177
+
178
+ // Commit all drafts atomically through the store owner (immutable replace).
179
+ this._episodicConsolidator.applyDreamUpdates( drafts )
180
+
181
+ // 3. Update pointer for next tick
182
+ this._lastReactivationIndex = ( this._lastReactivationIndex + reactivated ) % Math.max( 1, allEpisodes.length )
183
+
184
+ commands.metrics!.push(
185
+ [ 'dream.reactivated', reactivated ],
186
+ [ 'dream.recombinations', recombinations ],
187
+ )
188
+
189
+ if( reactivated > 0 && reactivated >= 3 )
190
+ events.push({
191
+ type: 'dream.activity',
192
+ source: this.name,
193
+ payload: { reactivated, recombinations },
194
+ })
195
+
196
+
197
+ // Phase C + F: publish cognitive event — gated by prediction error
198
+ const _bus = this._bus
199
+ if( _bus && reactivated > 0 ){
200
+ const predErr = this._model.observe( 'dream.reactivation', reactivated )
201
+ if( !predErr.gated )
202
+ _bus.publish({ type: 'dream.active', version: 1, sourceEngine: this.name, salience: Math.max( 0.3, predErr.salience ), payload: { reactivated } })
203
+ }
204
+ return { events: events.length > 0 ? events : undefined, commands }
205
+ }
206
+
207
+ // ── Internal ─────────────────────────────────────────────
208
+
209
+ private _selectForReactivation( episodes: ReadonlyArray<EpisodicMemory> ): EpisodicMemory[] {
210
+ // Score each episode for reactivation priority
211
+ const scored = episodes.map( ep => ({
212
+ episode: ep,
213
+ score: this._reactivationScore( ep ),
214
+ }))
215
+
216
+ // Sort by score (highest first)
217
+ scored.sort( ( a, b ) => b.score - a.score )
218
+
219
+ return scored.slice( 0, this._maxReactivationsPerTick * 3 ).map( s => s.episode )
220
+ }
221
+
222
+ private _reactivationScore( episode: EpisodicMemory ): number {
223
+ const
224
+ emotionalIntensity = Object.values( episode.emotionalTags )
225
+ .reduce( ( s, v ) => s + Math.abs( v ), 0 )
226
+ / Math.max( 1, Object.keys( episode.emotionalTags ).length ),
227
+ recency = 1, // All episodes are in the store; recency implicit by position
228
+ retrievalCount = Math.min( 1, episode.retrievalCount / 10 ),
229
+ novelty = 1 - retrievalCount // Less retrieved = more need for reactivation
230
+
231
+ return emotionalIntensity * 0.4
232
+ + recency * 0.3
233
+ + novelty * 0.3
234
+ }
235
+
236
+ private _pickRandomMemory(
237
+ episodes: ReadonlyArray<EpisodicMemory>,
238
+ exclude: EpisodicMemory,
239
+ prng: SeededPRNG
240
+ ): EpisodicMemory | null {
241
+ // Pick a memory that shares at least one tag with the source
242
+ const related = episodes.filter( ep =>
243
+ ep.id !== exclude.id
244
+ && ep.tags.some( t => exclude.tags.includes( t ) )
245
+ )
246
+
247
+ if( related.length === 0 ) return null
248
+
249
+ return related[ prng.nextInt( 0, related.length ) ]!
250
+ }
251
+ }
@@ -0,0 +1,253 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/empathy.simulator.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * EmpathySimulator — simulates what another agent is feeling.
7
+ *
8
+ * Uses TheoryOfMind models plus observed social signals to estimate
9
+ * another agent's emotional state. Produces empathic responses:
10
+ * - Emotional resonance (feeling what they feel)
11
+ * - Compassionate concern (wanting to help if they're distressed)
12
+ * - Empathic accuracy (how well the simulation matches reality)
13
+ *
14
+ * Part of Shard 1 (Social Layer) — runs every tick, synchronous.
15
+ */
16
+
17
+ import type {
18
+ Duration,
19
+ Tick,
20
+ SimulationContext,
21
+ ReadonlySimulationState,
22
+ StateCommands,
23
+ SimulationEvent,
24
+ } from '#core/types'
25
+ import type { AgentMentalModel, TheoryOfMind } from '#faculties/theory.of.mind'
26
+ import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
27
+ import type { CognitiveEventSchema } from '#cognition/schema.registry'
28
+ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
29
+ import { GenerativeModel } from '#cognition/generative.model'
30
+ import { readEffectiveParams } from '#cognition/persona.prior'
31
+
32
+ // Closeness amplifies emotion contagion — the Will resonates more with those it is bonded
33
+ // to. Scales the attachment bond (0..1, from the `bond-<keid>` entity) into the weight.
34
+ const EMPATHY_BOND_AMPLIFY = 0.5
35
+
36
+ export interface EmpathySimulatorConfig {
37
+ /** How strongly another's emotion resonates (0-1) */
38
+ resonanceStrength?: number
39
+ /** Threshold for triggering compassionate response */
40
+ compassionThreshold?: number
41
+ bus?: CognitiveBus
42
+ }
43
+
44
+ interface EmpathicState {
45
+ targetKeid: string
46
+ estimatedEmotion: { valence: number; arousal: number; dominantEmotion: string }
47
+ resonanceStrength: number // How strongly this resonates
48
+ compassionateConcern: number // 0-1: desire to help
49
+ confidence: number // How accurate the estimate is
50
+ }
51
+
52
+ export class EmpathySimulator implements SimulationEngine, CognitiveEngine {
53
+ readonly name = 'empathy-simulator'
54
+
55
+ private _resonanceStrength: number
56
+ private _compassionThreshold: number
57
+
58
+ private _theoryOfMind: TheoryOfMind | null = null
59
+ private _currentEmpathicStates: EmpathicState[] = []
60
+ private _pendingInteractions: Array<{
61
+ keid: string; valence: number; intensity: number; directedAtSelf: boolean
62
+ }> = []
63
+
64
+ private _bus: CognitiveBus | null = null
65
+
66
+ private readonly _model = new GenerativeModel()
67
+
68
+
69
+ constructor( config: EmpathySimulatorConfig = {} ){
70
+ this._bus = config.bus ?? null
71
+ this._resonanceStrength = config.resonanceStrength ?? 0.6
72
+ this._compassionThreshold = config.compassionThreshold ?? 0.3
73
+ }
74
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
75
+
76
+ attachTheoryOfMind( tom: TheoryOfMind ): void {
77
+ this._theoryOfMind = tom
78
+ }
79
+
80
+ // ── Engine interface ─────────────────────────────────────
81
+
82
+ subscribes(): string[] {
83
+ return [
84
+ 'executive.prediction.formed',
85
+ 'interaction.occurred',
86
+ ]
87
+ }
88
+
89
+ publishes(): CognitiveEventSchema[] { return [] }
90
+
91
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
92
+ this._model.observe( e.type, e.salience )
93
+ if( e.type === 'executive.prediction.formed' ){
94
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
95
+ if( p.predictedDomains.includes('social') )
96
+ this._model.setPrecision( 'empathy.active', 1.0 + p.confidence * 0.5 )
97
+ }
98
+ if( e.type === 'interaction.occurred' ){
99
+ const p = e.payload as { keid: string; valence: number; intensity: number; directedAtSelf: boolean }
100
+ if( p.keid && p.keid !== 'agent-self' )
101
+ this._pendingInteractions.push( p )
102
+ }
103
+ }
104
+
105
+ snapshot(): Record<string, unknown> { return {} }
106
+
107
+ async react(
108
+ _delta: Duration,
109
+ tick: Tick,
110
+ state: ReadonlySimulationState,
111
+ _context: SimulationContext
112
+ ): Promise<EngineResult> {
113
+ const
114
+ events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
115
+ commands: StateCommands = { set: [], metrics: [] }
116
+
117
+ // Channel A (agreeableness → empathy): how strongly the Will resonates with others'
118
+ // emotions is refreshed each tick as base ⊕ persona-prior. An agreeable Will develops
119
+ // a higher resonance and feels others' states more. (Seed existed but was ignored.)
120
+ this._resonanceStrength = readEffectiveParams( state, 'engine-config-empathy' ).resonanceStrength ?? this._resonanceStrength
121
+
122
+ if( !this._theoryOfMind ){
123
+ commands.metrics!.push([ 'empathy.active_models', 0 ])
124
+ return { commands }
125
+ }
126
+
127
+ const empathicStates: EmpathicState[] = []
128
+ let totalResonance = 0
129
+ let totalCompassion = 0
130
+
131
+ // Drain buffered interactions and simulate empathic response per agent
132
+ for( const interaction of this._pendingInteractions.splice( 0 ) ){
133
+ const { keid, valence: socialValence, directedAtSelf } = interaction
134
+
135
+ const tomModel = this._theoryOfMind.getModel( keid )
136
+
137
+ const estimatedEmotion = this._estimateEmotion( socialValence, tomModel )
138
+
139
+ const resonance = directedAtSelf
140
+ ? this._resonanceStrength * 1.5
141
+ : this._resonanceStrength
142
+
143
+ const compassion = estimatedEmotion.valence < -this._compassionThreshold
144
+ ? Math.abs( estimatedEmotion.valence ) * resonance
145
+ : 0
146
+
147
+ const entry: EmpathicState = {
148
+ targetKeid: keid,
149
+ estimatedEmotion,
150
+ resonanceStrength: resonance,
151
+ compassionateConcern: compassion,
152
+ confidence: tomModel?.modelConfidence ?? 0.3,
153
+ }
154
+
155
+ empathicStates.push( entry )
156
+ totalResonance += resonance
157
+ totalCompassion += compassion
158
+ }
159
+
160
+ this._currentEmpathicStates = empathicStates
161
+
162
+ // Persist
163
+ for( const es of empathicStates ){
164
+ commands.set!.push({
165
+ id: `empathy-${es.targetKeid}`,
166
+ type: 'empathic_state',
167
+ metadata: {
168
+ targetKeid: es.targetKeid,
169
+ estimatedValence: es.estimatedEmotion.valence,
170
+ estimatedArousal: es.estimatedEmotion.arousal,
171
+ estimatedDominantEmotion: es.estimatedEmotion.dominantEmotion,
172
+ resonance: es.resonanceStrength,
173
+ compassion: es.compassionateConcern,
174
+ confidence: es.confidence,
175
+ tick,
176
+ },
177
+ })
178
+ }
179
+
180
+ // Emotion contagion — the integration that makes empathy MOVE the Will. Each agent's
181
+ // inferred emotion pulls the Will's own affect toward it, weighted by resonance (#24,
182
+ // agreeableness-developable) and amplified by closeness (attachment bond). affective.
183
+ // blender folds the vicarious valence/arousal into the blended PAD.
184
+ let vicariousValence = 0, vicariousArousal = 0
185
+ for( const es of empathicStates ){
186
+ const bond = ( state.entities.get( `bond-${es.targetKeid}` )?.metadata?.attachmentStrength as number ) ?? 0
187
+ const w = es.resonanceStrength * ( 1 + EMPATHY_BOND_AMPLIFY * bond )
188
+ vicariousValence += es.estimatedEmotion.valence * w
189
+ vicariousArousal += es.estimatedEmotion.arousal * w
190
+ }
191
+ if( empathicStates.length > 0 ){
192
+ vicariousValence = Math.max( -1, Math.min( 1, vicariousValence / empathicStates.length ) )
193
+ vicariousArousal = Math.max( 0, Math.min( 1, vicariousArousal / empathicStates.length ) )
194
+ }
195
+
196
+ commands.metrics!.push(
197
+ [ 'empathy.active_models', empathicStates.length ],
198
+ [ 'empathy.total_resonance', Math.min( 1, totalResonance ) ],
199
+ [ 'empathy.total_compassion', Math.min( 1, totalCompassion ) ],
200
+ [ 'empathy.vicarious_valence', vicariousValence ],
201
+ [ 'empathy.vicarious_arousal', vicariousArousal ],
202
+ )
203
+
204
+ // Compassion event
205
+ if( totalCompassion > 0.5 )
206
+ events.push({
207
+ type: 'empathy.compassion_activated',
208
+ source: this.name,
209
+ payload: {
210
+ targets: empathicStates
211
+ .filter( s => s.compassionateConcern > 0.3 )
212
+ .map( s => s.targetKeid ),
213
+ },
214
+ })
215
+
216
+
217
+ // Phase C + F: publish cognitive event — gated by prediction error
218
+ const _bus = this._bus
219
+ if( _bus && empathicStates.length > 0 ){
220
+ const predErr = this._model.observe( 'empathy.active', empathicStates.length )
221
+ if( !predErr.gated )
222
+ _bus.publish({ type: 'empathy.resonance', version: 1, sourceEngine: this.name, salience: Math.max( 0.3, predErr.salience ), payload: { activeModels: empathicStates.length } })
223
+ }
224
+ return { events: events.length > 0 ? events : undefined, commands }
225
+ }
226
+
227
+ // ── Internal ─────────────────────────────────────────────
228
+
229
+ private _estimateEmotion(
230
+ socialValence: number,
231
+ tomModel?: AgentMentalModel
232
+ ): { valence: number; arousal: number; dominantEmotion: string } {
233
+ // Start with ToM model estimate if available
234
+ if( tomModel && tomModel.modelConfidence > 0.3 )
235
+ return tomModel.emotionalState
236
+
237
+ // Otherwise estimate from social valence
238
+ return {
239
+ valence: socialValence,
240
+ arousal: Math.abs( socialValence ) > 0.5 ? 0.7 : 0.3,
241
+ dominantEmotion: socialValence > 0.3 ? 'positive'
242
+ : socialValence < -0.3 ? 'distressed'
243
+ : 'neutral',
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Get current empathic states.
249
+ */
250
+ getEmpathicStates(): ReadonlyArray<EmpathicState> {
251
+ return this._currentEmpathicStates
252
+ }
253
+ }