@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,294 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/faculties/circadian.oscillator.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CircadianOscillator — maintains an internal biological clock.
|
|
7
|
+
*
|
|
8
|
+
* Produces a ~24-hour rhythm that modulates:
|
|
9
|
+
* - Energy baseline (lower at night)
|
|
10
|
+
* - Cognitive performance (peaks during day)
|
|
11
|
+
* - Sleep propensity (highest at night)
|
|
12
|
+
* - Mood baseline (varies with time of day)
|
|
13
|
+
*
|
|
14
|
+
* The oscillator can be entrained by external light/dark signals
|
|
15
|
+
* and can drift if isolated (free-running period slightly > 24h).
|
|
16
|
+
*
|
|
17
|
+
* Part of Shard 0 (Regulatory Layer) — runs every tick, synchronous.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type {
|
|
21
|
+
Duration,
|
|
22
|
+
Tick,
|
|
23
|
+
SimulationContext,
|
|
24
|
+
ReadonlySimulationState,
|
|
25
|
+
StateCommands,
|
|
26
|
+
SimulationEvent,
|
|
27
|
+
ModulationSignal,
|
|
28
|
+
} from '#core/types'
|
|
29
|
+
import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
|
|
30
|
+
import type { CognitiveEventSchema } from '#cognition/schema.registry'
|
|
31
|
+
import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
|
|
32
|
+
import { GenerativeModel } from '#cognition/generative.model'
|
|
33
|
+
|
|
34
|
+
export type CircadianPhase = 'morning' | 'afternoon' | 'evening' | 'night'
|
|
35
|
+
|
|
36
|
+
export interface CircadianConfig {
|
|
37
|
+
/** Period of the oscillator in hours (default 24.2 — free-running human) */
|
|
38
|
+
periodHours?: number
|
|
39
|
+
/** Phase offset in hours (shift the peak) */
|
|
40
|
+
phaseOffsetHours?: number
|
|
41
|
+
/** Whether external light signals entrain the oscillator */
|
|
42
|
+
entrainable?: boolean
|
|
43
|
+
/** Current simulated time of day in hours (0-24). If not provided, derived from tick. */
|
|
44
|
+
timeOfDayHours?: number
|
|
45
|
+
bus?: CognitiveBus
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class CircadianOscillator implements SimulationEngine, CognitiveEngine {
|
|
49
|
+
readonly name = 'circadian-oscillator'
|
|
50
|
+
|
|
51
|
+
private _periodHours: number
|
|
52
|
+
private _phaseOffsetHours: number
|
|
53
|
+
private _entrainable: boolean
|
|
54
|
+
private _timeOfDayHours?: number
|
|
55
|
+
|
|
56
|
+
private _bus: CognitiveBus | null = null
|
|
57
|
+
|
|
58
|
+
private readonly _model = new GenerativeModel()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
constructor( config: CircadianConfig = {} ){
|
|
62
|
+
this._bus = config.bus ?? null
|
|
63
|
+
this._periodHours = config.periodHours ?? 24.2
|
|
64
|
+
this._phaseOffsetHours = config.phaseOffsetHours ?? 0
|
|
65
|
+
this._entrainable = config.entrainable ?? true
|
|
66
|
+
this._timeOfDayHours = config.timeOfDayHours
|
|
67
|
+
}
|
|
68
|
+
attachBus( bus: CognitiveBus ): void { this._bus = bus }
|
|
69
|
+
|
|
70
|
+
// ── Engine interface ─────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
subscribes(): string[] {
|
|
74
|
+
return [
|
|
75
|
+
'executive.prediction.formed'
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
publishes(): CognitiveEventSchema[] { return [] }
|
|
79
|
+
|
|
80
|
+
onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
|
|
81
|
+
this._model.observe( e.type, e.salience )
|
|
82
|
+
if( e.type === 'executive.prediction.formed' ){
|
|
83
|
+
const p = e.payload as { predictedDomains: string[]; confidence: number }
|
|
84
|
+
if( p.predictedDomains.includes('circadian') )
|
|
85
|
+
this._model.setPrecision( 'circadian.alertness', 1.0 + p.confidence * 0.5 )
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
snapshot(): Record<string, unknown> { return {} }
|
|
90
|
+
|
|
91
|
+
async react(
|
|
92
|
+
delta: Duration,
|
|
93
|
+
tick: Tick,
|
|
94
|
+
state: ReadonlySimulationState,
|
|
95
|
+
context: SimulationContext
|
|
96
|
+
): Promise<EngineResult> {
|
|
97
|
+
this._readConfigFromState( state )
|
|
98
|
+
|
|
99
|
+
const
|
|
100
|
+
events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
|
|
101
|
+
commands: StateCommands = { metrics: [] }
|
|
102
|
+
|
|
103
|
+
// Determine current time of day
|
|
104
|
+
let timeOfDay = this._timeOfDayHours
|
|
105
|
+
|
|
106
|
+
if( timeOfDay === undefined ){
|
|
107
|
+
// Derive from tick, assuming 1 tick = 1 second
|
|
108
|
+
const totalSeconds = tick,
|
|
109
|
+
totalHours = totalSeconds / 3600,
|
|
110
|
+
dayFraction = ( totalHours % this._periodHours ) / this._periodHours
|
|
111
|
+
|
|
112
|
+
timeOfDay = ( dayFraction * 24 + this._phaseOffsetHours ) % 24
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Entrain to external light if available and enabled
|
|
116
|
+
if( this._entrainable ){
|
|
117
|
+
const externalLight = state.metrics.get('environment.light_level')
|
|
118
|
+
if( externalLight !== undefined ){
|
|
119
|
+
const isSubjectiveNight = timeOfDay >= 21 || timeOfDay < 6
|
|
120
|
+
if( isSubjectiveNight && externalLight > 0.5 )
|
|
121
|
+
timeOfDay = ( timeOfDay + ( externalLight - 0.5 ) * 0.1 ) % 24
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Clamp and normalize
|
|
126
|
+
timeOfDay = ( timeOfDay % 24 + 24 ) % 24
|
|
127
|
+
|
|
128
|
+
// Compute circadian outputs — all sinusoidal with phase offsets
|
|
129
|
+
const
|
|
130
|
+
radians = ( timeOfDay / 24 ) * Math.PI * 2,
|
|
131
|
+
|
|
132
|
+
// Core body temperature rhythm (peaks ~18:00, trough ~04:00)
|
|
133
|
+
temperaturePhase = radians - Math.PI * 0.75,
|
|
134
|
+
temperatureSignal = Math.sin( temperaturePhase ),
|
|
135
|
+
|
|
136
|
+
// Cognitive performance (peaks ~10:00-12:00, trough ~02:00-04:00)
|
|
137
|
+
cognitivePhase = radians - Math.PI * 0.25,
|
|
138
|
+
cognitiveSignal = Math.sin( cognitivePhase ),
|
|
139
|
+
|
|
140
|
+
// Sleep propensity (peaks ~02:00-04:00, secondary peak ~14:00)
|
|
141
|
+
sleepPhase = radians - Math.PI * 0.5,
|
|
142
|
+
sleepSignal = ( Math.sin( sleepPhase ) + 1 ) / 2, // Normalize 0-1
|
|
143
|
+
|
|
144
|
+
// Melatonin proxy (rises at dusk, peaks ~02:00, falls at dawn)
|
|
145
|
+
melatoninPhase = radians - Math.PI * 0.45,
|
|
146
|
+
melatoninSignal = Math.max( 0, Math.sin( melatoninPhase ) ),
|
|
147
|
+
|
|
148
|
+
// Alertness (peaks ~10:00, trough ~04:00)
|
|
149
|
+
alertnessPhase = radians - Math.PI * 0.3,
|
|
150
|
+
alertnessSignal = ( Math.sin( alertnessPhase ) + 1 ) / 2,
|
|
151
|
+
|
|
152
|
+
// Mood baseline (peaks ~12:00-14:00, trough ~03:00)
|
|
153
|
+
moodPhase = radians - Math.PI * 0.35,
|
|
154
|
+
moodSignal = ( Math.sin( moodPhase ) + 1 ) / 2
|
|
155
|
+
|
|
156
|
+
// Current phase label
|
|
157
|
+
const phaseLabel: CircadianPhase = timeOfDay >= 6 && timeOfDay < 12 ? 'morning'
|
|
158
|
+
: timeOfDay >= 12 && timeOfDay < 17 ? 'afternoon'
|
|
159
|
+
: timeOfDay >= 17 && timeOfDay < 21 ? 'evening'
|
|
160
|
+
: 'night'
|
|
161
|
+
|
|
162
|
+
// Phase transition detection
|
|
163
|
+
// state.metrics stores the previous phase as a numeric code for the
|
|
164
|
+
// metric system (metrics are always numbers). We compare the label
|
|
165
|
+
// against our own reconstruction.
|
|
166
|
+
const
|
|
167
|
+
previousPhaseCode = state.metrics.get('circadian.phase'),
|
|
168
|
+
previousPhaseLabel: CircadianPhase | undefined
|
|
169
|
+
= previousPhaseCode === 0 ? 'morning'
|
|
170
|
+
: previousPhaseCode === 1 ? 'afternoon'
|
|
171
|
+
: previousPhaseCode === 2 ? 'evening'
|
|
172
|
+
: previousPhaseCode === 3 ? 'night'
|
|
173
|
+
: undefined
|
|
174
|
+
|
|
175
|
+
if( previousPhaseLabel !== undefined && previousPhaseLabel !== phaseLabel )
|
|
176
|
+
events.push({
|
|
177
|
+
type: `circadian.${phaseLabel}`,
|
|
178
|
+
source: this.name,
|
|
179
|
+
payload: { timeOfDay, phase: phaseLabel, previousPhase: previousPhaseLabel },
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// Phase code for metric storage (metrics are numeric)
|
|
183
|
+
const phaseCode = phaseLabel === 'morning' ? 0
|
|
184
|
+
: phaseLabel === 'afternoon' ? 1
|
|
185
|
+
: phaseLabel === 'evening' ? 2
|
|
186
|
+
: 3
|
|
187
|
+
|
|
188
|
+
// Update circadian metrics
|
|
189
|
+
commands.metrics!.push(
|
|
190
|
+
[ 'circadian.time_of_day', timeOfDay ],
|
|
191
|
+
[ 'circadian.phase', phaseCode ],
|
|
192
|
+
[ 'circadian.temperature_phase', temperatureSignal ],
|
|
193
|
+
[ 'circadian.cognitive_phase', cognitiveSignal ],
|
|
194
|
+
[ 'circadian.sleep_propensity', sleepSignal ],
|
|
195
|
+
[ 'circadian.melatonin', melatoninSignal ],
|
|
196
|
+
[ 'circadian.alertness', alertnessSignal ],
|
|
197
|
+
[ 'circadian.mood_baseline', moodSignal ],
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
// Modulation signals for other engines
|
|
201
|
+
events.push({
|
|
202
|
+
type: 'modulation.circadian_alertness',
|
|
203
|
+
source: this.name,
|
|
204
|
+
payload: {
|
|
205
|
+
target: 'attention-allocator',
|
|
206
|
+
parameter: 'capacity',
|
|
207
|
+
factor: 0.7 + alertnessSignal * 0.6,
|
|
208
|
+
source: this.name,
|
|
209
|
+
} satisfies ModulationSignal,
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
events.push({
|
|
213
|
+
type: 'modulation.circadian_cognitive',
|
|
214
|
+
source: this.name,
|
|
215
|
+
payload: {
|
|
216
|
+
target: 'decision-engine',
|
|
217
|
+
parameter: 'cognitive_phase',
|
|
218
|
+
factor: 0.8 + cognitiveSignal * 0.4,
|
|
219
|
+
source: this.name,
|
|
220
|
+
} satisfies ModulationSignal,
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
events.push({
|
|
224
|
+
type: 'modulation.circadian_sleep_propensity',
|
|
225
|
+
source: this.name,
|
|
226
|
+
payload: {
|
|
227
|
+
target: 'sleep-pressure-regulator',
|
|
228
|
+
parameter: 'sleep_gate',
|
|
229
|
+
factor: sleepSignal,
|
|
230
|
+
source: this.name,
|
|
231
|
+
} satisfies ModulationSignal,
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
events.push({
|
|
235
|
+
type: 'modulation.circadian_mood',
|
|
236
|
+
source: this.name,
|
|
237
|
+
payload: {
|
|
238
|
+
target: 'affective-blender',
|
|
239
|
+
parameter: 'mood_baseline',
|
|
240
|
+
factor: moodSignal,
|
|
241
|
+
source: this.name,
|
|
242
|
+
} satisfies ModulationSignal,
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
// Phase C: publish cognitive event
|
|
247
|
+
const _bus = this._bus
|
|
248
|
+
if( _bus )
|
|
249
|
+
_bus.publish({ type: 'circadian.alertness.shifted', version: 1, sourceEngine: this.name, salience: Math.abs(alertnessSignal - 0.5) * 0.6, payload: { alertness: alertnessSignal, phase: phaseLabel } })
|
|
250
|
+
// Phase D + F: rich state-change event — gated by prediction error
|
|
251
|
+
if( _bus ){
|
|
252
|
+
const predErr = this._model.observe( 'circadian.alertness', alertnessSignal )
|
|
253
|
+
if( !predErr.gated )
|
|
254
|
+
_bus.publish({ type: 'circadian.state.changed', version: 1, sourceEngine: this.name, salience: predErr.salience, payload: { alertness: alertnessSignal, timeOfDay, phaseCode, moodBaseline: moodSignal } })
|
|
255
|
+
}
|
|
256
|
+
return { events: events.length > 0 ? events : undefined, commands }
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ── Config hot-reload ────────────────────────────────────
|
|
260
|
+
|
|
261
|
+
private _readConfigFromState( state: ReadonlySimulationState ): void {
|
|
262
|
+
const cfg = state.entities.get('engine-config-circadian')
|
|
263
|
+
if( !cfg ) return
|
|
264
|
+
|
|
265
|
+
const p = cfg.metadata?.params as Record<string, number> | undefined
|
|
266
|
+
if( !p ) return
|
|
267
|
+
|
|
268
|
+
if( p.periodHours != null ) this._periodHours = p.periodHours
|
|
269
|
+
if( p.phaseOffsetHours != null ) this._phaseOffsetHours = p.phaseOffsetHours
|
|
270
|
+
|
|
271
|
+
const entrainable = p.entrainable != null ? !!p.entrainable : undefined
|
|
272
|
+
if( entrainable != null ) this._entrainable = entrainable
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ── Public helpers ───────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Set the current time of day explicitly (for scenarios with controlled time).
|
|
279
|
+
*/
|
|
280
|
+
setTimeOfDay( hours: number ): void {
|
|
281
|
+
this._timeOfDayHours = ( hours % 24 + 24 ) % 24
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Get the current circadian phase.
|
|
286
|
+
*/
|
|
287
|
+
getPhase(): CircadianPhase {
|
|
288
|
+
const h = this._timeOfDayHours ?? 12
|
|
289
|
+
return h >= 6 && h < 12 ? 'morning'
|
|
290
|
+
: h >= 12 && h < 17 ? 'afternoon'
|
|
291
|
+
: h >= 17 && h < 21 ? 'evening'
|
|
292
|
+
: 'night'
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/faculties/confidence.calibrator.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ConfidenceCalibrator — compares decision confidence against actual
|
|
7
|
+
* outcomes to calibrate future confidence estimates.
|
|
8
|
+
*
|
|
9
|
+
* Maintains a calibration curve per action domain. If the agent
|
|
10
|
+
* consistently overestimates confidence in "planning" decisions,
|
|
11
|
+
* future planning confidence is adjusted downward.
|
|
12
|
+
*
|
|
13
|
+
* This is the mechanism behind "knowing what you don't know."
|
|
14
|
+
*
|
|
15
|
+
* Part of Shard 4 (Meta-Cognitive Layer) — runs every tick, synchronous.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
Tick,
|
|
20
|
+
SimulationContext,
|
|
21
|
+
ReadonlySimulationState,
|
|
22
|
+
StateCommands,
|
|
23
|
+
SimulationEvent,
|
|
24
|
+
Duration,
|
|
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, type GenerativeModelSnapshot } from '#cognition/generative.model'
|
|
30
|
+
import { readEffectiveParams } from '#cognition/persona.prior'
|
|
31
|
+
|
|
32
|
+
export interface ConfidenceCalibratorConfig {
|
|
33
|
+
/** Minimum outcome samples before calibration activates */
|
|
34
|
+
minSamplesPerDomain?: number
|
|
35
|
+
/** How aggressively to adjust calibration (learning rate) */
|
|
36
|
+
calibrationRate?: number
|
|
37
|
+
/** Maximum calibration adjustment per evaluation */
|
|
38
|
+
maxAdjustment?: number
|
|
39
|
+
bus?: CognitiveBus
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface CalibrationRecord {
|
|
43
|
+
confidence: number
|
|
44
|
+
outcomeQuality: number // 0-1: how good the outcome actually was
|
|
45
|
+
tick: Tick
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class ConfidenceCalibrator implements SimulationEngine, CognitiveEngine {
|
|
49
|
+
readonly name = 'confidence-calibrator'
|
|
50
|
+
|
|
51
|
+
private _executiveFlaggedBiasCount: number = 0
|
|
52
|
+
private _minSamplesPerDomain: number
|
|
53
|
+
private _calibrationRate: number
|
|
54
|
+
private _maxAdjustment: number
|
|
55
|
+
|
|
56
|
+
// Per-domain calibration records
|
|
57
|
+
private _records = new Map<string, CalibrationRecord[]>()
|
|
58
|
+
|
|
59
|
+
// Per-domain bias estimate (-1 = underconfident, +1 = overconfident)
|
|
60
|
+
private _domainBias = new Map<string, number>()
|
|
61
|
+
|
|
62
|
+
private _bus: CognitiveBus | null = null
|
|
63
|
+
private _restored = false
|
|
64
|
+
|
|
65
|
+
private readonly _model = new GenerativeModel()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
constructor( config: ConfidenceCalibratorConfig = {} ){
|
|
69
|
+
this._bus = config.bus ?? null
|
|
70
|
+
this._minSamplesPerDomain = config.minSamplesPerDomain ?? 5
|
|
71
|
+
this._calibrationRate = config.calibrationRate ?? 0.1
|
|
72
|
+
this._maxAdjustment = config.maxAdjustment ?? 0.3
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── Public API ───────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
attachBus( bus: CognitiveBus ): void { this._bus = bus }
|
|
78
|
+
subscribes(): string[] {
|
|
79
|
+
return [
|
|
80
|
+
'executive.self.reflection',
|
|
81
|
+
'executive.prediction.formed',
|
|
82
|
+
'action.outcome'
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
publishes(): CognitiveEventSchema[] { return [] }
|
|
86
|
+
|
|
87
|
+
onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
|
|
88
|
+
this._model.observe( e.type, e.salience )
|
|
89
|
+
switch( e.type ){
|
|
90
|
+
case 'executive.self.reflection': {
|
|
91
|
+
// Executive named biases — flag for next calibration pass
|
|
92
|
+
const p = e.payload as Record<string, unknown>
|
|
93
|
+
this._executiveFlaggedBiasCount = ( ( p['identifiedBiases'] as string[] ) ?? [] ).length
|
|
94
|
+
|
|
95
|
+
break
|
|
96
|
+
}
|
|
97
|
+
case 'executive.prediction.formed': {
|
|
98
|
+
const p = e.payload as { predictedDomains: string[]; confidence: number }
|
|
99
|
+
if( p.predictedDomains.includes('metacognition') )
|
|
100
|
+
this._model.setPrecision( 'metacognition.bias', 1.0 + p.confidence * 0.5 )
|
|
101
|
+
|
|
102
|
+
break
|
|
103
|
+
}
|
|
104
|
+
case 'action.outcome': {
|
|
105
|
+
// Pair the decision's predicted confidence against the realised outcome
|
|
106
|
+
// quality so per-domain over/under-confidence can be learned in react().
|
|
107
|
+
const p = e.payload as { domain?: string; confidence?: number; outcomeQuality?: number; tick?: number }
|
|
108
|
+
if( typeof p.confidence === 'number' && typeof p.outcomeQuality === 'number' )
|
|
109
|
+
this.recordOutcome( p.domain ?? 'general', p.confidence, p.outcomeQuality, ( ( p.tick ?? 0 ) as Tick ) )
|
|
110
|
+
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
snapshot(): Record<string, unknown> {
|
|
117
|
+
// FN9: capture the learned calibration curve (_domainBias) and the raw
|
|
118
|
+
// outcome records that produce it, plus the salience/generative sub-states,
|
|
119
|
+
// so a restored Will keeps "knowing what it doesn't know" instead of
|
|
120
|
+
// re-learning every domain bias from zero. Maps are flattened to entry
|
|
121
|
+
// arrays so the payload stays plain-JSON-safe.
|
|
122
|
+
return {
|
|
123
|
+
executiveFlaggedBiasCount: this._executiveFlaggedBiasCount,
|
|
124
|
+
records: [ ...this._records.entries() ].map( ([ k, v ]) => [ k, v.map( r => ({ ...r }) ) ] ),
|
|
125
|
+
domainBias: [ ...this._domainBias.entries() ],
|
|
126
|
+
model: this._model.snapshot()
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
restore( snap: Record<string, unknown> ): void {
|
|
131
|
+
if( !snap ) return
|
|
132
|
+
|
|
133
|
+
if( typeof snap.executiveFlaggedBiasCount === 'number' )
|
|
134
|
+
this._executiveFlaggedBiasCount = snap.executiveFlaggedBiasCount
|
|
135
|
+
|
|
136
|
+
if( Array.isArray( snap.records ) )
|
|
137
|
+
this._records = new Map( snap.records as Array<[ string, CalibrationRecord[] ]> )
|
|
138
|
+
|
|
139
|
+
if( Array.isArray( snap.domainBias ) )
|
|
140
|
+
this._domainBias = new Map( snap.domainBias as Array<[ string, number ]> )
|
|
141
|
+
|
|
142
|
+
if( snap.model ) this._model.restore( snap.model as GenerativeModelSnapshot )
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Rehydrate the learned calibration curve from the persisted `calibration-state`
|
|
147
|
+
* entity (Phase 2, Option B — the entity-restore path that's actually wired at
|
|
148
|
+
* boot). Called once on the first react after a restore. Bias is restored
|
|
149
|
+
* directly so calibration is continuous immediately, rather than waiting to
|
|
150
|
+
* re-accumulate minSamplesPerDomain fresh outcomes.
|
|
151
|
+
*/
|
|
152
|
+
private _restoreFromState( state: ReadonlySimulationState ): void {
|
|
153
|
+
const m = state.entities.get('calibration-state')?.metadata?.domainBias as Record<string, number> | undefined
|
|
154
|
+
if( !m ) return
|
|
155
|
+
|
|
156
|
+
for( const [ domain, bias ] of Object.entries( m ) )
|
|
157
|
+
if( typeof bias === 'number' )
|
|
158
|
+
this._domainBias.set( domain, bias )
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Record a decision and its eventual outcome.
|
|
163
|
+
* Called when an action's outcome is known.
|
|
164
|
+
*/
|
|
165
|
+
recordOutcome(
|
|
166
|
+
domain: string,
|
|
167
|
+
confidence: number,
|
|
168
|
+
outcomeQuality: number,
|
|
169
|
+
tick: Tick
|
|
170
|
+
): void {
|
|
171
|
+
const records = this._records.get( domain ) ?? []
|
|
172
|
+
records.push({ confidence, outcomeQuality, tick })
|
|
173
|
+
|
|
174
|
+
// Keep last 100 records per domain
|
|
175
|
+
if( records.length > 100 ) records.shift()
|
|
176
|
+
|
|
177
|
+
this._records.set( domain, records )
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get the calibrated confidence for a given raw confidence in a domain.
|
|
182
|
+
* Adjusts based on historical over/under-confidence patterns.
|
|
183
|
+
*/
|
|
184
|
+
getCalibratedConfidence( domain: string, rawConfidence: number ): number {
|
|
185
|
+
const bias = this._domainBias.get( domain ) ?? 0
|
|
186
|
+
// Overconfident → reduce. Underconfident → increase.
|
|
187
|
+
const adjusted = rawConfidence - bias * rawConfidence
|
|
188
|
+
|
|
189
|
+
return Math.max( 0, Math.min( 1, adjusted ) )
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Effective config = base engine-config-confidence ⊕ persona-prior. Seeded base
|
|
194
|
+
* matches the constructor defaults (no drift), so this just single-sources the
|
|
195
|
+
* tunables and lets a future persona-prior modulate calibration aggressiveness.
|
|
196
|
+
*/
|
|
197
|
+
private _readConfigFromState( state: ReadonlySimulationState ): void {
|
|
198
|
+
const p = readEffectiveParams( state, 'engine-config-confidence' )
|
|
199
|
+
|
|
200
|
+
if( p.minSamplesPerDomain != null ) this._minSamplesPerDomain = p.minSamplesPerDomain
|
|
201
|
+
if( p.calibrationRate != null ) this._calibrationRate = p.calibrationRate
|
|
202
|
+
if( p.maxAdjustment != null ) this._maxAdjustment = p.maxAdjustment
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ── Engine interface ─────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
async react(
|
|
208
|
+
_delta: Duration,
|
|
209
|
+
tick: Tick,
|
|
210
|
+
state: ReadonlySimulationState,
|
|
211
|
+
context: SimulationContext
|
|
212
|
+
): Promise<EngineResult> {
|
|
213
|
+
const
|
|
214
|
+
events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
|
|
215
|
+
commands: StateCommands = { set: [], metrics: [] }
|
|
216
|
+
|
|
217
|
+
// On the first tick after a restart, rehydrate the learned calibration curve
|
|
218
|
+
// from its persisted entity (Phase 2, Option B). Entity state is the wired
|
|
219
|
+
// restore path, so the Will keeps "knowing what it doesn't know" across
|
|
220
|
+
// restarts even though the engine-internal restore() seam isn't invoked at boot.
|
|
221
|
+
if( !this._restored ){
|
|
222
|
+
this._restoreFromState( state )
|
|
223
|
+
this._restored = true
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Effective config = base engine-config-confidence ⊕ persona-prior (single-source).
|
|
227
|
+
this._readConfigFromState( state )
|
|
228
|
+
|
|
229
|
+
// Recalibrate each domain with enough samples
|
|
230
|
+
for( const [ domain, records ] of this._records ){
|
|
231
|
+
if( records.length < this._minSamplesPerDomain ) continue
|
|
232
|
+
|
|
233
|
+
const bias = this._computeBias( records )
|
|
234
|
+
const previousBias = this._domainBias.get( domain ) ?? 0
|
|
235
|
+
|
|
236
|
+
// Smooth update
|
|
237
|
+
const newBias = previousBias + ( bias - previousBias ) * this._calibrationRate
|
|
238
|
+
const clampedBias = Math.max( -this._maxAdjustment, Math.min( this._maxAdjustment, newBias ) )
|
|
239
|
+
|
|
240
|
+
this._domainBias.set( domain, clampedBias )
|
|
241
|
+
|
|
242
|
+
// Metric
|
|
243
|
+
commands.metrics!.push([ `calibration.${domain}.bias`, clampedBias ])
|
|
244
|
+
|
|
245
|
+
// Event if bias changed significantly
|
|
246
|
+
if( Math.abs( clampedBias - previousBias ) > 0.1 )
|
|
247
|
+
events.push({
|
|
248
|
+
type: 'calibration.shift',
|
|
249
|
+
source: this.name,
|
|
250
|
+
payload: {
|
|
251
|
+
domain,
|
|
252
|
+
previousBias,
|
|
253
|
+
newBias: clampedBias,
|
|
254
|
+
interpretation: clampedBias > 0.1 ? 'overconfident'
|
|
255
|
+
: clampedBias < -0.1 ? 'underconfident'
|
|
256
|
+
: 'well_calibrated'
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Overall calibration metric
|
|
262
|
+
const allBiases = Array.from( this._domainBias.values() )
|
|
263
|
+
const overallBias = allBiases.length > 0 ? allBiases.reduce( ( s, b ) => s + b, 0 ) / allBiases.length : 0
|
|
264
|
+
|
|
265
|
+
commands.metrics!.push([ 'metacognition.calibration_bias', overallBias ])
|
|
266
|
+
|
|
267
|
+
// Persist the learned calibration curve as an entity so it survives restart
|
|
268
|
+
// (durable persona). Written from _domainBias — independent of _records,
|
|
269
|
+
// which re-accumulate from action.outcome after a restart. `tick` keeps the
|
|
270
|
+
// write deterministic (R2).
|
|
271
|
+
if( this._domainBias.size > 0 )
|
|
272
|
+
commands.set!.push({
|
|
273
|
+
id: 'calibration-state',
|
|
274
|
+
type: 'calibration.state',
|
|
275
|
+
metadata: {
|
|
276
|
+
domainBias: Object.fromEntries( this._domainBias ),
|
|
277
|
+
updatedAtTick: tick
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
// Phase C: publish cognitive event
|
|
283
|
+
const _bus = this._bus
|
|
284
|
+
if( _bus )
|
|
285
|
+
_bus.publish({
|
|
286
|
+
type: 'confidence.calibrated',
|
|
287
|
+
version: 1,
|
|
288
|
+
sourceEngine: this.name,
|
|
289
|
+
salience: Math.max( 0.2, this._model.observe( 'calibration.event', overallBias ).salience ),
|
|
290
|
+
payload: { calibrationBias: overallBias }
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
// Phase D + F: rich state-change event — gated by prediction error
|
|
294
|
+
if( _bus ){
|
|
295
|
+
const predErr = this._model.observe( 'metacognition.bias', overallBias )
|
|
296
|
+
if( !predErr.gated )
|
|
297
|
+
_bus.publish({
|
|
298
|
+
type: 'metacognition.state.changed',
|
|
299
|
+
version: 1,
|
|
300
|
+
sourceEngine: this.name,
|
|
301
|
+
salience: predErr.salience,
|
|
302
|
+
payload: { confidence: overallBias }
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return { events: events.length > 0 ? events : undefined, commands }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ── Internal ─────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Compute calibration bias from records.
|
|
313
|
+
* Positive bias = overconfident (confidence > outcome).
|
|
314
|
+
* Negative bias = underconfident (confidence < outcome).
|
|
315
|
+
*/
|
|
316
|
+
private _computeBias( records: CalibrationRecord[] ): number {
|
|
317
|
+
let totalBias = 0
|
|
318
|
+
|
|
319
|
+
for( const record of records )
|
|
320
|
+
totalBias += record.confidence - record.outcomeQuality
|
|
321
|
+
|
|
322
|
+
return totalBias / records.length
|
|
323
|
+
}
|
|
324
|
+
}
|