@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,437 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/moral.evaluator.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * MoralEvaluator — compares actions against internalized values and norms.
7
+ *
8
+ * Evaluates:
9
+ * - Own actions against personal standards
10
+ * - Others' actions against social norms
11
+ * - Value alignment and violation
12
+ * - Social comparison (am I better/worse than my standards?)
13
+ *
14
+ * Produces: guilt, shame, pride, indignation, disgust
15
+ *
16
+ * Guilt = "I did something bad" (action-focused, private)
17
+ * Shame = "I am bad" (self-focused, social exposure)
18
+ * Pride = "I did something good" (achievement against standards)
19
+ * Indignation = "They did something wrong" (moral anger at others)
20
+ * Disgust = "That violates a sacred value" (visceral moral rejection)
21
+ *
22
+ * Requires a value system — initialized with basic moral foundations
23
+ * and extensible through learning.
24
+ *
25
+ * Part of Shard 1 (Affective Layer) — runs every tick, synchronous.
26
+ */
27
+
28
+ import type {
29
+ Duration,
30
+ Tick,
31
+ SimulationContext,
32
+ ReadonlySimulationState,
33
+ StateCommands,
34
+ SimulationEvent,
35
+ } from '#core/types'
36
+ import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
37
+ import type { CognitiveEventSchema } from '#cognition/schema.registry'
38
+ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
39
+ import { GenerativeModel } from '#cognition/generative.model'
40
+ import { readEffectiveParams } from '#cognition/persona.prior'
41
+
42
+ export interface MoralEvaluatorConfig {
43
+ /** Moral foundations and their weights */
44
+ foundations?: Record<string, number>
45
+ /** Threshold for moral emotions to trigger events */
46
+ eventThreshold?: number
47
+ /** Rate at which moral emotions decay without re-trigger */
48
+ decayRate?: number
49
+ bus?: CognitiveBus
50
+ }
51
+
52
+ /**
53
+ * Moral Foundations Theory — six core dimensions.
54
+ * Each foundation has a care/harm and fairness/cheating polarity.
55
+ */
56
+ interface MoralFoundation {
57
+ name: string
58
+ weight: number // How much this foundation matters to the agent
59
+ threshold: number // Sensitivity — how easily triggered
60
+ }
61
+
62
+ interface ValueViolation {
63
+ foundation: string
64
+ severity: number // 0-1: how severe the violation
65
+ agent: 'self' | 'other'
66
+ keid: string
67
+ }
68
+
69
+ export class MoralEvaluator implements SimulationEngine, CognitiveEngine {
70
+ readonly name = 'moral-evaluator'
71
+
72
+ private _foundations: MoralFoundation[]
73
+ private _eventThreshold: number
74
+ private _decayRate: number
75
+
76
+ // Track own action history for guilt/pride evaluation
77
+ private _recentOwnActions: Array<{
78
+ action: string
79
+ outcome: string
80
+ tick: number
81
+ valueAlignment: Record<string, number>
82
+ }> = []
83
+
84
+ private _bus: CognitiveBus | null = null
85
+
86
+ private readonly _model = new GenerativeModel()
87
+
88
+
89
+ constructor( config: MoralEvaluatorConfig = {} ){
90
+ this._bus = config.bus ?? null
91
+ const foundationWeights = config.foundations ?? {
92
+ care: 0.25, // Harm/care — protection of vulnerable
93
+ fairness: 0.25, // Cheating/fairness — proportional outcomes
94
+ loyalty: 0.15, // Betrayal/loyalty — group commitment
95
+ authority: 0.10, // Subversion/authority — respect for tradition/order
96
+ sanctity: 0.10, // Degradation/sanctity — purity/disgust
97
+ liberty: 0.15, // Oppression/liberty — freedom from domination
98
+ }
99
+
100
+ this._foundations = Object.entries( foundationWeights ).map( ([ name, weight ]) => ({
101
+ name,
102
+ weight: weight / Object.values( foundationWeights ).reduce( ( s, w ) => s + w, 0 ),
103
+ threshold: 0.3,
104
+ }))
105
+
106
+ this._eventThreshold = config.eventThreshold ?? 0.3
107
+ this._decayRate = config.decayRate ?? 0.02
108
+ }
109
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
110
+
111
+ // ── Engine interface ─────────────────────────────────────
112
+
113
+ private _readConfigFromState( state: ReadonlySimulationState ): void {
114
+ // Effective config = base engine-config-moral ⊕ persona-prior. Channel A: a
115
+ // conscientious Will develops a lower eventThreshold and so registers moral
116
+ // self-evaluations (guilt/shame/pride) more readily — the dutifulness facet.
117
+ const p = readEffectiveParams( state, 'engine-config-moral' )
118
+
119
+ if( p.eventThreshold != null ) this._eventThreshold = p.eventThreshold
120
+ if( p.decayRate != null ) this._decayRate = p.decayRate
121
+
122
+ // Update moral foundations from flat params. Keys map 1:1 to the six MFT
123
+ // foundations the engine tracks: the foundation is `sanctity` (carried by
124
+ // `foundationSanctity`, NOT `foundationPurity`), and `liberty` must be
125
+ // carried too — otherwise a state override silently drops both.
126
+ const foundationUpdates: Record<string, number | undefined> = {
127
+ care: p.foundationCare,
128
+ fairness: p.foundationFairness,
129
+ loyalty: p.foundationLoyalty,
130
+ authority: p.foundationAuthority,
131
+ sanctity: p.foundationSanctity,
132
+ liberty: p.foundationLiberty,
133
+ }
134
+ let updated = false
135
+ for( const f of this._foundations )
136
+ if( foundationUpdates[ f.name ] != null ){ f.weight = foundationUpdates[ f.name ]!; updated = true }
137
+
138
+ // Re-normalize to the constructor's invariant (weights sum to 1.0). The
139
+ // override path previously assigned raw weights directly, de-normalizing
140
+ // the set and skewing the relative weights guilt & indignation depend on.
141
+ if( updated ){
142
+ const total = this._foundations.reduce( ( s, f ) => s + f.weight, 0 )
143
+ if( total > 0 ) for( const f of this._foundations ) f.weight = f.weight / total
144
+ }
145
+ }
146
+
147
+
148
+ subscribes(): string[] { return ['executive.prediction.formed'] }
149
+ publishes(): CognitiveEventSchema[] { return [] }
150
+
151
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
152
+ this._model.observe( e.type, e.salience )
153
+ if( e.type === 'executive.prediction.formed' ){
154
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
155
+ if( p.predictedDomains.includes('moral') )
156
+ this._model.setPrecision( 'emotion.guilt', 1.0 + p.confidence * 0.5 )
157
+ }
158
+ }
159
+
160
+ snapshot(): Record<string, unknown> { return {} }
161
+
162
+ async react(
163
+ delta: Duration,
164
+ _tick: Tick,
165
+ state: ReadonlySimulationState,
166
+ context: SimulationContext
167
+ ): Promise<EngineResult> {
168
+ this._readConfigFromState( state )
169
+
170
+ const
171
+ events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
172
+ commands: StateCommands = { metrics: [] }
173
+
174
+ // Detect value violations from percepts and own actions
175
+ const violations = this._detectViolations( state )
176
+
177
+ // Categorize violations by target (self vs. other)
178
+ const
179
+ selfViolations = violations.filter( v => v.agent === 'self'),
180
+ otherViolations = violations.filter( v => v.agent === 'other')
181
+
182
+ // Compute moral emotions
183
+ const previousGuilt = state.metrics.get('emotion.guilt') ?? 0
184
+ const previousShame = state.metrics.get('emotion.shame') ?? 0
185
+ const previousPride = state.metrics.get('emotion.pride') ?? 0
186
+
187
+ // Guilt: self-caused harm or fairness violation
188
+ const guiltRaw = this._computeGuilt( selfViolations )
189
+ const guilt = guiltRaw > 0.01
190
+ ? Math.min( 1, guiltRaw )
191
+ : Math.max( 0, previousGuilt - this._decayRate * ( delta / 1000 ) )
192
+
193
+ // Shame: self-violation with social exposure
194
+ const shameRaw = this._computeShame( selfViolations, state )
195
+ const shame = shameRaw > 0.01
196
+ ? Math.min( 1, shameRaw )
197
+ : Math.max( 0, previousShame - this._decayRate * ( delta / 1000 ) )
198
+
199
+ // Pride: self-exceeding moral standards
200
+ const prideRaw = this._computePride( state )
201
+ const pride = prideRaw > 0.01
202
+ ? Math.min( 1, prideRaw )
203
+ : Math.max( 0, previousPride - this._decayRate * ( delta / 1000 ) )
204
+
205
+ // Indignation: others' violations (moral anger)
206
+ const indignation = this._computeIndignation( otherViolations )
207
+
208
+ // Disgust: sanctity/degradation violations
209
+ const disgust = this._computeDisgust( violations )
210
+
211
+ commands.metrics!.push(
212
+ [ 'emotion.guilt', guilt ],
213
+ [ 'emotion.shame', shame ],
214
+ [ 'emotion.pride', pride ],
215
+ [ 'emotion.indignation', indignation ],
216
+ [ 'emotion.disgust', disgust ],
217
+ [ 'moral.self_violations', selfViolations.length ],
218
+ [ 'moral.other_violations', otherViolations.length ],
219
+ )
220
+
221
+ // Significant moral emotion events
222
+ if( guilt > this._eventThreshold )
223
+ events.push({
224
+ type: 'emotion.guilt.significant',
225
+ source: this.name,
226
+ payload: { guilt, selfViolations: selfViolations.map( v => v.foundation ) },
227
+ })
228
+
229
+ if( shame > this._eventThreshold )
230
+ events.push({
231
+ type: 'emotion.shame.significant',
232
+ source: this.name,
233
+ payload: { shame },
234
+ })
235
+
236
+ if( pride > this._eventThreshold )
237
+ events.push({
238
+ type: 'emotion.pride.significant',
239
+ source: this.name,
240
+ payload: { pride },
241
+ })
242
+
243
+ if( indignation > 0.6 )
244
+ events.push({
245
+ type: 'emotion.indignation.elevated',
246
+ source: this.name,
247
+ payload: { indignation },
248
+ })
249
+
250
+
251
+ // Phase C + F: publish cognitive event — gated by prediction error
252
+ const _bus = this._bus
253
+ if( _bus && ( guilt > 0.4 || shame > 0.4 ) ){
254
+ const predErr = this._model.observe( 'emotion.guilt', guilt )
255
+ if( !predErr.gated )
256
+ _bus.publish({ type: 'emotion.guilt.elevated', version: 1, sourceEngine: this.name, salience: Math.min(1, guilt * 1.5), payload: { guilt, shame, pride } })
257
+ }
258
+ return { events: events.length > 0 ? events : undefined, commands }
259
+ }
260
+
261
+ // ── Violation detection ──────────────────────────────────
262
+
263
+ private _detectViolations( state: ReadonlySimulationState ): ValueViolation[] {
264
+ const violations: ValueViolation[] = []
265
+
266
+ for( const entity of state.entities.values() ){
267
+ // Social percepts can encode moral violations
268
+ if( entity.type === 'percept.social'){
269
+ const
270
+ action = ( entity.metadata?.action as string ) ?? '',
271
+ keid = ( entity.metadata?.keid as string ) ?? '',
272
+ directedAtSelf = entity.metadata?.directedAtSelf === true,
273
+ harmful = entity.metadata?.harmful === true,
274
+ unfair = entity.metadata?.unfair === true,
275
+ deceptive = entity.metadata?.deceptive === true,
276
+ disrespectful = entity.metadata?.disrespectful === true
277
+
278
+ if( harmful )
279
+ violations.push({
280
+ foundation: 'care',
281
+ severity: 0.7,
282
+ agent: directedAtSelf ? 'other' : 'other',
283
+ keid,
284
+ })
285
+
286
+ if( unfair )
287
+ violations.push({
288
+ foundation: 'fairness',
289
+ severity: 0.6,
290
+ agent: 'other',
291
+ keid,
292
+ })
293
+
294
+ if( deceptive )
295
+ violations.push({
296
+ foundation: 'fairness',
297
+ severity: 0.5,
298
+ agent: 'other',
299
+ keid,
300
+ })
301
+
302
+ if( disrespectful )
303
+ violations.push({
304
+ foundation: 'authority',
305
+ severity: 0.4,
306
+ agent: 'other',
307
+ keid,
308
+ })
309
+ }
310
+
311
+ // Own actions that could be violations
312
+ if( entity.type === 'action.own' || entity.type === 'decision.record'){
313
+ const
314
+ outcome = entity.metadata?.outcome as string ?? '',
315
+ harmful = entity.metadata?.harmful === true,
316
+ unfair = entity.metadata?.unfair === true,
317
+ dishonest = entity.metadata?.dishonest === true
318
+
319
+ if( harmful )
320
+ violations.push({
321
+ foundation: 'care',
322
+ severity: 0.65,
323
+ agent: 'self',
324
+ keid: 'agent-self',
325
+ })
326
+
327
+ if( unfair )
328
+ violations.push({
329
+ foundation: 'fairness',
330
+ severity: 0.55,
331
+ agent: 'self',
332
+ keid: 'agent-self',
333
+ })
334
+
335
+ if( dishonest )
336
+ violations.push({
337
+ foundation: 'fairness',
338
+ severity: 0.5,
339
+ agent: 'self',
340
+ keid: 'agent-self',
341
+ })
342
+ }
343
+ }
344
+
345
+ return violations
346
+ }
347
+
348
+ // ── Emotion computation ──────────────────────────────────
349
+
350
+ private _computeGuilt( selfViolations: ValueViolation[] ): number {
351
+ if( selfViolations.length === 0 ) return 0
352
+
353
+ // Guilt is weighted by foundation importance
354
+ let weightedSeverity = 0
355
+ let totalWeight = 0
356
+
357
+ for( const violation of selfViolations ){
358
+ const foundation = this._foundations.find( f => f.name === violation.foundation )
359
+ const weight = foundation?.weight ?? 0.1
360
+ weightedSeverity += violation.severity * weight
361
+ totalWeight += weight
362
+ }
363
+
364
+ return totalWeight > 0 ? weightedSeverity / totalWeight : 0
365
+ }
366
+
367
+ private _computeShame(
368
+ selfViolations: ValueViolation[],
369
+ state: ReadonlySimulationState
370
+ ): number {
371
+ if( selfViolations.length === 0 ) return 0
372
+
373
+ // Shame = guilt + social exposure
374
+ const guiltLevel = this._computeGuilt( selfViolations )
375
+ const
376
+ socialPresence = state.metrics.get('social.active_agents') ?? 0,
377
+ evaluationThreat = state.metrics.get('social.evaluation_threat') ?? 0,
378
+ exposureLevel = Math.min( 1, ( socialPresence / 5 ) * 0.5 + evaluationThreat * 0.5 )
379
+
380
+ return guiltLevel * ( 0.4 + exposureLevel * 0.6 )
381
+ }
382
+
383
+ private _computePride( state: ReadonlySimulationState ): number {
384
+ // Pride = own actions exceeded moral standards
385
+ let prideScore = 0
386
+ let actionCount = 0
387
+
388
+ for( const entity of state.entities.values() ){
389
+ if( entity.type !== 'action.own' && entity.type !== 'decision.record') continue
390
+
391
+ const
392
+ virtuous = entity.metadata?.virtuous === true,
393
+ helpful = entity.metadata?.helpful === true,
394
+ generous = entity.metadata?.generous === true,
395
+ honest = entity.metadata?.honest === true
396
+
397
+ if( virtuous ) prideScore += 0.4
398
+ if( helpful ) prideScore += 0.3
399
+ if( generous ) prideScore += 0.2
400
+ if( honest ) prideScore += 0.1
401
+
402
+ actionCount++
403
+ }
404
+
405
+ return actionCount > 0
406
+ ? Math.min( 1, prideScore / Math.max( actionCount, 1 ) )
407
+ : 0
408
+ }
409
+
410
+ private _computeIndignation( otherViolations: ValueViolation[] ): number {
411
+ if( otherViolations.length === 0 ) return 0
412
+
413
+ // Indignation is moral anger at others' violations
414
+ let weightedSeverity = 0
415
+ let totalWeight = 0
416
+
417
+ for( const violation of otherViolations ){
418
+ const foundation = this._foundations.find( f => f.name === violation.foundation )
419
+ const weight = foundation?.weight ?? 0.1
420
+ weightedSeverity += violation.severity * weight
421
+ totalWeight += weight
422
+ }
423
+
424
+ return totalWeight > 0
425
+ ? Math.min( 1, ( weightedSeverity / totalWeight ) * 1.2 )
426
+ : 0
427
+ }
428
+
429
+ private _computeDisgust( violations: ValueViolation[] ): number {
430
+ // Disgust is specifically for sanctity/degradation violations
431
+ const sanctityViolations = violations.filter( v => v.foundation === 'sanctity')
432
+ if( sanctityViolations.length === 0 ) return 0
433
+
434
+ const maxSeverity = Math.max( ...sanctityViolations.map( v => v.severity ) )
435
+ return maxSeverity
436
+ }
437
+ }
@@ -0,0 +1,254 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/novelty.detector.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * NoveltyDetector — compares current percepts against expectations.
7
+ *
8
+ * Computes prediction error by comparing incoming percept patterns
9
+ * against a running model of expected patterns. High novelty signals:
10
+ * - Something unexpected happened
11
+ * - The world has changed in a meaningful way
12
+ * - The current mental model needs updating
13
+ *
14
+ * Novelty drives:
15
+ * - Curiosity (positive valence + novelty → exploration)
16
+ * - Anxiety (negative valence + novelty → caution)
17
+ * - Learning (high novelty events are consolidated more strongly)
18
+ *
19
+ * Uses a simple exponential moving average of recent percept counts
20
+ * per category as the expectation baseline.
21
+ *
22
+ * Part of Shard 0 (Perceptual Layer) — runs every tick, synchronous.
23
+ */
24
+
25
+ import type {
26
+ Duration,
27
+ Tick,
28
+ SimulationContext,
29
+ ReadonlySimulationState,
30
+ StateCommands,
31
+ SimulationEvent,
32
+ } from '#core/types'
33
+ import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
34
+ import type { CognitiveEventSchema } from '#cognition/schema.registry'
35
+ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
36
+ import { GenerativeModel } from '#cognition/generative.model'
37
+ import { readEffectiveParams } from '#cognition/persona.prior'
38
+
39
+ export interface NoveltyDetectorConfig {
40
+ /** Learning rate for expectation updates (0-1) */
41
+ learningRate?: number
42
+ /** How many ticks to look back for pattern comparison */
43
+ windowSize?: number
44
+ /** Threshold above which novelty is considered significant */
45
+ significanceThreshold?: number
46
+ bus?: CognitiveBus
47
+ }
48
+
49
+ interface CategoryStats {
50
+ count: number
51
+ averageSalience: number
52
+ }
53
+
54
+ export class NoveltyDetector implements SimulationEngine, CognitiveEngine {
55
+ readonly name = 'novelty-detector'
56
+
57
+ private _learningRate: number
58
+ private _windowSize: number
59
+ private _significanceThreshold: number
60
+
61
+ // Expected patterns — EMA of percept counts per category
62
+ private _expectedCounts = new Map<string, number>()
63
+ private _expectedSalience = new Map<string, number>()
64
+ private _recentHistory: Array<Map<string, CategoryStats>> = []
65
+
66
+ private _bus: CognitiveBus | null = null
67
+
68
+ private readonly _model = new GenerativeModel()
69
+
70
+
71
+ constructor( config: NoveltyDetectorConfig = {} ){
72
+ this._bus = config.bus ?? null
73
+ this._learningRate = config.learningRate ?? 0.1
74
+ this._windowSize = config.windowSize ?? 10
75
+ this._significanceThreshold = config.significanceThreshold ?? 0.4
76
+ }
77
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
78
+
79
+ // ── Engine interface ─────────────────────────────────────
80
+
81
+ private _readConfigFromState( state: ReadonlySimulationState ): void {
82
+ // Effective config = base engine-config-novelty ⊕ persona-prior. Channel A: an open
83
+ // Will develops a lower significanceThreshold and so registers novelty more readily.
84
+ const p = readEffectiveParams( state, 'engine-config-novelty' )
85
+ if( p.learningRate != null ) this._learningRate = p.learningRate
86
+ if( p.windowSize != null ) this._windowSize = p.windowSize
87
+ if( p.significanceThreshold != null ) this._significanceThreshold = p.significanceThreshold
88
+ }
89
+
90
+
91
+ subscribes(): string[] { return ['executive.prediction.formed'] }
92
+ publishes(): CognitiveEventSchema[] { return [] }
93
+
94
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
95
+ this._model.observe( e.type, e.salience )
96
+ if( e.type === 'executive.prediction.formed' ){
97
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
98
+ if( p.predictedDomains.includes('perception') )
99
+ this._model.setPrecision( 'novelty.score', 1.0 + p.confidence * 0.5 )
100
+ }
101
+ }
102
+
103
+ snapshot(): Record<string, unknown> { return {} }
104
+
105
+ async react(
106
+ _delta: Duration,
107
+ tick: Tick,
108
+ state: ReadonlySimulationState,
109
+ context: SimulationContext
110
+ ): Promise<EngineResult> {
111
+ this._readConfigFromState( state )
112
+
113
+ const
114
+ events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
115
+ commands: StateCommands = { metrics: [] }
116
+
117
+ // Aggregate current percepts by category
118
+ const currentStats = this._aggregatePercepts( state )
119
+
120
+ // Compute novelty per category
121
+ let totalNovelty = 0
122
+ let categoriesWithNovelty = 0
123
+
124
+ for( const [ category, stats ] of currentStats ){
125
+ const
126
+ expectedCount = this._expectedCounts.get( category ) ?? 0,
127
+ expectedSalience = this._expectedSalience.get( category ) ?? 0.3,
128
+ countNovelty = this._computeNovelty( stats.count, expectedCount ),
129
+ salienceNovelty = this._computeNovelty( stats.averageSalience, expectedSalience ),
130
+ categoryNovelty = ( countNovelty + salienceNovelty ) / 2
131
+
132
+ // Update running metric per category
133
+ commands.metrics!.push([ `novelty.${category}`, categoryNovelty ])
134
+
135
+ if( categoryNovelty > this._significanceThreshold )
136
+ categoriesWithNovelty++
137
+
138
+ totalNovelty += categoryNovelty
139
+ }
140
+
141
+ // Overall novelty
142
+ const overallNovelty = currentStats.size > 0
143
+ ? totalNovelty / currentStats.size
144
+ : 0
145
+
146
+ commands.metrics!.push(
147
+ [ 'perception.novelty', overallNovelty ],
148
+ [ 'novelty.significant_categories', categoriesWithNovelty ],
149
+ )
150
+
151
+ // Significant novelty event
152
+ if( overallNovelty > this._significanceThreshold )
153
+ events.push({
154
+ type: 'novelty.significant',
155
+ source: this.name,
156
+ payload: {
157
+ overallNovelty,
158
+ categoriesWithNovelty,
159
+ tick,
160
+ },
161
+ })
162
+
163
+ // Update expectations for next tick
164
+ this._updateExpectations( currentStats )
165
+ this._updateHistory( currentStats )
166
+
167
+
168
+ // Phase C: publish cognitive event
169
+ const _bus = this._bus
170
+ if( _bus && overallNovelty > 0.3 )
171
+ _bus.publish({ type: 'perception.novelty.spike', version: 1, sourceEngine: this.name, salience: Math.min(1, overallNovelty * 2), payload: { novelty: overallNovelty } })
172
+ // Phase D + F: rich state-change event — gated by prediction error
173
+ if( _bus ){
174
+ const predErr = this._model.observe( 'novelty.score', overallNovelty )
175
+ if( !predErr.gated )
176
+ _bus.publish({ type: 'novelty.state.changed', version: 1, sourceEngine: this.name, salience: predErr.salience, payload: { novelty: overallNovelty, socialEvaluationThreat: state.metrics.get('social.evaluation_threat') ?? 0, activeAgents: state.metrics.get('social.active_agents') ?? 0, fearLevel: state.metrics.get('emotion.fear') ?? 0 } })
177
+ }
178
+ return { events: events.length > 0 ? events : undefined, commands }
179
+ }
180
+
181
+ // ── Internal ─────────────────────────────────────────────
182
+
183
+ /**
184
+ * Aggregate current percept entities by category.
185
+ */
186
+ private _aggregatePercepts( state: ReadonlySimulationState ): Map<string, CategoryStats> {
187
+ const stats = new Map<string, CategoryStats>()
188
+
189
+ for( const entity of state.entities.values() ){
190
+ if( entity.type !== 'percept' && entity.type !== 'percept.social')
191
+ continue
192
+
193
+ const category = ( entity.metadata?.category as string ) ?? 'unknown'
194
+ const salience = ( entity.metadata?.salience as number ) ?? 0.3
195
+
196
+ const existing = stats.get( category )
197
+ if( existing ){
198
+ existing.count++
199
+ existing.averageSalience = ( existing.averageSalience * ( existing.count - 1 ) + salience ) / existing.count
200
+ }
201
+ else {
202
+ stats.set( category, { count: 1, averageSalience: salience } )
203
+ }
204
+ }
205
+
206
+ return stats
207
+ }
208
+
209
+ /**
210
+ * Compute novelty as normalized deviation from expected value.
211
+ */
212
+ private _computeNovelty( current: number, expected: number ): number {
213
+ if( expected === 0 )
214
+ return current > 0 ? 1.0 : 0.0
215
+
216
+ const deviation = Math.abs( current - expected )
217
+ const normalized = deviation / Math.max( expected, 1 )
218
+
219
+ return Math.min( 1, normalized )
220
+ }
221
+
222
+ /**
223
+ * Update exponential moving averages for each category.
224
+ */
225
+ private _updateExpectations( stats: Map<string, CategoryStats> ): void {
226
+ // Update for observed categories
227
+ for( const [ category, s ] of stats ){
228
+ const prevCount = this._expectedCounts.get( category ) ?? 0,
229
+ prevSalience = this._expectedSalience.get( category ) ?? 0.3
230
+
231
+ this._expectedCounts.set(
232
+ category,
233
+ prevCount + this._learningRate * ( s.count - prevCount )
234
+ )
235
+
236
+ this._expectedSalience.set(
237
+ category,
238
+ prevSalience + this._learningRate * ( s.averageSalience - prevSalience )
239
+ )
240
+ }
241
+
242
+ // Decay expectations for unobserved categories
243
+ for( const [ category, count ] of this._expectedCounts ){
244
+ if( !stats.has( category ) )
245
+ this._expectedCounts.set( category, count * ( 1 - this._learningRate * 0.5 ) )
246
+ }
247
+ }
248
+
249
+ private _updateHistory( stats: Map<string, CategoryStats> ): void {
250
+ this._recentHistory.push( new Map( stats ) )
251
+ if( this._recentHistory.length > this._windowSize )
252
+ this._recentHistory.shift()
253
+ }
254
+ }