@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,422 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/faculties/attention.allocator.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* AttentionAllocator — manages a finite attention budget.
|
|
7
|
+
*
|
|
8
|
+
* Attention is a limited resource. Attending to entities consumes
|
|
9
|
+
* budget. Salient stimuli (from Perception) compete for allocation.
|
|
10
|
+
* Multiple simultaneous demands cause fragmentation (reduced depth).
|
|
11
|
+
*
|
|
12
|
+
* Receives modulation from:
|
|
13
|
+
* - EnergyRegulator (low energy → reduced capacity)
|
|
14
|
+
* - SleepPressureRegulator (fatigue → reduced capacity)
|
|
15
|
+
* - CircadianOscillator (time-of-day capacity variation)
|
|
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
|
+
} from '#core/types'
|
|
28
|
+
import type { CognitiveEngine, SimulationEngine, EngineResult } from '#cognition/types'
|
|
29
|
+
import type { CognitiveEventSchema } from '#cognition/schema.registry'
|
|
30
|
+
import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
|
|
31
|
+
import { GenerativeModel } from '#cognition/generative.model'
|
|
32
|
+
import { readEffectiveParams } from '#cognition/persona.prior'
|
|
33
|
+
|
|
34
|
+
export interface AttentionAllocatorConfig {
|
|
35
|
+
/** Maximum attention capacity (baseline, before modulation) */
|
|
36
|
+
maxCapacity?: number
|
|
37
|
+
/** Cost per tick to maintain attention on a single focus */
|
|
38
|
+
costPerFocus?: number
|
|
39
|
+
/** Maximum number of simultaneous attention foci */
|
|
40
|
+
maxFoci?: number
|
|
41
|
+
/** How quickly attention shifts between foci (lower = more sticky) */
|
|
42
|
+
shiftInertia?: number
|
|
43
|
+
bus?: CognitiveBus
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface AttentionFocus {
|
|
47
|
+
entityId: string
|
|
48
|
+
depth: number // 0-1: how deeply attended this focus is
|
|
49
|
+
duration: number // ticks this focus has been maintained
|
|
50
|
+
salienceAtCapture: number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── Voluntary attention effort (Option C) ─────────────────────
|
|
54
|
+
// `effort` is how much of the vitals-permitted capacity ceiling the mind chooses
|
|
55
|
+
// to engage. The executive sets it explicitly via its action vocabulary (a
|
|
56
|
+
// `focus` action mobilizes; `rest`/`sleep`/`wait`/`meditate` stand down); absent
|
|
57
|
+
// a choice it relaxes back to baseline, so focus/rest are transient unless
|
|
58
|
+
// renewed. Effort scales the ceiling, so vitals always win — you cannot focus
|
|
59
|
+
// past exhaustion (a collapsed ceiling leaves nothing to engage).
|
|
60
|
+
const EFFORT_BASELINE = 0.7 // homeostatic default utilization of the ceiling
|
|
61
|
+
const EFFORT_MIN = 0.4 // deepest voluntary stand-down (rest)
|
|
62
|
+
const EFFORT_MAX = 1.0 // full mobilization (focus)
|
|
63
|
+
const EFFORT_RELAX = 0.05 // fraction of the gap-to-baseline relaxed per tick
|
|
64
|
+
|
|
65
|
+
// ── Arousal-driven ceiling (Option A — involuntary upward lever) ──────────────
|
|
66
|
+
// Threat/reward arousal (the integrated `affect.arousal`) mobilizes capacity
|
|
67
|
+
// ABOVE baseline — fight-or-flight — but with a Yerkes–Dodson inverted-U: moderate
|
|
68
|
+
// arousal lifts the ceiling, while extreme arousal collapses it (fragmentation /
|
|
69
|
+
// tunnel vision). This is the involuntary counterpart to voluntary focus: it
|
|
70
|
+
// raises the ceiling effort utilizes, so a calm mind sits at baseline, a roused
|
|
71
|
+
// one can engage more, and a panicked one loses capacity it cannot will back.
|
|
72
|
+
const AROUSAL_REST = 0.3 // at/below this — calm; no mobilization (factor 1.0)
|
|
73
|
+
const AROUSAL_PEAK = 0.65 // optimal arousal — maximum mobilization
|
|
74
|
+
const AROUSAL_GAIN = 0.3 // peak upward mobilization (+30% ceiling at the peak)
|
|
75
|
+
const AROUSAL_OVERLOAD = 0.4 // impairment at maximum arousal (factor → 1 − 0.4)
|
|
76
|
+
|
|
77
|
+
export class AttentionAllocator implements SimulationEngine, CognitiveEngine {
|
|
78
|
+
readonly name = 'attention-allocator'
|
|
79
|
+
|
|
80
|
+
private _maxCapacity: number
|
|
81
|
+
private _costPerFocus: number
|
|
82
|
+
private _maxFoci: number
|
|
83
|
+
private _shiftInertia: number
|
|
84
|
+
private _activeFocus: AttentionFocus[] = []
|
|
85
|
+
|
|
86
|
+
private _energyLevel: number = 100
|
|
87
|
+
private _sleepPressure: number = 0
|
|
88
|
+
|
|
89
|
+
/** Voluntary effort set-point (0.4–1.0); see EFFORT_* constants. */
|
|
90
|
+
private _effort: number = EFFORT_BASELINE
|
|
91
|
+
/** A one-shot focus/rest request from the executive, applied next react(). */
|
|
92
|
+
private _effortRequest: number | null = null
|
|
93
|
+
|
|
94
|
+
private _bus: CognitiveBus | null = null
|
|
95
|
+
|
|
96
|
+
private readonly _model = new GenerativeModel()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
constructor( config: AttentionAllocatorConfig = {} ){
|
|
100
|
+
this._bus = config.bus ?? null
|
|
101
|
+
this._maxCapacity = config.maxCapacity ?? 100
|
|
102
|
+
this._costPerFocus = config.costPerFocus ?? 5
|
|
103
|
+
this._maxFoci = config.maxFoci ?? 4
|
|
104
|
+
this._shiftInertia = config.shiftInertia ?? 0.7
|
|
105
|
+
}
|
|
106
|
+
attachBus( bus: CognitiveBus ): void { this._bus = bus }
|
|
107
|
+
|
|
108
|
+
// ── Engine interface ─────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
subscribes(): string[] {
|
|
112
|
+
return [
|
|
113
|
+
'energy.state.changed',
|
|
114
|
+
'sleep.state.changed',
|
|
115
|
+
'executive.prediction.formed',
|
|
116
|
+
'attention.regulate', // voluntary focus/rest from the executive (Option C)
|
|
117
|
+
'senses.*', // all sense percepts — salience feeds attention allocation
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
publishes(): CognitiveEventSchema[] { return [] }
|
|
121
|
+
|
|
122
|
+
onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
|
|
123
|
+
this._model.observe( e.type, e.salience )
|
|
124
|
+
switch( e.type ){
|
|
125
|
+
case 'energy.state.changed':
|
|
126
|
+
this._energyLevel = (e.payload as Record<string,number>)['level'] ?? this._energyLevel
|
|
127
|
+
break
|
|
128
|
+
case 'sleep.state.changed':
|
|
129
|
+
this._sleepPressure = (e.payload as Record<string,number>)['pressure'] ?? this._sleepPressure
|
|
130
|
+
break
|
|
131
|
+
case 'attention.regulate': {
|
|
132
|
+
// Voluntary focus/rest (Option C). Clamp the requested set-point; react()
|
|
133
|
+
// applies it next tick and then decays it back toward baseline.
|
|
134
|
+
const t = (e.payload as Record<string,number>)['effortTarget']
|
|
135
|
+
if( typeof t === 'number' )
|
|
136
|
+
this._effortRequest = Math.min( EFFORT_MAX, Math.max( EFFORT_MIN, t ) )
|
|
137
|
+
break
|
|
138
|
+
}
|
|
139
|
+
case 'executive.prediction.formed': {
|
|
140
|
+
const p = e.payload as { predictedDomains: string[]; confidence: number }
|
|
141
|
+
if( p.predictedDomains.includes('attention') )
|
|
142
|
+
this._model.setPrecision( 'attention.usage', 1.0 + p.confidence * 0.5 )
|
|
143
|
+
break
|
|
144
|
+
}
|
|
145
|
+
default:
|
|
146
|
+
// senses.*.percept — use the event's pre-computed salience as an
|
|
147
|
+
// attention signal for the percept's source entity.
|
|
148
|
+
if( e.type.startsWith( 'senses.' ) ){
|
|
149
|
+
const percept = e.payload as { sourceEntityId?: string; salience?: number }
|
|
150
|
+
if( percept.sourceEntityId )
|
|
151
|
+
this._model.observe( `attention.entity.${percept.sourceEntityId}`, e.salience )
|
|
152
|
+
}
|
|
153
|
+
break
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
snapshot(): Record<string, unknown> {
|
|
158
|
+
return {
|
|
159
|
+
energyLevel: this._energyLevel,
|
|
160
|
+
sleepPressure: this._sleepPressure,
|
|
161
|
+
effort: this._effort,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async react(
|
|
166
|
+
delta: Duration,
|
|
167
|
+
_tick: Tick,
|
|
168
|
+
state: ReadonlySimulationState,
|
|
169
|
+
context: SimulationContext
|
|
170
|
+
): Promise<EngineResult> {
|
|
171
|
+
this._readConfigFromState( state )
|
|
172
|
+
|
|
173
|
+
const
|
|
174
|
+
events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
|
|
175
|
+
commands: StateCommands = { metrics: [] }
|
|
176
|
+
|
|
177
|
+
// Voluntary effort (Option C): an explicit focus/rest request snaps the
|
|
178
|
+
// set-point; otherwise it relaxes toward the homeostatic baseline so focus/
|
|
179
|
+
// rest fade unless renewed each cycle.
|
|
180
|
+
if( this._effortRequest != null ){
|
|
181
|
+
this._effort = this._effortRequest
|
|
182
|
+
this._effortRequest = null
|
|
183
|
+
}
|
|
184
|
+
else
|
|
185
|
+
this._effort += ( EFFORT_BASELINE - this._effort ) * EFFORT_RELAX
|
|
186
|
+
this._effort = Math.min( EFFORT_MAX, Math.max( EFFORT_MIN, this._effort ) )
|
|
187
|
+
|
|
188
|
+
// Apply modulations to capacity — derived from regulatory metrics directly
|
|
189
|
+
// (modulation events are not routed as metrics, so we compute factors here).
|
|
190
|
+
// The CEILING (what the body permits) is set by the vitals AND arousal:
|
|
191
|
+
// energy/sleep can collapse it to forced rest/shutdown; arousal (fight-or-
|
|
192
|
+
// flight) can lift it above baseline, or — at the extreme — collapse it via
|
|
193
|
+
// fragmentation. Effort then scales how much of that ceiling is engaged.
|
|
194
|
+
const
|
|
195
|
+
energyLevel = this._energyLevel,
|
|
196
|
+
sleepPressure = this._sleepPressure,
|
|
197
|
+
arousal = state.metrics.get( 'affect.arousal' ) ?? AROUSAL_REST,
|
|
198
|
+
energyFactor = energyLevel < 30 ? 0.3 + ( energyLevel / 30 ) * 0.7 : 1.0,
|
|
199
|
+
sleepFactor = sleepPressure > 40
|
|
200
|
+
? 1 - ( ( sleepPressure - 40 ) / 60 ) * 0.5
|
|
201
|
+
: 1.0,
|
|
202
|
+
arousalFactor = this._arousalFactor( arousal ),
|
|
203
|
+
ceiling = this._maxCapacity * energyFactor * sleepFactor * arousalFactor,
|
|
204
|
+
effectiveCapacity = ceiling * this._effort
|
|
205
|
+
|
|
206
|
+
// Get incoming salience signals (entities demanding attention)
|
|
207
|
+
const salienceMap = this._extractSalienceSignals( state )
|
|
208
|
+
|
|
209
|
+
// Decay existing focuses (attention fades if not reinforced)
|
|
210
|
+
this._decayFocuses( delta )
|
|
211
|
+
|
|
212
|
+
// Allocate attention: balance new salience vs. existing focus inertia
|
|
213
|
+
this._allocate( salienceMap, effectiveCapacity )
|
|
214
|
+
|
|
215
|
+
// Compute attention metrics
|
|
216
|
+
const
|
|
217
|
+
totalAllocated = this._activeFocus.reduce( ( s, f ) => s + f.depth * this._costPerFocus, 0 ),
|
|
218
|
+
attentionUsage = effectiveCapacity > 0 ? totalAllocated / effectiveCapacity : 0,
|
|
219
|
+
focusCount = this._activeFocus.length,
|
|
220
|
+
deepestFocus = this._activeFocus.reduce( ( best, f ) => f.depth > best.depth ? f : best, { depth: 0 } as AttentionFocus )
|
|
221
|
+
|
|
222
|
+
const
|
|
223
|
+
freeCapacity = Math.max( 0, effectiveCapacity - totalAllocated ),
|
|
224
|
+
// Normalized 0–1 spare-attention fraction vs the BASELINE capacity — the
|
|
225
|
+
// signal the FacetSupervisor budgets on. Normalizing by baseline (not the
|
|
226
|
+
// already-modulated effectiveCapacity) is what makes vital-reduced capacity
|
|
227
|
+
// (low energy / high sleep pressure) actually shrink the facet budget,
|
|
228
|
+
// instead of the raw 0–100 capacity inflating it ~100× so it never binds.
|
|
229
|
+
freeFraction = this._maxCapacity > 0 ? Math.max( 0, freeCapacity / this._maxCapacity ) : 0,
|
|
230
|
+
isIdle = attentionUsage < 0.2 ? 1 : 0
|
|
231
|
+
|
|
232
|
+
commands.metrics!.push(
|
|
233
|
+
[ 'attention.usage', attentionUsage ],
|
|
234
|
+
[ 'attention.focus_count', focusCount ],
|
|
235
|
+
[ 'attention.capacity', effectiveCapacity ],
|
|
236
|
+
[ 'attention.free_capacity', freeCapacity ],
|
|
237
|
+
[ 'attention.free_fraction', freeFraction ],
|
|
238
|
+
[ 'attention.effort', this._effort ],
|
|
239
|
+
[ 'attention.arousal_factor', arousalFactor ],
|
|
240
|
+
[ 'attention.idle', isIdle ],
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
// Collect all stale attention.focus entities to replace them cleanly
|
|
244
|
+
const staleIds: string[] = []
|
|
245
|
+
for( const [ id, entity ] of state.entities ){
|
|
246
|
+
if( entity.type === 'attention.focus')
|
|
247
|
+
staleIds.push( id )
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Persist current attention foci as entities (for other engines to read)
|
|
251
|
+
if( this._activeFocus.length > 0 ){
|
|
252
|
+
commands.set = this._activeFocus.map( f => ({
|
|
253
|
+
id: `attention-focus-${f.entityId}`,
|
|
254
|
+
type: 'attention.focus',
|
|
255
|
+
metadata: {
|
|
256
|
+
entityId: f.entityId,
|
|
257
|
+
depth: f.depth,
|
|
258
|
+
duration: f.duration,
|
|
259
|
+
},
|
|
260
|
+
}))
|
|
261
|
+
|
|
262
|
+
commands.delete = staleIds.filter( id => !this._activeFocus.some( f => `attention-focus-${f.entityId}` === id ) )
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
commands.set = [{
|
|
266
|
+
id: 'attention-focus-none',
|
|
267
|
+
type: 'attention.focus',
|
|
268
|
+
metadata: { entityId: null, depth: 0, duration: 0 },
|
|
269
|
+
}]
|
|
270
|
+
commands.delete = staleIds.filter( id => id !== 'attention-focus-none')
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Event when attention shifts significantly
|
|
274
|
+
if( focusCount === 0 && this._activeFocus.length > 0 )
|
|
275
|
+
events.push({
|
|
276
|
+
type: 'attention.engaged',
|
|
277
|
+
source: this.name,
|
|
278
|
+
payload: { focus: deepestFocus.entityId, depth: deepestFocus.depth },
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
if( focusCount > 2 )
|
|
282
|
+
events.push({
|
|
283
|
+
type: 'attention.fragmented',
|
|
284
|
+
source: this.name,
|
|
285
|
+
payload: { focusCount, usage: attentionUsage },
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
// Phase C: publish cognitive event
|
|
290
|
+
const _bus = this._bus
|
|
291
|
+
if( _bus && attentionUsage > 0.8 )
|
|
292
|
+
_bus.publish({ type: 'working_memory.capacity.reached', version: 1, sourceEngine: this.name, salience: Math.min(1, attentionUsage), payload: { usage: attentionUsage, focusCount } })
|
|
293
|
+
// Phase D + F: rich state-change event — gated by prediction error on EITHER
|
|
294
|
+
// usage OR free fraction. Gating on free fraction too is what lets a voluntary
|
|
295
|
+
// effort change (focus/rest) reach the FacetSupervisor even when perceptual
|
|
296
|
+
// load — and thus usage — is steady; otherwise the budget would not move in a
|
|
297
|
+
// quiet mind.
|
|
298
|
+
if( _bus ){
|
|
299
|
+
const usageErr = this._model.observe( 'attention.usage', attentionUsage )
|
|
300
|
+
const freeErr = this._model.observe( 'attention.free_fraction', freeFraction )
|
|
301
|
+
if( !usageErr.gated || !freeErr.gated )
|
|
302
|
+
_bus.publish({ type: 'attention.state.changed', version: 1, sourceEngine: this.name, salience: Math.max( usageErr.salience, freeErr.salience ), payload: { usage: attentionUsage, focusCount, capacity: effectiveCapacity, freeFraction } })
|
|
303
|
+
}
|
|
304
|
+
return { events: events.length > 0 ? events : undefined, commands }
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ── Config hot-reload ────────────────────────────────────
|
|
308
|
+
|
|
309
|
+
private _readConfigFromState( state: ReadonlySimulationState ): void {
|
|
310
|
+
// Effective config = base engine-config-attention ⊕ persona-prior (single-source).
|
|
311
|
+
// The persona-consolidator lowers shiftInertia on belief-formation bias (edge 8)
|
|
312
|
+
// → attention shifts more readily, loosening the fixation that feeds the bias.
|
|
313
|
+
const p = readEffectiveParams( state, 'engine-config-attention' )
|
|
314
|
+
if( p.maxCapacity != null ) this._maxCapacity = p.maxCapacity
|
|
315
|
+
if( p.costPerFocus != null ) this._costPerFocus = p.costPerFocus
|
|
316
|
+
if( p.maxFoci != null ) this._maxFoci = p.maxFoci
|
|
317
|
+
if( p.shiftInertia != null ) this._shiftInertia = p.shiftInertia
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ── Internal ─────────────────────────────────────────────
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Yerkes–Dodson arousal → ceiling factor. Calm (≤ AROUSAL_REST) → 1.0 (no
|
|
324
|
+
* boost); rises linearly to 1 + AROUSAL_GAIN at AROUSAL_PEAK (mobilization);
|
|
325
|
+
* then declines below 1 toward 1 − AROUSAL_OVERLOAD at maximum arousal
|
|
326
|
+
* (fragmentation / tunnel vision — capacity the mind cannot will back).
|
|
327
|
+
*/
|
|
328
|
+
private _arousalFactor( arousal: number ): number {
|
|
329
|
+
const a = Math.min( 1, Math.max( 0, arousal ) )
|
|
330
|
+
if( a <= AROUSAL_REST ) return 1.0
|
|
331
|
+
if( a <= AROUSAL_PEAK )
|
|
332
|
+
return 1 + AROUSAL_GAIN * ( ( a - AROUSAL_REST ) / ( AROUSAL_PEAK - AROUSAL_REST ) )
|
|
333
|
+
return ( 1 + AROUSAL_GAIN ) - ( AROUSAL_GAIN + AROUSAL_OVERLOAD ) * ( ( a - AROUSAL_PEAK ) / ( 1 - AROUSAL_PEAK ) )
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Extract salience signals from perceptual entities.
|
|
338
|
+
* Entities with higher salience demand more attention.
|
|
339
|
+
*/
|
|
340
|
+
private _extractSalienceSignals( state: ReadonlySimulationState ): Map<string, number> {
|
|
341
|
+
const salienceMap = new Map<string, number>()
|
|
342
|
+
|
|
343
|
+
for( const [ id, entity ] of state.entities ){
|
|
344
|
+
if( entity.type === 'percept' && entity.metadata?.salience !== undefined )
|
|
345
|
+
salienceMap.set( id, entity.metadata.salience as number )
|
|
346
|
+
|
|
347
|
+
// Also check for explicit attention demands (urgent events)
|
|
348
|
+
if( entity.type === 'attention.demand')
|
|
349
|
+
salienceMap.set( id, ( entity.metadata?.urgency as number ) ?? 0.5 )
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return salienceMap
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Decay existing attention focuses over time.
|
|
357
|
+
* Sustained attention on a single focus resists decay (vigilance effect).
|
|
358
|
+
*/
|
|
359
|
+
private _decayFocuses( delta: Duration ): void {
|
|
360
|
+
const decayRate = 0.01 * ( delta / 1000 )
|
|
361
|
+
|
|
362
|
+
this._activeFocus = this._activeFocus
|
|
363
|
+
.map( f => ({
|
|
364
|
+
...f,
|
|
365
|
+
// Sustained focus decays slower (inertia)
|
|
366
|
+
depth: Math.max( 0, f.depth - decayRate * ( 1 - this._shiftInertia * Math.min( 1, f.duration / 100 ) ) ),
|
|
367
|
+
duration: f.duration + 1,
|
|
368
|
+
}))
|
|
369
|
+
.filter( f => f.depth > 0.01 ) // Prune negligible focuses
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Allocate attention budget across competing salience signals.
|
|
374
|
+
* Balances capturing new salient stimuli against maintaining existing focus.
|
|
375
|
+
*/
|
|
376
|
+
private _allocate( salienceMap: Map<string, number>, capacity: number ): void {
|
|
377
|
+
if( salienceMap.size === 0 ) return
|
|
378
|
+
|
|
379
|
+
// Sort by salience (highest first)
|
|
380
|
+
const sorted = Array.from( salienceMap.entries() )
|
|
381
|
+
.sort( ( a, b ) => b[1] - a[1] )
|
|
382
|
+
|
|
383
|
+
// Allocate to top-K salient entities (respecting max foci)
|
|
384
|
+
const allocated: AttentionFocus[] = []
|
|
385
|
+
let remainingCapacity = capacity
|
|
386
|
+
|
|
387
|
+
for( const [ entityId, salience ] of sorted ){
|
|
388
|
+
if( allocated.length >= this._maxFoci ) break
|
|
389
|
+
|
|
390
|
+
// Check if this entity already has attention
|
|
391
|
+
const existing = this._activeFocus.find( f => f.entityId === entityId )
|
|
392
|
+
|
|
393
|
+
if( existing ){
|
|
394
|
+
// Maintain existing focus — reinforce with salience
|
|
395
|
+
const reinforcedDepth = Math.min( 1, existing.depth + salience * 0.2 )
|
|
396
|
+
const cost = reinforcedDepth * this._costPerFocus
|
|
397
|
+
|
|
398
|
+
if( cost <= remainingCapacity ){
|
|
399
|
+
allocated.push({ ...existing, depth: reinforcedDepth })
|
|
400
|
+
remainingCapacity -= cost
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
// New focus — cost of shifting attention
|
|
405
|
+
const shiftCost = this._costPerFocus * ( 1 + this._shiftInertia )
|
|
406
|
+
const initialDepth = Math.min( 0.5, salience )
|
|
407
|
+
|
|
408
|
+
if( shiftCost + initialDepth * this._costPerFocus <= remainingCapacity ){
|
|
409
|
+
allocated.push({
|
|
410
|
+
entityId,
|
|
411
|
+
depth: initialDepth,
|
|
412
|
+
duration: 0,
|
|
413
|
+
salienceAtCapture: salience,
|
|
414
|
+
})
|
|
415
|
+
remainingCapacity -= shiftCost + initialDepth * this._costPerFocus
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
this._activeFocus = allocated
|
|
421
|
+
}
|
|
422
|
+
}
|