@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,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
|
+
}
|