@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,294 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/reputation.tracker.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * ReputationTracker — maintains models of others' reliability,
7
+ * cooperativeness, and social standing.
8
+ *
9
+ * Tracks per-agent:
10
+ * - Reliability (did they do what they said they would?)
11
+ * - Cooperativeness (did they help or hinder?)
12
+ * - Social standing (how do others seem to regard them?)
13
+ * - Trustworthiness (composite of reliability + cooperativeness)
14
+ *
15
+ * Part of Shard 1 (Social Layer) — runs every tick, synchronous.
16
+ */
17
+
18
+ import type {
19
+ Duration,
20
+ Tick,
21
+ SimulationContext,
22
+ ReadonlySimulationState,
23
+ StateCommands,
24
+ SimulationEvent,
25
+ } from '#core/types'
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
+ export interface ReputationTrackerConfig {
33
+ /** Maximum agents to track */
34
+ maxTrackedAgents?: number
35
+ /** How quickly reputation decays without new observations */
36
+ decayRate?: number
37
+ /** Minimum interactions before reputation is considered reliable */
38
+ minInteractions?: number
39
+ /** How much a cooperative interaction raises a tracked agent's cooperativeness (trust step) */
40
+ trustGrowthStep?: number
41
+ bus?: CognitiveBus
42
+ }
43
+
44
+ interface Reputation {
45
+ keid: string
46
+ reliability: number // 0-1: keeps promises
47
+ cooperativeness: number // 0-1: helps vs. hinders
48
+ socialStanding: number // 0-1: regarded by others
49
+ trustworthiness: number // 0-1: composite
50
+ interactionCount: number
51
+ positiveInteractions: number
52
+ negativeInteractions: number
53
+ lastInteractionTick: Tick
54
+ confidence: number // 0-1: how confident in this assessment
55
+ }
56
+
57
+ export class ReputationTracker implements SimulationEngine, CognitiveEngine {
58
+ readonly name = 'reputation-tracker'
59
+
60
+ private _maxTrackedAgents: number
61
+ private _decayRate: number
62
+ private _minInteractions: number
63
+ private _trustGrowthStep: number
64
+
65
+ private _reputations = new Map<string, Reputation>()
66
+ /** True after reputations have been rehydrated from persisted state on first tick. */
67
+ private _restored = false
68
+ private _pendingInteractions: Array<{
69
+ keid: string; valence: number; intensity: number; directedAtSelf: boolean
70
+ }> = []
71
+
72
+ private _bus: CognitiveBus | null = null
73
+
74
+ private readonly _model = new GenerativeModel()
75
+
76
+
77
+ constructor( config: ReputationTrackerConfig = {} ){
78
+ this._bus = config.bus ?? null
79
+ this._maxTrackedAgents = config.maxTrackedAgents ?? 20
80
+ this._decayRate = config.decayRate ?? 0.001
81
+ this._minInteractions = config.minInteractions ?? 3
82
+ this._trustGrowthStep = config.trustGrowthStep ?? 0.05
83
+ }
84
+
85
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
86
+
87
+ // ── Engine interface ─────────────────────────────────────
88
+
89
+ subscribes(): string[] {
90
+ return [
91
+ 'executive.prediction.formed',
92
+ 'interaction.occurred',
93
+ ]
94
+ }
95
+
96
+ publishes(): CognitiveEventSchema[] { return [] }
97
+
98
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
99
+ this._model.observe( e.type, e.salience )
100
+ if( e.type === 'executive.prediction.formed' ){
101
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
102
+ if( p.predictedDomains.includes('social') )
103
+ this._model.setPrecision( 'reputation.tracked', 1.0 + p.confidence * 0.5 )
104
+ }
105
+ if( e.type === 'interaction.occurred' ){
106
+ const p = e.payload as { keid: string; valence: number; intensity: number; directedAtSelf: boolean }
107
+ this._pendingInteractions.push( p )
108
+ }
109
+ }
110
+
111
+ snapshot(): Record<string, unknown> { return {} }
112
+
113
+ async react(
114
+ _delta: Duration,
115
+ tick: Tick,
116
+ state: ReadonlySimulationState,
117
+ _context: SimulationContext
118
+ ): Promise<EngineResult> {
119
+ const
120
+ events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
121
+ commands: StateCommands = { set: [], metrics: [] }
122
+
123
+ // Channel A (agreeableness → trust): how much a cooperative interaction raises an
124
+ // agent's cooperativeness is refreshed each tick as base ⊕ persona-prior. An agreeable
125
+ // Will develops a larger step — it extends benefit-of-the-doubt and credits cooperation
126
+ // more readily (the trust facet).
127
+ this._trustGrowthStep = readEffectiveParams( state, 'engine-config-reputation' ).trustGrowthStep ?? this._trustGrowthStep
128
+
129
+ // Rehydrate _reputations from persisted state on the first tick after
130
+ // construction / snapshot restore. Without this, every session restart
131
+ // begins with an empty reputation map and immediately overwrites all
132
+ // saved relationship data with zeros on the first persist cycle.
133
+ if( !this._restored ){
134
+ this._restoreFromState( state )
135
+ this._restored = true
136
+ }
137
+
138
+ // 1. Drain buffered interactions to update reputations
139
+ for( const interaction of this._pendingInteractions.splice( 0 ) ){
140
+ const { keid, valence } = interaction
141
+ if( !keid || keid === 'agent-self') continue
142
+
143
+ const rep = this._getOrCreate( keid, tick )
144
+
145
+ rep.interactionCount++
146
+ rep.lastInteractionTick = tick
147
+
148
+ if( valence > 0 ){
149
+ rep.positiveInteractions++
150
+ rep.cooperativeness = Math.min( 1, rep.cooperativeness + this._trustGrowthStep )
151
+ }
152
+
153
+ if( valence < -0.3 ){
154
+ rep.negativeInteractions++
155
+ rep.cooperativeness = Math.max( 0, rep.cooperativeness - 0.08 )
156
+ }
157
+
158
+ // Reliability proxy: strong positive valence → consistent/reliable behaviour;
159
+ // strong negative → deceptive/unreliable. The original used explicit `reliable`
160
+ // and `deceptive` metadata flags on percept entities; those aren't in interaction.occurred,
161
+ // so we use extreme valence as the best available signal.
162
+ if( valence > 0.7 )
163
+ rep.reliability = Math.min( 1, rep.reliability + 0.04 )
164
+ else if( valence < -0.7 )
165
+ rep.reliability = Math.max( 0, rep.reliability - 0.08 )
166
+
167
+ // Composite trustworthiness
168
+ rep.trustworthiness = ( rep.reliability * 0.5 + rep.cooperativeness * 0.5 )
169
+
170
+ // Confidence increases with more interactions
171
+ rep.confidence = Math.min( 1, rep.interactionCount / this._minInteractions )
172
+ }
173
+
174
+ // 2. Decay old reputations
175
+ for( const rep of this._reputations.values() ){
176
+ const ticksSince = tick - rep.lastInteractionTick
177
+ if( ticksSince > 200 ){
178
+ rep.confidence = Math.max( 0.05, rep.confidence - this._decayRate * ticksSince )
179
+ }
180
+ }
181
+
182
+ // 3. Prune
183
+ this._prune()
184
+
185
+ // 4. Persist
186
+ for( const rep of this._reputations.values() ){
187
+ if( rep.interactionCount === 0 ) continue
188
+
189
+ commands.set!.push({
190
+ id: `reputation-${rep.keid}`,
191
+ type: 'reputation',
192
+ metadata: {
193
+ keid: rep.keid,
194
+ reliability: rep.reliability,
195
+ cooperativeness: rep.cooperativeness,
196
+ socialStanding: rep.socialStanding,
197
+ trustworthiness: rep.trustworthiness,
198
+ interactionCount: rep.interactionCount,
199
+ positiveInteractions: rep.positiveInteractions,
200
+ negativeInteractions: rep.negativeInteractions,
201
+ lastInteractionTick: rep.lastInteractionTick,
202
+ confidence: rep.confidence,
203
+ tick,
204
+ },
205
+ })
206
+ }
207
+
208
+ commands.metrics!.push(
209
+ [ 'reputation.tracked_agents', this._reputations.size ],
210
+ )
211
+
212
+
213
+ // Phase C + F: publish cognitive event — gated by prediction error
214
+ const _bus = this._bus
215
+ if( _bus && this._reputations.size > 0 ){
216
+ const predErr = this._model.observe( 'reputation.tracked', this._reputations.size )
217
+ if( !predErr.gated )
218
+ _bus.publish({ type: 'reputation.updated', version: 1, sourceEngine: this.name, salience: Math.max( 0.2, predErr.salience ), payload: { trackedAgents: this._reputations.size } })
219
+ }
220
+ return { events: events.length > 0 ? events : undefined, commands }
221
+ }
222
+
223
+ // ── Public API ───────────────────────────────────────────
224
+
225
+ getReputation( keid: string ): Reputation | undefined {
226
+ return this._reputations.get( keid )
227
+ }
228
+
229
+ isTrusted( keid: string, threshold: number = 0.5 ): boolean {
230
+ const rep = this._reputations.get( keid )
231
+ return ( rep?.trustworthiness ?? 0.5 ) >= threshold
232
+ }
233
+
234
+ // ── Internal ─────────────────────────────────────────────
235
+
236
+ /**
237
+ * Rehydrate _reputations from 'reputation' entities in state.
238
+ * Called once on the first tick after snapshot restore so that relationship
239
+ * models formed in previous sessions survive a server restart.
240
+ */
241
+ private _restoreFromState( state: ReadonlySimulationState ): void {
242
+ for( const entity of state.entities.values() ){
243
+ if( entity.type !== 'reputation' ) continue
244
+ const m = entity.metadata ?? {}
245
+ const keid = m['keid'] as string | undefined
246
+ if( !keid ) continue
247
+ if( this._reputations.has( keid ) ) continue // prefer in-memory if already present
248
+
249
+ this._reputations.set( keid, {
250
+ keid,
251
+ reliability: ( m['reliability'] as number ) ?? 0.5,
252
+ cooperativeness: ( m['cooperativeness'] as number ) ?? 0.5,
253
+ socialStanding: ( m['socialStanding'] as number ) ?? 0.5,
254
+ trustworthiness: ( m['trustworthiness'] as number ) ?? 0.5,
255
+ interactionCount: ( m['interactionCount'] as number ) ?? 0,
256
+ positiveInteractions: ( m['positiveInteractions'] as number ) ?? 0,
257
+ negativeInteractions: ( m['negativeInteractions'] as number ) ?? 0,
258
+ lastInteractionTick: ( m['lastInteractionTick'] as number ) ?? 0,
259
+ confidence: ( m['confidence'] as number ) ?? 0.1,
260
+ })
261
+ }
262
+ }
263
+
264
+ private _getOrCreate( keid: string, tick: Tick ): Reputation {
265
+ const existing = this._reputations.get( keid )
266
+ if( existing ) return existing
267
+
268
+ const rep: Reputation = {
269
+ keid,
270
+ reliability: 0.5,
271
+ cooperativeness: 0.5,
272
+ socialStanding: 0.5,
273
+ trustworthiness: 0.5,
274
+ interactionCount: 0,
275
+ positiveInteractions: 0,
276
+ negativeInteractions: 0,
277
+ lastInteractionTick: tick,
278
+ confidence: 0.1,
279
+ }
280
+
281
+ this._reputations.set( keid, rep )
282
+ return rep
283
+ }
284
+
285
+ private _prune(): void {
286
+ if( this._reputations.size <= this._maxTrackedAgents ) return
287
+
288
+ const sorted = Array.from( this._reputations.entries() )
289
+ .sort( ( a, b ) => b[1].interactionCount - a[1].interactionCount )
290
+
291
+ for( const [ id ] of sorted.slice( this._maxTrackedAgents ) )
292
+ this._reputations.delete( id )
293
+ }
294
+ }
@@ -0,0 +1,304 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/reward.evaluator.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * RewardEvaluator — detects positive outcomes and progress.
7
+ *
8
+ * Evaluates:
9
+ * - Goal progress and completion
10
+ * - Positive social feedback
11
+ * - Resource gains
12
+ * - Novel discoveries (when safe)
13
+ *
14
+ * Produces: joy, satisfaction, excitement
15
+ *
16
+ * Joy = immediate positive outcome
17
+ * Satisfaction = goal completion / steady progress
18
+ * Excitement = anticipated positive outcome + high arousal
19
+ *
20
+ * Part of Shard 1 (Affective Layer) — runs every tick, synchronous.
21
+ */
22
+
23
+ import type {
24
+ Duration,
25
+ Tick,
26
+ SimulationContext,
27
+ ReadonlySimulationState,
28
+ StateCommands,
29
+ } from '#core/types'
30
+ import type { SimulationEngine, CognitiveEngine, EngineResult } from '#cognition/types'
31
+ import type { CognitiveEventSchema } from '#cognition/schema.registry'
32
+ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
33
+ import { readEffectiveParams } from '#cognition/persona.prior'
34
+ import { GenerativeModel } from '#cognition/generative.model'
35
+
36
+ export interface RewardEvaluatorConfig {
37
+ /** Weight for goal-related rewards */
38
+ goalWeight?: number
39
+ /** Weight for social rewards */
40
+ socialWeight?: number
41
+ /** Weight for resource gains */
42
+ resourceWeight?: number
43
+ /** Weight for discovery/novelty rewards */
44
+ discoveryWeight?: number
45
+ /**
46
+ * How fast the social reward signal decays per tick (0-1).
47
+ * Default 0.02 → social warmth fades to zero over ~50 ticks after last interaction.
48
+ */
49
+ socialDecayRate?: number
50
+ /** How much each positive directed interaction warms the social reward (warmth intensity) */
51
+ socialWarmthBoost?: number
52
+ bus?: CognitiveBus
53
+ }
54
+
55
+ export class RewardEvaluator implements SimulationEngine, CognitiveEngine {
56
+ readonly name = 'reward-evaluator'
57
+
58
+ private _goalWeight: number
59
+ private _socialWeight: number
60
+ private _resourceWeight: number
61
+ private _discoveryWeight: number
62
+ private _socialDecayRate: number
63
+ private _socialWarmthBoost: number
64
+ /**
65
+ * How fast goal-reward signal decays per tick (0-1).
66
+ * Default 0.05 → a priority-1.0 goal reward fades to ~0 over ~20 ticks,
67
+ * giving a noticeable joy/satisfaction window after each completion.
68
+ */
69
+ private _goalRewardDecayRate: number
70
+
71
+ // Domain state — updated per-event
72
+ private _cachedEnergyLevel: number = 50
73
+ private _cachedNovelty: number = 0
74
+ private _cachedFearLevel: number = 0
75
+
76
+ // Cached reward components
77
+ /** Transient goal-achievement signal — spiked by goal.achieved, decays each tick. */
78
+ private _cachedGoalReward: number = 0
79
+ /** Transient social warmth — boosted by interaction.occurred, decays each tick. */
80
+ private _cachedSocialReward: number = 0
81
+ /** Count of goals completed recently — decays gradually, feeds satisfaction formula. */
82
+ private _goalsCompletedRecently: number = 0
83
+
84
+ // Track previous goal states for future entity-event updates
85
+ private _previousGoalProgress = new Map<string, number>()
86
+
87
+ private _bus: CognitiveBus | null = null
88
+
89
+ private readonly _model = new GenerativeModel()
90
+
91
+
92
+ constructor( config: RewardEvaluatorConfig = {} ){
93
+ this._bus = config.bus ?? null
94
+ this._goalWeight = config.goalWeight ?? 0.40
95
+ this._socialWeight = config.socialWeight ?? 0.25
96
+ this._resourceWeight = config.resourceWeight ?? 0.15
97
+ this._discoveryWeight = config.discoveryWeight ?? 0.20
98
+ this._socialDecayRate = config.socialDecayRate ?? 0.02
99
+ this._socialWarmthBoost = config.socialWarmthBoost ?? 0.4
100
+ this._goalRewardDecayRate = 0.05
101
+ }
102
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
103
+
104
+ // ── Engine interface ─────────────────────────────────────
105
+
106
+ subscribes(): string[] { return ["energy.state.changed","sleep.state.changed","stress.state.changed","novelty.state.changed","executive.prediction.formed","interaction.occurred","goal.achieved","action.outcome"] }
107
+ publishes(): CognitiveEventSchema[] { return [] }
108
+
109
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
110
+ this._model.observe( e.type, e.salience )
111
+
112
+ switch( e.type ){
113
+
114
+ case 'energy.state.changed': {
115
+ const p = e.payload as { level: number }
116
+ this._cachedEnergyLevel = p.level ?? this._cachedEnergyLevel
117
+ break
118
+ }
119
+
120
+ case 'sleep.state.changed':
121
+ // sleep pressure doesn't directly drive reward — absorbed for completeness
122
+ break
123
+
124
+ case 'stress.state.changed':
125
+ // stress payload doesn't carry reward-relevant data beyond what novelty provides
126
+ break
127
+
128
+ case 'novelty.state.changed': {
129
+ const p = e.payload as { novelty: number; fearLevel: number }
130
+ this._cachedNovelty = p.novelty ?? this._cachedNovelty
131
+ this._cachedFearLevel = p.fearLevel ?? this._cachedFearLevel
132
+ break
133
+ }
134
+
135
+ case 'executive.prediction.formed': {
136
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
137
+ if( p.predictedDomains.includes('energy') )
138
+ this._model.setPrecision('reward.value', 1.0 + p.confidence * 0.3)
139
+ break
140
+ }
141
+
142
+ case 'interaction.occurred': {
143
+ // Social warmth — each directed positive interaction boosts social reward.
144
+ // Negative interactions dampen it. Capped at [0, 1]; decays via react().
145
+ const p = e.payload as { keid: string; valence: number; intensity: number; directedAtSelf: boolean }
146
+ if( p.directedAtSelf ){
147
+ const contribution = p.valence * Math.max( 0.3, p.intensity )
148
+ this._cachedSocialReward = Math.min( 1, Math.max( 0,
149
+ this._cachedSocialReward + contribution * this._socialWarmthBoost
150
+ ))
151
+ this._model.observe( 'reward.social', this._cachedSocialReward )
152
+ }
153
+ break
154
+ }
155
+
156
+ case 'goal.achieved': {
157
+ // Goal completion — spike goal reward proportional to goal priority,
158
+ // and increment the goals-completed counter that feeds satisfaction.
159
+ // Both decay in react() so the joy/satisfaction window is ~15-20 ticks.
160
+ const p = e.payload as { goalId: string; priority: number; timeToComplete: number }
161
+ const priority = p.priority ?? 0.5
162
+ this._cachedGoalReward = Math.min( 1, this._cachedGoalReward + priority * 0.9 )
163
+ this._goalsCompletedRecently = Math.min( 2, this._goalsCompletedRecently + 1 )
164
+ this._model.observe( 'reward.goal', this._cachedGoalReward )
165
+ break
166
+ }
167
+
168
+ default:
169
+ return
170
+ }
171
+
172
+ return this._computeAndEmit()
173
+ }
174
+
175
+ snapshot(): Record<string, unknown> {
176
+ return {
177
+ cachedEnergyLevel: this._cachedEnergyLevel,
178
+ cachedNovelty: this._cachedNovelty,
179
+ cachedSocialReward: this._cachedSocialReward,
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Effective config = base engine-config-reward ⊕ persona-prior (single-source).
185
+ * No-op at boot: mirror params equal the constructor defaults (reconciled in #83).
186
+ */
187
+ private _readConfigFromState( state: ReadonlySimulationState ): void {
188
+ const p = readEffectiveParams( state, 'engine-config-reward' )
189
+ if( p.goalWeight != null ) this._goalWeight = p.goalWeight
190
+ if( p.socialWeight != null ) this._socialWeight = p.socialWeight
191
+ if( p.resourceWeight != null ) this._resourceWeight = p.resourceWeight
192
+ if( p.discoveryWeight != null ) this._discoveryWeight = p.discoveryWeight
193
+ if( p.socialDecayRate != null ) this._socialDecayRate = p.socialDecayRate
194
+ // Channel A (agreeableness → warmth): each interaction warms more, and warmth lingers
195
+ // (socialDecayRate lowered). _socialWarmthBoost is the cached field the interaction
196
+ // handler reads (onCognitiveEvent has no state).
197
+ if( p.socialWarmthBoost != null ) this._socialWarmthBoost = p.socialWarmthBoost
198
+ }
199
+
200
+ async react(
201
+ _delta: Duration,
202
+ _tick: Tick,
203
+ state: ReadonlySimulationState,
204
+ _ctx: SimulationContext,
205
+ ): Promise<EngineResult> {
206
+ // Effective config = base engine-config-reward ⊕ persona-prior (single-source).
207
+ this._readConfigFromState( state )
208
+
209
+ // Decay social reward each tick so warmth fades naturally between interactions.
210
+ // With default rate 0.02: a social reward of 1.0 decays to ~0 over ~50 ticks.
211
+ if( this._cachedSocialReward > 0 )
212
+ this._cachedSocialReward = Math.max( 0, this._cachedSocialReward - this._socialDecayRate )
213
+
214
+ // Decay goal-reward signal each tick.
215
+ // Default rate 0.05: a priority-1.0 achievement fades to ~0 over ~20 ticks,
216
+ // giving a clear joy/satisfaction window after each completion.
217
+ if( this._cachedGoalReward > 0 )
218
+ this._cachedGoalReward = Math.max( 0, this._cachedGoalReward - this._goalRewardDecayRate )
219
+
220
+ // Decay the goals-completed counter (feeds satisfaction formula).
221
+ if( this._goalsCompletedRecently > 0 )
222
+ this._goalsCompletedRecently = Math.max( 0, this._goalsCompletedRecently - 0.08 )
223
+
224
+ // Re-emit emotion metrics every tick so the affective blender always has fresh values.
225
+ return { commands: this._computeAndEmit() }
226
+ }
227
+
228
+ // ── Private helpers ──────────────────────────────────────
229
+
230
+ private _computeResourceReward(): number {
231
+ const energyRatio = this._cachedEnergyLevel / 100
232
+ return energyRatio > 0.5
233
+ ? ( energyRatio - 0.5 ) * 0.6
234
+ : 0
235
+ }
236
+
237
+ private _computeDiscoveryReward(): number {
238
+ const safety = 1 - this._cachedFearLevel
239
+ return this._cachedNovelty * safety * 0.8
240
+ }
241
+
242
+ private _computeAndEmit(): StateCommands {
243
+ const
244
+ goalReward = this._cachedGoalReward,
245
+ socialReward = this._cachedSocialReward,
246
+ resourceReward = this._computeResourceReward(),
247
+ discoveryReward = this._computeDiscoveryReward()
248
+
249
+ const rewardLevel
250
+ = goalReward * this._goalWeight
251
+ + socialReward * this._socialWeight
252
+ + resourceReward * this._resourceWeight
253
+ + discoveryReward * this._discoveryWeight
254
+
255
+ const
256
+ joy = Math.min( 1,
257
+ goalReward * 0.5
258
+ + socialReward * 0.3
259
+ + resourceReward * 0.2
260
+ ),
261
+
262
+ satisfaction = Math.min( 1,
263
+ goalReward * 0.6
264
+ + this._goalsCompletedRecently * 0.4
265
+ ),
266
+
267
+ excitement = Math.min( 1,
268
+ discoveryReward * 0.4
269
+ + goalReward * 0.3
270
+ + rewardLevel * 0.3
271
+ )
272
+
273
+ const commands: StateCommands = { metrics: [] }
274
+
275
+ commands.metrics!.push(
276
+ [ 'emotion.joy', joy ],
277
+ [ 'emotion.satisfaction', satisfaction ],
278
+ [ 'emotion.excitement', excitement ],
279
+ [ 'reward.level', rewardLevel ],
280
+ [ 'reward.goal', goalReward ],
281
+ [ 'reward.social', socialReward ],
282
+ [ 'reward.resource', resourceReward ],
283
+ [ 'reward.discovery', discoveryReward ],
284
+ )
285
+
286
+ const _bus = this._bus
287
+ if( _bus ){
288
+ // Option B: reward-prediction-error salience — a CHANGE in reward grabs the
289
+ // workspace, a fully-expected reward goes quiet (dopamine-RPE analogue),
290
+ // precision-modulated (set on 'reward.value'). observe() runs every tick so
291
+ // the baseline tracks; the one value is reused across the reward events.
292
+ const rewardSalience = this._model.observe( 'reward.value', rewardLevel ).salience
293
+
294
+ if( joy > 0.5 )
295
+ _bus.publish({ type: 'emotion.joy.peak', version: 1, sourceEngine: this.name, salience: rewardSalience, payload: { joy } })
296
+ if( joy > 0.6 )
297
+ _bus.publish({ type: 'emotion.joy.significant', version: 1, sourceEngine: this.name, salience: rewardSalience, payload: { joy, satisfaction, excitement, rewardLevel } })
298
+ if( goalReward > 0.8 )
299
+ _bus.publish({ type: 'reward.goal_significant', version: 1, sourceEngine: this.name, salience: rewardSalience, payload: { goalReward, satisfaction } })
300
+ }
301
+
302
+ return commands
303
+ }
304
+ }