@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,677 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/faculties/self.model.updater.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SelfModelUpdater — maintains beliefs about own capabilities, traits,
|
|
7
|
+
* and patterns.
|
|
8
|
+
*
|
|
9
|
+
* Periodically evaluates recent performance across domains and updates
|
|
10
|
+
* the will.identity entity with refined self-knowledge. This is how
|
|
11
|
+
* the mind learns "I am good at X, bad at Y" through experience.
|
|
12
|
+
*
|
|
13
|
+
* Uses AsyncEngine — deep self-evaluation may involve LLM introspection.
|
|
14
|
+
*
|
|
15
|
+
* Part of Shard 4 (Meta-Cognitive Layer).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
Tick,
|
|
20
|
+
SimulationContext,
|
|
21
|
+
ReadonlySimulationState,
|
|
22
|
+
ReasoningFootprint,
|
|
23
|
+
StateCommands,
|
|
24
|
+
} from '#core/types'
|
|
25
|
+
import { AsyncEngine } from '#core/async.engine'
|
|
26
|
+
import type { IntermediateStream } from '#core/async.engine'
|
|
27
|
+
import type { SemanticIntegrator, Belief } from '#faculties/semantic.engine/integrator'
|
|
28
|
+
import type { CognitiveEngine } from '#cognition/types'
|
|
29
|
+
import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
|
|
30
|
+
import type { CognitiveEventSchema } from '#cognition/schema.registry'
|
|
31
|
+
import { GenerativeModel, type GenerativeModelSnapshot } from '#cognition/generative.model'
|
|
32
|
+
import { readEffectiveParams } from '#cognition/persona.prior'
|
|
33
|
+
|
|
34
|
+
/** Shape of the will.identity entity — defined locally to avoid external dependencies. */
|
|
35
|
+
interface Keidentity {
|
|
36
|
+
prompt: string
|
|
37
|
+
values: string[]
|
|
38
|
+
traits: Record<string, number>
|
|
39
|
+
style: string
|
|
40
|
+
version: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Per-trait self-knowledge accumulated over the Will's life — the substrate for graded
|
|
45
|
+
* salience options B (baseline-relative) and C (recency). Stored on identity-self
|
|
46
|
+
* beside `traits` and recomputed only at each (infrequent) self-model evaluation, so it
|
|
47
|
+
* changes exactly when identity-self changes — the same cache breakpoint the trait
|
|
48
|
+
* values already ride, adding no extra prompt churn between evaluations. R2-safe:
|
|
49
|
+
* derived purely from state + sim-tick.
|
|
50
|
+
*/
|
|
51
|
+
export interface TraitStat {
|
|
52
|
+
/** Slow EMA of the trait value — the Will's personal baseline ("my own norm"). */
|
|
53
|
+
mean: number
|
|
54
|
+
/** Direction of the most recent SIGNIFICANT shift: +1 rising / −1 easing / 0 none. */
|
|
55
|
+
shiftDir: number
|
|
56
|
+
/** Sim-tick of that shift — drives the eval-aligned recency decay. */
|
|
57
|
+
shiftTick: number
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Population baselines seed a fresh Will's personal mean before it has a history of its
|
|
61
|
+
// own — this is how Option B's lighter "population" sub-option folds into the personal
|
|
62
|
+
// one. 0.5 for all by default; this map is the extension point for a trait with a known
|
|
63
|
+
// population prior.
|
|
64
|
+
const TRAIT_POPULATION_BASELINE: Record<string, number> = {}
|
|
65
|
+
const DEFAULT_TRAIT_BASELINE = 0.5
|
|
66
|
+
const TRAIT_MEAN_EMA_ALPHA = 0.2 // baseline tracks slowly → "my norm" reflects the sustained level, not a blip
|
|
67
|
+
const TRAIT_SHIFT_SIGNIFICANCE = 0.05 // |Δ| at one evaluation to count as a real shift (matches _diffTraits)
|
|
68
|
+
const TRAIT_RECENCY_WINDOW = 600 // ticks a shift stays "lately" (~3 evals at the 200-tick cadence), decayed at an eval
|
|
69
|
+
|
|
70
|
+
export interface SelfModelUpdaterConfig {
|
|
71
|
+
/** Minimum ticks between self-model evaluations */
|
|
72
|
+
minIntervalTicks?: number
|
|
73
|
+
/** Minimum new experiences before re-evaluating */
|
|
74
|
+
minNewExperiences?: number
|
|
75
|
+
bus?: CognitiveBus
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Keyword cues mapping a self-belief statement to the trait(s) it is actually about, so a
|
|
79
|
+
// positive self-belief nudges only the relevant trait — NOT every trait. (The prior code
|
|
80
|
+
// nudged all traits up indiscriminately, inflating every Channel-A disposition the
|
|
81
|
+
// persona-prior reads, regardless of behaviour.) Trait keys match the self-model traits.
|
|
82
|
+
const SELF_BELIEF_TRAIT_KEYWORDS: Record<string, string[]> = {
|
|
83
|
+
openness: [ 'open', 'curious', 'curiosity', 'explore', 'novel' ],
|
|
84
|
+
conscientiousness: [ 'careful', 'diligent', 'organized', 'thorough', 'responsible', 'disciplined', 'reliable' ],
|
|
85
|
+
agreeableness: [ 'kind', 'warm', 'helpful', 'caring', 'compassion', 'cooperative', 'friendly', 'gentle' ],
|
|
86
|
+
analytical: [ 'analytical', 'logical', 'rational', 'reason', 'precise' ],
|
|
87
|
+
creativity: [ 'creative', 'inventive', 'original', 'imaginative' ],
|
|
88
|
+
persistence: [ 'persistent', 'determined', 'persevere', 'tenacious', 'driven' ],
|
|
89
|
+
resilience: [ 'resilient', 'tough', 'recover', 'cope', 'bounce back' ],
|
|
90
|
+
decisiveness: [ 'decisive', 'confident', 'assertive', 'bold' ],
|
|
91
|
+
'emotional-stability': [ 'calm', 'steady', 'stable', 'composed', 'even-keeled', 'unflappable' ],
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Traits a self-belief statement is plausibly *about*, by keyword cue. Pure. */
|
|
95
|
+
export function traitsCuedBySelfBelief( statement: string ): string[] {
|
|
96
|
+
const text = statement.toLowerCase()
|
|
97
|
+
return Object.entries( SELF_BELIEF_TRAIT_KEYWORDS )
|
|
98
|
+
.filter( ( [ , keywords ] ) => keywords.some( k => text.includes( k ) ) )
|
|
99
|
+
.map( ( [ trait ] ) => trait )
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class SelfModelUpdater extends AsyncEngine implements CognitiveEngine {
|
|
103
|
+
readonly name = 'self-model-updater'
|
|
104
|
+
|
|
105
|
+
private _executiveReflectionBiases: string[] = []
|
|
106
|
+
private _executiveReflectionTick: number = 0
|
|
107
|
+
private _minIntervalTicks: number
|
|
108
|
+
private _minNewExperiences: number
|
|
109
|
+
|
|
110
|
+
private _lastEvaluationTick: number = 0
|
|
111
|
+
private _experienceCountAtLastEval: number = 0
|
|
112
|
+
private _cachedEpisodicTotal: number = 0
|
|
113
|
+
|
|
114
|
+
// Track domain performance for self-assessment
|
|
115
|
+
private _domainPerformance = new Map<string, Array<{ success: boolean; tick: Tick }>>()
|
|
116
|
+
|
|
117
|
+
// Affect-stability self-observation → the evidence behind the 'emotional-stability'
|
|
118
|
+
// trait. A slow EMA of how steady the Will's negative affect has been, sampled every
|
|
119
|
+
// tick in shouldAct. The trait then forms from this the same way task traits form from
|
|
120
|
+
// domain success rates — only here the "behaviour" observed is the Will's own affect.
|
|
121
|
+
private _affectStabilityEma: number = 0.5
|
|
122
|
+
private _affectObservations: number = 0
|
|
123
|
+
|
|
124
|
+
private _bus: CognitiveBus | null = null
|
|
125
|
+
private _semanticIntegrator: SemanticIntegrator | null = null
|
|
126
|
+
|
|
127
|
+
private readonly _model = new GenerativeModel()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
constructor( config: SelfModelUpdaterConfig = {} ){
|
|
131
|
+
// _bus set after super()
|
|
132
|
+
super({
|
|
133
|
+
defaultStrategy: 'FORCE',
|
|
134
|
+
maxPendingTicks: 300,
|
|
135
|
+
logConflicts: false,
|
|
136
|
+
rerunOnRejection: false,
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
this._minIntervalTicks = config.minIntervalTicks ?? 200
|
|
140
|
+
this._minNewExperiences = config.minNewExperiences ?? 20
|
|
141
|
+
this._bus = config.bus ?? null
|
|
142
|
+
}
|
|
143
|
+
attachBus( bus: CognitiveBus ): void { this._bus = bus }
|
|
144
|
+
|
|
145
|
+
attachSemanticIntegrator( si: SemanticIntegrator ): void { this._semanticIntegrator = si }
|
|
146
|
+
|
|
147
|
+
subscribes(): string[] {
|
|
148
|
+
return [
|
|
149
|
+
'executive.self.reflection',
|
|
150
|
+
'executive.prediction.formed',
|
|
151
|
+
'action.outcome'
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
publishes(): CognitiveEventSchema[] { return [] }
|
|
155
|
+
|
|
156
|
+
onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
|
|
157
|
+
this._model.observe( e.type, e.salience )
|
|
158
|
+
switch( e.type ){
|
|
159
|
+
case 'executive.self.reflection': {
|
|
160
|
+
const p = e.payload as Record<string, unknown>
|
|
161
|
+
|
|
162
|
+
this._executiveReflectionBiases = ( p['identifiedBiases'] as string[] ) ?? []
|
|
163
|
+
this._executiveReflectionTick = ( p['tick'] as number ) ?? 0
|
|
164
|
+
|
|
165
|
+
break
|
|
166
|
+
}
|
|
167
|
+
case 'executive.prediction.formed': {
|
|
168
|
+
const p = e.payload as { predictedDomains: string[]; confidence: number }
|
|
169
|
+
if( p.predictedDomains.includes('metacognition') )
|
|
170
|
+
this._model.setPrecision( 'self_model.change', 1.0 + p.confidence * 0.5 )
|
|
171
|
+
|
|
172
|
+
break
|
|
173
|
+
}
|
|
174
|
+
case 'action.outcome': {
|
|
175
|
+
const p = e.payload as { actionType: string; domain: string; success: boolean; outcomeQuality: number; tick: Tick }
|
|
176
|
+
this.recordOutcome( p.domain, p.success, p.tick )
|
|
177
|
+
break
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
snapshot(): Record<string, unknown> {
|
|
183
|
+
// FN9: capture the per-domain performance history (the basis for "I am good
|
|
184
|
+
// at X / bad at Y"), the evaluation-gating ticks (so re-evaluation timing
|
|
185
|
+
// replays deterministically — R2), and the salience/generative sub-states.
|
|
186
|
+
return {
|
|
187
|
+
executiveReflectionTick: this._executiveReflectionTick,
|
|
188
|
+
biasCount: this._executiveReflectionBiases.length,
|
|
189
|
+
lastEvaluationTick: this._lastEvaluationTick,
|
|
190
|
+
experienceCountAtLastEval: this._experienceCountAtLastEval,
|
|
191
|
+
domainPerformance: [ ...this._domainPerformance.entries() ].map( ([ k, v ]) => [ k, v.map( r => ({ ...r }) ) ] ),
|
|
192
|
+
model: this._model.snapshot()
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
restore( snap: Record<string, unknown> ): void {
|
|
197
|
+
if( !snap ) return
|
|
198
|
+
|
|
199
|
+
if( typeof snap.executiveReflectionTick === 'number' )
|
|
200
|
+
this._executiveReflectionTick = snap.executiveReflectionTick
|
|
201
|
+
|
|
202
|
+
if( typeof snap.lastEvaluationTick === 'number' )
|
|
203
|
+
this._lastEvaluationTick = snap.lastEvaluationTick
|
|
204
|
+
|
|
205
|
+
if( typeof snap.experienceCountAtLastEval === 'number' )
|
|
206
|
+
this._experienceCountAtLastEval = snap.experienceCountAtLastEval
|
|
207
|
+
|
|
208
|
+
if( Array.isArray( snap.domainPerformance ) )
|
|
209
|
+
this._domainPerformance = new Map( snap.domainPerformance as Array<[ string, Array<{ success: boolean; tick: Tick }> ]> )
|
|
210
|
+
|
|
211
|
+
if( snap.model )
|
|
212
|
+
this._model.restore( snap.model as GenerativeModelSnapshot )
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Record an action outcome for domain-specific self-assessment.
|
|
217
|
+
*/
|
|
218
|
+
recordOutcome( domain: string, success: boolean, tick: Tick ): void {
|
|
219
|
+
const records = this._domainPerformance.get( domain ) ?? []
|
|
220
|
+
records.push({ success, tick })
|
|
221
|
+
|
|
222
|
+
// Keep last 100 records per domain
|
|
223
|
+
if( records.length > 100 ) records.shift()
|
|
224
|
+
this._domainPerformance.set( domain, records )
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ── AsyncEngine contract ─────────────────────────────────
|
|
228
|
+
|
|
229
|
+
private _readConfigFromState( state: ReadonlySimulationState ): void {
|
|
230
|
+
// Effective config = base engine-config ⊕ persona-prior. This is the first
|
|
231
|
+
// closed edge of the metacognition cycle: a learned prior (written back by
|
|
232
|
+
// the consolidator from the Will's own introspection) modulates how often
|
|
233
|
+
// the self-model re-evaluates, without ever mutating the seeded base.
|
|
234
|
+
const p = readEffectiveParams( state, 'engine-config-self-model' )
|
|
235
|
+
|
|
236
|
+
if( p.minIntervalTicks != null ) this._minIntervalTicks = p.minIntervalTicks
|
|
237
|
+
if( p.minNewExperiences != null ) this._minNewExperiences = p.minNewExperiences
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Sample current negative affect into a slow EMA — the running self-observation the
|
|
242
|
+
* self-model turns into an 'emotional-stability' trait. Called every tick (in
|
|
243
|
+
* shouldAct), so it tracks affect continuously even between the gated evaluations.
|
|
244
|
+
* Pure/deterministic: the same emotion-metric stream reproduces the same EMA on
|
|
245
|
+
* replay (no wall-clock, no RNG). Stability is read as low sustained negative affect;
|
|
246
|
+
* volatility (variance) would be a finer refinement but a level EMA is enough to drive
|
|
247
|
+
* the self-regulation loop (steadier ⇒ slower frustration build ⇒ steadier).
|
|
248
|
+
*/
|
|
249
|
+
private _sampleAffectStability( state: ReadonlySimulationState ): void {
|
|
250
|
+
const negativeAffect = Math.max(
|
|
251
|
+
state.metrics.get('emotion.frustration') ?? 0,
|
|
252
|
+
state.metrics.get('emotion.anger') ?? 0,
|
|
253
|
+
state.metrics.get('emotion.irritability') ?? 0,
|
|
254
|
+
)
|
|
255
|
+
const stabilityInstant = Math.max( 0, Math.min( 1, 1 - negativeAffect ) )
|
|
256
|
+
const alpha = 0.05
|
|
257
|
+
this._affectStabilityEma = this._affectObservations === 0
|
|
258
|
+
? stabilityInstant
|
|
259
|
+
: this._affectStabilityEma * ( 1 - alpha ) + stabilityInstant * alpha
|
|
260
|
+
this._affectObservations++
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
protected override shouldAct(
|
|
264
|
+
state: ReadonlySimulationState,
|
|
265
|
+
tick: Tick,
|
|
266
|
+
_context: SimulationContext
|
|
267
|
+
): boolean {
|
|
268
|
+
this._readConfigFromState( state )
|
|
269
|
+
this._sampleAffectStability( state )
|
|
270
|
+
|
|
271
|
+
const
|
|
272
|
+
ticksSinceLast = tick - this._lastEvaluationTick,
|
|
273
|
+
totalExperiences = state.metrics.get('memory.episodic_total') ?? 0,
|
|
274
|
+
newExperiences = totalExperiences - this._experienceCountAtLastEval
|
|
275
|
+
|
|
276
|
+
this._cachedEpisodicTotal = totalExperiences
|
|
277
|
+
|
|
278
|
+
return ticksSinceLast >= this._minIntervalTicks
|
|
279
|
+
&& newExperiences >= this._minNewExperiences
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
protected override readState(
|
|
283
|
+
state: ReadonlySimulationState,
|
|
284
|
+
tick: Tick
|
|
285
|
+
): ReasoningFootprint {
|
|
286
|
+
return {
|
|
287
|
+
tickObserved: tick,
|
|
288
|
+
entitiesRead: new Set([ 'identity-self' ]),
|
|
289
|
+
metricsRead: new Set([
|
|
290
|
+
'affect.valence', 'affect.dominance',
|
|
291
|
+
'metacognition.confidence',
|
|
292
|
+
]),
|
|
293
|
+
entitiesModified: new Set([ 'identity-self' ]),
|
|
294
|
+
intendedCommands: {},
|
|
295
|
+
source: this.name,
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Recompute per-trait self-knowledge at an evaluation (Options B/C substrate). Pure +
|
|
301
|
+
* deterministic — sim-tick only. The EMA tracks the personal baseline; a significant Δ
|
|
302
|
+
* this evaluation stamps a recency direction that ages out after TRAIT_RECENCY_WINDOW.
|
|
303
|
+
* The window is checked ONLY here (at evals), so the recency stamp never churns the
|
|
304
|
+
* prompt between evaluations.
|
|
305
|
+
*/
|
|
306
|
+
private _computeTraitStats(
|
|
307
|
+
oldTraits: Record<string, number>,
|
|
308
|
+
newTraits: Record<string, number>,
|
|
309
|
+
prevStats: Record<string, TraitStat>,
|
|
310
|
+
evalTick: number,
|
|
311
|
+
): Record<string, TraitStat> {
|
|
312
|
+
const stats: Record<string, TraitStat> = {}
|
|
313
|
+
|
|
314
|
+
for( const key of Object.keys( newTraits ) ){
|
|
315
|
+
const newVal = newTraits[ key ] ?? DEFAULT_TRAIT_BASELINE
|
|
316
|
+
const oldVal = oldTraits[ key ] ?? newVal
|
|
317
|
+
const prev = prevStats[ key ]
|
|
318
|
+
|
|
319
|
+
// Personal baseline: EMA seeded from the population prior on first sight.
|
|
320
|
+
const seed = prev?.mean ?? ( TRAIT_POPULATION_BASELINE[ key ] ?? DEFAULT_TRAIT_BASELINE )
|
|
321
|
+
const mean = seed + TRAIT_MEAN_EMA_ALPHA * ( newVal - seed )
|
|
322
|
+
|
|
323
|
+
// Recency: a significant move this eval (re)stamps the direction; otherwise carry the
|
|
324
|
+
// prior stamp until it ages past the window, then clear it.
|
|
325
|
+
const delta = newVal - oldVal
|
|
326
|
+
let shiftDir = prev?.shiftDir ?? 0
|
|
327
|
+
let shiftTick = prev?.shiftTick ?? evalTick
|
|
328
|
+
if( Math.abs( delta ) >= TRAIT_SHIFT_SIGNIFICANCE ){
|
|
329
|
+
shiftDir = Math.sign( delta )
|
|
330
|
+
shiftTick = evalTick
|
|
331
|
+
}
|
|
332
|
+
else if( shiftDir !== 0 && evalTick - shiftTick > TRAIT_RECENCY_WINDOW ){
|
|
333
|
+
shiftDir = 0
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
stats[ key ] = { mean, shiftDir, shiftTick }
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return stats
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
protected async reasonAsync(
|
|
343
|
+
footprint: ReasoningFootprint,
|
|
344
|
+
state: ReadonlySimulationState,
|
|
345
|
+
context: SimulationContext,
|
|
346
|
+
stream: IntermediateStream
|
|
347
|
+
): Promise<unknown> {
|
|
348
|
+
// 1. Compute domain success rates
|
|
349
|
+
const domainAssessments = this._computeDomainAssessments()
|
|
350
|
+
|
|
351
|
+
stream.report('domains_evaluated', {
|
|
352
|
+
domainCount: domainAssessments.length,
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// 2. Gather self-beliefs from state entities
|
|
356
|
+
const selfBeliefs = [ ...state.entities.values() ]
|
|
357
|
+
.filter( e => e.type === 'belief'
|
|
358
|
+
&& (e.metadata?.['category'] as string | undefined) === 'self_belief'
|
|
359
|
+
&& ((e.metadata?.['confidence'] as number | undefined) ?? 0) >= 0.3
|
|
360
|
+
)
|
|
361
|
+
.map( e => ({
|
|
362
|
+
id: e.id,
|
|
363
|
+
statement: ( e.metadata?.['statement'] as string ) ?? '',
|
|
364
|
+
category: 'self_belief' as const,
|
|
365
|
+
confidence: ( e.metadata?.['confidence'] as number ) ?? 0.5,
|
|
366
|
+
supportingEpisodes: ( e.metadata?.['supportingEpisodes'] as number ) ?? 0,
|
|
367
|
+
lastUpdatedAt: 0,
|
|
368
|
+
tags: ( e.metadata?.['tags'] as string[] ) ?? [],
|
|
369
|
+
}))
|
|
370
|
+
|
|
371
|
+
// 3. Extract current identity from state
|
|
372
|
+
const currentIdentity = this._extractIdentity( state )
|
|
373
|
+
|
|
374
|
+
// 4. Generate updated identity
|
|
375
|
+
const updatedIdentity = this._evaluateSelf(
|
|
376
|
+
currentIdentity,
|
|
377
|
+
domainAssessments,
|
|
378
|
+
selfBeliefs
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
const changeMagnitude = this._identityChangeMagnitude( currentIdentity, updatedIdentity )
|
|
382
|
+
|
|
383
|
+
// Per-trait self-knowledge (baseline + recency) for graded salience B/C. Read the
|
|
384
|
+
// prior stats off identity-self and roll them forward at this evaluation's sim-tick.
|
|
385
|
+
const prevTraitStats = ( state.entities.get('identity-self')?.metadata?.traitStats ?? {} ) as Record<string, TraitStat>
|
|
386
|
+
const traitStats = this._computeTraitStats(
|
|
387
|
+
currentIdentity.traits, updatedIdentity.traits, prevTraitStats, footprint.tickObserved as unknown as number
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
stream.report('identity_updated', {
|
|
391
|
+
traitChanges: this._diffTraits( currentIdentity.traits, updatedIdentity.traits ),
|
|
392
|
+
newValues: updatedIdentity.values.filter( v => !currentIdentity.values.includes( v ) )
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
return { updatedIdentity, domainAssessments, changeMagnitude, traitStats }
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
protected override onIntermediateResult(
|
|
399
|
+
step: string,
|
|
400
|
+
result: unknown,
|
|
401
|
+
_footprint: ReasoningFootprint,
|
|
402
|
+
_context: SimulationContext
|
|
403
|
+
): StateCommands | null {
|
|
404
|
+
const data = result as Record<string, unknown>
|
|
405
|
+
|
|
406
|
+
if( step === 'domains_evaluated')
|
|
407
|
+
return {
|
|
408
|
+
metrics: [
|
|
409
|
+
[ 'self_model.phase', 0 ],
|
|
410
|
+
[ 'self_model.domains', ( data.domainCount as number ) ?? 0 ]
|
|
411
|
+
]
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if( step === 'identity_updated')
|
|
415
|
+
return {
|
|
416
|
+
metrics: [
|
|
417
|
+
[ 'self_model.phase', 1 ]
|
|
418
|
+
]
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return null
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
protected onReasoningComplete(
|
|
425
|
+
output: unknown,
|
|
426
|
+
footprint: ReasoningFootprint,
|
|
427
|
+
context: SimulationContext
|
|
428
|
+
): StateCommands {
|
|
429
|
+
const { updatedIdentity, domainAssessments, changeMagnitude, traitStats } = output as {
|
|
430
|
+
updatedIdentity: Keidentity
|
|
431
|
+
domainAssessments: Array<{ domain: string; successRate: number; sampleSize: number }>
|
|
432
|
+
changeMagnitude: number
|
|
433
|
+
traitStats: Record<string, TraitStat>
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Sim tick of this evaluation (footprint.tickObserved), not wall-clock. It
|
|
437
|
+
// gates re-evaluation via `tick - _lastEvaluationTick >= minIntervalTicks`
|
|
438
|
+
// (shouldAct), so it must be a deterministic tick to replay identically (R2).
|
|
439
|
+
this._lastEvaluationTick = footprint.tickObserved
|
|
440
|
+
this._experienceCountAtLastEval = this._cachedEpisodicTotal
|
|
441
|
+
|
|
442
|
+
const commands: StateCommands = {
|
|
443
|
+
set: [{
|
|
444
|
+
id: 'identity-self',
|
|
445
|
+
type: 'will.identity',
|
|
446
|
+
metadata: {
|
|
447
|
+
prompt: updatedIdentity.prompt,
|
|
448
|
+
values: updatedIdentity.values,
|
|
449
|
+
traits: updatedIdentity.traits,
|
|
450
|
+
traitStats, // per-trait baseline + recency (B/C)
|
|
451
|
+
style: updatedIdentity.style,
|
|
452
|
+
version: ( updatedIdentity.version ?? 1 ) + 1
|
|
453
|
+
}
|
|
454
|
+
}],
|
|
455
|
+
metrics: []
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Domain-specific success rates as metrics
|
|
459
|
+
for( const da of domainAssessments )
|
|
460
|
+
commands.metrics!.push(
|
|
461
|
+
[ `self_model.${da.domain}.success_rate`, da.successRate ],
|
|
462
|
+
[ `self_model.${da.domain}.sample_size`, da.sampleSize ]
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
commands.metrics!.push([ 'self_model.version', ( updatedIdentity.version ?? 1 ) + 1 ])
|
|
466
|
+
|
|
467
|
+
// Push domain competence beliefs to SemanticIntegrator.
|
|
468
|
+
// Self-model traits are a numeric signal; these natural-language beliefs
|
|
469
|
+
// are what actually surface in the executive's reasoning context.
|
|
470
|
+
// Only domains with ≥5 samples and a clear directional signal produce beliefs.
|
|
471
|
+
if( this._semanticIntegrator ){
|
|
472
|
+
const tick = footprint.tickObserved
|
|
473
|
+
for( const da of domainAssessments ){
|
|
474
|
+
if( da.sampleSize < 5 ) continue
|
|
475
|
+
const confidence = Math.min( 0.82, ( da.sampleSize / ( da.sampleSize + 10 ) ) * 0.9 )
|
|
476
|
+
|
|
477
|
+
if( da.successRate > 0.70 )
|
|
478
|
+
this._semanticIntegrator.integrateExecutiveBelief({
|
|
479
|
+
id: `belief-competence-${da.domain}-positive`,
|
|
480
|
+
statement: `I am effective at ${da.domain} tasks`,
|
|
481
|
+
category: 'self_belief',
|
|
482
|
+
confidence,
|
|
483
|
+
supportingEpisodes: da.sampleSize,
|
|
484
|
+
lastUpdatedAt: tick,
|
|
485
|
+
tags: [ da.domain, 'competence', 'self', 'positive' ]
|
|
486
|
+
}, tick, 'self-model' )
|
|
487
|
+
|
|
488
|
+
else if( da.successRate < 0.30 )
|
|
489
|
+
this._semanticIntegrator.integrateExecutiveBelief({
|
|
490
|
+
id: `belief-competence-${da.domain}-negative`,
|
|
491
|
+
statement: `I often struggle with ${da.domain} tasks`,
|
|
492
|
+
category: 'self_belief',
|
|
493
|
+
confidence,
|
|
494
|
+
supportingEpisodes: da.sampleSize,
|
|
495
|
+
lastUpdatedAt: tick,
|
|
496
|
+
tags: [ da.domain, 'competence', 'self', 'negative' ]
|
|
497
|
+
}, tick, 'self-model' )
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Phase C + F: publish cognitive event — gated by prediction error
|
|
502
|
+
const _bus = this._bus
|
|
503
|
+
if( _bus ){
|
|
504
|
+
const predErr = this._model.observe( 'self_model.change', changeMagnitude )
|
|
505
|
+
if( !predErr.gated )
|
|
506
|
+
_bus.publish({
|
|
507
|
+
type: 'self_model.updated',
|
|
508
|
+
version: 1,
|
|
509
|
+
sourceEngine: this.name,
|
|
510
|
+
salience: Math.max( 0.4, predErr.salience ),
|
|
511
|
+
payload: {
|
|
512
|
+
version: ( updatedIdentity.version ?? 1 ) + 1,
|
|
513
|
+
changeMagnitude
|
|
514
|
+
}
|
|
515
|
+
})
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return commands
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ── Self-evaluation ──────────────────────────────────────
|
|
522
|
+
|
|
523
|
+
private _computeDomainAssessments(): Array<{
|
|
524
|
+
domain: string
|
|
525
|
+
successRate: number
|
|
526
|
+
sampleSize: number
|
|
527
|
+
}> {
|
|
528
|
+
const assessments: Array<{ domain: string; successRate: number; sampleSize: number }> = []
|
|
529
|
+
|
|
530
|
+
for( const [ domain, records ] of this._domainPerformance ){
|
|
531
|
+
if( records.length < 3 ) continue
|
|
532
|
+
|
|
533
|
+
const successCount = records.filter( r => r.success ).length
|
|
534
|
+
assessments.push({
|
|
535
|
+
domain,
|
|
536
|
+
successRate: successCount / records.length,
|
|
537
|
+
sampleSize: records.length,
|
|
538
|
+
})
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Self-observed emotional stability — formed from affect dynamics, not a task
|
|
542
|
+
// success rate. Confidence ramps with how much affect has been sampled (capped),
|
|
543
|
+
// so a fresh Will's neutral 0.5 barely moves the trait until evidence accumulates.
|
|
544
|
+
if( this._affectObservations > 0 )
|
|
545
|
+
assessments.push({
|
|
546
|
+
domain: 'emotional-regulation',
|
|
547
|
+
successRate: this._affectStabilityEma,
|
|
548
|
+
sampleSize: Math.min( 20, this._affectObservations ),
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
assessments.sort( ( a, b ) => b.sampleSize - a.sampleSize )
|
|
552
|
+
return assessments
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private _extractIdentity( state: ReadonlySimulationState ): Keidentity {
|
|
556
|
+
for( const entity of state.entities.values() )
|
|
557
|
+
if( entity.type === 'will.identity')
|
|
558
|
+
return {
|
|
559
|
+
prompt: ( entity.metadata?.prompt as string ) ?? '',
|
|
560
|
+
values: ( entity.metadata?.values as string[] ) ?? [],
|
|
561
|
+
traits: ( entity.metadata?.traits as Record<string, number> ) ?? {},
|
|
562
|
+
style: ( entity.metadata?.style as string ) ?? '',
|
|
563
|
+
version: ( entity.metadata?.version as number ) ?? 1
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return { prompt: '', values: [], traits: {}, style: '', version: 1 }
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Evaluate self based on performance data and existing beliefs.
|
|
571
|
+
* Adjusts trait estimates and updates values.
|
|
572
|
+
*/
|
|
573
|
+
private _evaluateSelf(
|
|
574
|
+
current: Keidentity,
|
|
575
|
+
assessments: Array<{ domain: string; successRate: number; sampleSize: number }>,
|
|
576
|
+
beliefs: Belief[]
|
|
577
|
+
): Keidentity {
|
|
578
|
+
const updated = {
|
|
579
|
+
...current,
|
|
580
|
+
traits: { ...current.traits },
|
|
581
|
+
values: [ ...current.values ]
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Update traits based on performance
|
|
585
|
+
for( const assessment of assessments ){
|
|
586
|
+
const traitKey = this._domainToTrait( assessment.domain )
|
|
587
|
+
if( !traitKey ) continue
|
|
588
|
+
|
|
589
|
+
const currentTrait = updated.traits[ traitKey ] ?? 0.5
|
|
590
|
+
const confidence = Math.min( 1, assessment.sampleSize / 20 )
|
|
591
|
+
|
|
592
|
+
// Exponential moving average — weight by sample confidence
|
|
593
|
+
updated.traits[ traitKey ] = currentTrait * ( 1 - confidence * 0.3 )
|
|
594
|
+
+ assessment.successRate * confidence * 0.3
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Integrate self-beliefs into trait updates
|
|
598
|
+
for( const belief of beliefs ){
|
|
599
|
+
if( belief.category !== 'self_belief') continue
|
|
600
|
+
|
|
601
|
+
// A positive self-belief nudges only the trait(s) it is actually about (keyword
|
|
602
|
+
// cue) — not every trait. Self-concept still shapes traits, without inflating
|
|
603
|
+
// unrelated dispositions the persona-prior would then develop.
|
|
604
|
+
const text = belief.statement.toLowerCase()
|
|
605
|
+
const isPositive = !text.includes('not') && !text.includes('bad') && !text.includes('poor')
|
|
606
|
+
if( !isPositive ) continue
|
|
607
|
+
|
|
608
|
+
for( const trait of traitsCuedBySelfBelief( belief.statement ) )
|
|
609
|
+
updated.traits[ trait ] = Math.min( 1, ( updated.traits[ trait ] ?? 0.5 ) + belief.confidence * 0.02 )
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Adopt new values if consistently successful in related domains
|
|
613
|
+
if( assessments.some( a => a.domain === 'helping' && a.successRate > 0.7 ) ){
|
|
614
|
+
if( !updated.values.includes('compassion') )
|
|
615
|
+
updated.values.push('compassion')
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if( assessments.some( a => a.domain === 'learning' && a.successRate > 0.6 ) ){
|
|
619
|
+
if( !updated.values.includes('growth') )
|
|
620
|
+
updated.values.push('growth')
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return updated
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
private _domainToTrait( domain: string ): string | null {
|
|
627
|
+
const mapping: Record<string, string> = {
|
|
628
|
+
'planning': 'conscientiousness',
|
|
629
|
+
'deciding': 'decisiveness',
|
|
630
|
+
'exploring': 'openness',
|
|
631
|
+
'social': 'agreeableness',
|
|
632
|
+
'helping': 'agreeableness',
|
|
633
|
+
'learning': 'openness',
|
|
634
|
+
'persisting': 'persistence',
|
|
635
|
+
'coping': 'resilience',
|
|
636
|
+
'creating': 'creativity',
|
|
637
|
+
'analyzing': 'analytical',
|
|
638
|
+
'emotional-regulation': 'emotional-stability',
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return mapping[ domain ] ?? null
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private _diffTraits(
|
|
645
|
+
oldTraits: Record<string, number>,
|
|
646
|
+
newTraits: Record<string, number>
|
|
647
|
+
): string[] {
|
|
648
|
+
const changes: string[] = []
|
|
649
|
+
|
|
650
|
+
for( const key of new Set([ ...Object.keys( oldTraits ), ...Object.keys( newTraits ) ]) ){
|
|
651
|
+
const oldVal = oldTraits[ key ] ?? 0.5
|
|
652
|
+
const newVal = newTraits[ key ] ?? 0.5
|
|
653
|
+
|
|
654
|
+
if( Math.abs( newVal - oldVal ) > 0.05 )
|
|
655
|
+
changes.push(`${key}: ${oldVal.toFixed( 2 )} → ${newVal.toFixed( 2 )}`)
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return changes
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Scalar magnitude of how much the identity actually moved this evaluation —
|
|
663
|
+
* summed absolute trait deltas plus a per-new-value increment. This is the
|
|
664
|
+
* surprise signal the generative model gates on (a real self-revision is a
|
|
665
|
+
* large change), replacing the monotonically-incrementing version counter
|
|
666
|
+
* which always looked like the same "+1" of error.
|
|
667
|
+
*/
|
|
668
|
+
private _identityChangeMagnitude( oldId: Keidentity, newId: Keidentity ): number {
|
|
669
|
+
let mag = 0
|
|
670
|
+
for( const key of new Set([ ...Object.keys( oldId.traits ), ...Object.keys( newId.traits ) ]) )
|
|
671
|
+
mag += Math.abs( ( newId.traits[ key ] ?? 0.5 ) - ( oldId.traits[ key ] ?? 0.5 ) )
|
|
672
|
+
|
|
673
|
+
mag += newId.values.filter( v => !oldId.values.includes( v ) ).length * 0.1
|
|
674
|
+
|
|
675
|
+
return mag
|
|
676
|
+
}
|
|
677
|
+
}
|