@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,246 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/agency/schemas/repertoire.ts — the store of what the Will can do
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// The repertoire holds two things: the schema TEMPLATES the Will can enact
|
|
6
|
+
// (innate floor + learned composites) and the per-schema LearnedSkill stats that
|
|
7
|
+
// accrete from experience. It is the competence layer — the part of a Will that
|
|
8
|
+
// makes it *act like itself*, and the part the PMA must carry across re-embodiment
|
|
9
|
+
// (Phase 6). It is an in-memory manager (like GoalManager); the ReafferenceEngine
|
|
10
|
+
// mirrors per-schema skills into `agency.skill` state entities AND learned
|
|
11
|
+
// composite templates into `agency.schema` entities, so both survive a
|
|
12
|
+
// snapshot/restore. The skills carry habit/value; the composites carry the
|
|
13
|
+
// invented multi-step *programs* the executor must resolve to run a macro.
|
|
14
|
+
// restoreComposites() rehydrates those templates after a restore reconstructs
|
|
15
|
+
// the repertoire innate-only — analogous to GoalManager rehydrating `goal`s.
|
|
16
|
+
//
|
|
17
|
+
// Learning rules (the proceduralization curve):
|
|
18
|
+
// valueEstimate — EMA toward observed outcome quality
|
|
19
|
+
// habitStrength — rises only on CONFIDENT success (low prediction error);
|
|
20
|
+
// a surprising or failed outcome erodes it. This is the
|
|
21
|
+
// instrumental→habitual gradient: an action becomes a habit
|
|
22
|
+
// by working reliably AND predictably, not merely often.
|
|
23
|
+
// avgPredictionError — EMA of |predicted − actual|; the destabilizer
|
|
24
|
+
// paramPriors — last known-good parameters become defaults
|
|
25
|
+
// ─────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
import type { MotorSchema, LearnedSkill } from '#agency/types'
|
|
28
|
+
import type { EntityInput, ReadonlySimulationState } from '#core/types'
|
|
29
|
+
import { INNATE_SCHEMAS } from '#agency/schemas/innate'
|
|
30
|
+
|
|
31
|
+
const VALUE_ALPHA = 0.2 // value EMA rate
|
|
32
|
+
const ERROR_BETA = 0.2 // prediction-error EMA rate
|
|
33
|
+
const HABIT_GAIN = 0.12 // habit growth on confident success
|
|
34
|
+
const HABIT_FAIL_DECAY = 0.10 // habit erosion on surprise / failure
|
|
35
|
+
const CONFIDENT_ERROR = 0.30 // prediction error below this counts as "predictable"
|
|
36
|
+
const PROC_THRESHOLD = 0.60 // habit at/above this is "proceduralized"
|
|
37
|
+
|
|
38
|
+
const IDLE_TICKS = 200 // ticks of disuse before forgetting starts
|
|
39
|
+
const DECAY_RATE = 0.02 // habit lost per decay application
|
|
40
|
+
const DROP_HABIT = 0.05 // below this (and learned) the skill is forgotten
|
|
41
|
+
|
|
42
|
+
export interface OutcomeObservation {
|
|
43
|
+
schema: string
|
|
44
|
+
success: boolean
|
|
45
|
+
outcomeQuality: number
|
|
46
|
+
predictedReward: number
|
|
47
|
+
/** Last-known-good parameters to fold into priors (only kept on success). */
|
|
48
|
+
params?: Record<string, unknown>
|
|
49
|
+
tick: number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class SchemaRepertoire {
|
|
53
|
+
private _templates = new Map<string, MotorSchema>()
|
|
54
|
+
private _skills = new Map<string, LearnedSkill>()
|
|
55
|
+
/** Tracks which templates were learned at runtime (vs innate) so decay can forget them. */
|
|
56
|
+
private _learned = new Set<string>()
|
|
57
|
+
|
|
58
|
+
constructor( seed: MotorSchema[] = INNATE_SCHEMAS ){
|
|
59
|
+
for( const s of seed ) this._templates.set( s.id, s )
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── templates ─────────────────────────────────────────────────
|
|
63
|
+
schemas(): MotorSchema[] { return [ ...this._templates.values() ] }
|
|
64
|
+
getSchema( id: string ): MotorSchema | undefined { return this._templates.get( id ) }
|
|
65
|
+
|
|
66
|
+
/** Register a learned composite skill template (starts with no habit). */
|
|
67
|
+
registerComposite( schema: MotorSchema ): void {
|
|
68
|
+
this._templates.set( schema.id, schema )
|
|
69
|
+
this._learned.add( schema.id )
|
|
70
|
+
if( !this._skills.has( schema.id ) )
|
|
71
|
+
this._skills.set( schema.id, freshSkill( schema.id, 0.4, 0 ) )
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── skills ────────────────────────────────────────────────────
|
|
75
|
+
skills(): ReadonlyMap<string, LearnedSkill> { return this._skills }
|
|
76
|
+
getSkill( id: string ): LearnedSkill | undefined { return this._skills.get( id ) }
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Fold one outcome into the schema's learned skill. Returns the updated skill
|
|
80
|
+
* and whether it just crossed the proceduralization threshold this update.
|
|
81
|
+
*/
|
|
82
|
+
recordOutcome( o: OutcomeObservation ): { skill: LearnedSkill; proceduralized: boolean } {
|
|
83
|
+
const prior = this._skills.get( o.schema ) ?? freshSkill( o.schema, o.predictedReward, o.tick )
|
|
84
|
+
const wasProceduralized = prior.habitStrength >= PROC_THRESHOLD
|
|
85
|
+
|
|
86
|
+
const error = clamp01( Math.abs( o.predictedReward - o.outcomeQuality ) )
|
|
87
|
+
const confident = o.success && error < CONFIDENT_ERROR
|
|
88
|
+
|
|
89
|
+
const habitStrength = clamp01(
|
|
90
|
+
confident
|
|
91
|
+
? prior.habitStrength + HABIT_GAIN * ( 1 - prior.habitStrength )
|
|
92
|
+
: prior.habitStrength - HABIT_FAIL_DECAY * prior.habitStrength,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
const skill: LearnedSkill = {
|
|
96
|
+
schema: o.schema,
|
|
97
|
+
habitStrength,
|
|
98
|
+
valueEstimate: clamp01( prior.valueEstimate + VALUE_ALPHA * ( o.outcomeQuality - prior.valueEstimate ) ),
|
|
99
|
+
paramPriors: o.success && o.params ? { ...prior.paramPriors, ...o.params } : prior.paramPriors,
|
|
100
|
+
enactments: prior.enactments + 1,
|
|
101
|
+
successes: prior.successes + ( o.success ? 1 : 0 ),
|
|
102
|
+
avgPredictionError: prior.avgPredictionError + ERROR_BETA * ( error - prior.avgPredictionError ),
|
|
103
|
+
lastEnactedTick: o.tick,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this._skills.set( o.schema, skill )
|
|
107
|
+
return { skill, proceduralized: !wasProceduralized && habitStrength >= PROC_THRESHOLD }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Forgetting curve over the competence layer. Skills unused for IDLE_TICKS
|
|
112
|
+
* lose habit; learned composites that fall below DROP_HABIT are dropped
|
|
113
|
+
* entirely (template + skill). Returns the schema ids that were forgotten.
|
|
114
|
+
*/
|
|
115
|
+
decay( tick: number ): string[] {
|
|
116
|
+
const dropped: string[] = []
|
|
117
|
+
for( const [ id, skill ] of this._skills ){
|
|
118
|
+
if( tick - skill.lastEnactedTick <= IDLE_TICKS ) continue
|
|
119
|
+
|
|
120
|
+
const habitStrength = clamp01( skill.habitStrength - DECAY_RATE )
|
|
121
|
+
if( this._learned.has( id ) && habitStrength < DROP_HABIT ){
|
|
122
|
+
this._skills.delete( id )
|
|
123
|
+
this._templates.delete( id )
|
|
124
|
+
this._learned.delete( id )
|
|
125
|
+
dropped.push( id )
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
this._skills.set( id, { ...skill, habitStrength } )
|
|
129
|
+
}
|
|
130
|
+
return dropped
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ── PMA portability (Phase 6 reads these) ─────────────────────
|
|
134
|
+
/** Learned composite templates + all skills above a confidence floor. */
|
|
135
|
+
export( minHabit = 0.0 ): { composites: MotorSchema[]; skills: LearnedSkill[] } {
|
|
136
|
+
return {
|
|
137
|
+
composites: [ ...this._learned ].map( id => this._templates.get( id ) ).filter( ( s ): s is MotorSchema => !!s ),
|
|
138
|
+
skills: [ ...this._skills.values() ].filter( s => s.habitStrength >= minHabit ),
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
import( data: { composites?: MotorSchema[]; skills?: LearnedSkill[] } ): void {
|
|
143
|
+
for( const c of data.composites ?? [] ){ this._templates.set( c.id, c ); this._learned.add( c.id ) }
|
|
144
|
+
for( const s of data.skills ?? [] ) this._skills.set( s.schema, s )
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── snapshot / replay portability (deterministic state path) ──
|
|
148
|
+
// Skills mirror to `agency.skill`; the invented composite *definitions* must
|
|
149
|
+
// travel too, or a snapshot/restore brings back an `agency.skill` whose schema
|
|
150
|
+
// is gone and the MotorSchemaExecutor can't expand it (it misroutes the intent
|
|
151
|
+
// to the host and times out). compositeEntities() is the write side (the
|
|
152
|
+
// ReafferenceEngine pushes these each tick); restoreComposites() is the read
|
|
153
|
+
// side, called from the first agency engine after a restore.
|
|
154
|
+
|
|
155
|
+
/** Learned composite templates encoded as `agency.schema` state entities. */
|
|
156
|
+
compositeEntities(): EntityInput[] {
|
|
157
|
+
const out: EntityInput[] = []
|
|
158
|
+
for( const id of this._learned ){
|
|
159
|
+
const s = this._templates.get( id )
|
|
160
|
+
if( s && s.kind === 'composite' ) out.push( schemaEntity( s ) )
|
|
161
|
+
}
|
|
162
|
+
return out
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Re-register learned composites from `agency.schema` state entities after a
|
|
167
|
+
* restore rebuilt the repertoire innate-only. Idempotent — skips composites
|
|
168
|
+
* already present and never seeds a fresh skill (the restored `agency.skill`
|
|
169
|
+
* entity and future outcomes own the habit; seeding here would reset it).
|
|
170
|
+
* Mirrors GoalManager._syncFromStateGoals.
|
|
171
|
+
*/
|
|
172
|
+
restoreComposites( entities: ReadonlySimulationState['entities'] ): void {
|
|
173
|
+
for( const e of entities.values() ){
|
|
174
|
+
if( e.type !== SCHEMA_ENTITY_TYPE ) continue
|
|
175
|
+
const s = readSchema( e.metadata as Record<string, unknown> | undefined )
|
|
176
|
+
if( !s || this._templates.has( s.id ) ) continue
|
|
177
|
+
this._templates.set( s.id, s )
|
|
178
|
+
this._learned.add( s.id )
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ─── helpers ─────────────────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
function freshSkill( schema: string, value: number, tick: number ): LearnedSkill {
|
|
186
|
+
return {
|
|
187
|
+
schema,
|
|
188
|
+
habitStrength: 0,
|
|
189
|
+
valueEstimate: clamp01( value ),
|
|
190
|
+
paramPriors: {},
|
|
191
|
+
enactments: 0,
|
|
192
|
+
successes: 0,
|
|
193
|
+
avgPredictionError: 0,
|
|
194
|
+
lastEnactedTick: tick,
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function clamp01( n: number ): number {
|
|
199
|
+
return n < 0 ? 0 : n > 1 ? 1 : n
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ─── composite ⇄ state-entity codec (snapshot/replay) ────────────────────────
|
|
203
|
+
|
|
204
|
+
/** State-entity type for a mirrored composite template. */
|
|
205
|
+
export const SCHEMA_ENTITY_TYPE = 'agency.schema'
|
|
206
|
+
|
|
207
|
+
/** Stable entity id for a schema's mirror (no timestamp → idempotent re-writes). */
|
|
208
|
+
export function schemaEntityId( schemaId: string ): string { return `agency-schema-${ schemaId }` }
|
|
209
|
+
|
|
210
|
+
/** Encode a composite template as a state entity (round-trips via readSchema). */
|
|
211
|
+
function schemaEntity( s: MotorSchema ): EntityInput {
|
|
212
|
+
return {
|
|
213
|
+
id: schemaEntityId( s.id ),
|
|
214
|
+
type: SCHEMA_ENTITY_TYPE,
|
|
215
|
+
metadata: {
|
|
216
|
+
id: s.id,
|
|
217
|
+
kind: s.kind,
|
|
218
|
+
source: s.source,
|
|
219
|
+
cost: s.cost,
|
|
220
|
+
binds: s.binds,
|
|
221
|
+
preconditions: s.preconditions,
|
|
222
|
+
composedOf: s.composedOf,
|
|
223
|
+
baseValence: s.baseValence,
|
|
224
|
+
tags: s.tags,
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Decode a mirrored schema entity back into a MotorSchema (undefined if malformed). */
|
|
230
|
+
function readSchema( m: Record<string, unknown> | undefined ): MotorSchema | undefined {
|
|
231
|
+
const meta = m ?? {}
|
|
232
|
+
const id = typeof meta['id'] === 'string' ? meta['id'] as string : undefined
|
|
233
|
+
const kind = meta['kind']
|
|
234
|
+
if( !id || ( kind !== 'composite' && kind !== 'primitive' ) ) return undefined
|
|
235
|
+
return {
|
|
236
|
+
id,
|
|
237
|
+
kind,
|
|
238
|
+
source: ( meta['source'] as MotorSchema['source'] ) ?? 'repertoire',
|
|
239
|
+
cost: typeof meta['cost'] === 'number' ? meta['cost'] as number : 0,
|
|
240
|
+
binds: ( meta['binds'] as MotorSchema['binds'] ) ?? 'none',
|
|
241
|
+
preconditions: meta['preconditions'] as MotorSchema['preconditions'],
|
|
242
|
+
composedOf: Array.isArray( meta['composedOf'] ) ? meta['composedOf'] as string[] : undefined,
|
|
243
|
+
baseValence: typeof meta['baseValence'] === 'number' ? meta['baseValence'] as number : undefined,
|
|
244
|
+
tags: Array.isArray( meta['tags'] ) ? meta['tags'] as string[] : undefined,
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/agency/selection.scoring.ts — the competition's activation function
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// A mind does not pick a tool off a menu; affordances *compete*, and the
|
|
6
|
+
// competition is biased by what the body needs, what it wants, what it has
|
|
7
|
+
// learned, and what it fears. This is the basal-ganglia Go/NoGo arithmetic,
|
|
8
|
+
// kept pure so it is trivially testable and replay-deterministic.
|
|
9
|
+
//
|
|
10
|
+
// activation = goalRelevance + expectedReward + novelty + driveUrgency
|
|
11
|
+
// + habitStrength·W (additive habit bonus)
|
|
12
|
+
// + planBias·W (top-down prior)
|
|
13
|
+
// − cost − inhibition − risk
|
|
14
|
+
//
|
|
15
|
+
// The habit term is additive (per design decision): an overlearned schema wins
|
|
16
|
+
// cheaply AND lowers its own deliberation threshold (see selector.ts), which is
|
|
17
|
+
// how the instrumental→habitual gradient cashes out as falling LLM spend.
|
|
18
|
+
// ─────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
import type { Affordance } from '#agency/types'
|
|
21
|
+
import type { ReadonlySimulationState } from '#core/types'
|
|
22
|
+
|
|
23
|
+
/** Bias signals assembled from live state + the cognitive bus each tick. */
|
|
24
|
+
export interface BiasContext {
|
|
25
|
+
/** Entity ids that an active goal is directed at. */
|
|
26
|
+
goalTargets: ReadonlySet<string>
|
|
27
|
+
/** Priority of the highest-priority active goal (0..1). */
|
|
28
|
+
maxGoalPriority: number
|
|
29
|
+
/** Homeostatic drive pressures, each 0..1 (0 = satisfied). */
|
|
30
|
+
drives: { energy: number; sleep: number; stress: number; social: number }
|
|
31
|
+
/** Ambient threat 0..1. */
|
|
32
|
+
threat: number
|
|
33
|
+
/** Global NoGo pressure 0..1 from the InhibitionController. */
|
|
34
|
+
inhibition: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ScoreWeights {
|
|
38
|
+
goal: number
|
|
39
|
+
reward: number
|
|
40
|
+
novelty: number
|
|
41
|
+
drive: number
|
|
42
|
+
habit: number
|
|
43
|
+
/** Top-down planning prior — how strongly an executing plan biases the competition. */
|
|
44
|
+
plan: number
|
|
45
|
+
cost: number
|
|
46
|
+
inhib: number
|
|
47
|
+
risk: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const DEFAULT_WEIGHTS: ScoreWeights = {
|
|
51
|
+
goal: 0.30,
|
|
52
|
+
reward: 0.25,
|
|
53
|
+
novelty: 0.10,
|
|
54
|
+
drive: 0.25,
|
|
55
|
+
habit: 0.20,
|
|
56
|
+
plan: 0.30,
|
|
57
|
+
cost: 0.20,
|
|
58
|
+
inhib: 0.30,
|
|
59
|
+
risk: 0.20,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Decision temperature for the entropy softmax. Activations live in a narrow
|
|
64
|
+
* band (~0..1), so a low temperature is needed for a real activation gap to read
|
|
65
|
+
* as a confident winner rather than near-uniform noise. Lower = sharper / more
|
|
66
|
+
* decisive; this is the knob a future arousal signal would modulate.
|
|
67
|
+
*/
|
|
68
|
+
export const DEFAULT_TEMPERATURE = 0.15
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Active-goal targets → the highest priority directed at each entity. Relevance is
|
|
72
|
+
* structural (target match), never lexical: a goal links to an entity EITHER via
|
|
73
|
+
* its `targetEntityId`/`requestingEntityId` metadata OR via a `keid:<id>` tag —
|
|
74
|
+
* the convention KnownEntityTracker uses for the curiosity goals it forms. Shared
|
|
75
|
+
* by the synthesizer (to lift goal-relevant affordances at the attention cap) and
|
|
76
|
+
* the selector (to score goalRelevance), so both stages see the same links.
|
|
77
|
+
*/
|
|
78
|
+
export function collectGoalTargets( state: ReadonlySimulationState ): Map<string, number> {
|
|
79
|
+
const targets = new Map<string, number>()
|
|
80
|
+
for( const e of state.entities.values() ){
|
|
81
|
+
if( e.type !== 'goal' ) continue
|
|
82
|
+
const m = e.metadata as Record<string, unknown> | undefined
|
|
83
|
+
const status = typeof m?.['status'] === 'string' ? m['status'] : undefined
|
|
84
|
+
if( status !== 'active' && status !== 'in_progress' ) continue
|
|
85
|
+
const priority = typeof m?.['priority'] === 'number' ? m['priority'] as number : 0
|
|
86
|
+
const link = ( id: unknown ): void => {
|
|
87
|
+
if( typeof id !== 'string' || !id ) return
|
|
88
|
+
targets.set( id, Math.max( targets.get( id ) ?? 0, priority ) )
|
|
89
|
+
}
|
|
90
|
+
link( m?.['targetEntityId'] )
|
|
91
|
+
link( m?.['requestingEntityId'] )
|
|
92
|
+
if( Array.isArray( m?.['tags'] ) )
|
|
93
|
+
for( const t of m['tags'] as unknown[] )
|
|
94
|
+
if( typeof t === 'string' && t.startsWith( 'keid:' ) ) link( t.slice( 5 ) )
|
|
95
|
+
}
|
|
96
|
+
return targets
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Goal relevance: structural (target match), not lexical. 0 when the affordance has no goal-linked target. */
|
|
100
|
+
export function goalRelevance( a: Affordance, bias: BiasContext ): number {
|
|
101
|
+
if( a.targetEntityId && bias.goalTargets.has( a.targetEntityId ) )
|
|
102
|
+
return bias.maxGoalPriority
|
|
103
|
+
return 0
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Homeostatic urgency routed to the affordance by its schema tags. */
|
|
107
|
+
export function driveUrgency( a: Affordance, bias: BiasContext ): number {
|
|
108
|
+
const t = a.tags
|
|
109
|
+
if( t.includes( 'regulatory' ) || t.includes( 'self-care' ) || t.includes( 'rest' ) )
|
|
110
|
+
return Math.max( bias.drives.energy, bias.drives.sleep )
|
|
111
|
+
if( t.includes( 'social' ) )
|
|
112
|
+
return bias.drives.social
|
|
113
|
+
if( t.includes( 'self-protection' ) )
|
|
114
|
+
return Math.max( bias.drives.stress, bias.threat )
|
|
115
|
+
return 0
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Curiosity pull toward the unpracticed — proxied by the inverse of habit until a novelty engine feeds in. */
|
|
119
|
+
export function novelty( a: Affordance ): number {
|
|
120
|
+
return clamp01( 1 - a.habitStrength )
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Risk from anticipated negative affect and ambient threat. */
|
|
124
|
+
export function risk( a: Affordance, bias: BiasContext ): number {
|
|
125
|
+
return clamp01( Math.max( 0, -a.expectedValence ) * 0.5 + bias.threat * 0.5 )
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Full competition activation for one affordance. */
|
|
129
|
+
export function scoreAffordance(
|
|
130
|
+
a: Affordance,
|
|
131
|
+
bias: BiasContext,
|
|
132
|
+
w: ScoreWeights = DEFAULT_WEIGHTS,
|
|
133
|
+
): number {
|
|
134
|
+
return (
|
|
135
|
+
w.goal * goalRelevance( a, bias )
|
|
136
|
+
+ w.reward * a.expectedReward
|
|
137
|
+
+ w.novelty * novelty( a )
|
|
138
|
+
+ w.drive * driveUrgency( a, bias )
|
|
139
|
+
+ w.habit * a.habitStrength
|
|
140
|
+
+ w.plan * ( a.planBias ?? 0 )
|
|
141
|
+
- w.cost * a.cost
|
|
142
|
+
- w.inhib * bias.inhibition
|
|
143
|
+
- w.risk * risk( a, bias )
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Stakes of committing to `winner` — how consequential the leading choice is.
|
|
149
|
+
* High stakes recruit deliberation even when the field is not ambiguous.
|
|
150
|
+
*/
|
|
151
|
+
export function stakes( winner: Affordance, bias: BiasContext ): number {
|
|
152
|
+
// Consequence, not unfamiliarity: ambient threat and the affective charge of
|
|
153
|
+
// the leading choice dominate. Novelty contributes only weakly — a brand-new
|
|
154
|
+
// schema is novel (1) but that alone should not force the LLM on every action
|
|
155
|
+
// before any habit has formed; curiosity already lives in the activation.
|
|
156
|
+
return clamp01( Math.max(
|
|
157
|
+
bias.threat,
|
|
158
|
+
Math.abs( winner.expectedValence ),
|
|
159
|
+
novelty( winner ) * 0.5,
|
|
160
|
+
) )
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Normalized competition entropy (0..1). 0 = a single dominant winner; 1 = a
|
|
165
|
+
* flat field with no clear choice. Softmax over activations, Shannon-normalized
|
|
166
|
+
* by log(n). This is the ambiguity signal that recruits the executive.
|
|
167
|
+
*/
|
|
168
|
+
export function competitionEntropy(
|
|
169
|
+
activations: number[],
|
|
170
|
+
temperature: number = DEFAULT_TEMPERATURE,
|
|
171
|
+
): number {
|
|
172
|
+
const n = activations.length
|
|
173
|
+
if( n <= 1 ) return 0
|
|
174
|
+
|
|
175
|
+
const t = temperature > 0 ? temperature : DEFAULT_TEMPERATURE
|
|
176
|
+
const max = Math.max( ...activations )
|
|
177
|
+
const exps = activations.map( a => Math.exp( ( a - max ) / t ) )
|
|
178
|
+
const sum = exps.reduce( ( s, e ) => s + e, 0 )
|
|
179
|
+
if( sum === 0 ) return 1
|
|
180
|
+
|
|
181
|
+
let h = 0
|
|
182
|
+
for( const e of exps ){
|
|
183
|
+
const p = e / sum
|
|
184
|
+
if( p > 0 ) h -= p * Math.log( p )
|
|
185
|
+
}
|
|
186
|
+
return clamp01( h / Math.log( n ) )
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function clamp01( n: number ): number {
|
|
190
|
+
return n < 0 ? 0 : n > 1 ? 1 : n
|
|
191
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/agency/types.ts — core contracts for the agency pipeline
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// The vocabulary of a mind that *finds* its actions in the situation rather
|
|
6
|
+
// than looking them up in a catalog. An Affordance is a possibility the current
|
|
7
|
+
// state affords; a MotorSchema is the control program that would enact it; a
|
|
8
|
+
// LearnedSkill is what the Will has accreted about running that schema; a
|
|
9
|
+
// SchemaOutcome is the reafference that teaches it. Nothing here owns behaviour —
|
|
10
|
+
// these are the stable nouns every agency engine references.
|
|
11
|
+
//
|
|
12
|
+
// See AGENCY_PIPELINE_TODO.md for the end-to-end design.
|
|
13
|
+
// ─────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
/** Where an affordance came from — its evoking origin in the perception field. */
|
|
16
|
+
export type AffordanceSource =
|
|
17
|
+
| 'innate' // always-on floor; needs no object (orient, rest, reflect, wait)
|
|
18
|
+
| 'perceptual' // evoked by a salient percept (inspect this, attend to that)
|
|
19
|
+
| 'social' // evoked by a known sentient entity (reach out to them)
|
|
20
|
+
| 'repertoire' // a learned composite skill whose trigger matches now
|
|
21
|
+
| 'ideomotor' // pre-activated by the executive imagining the outcome
|
|
22
|
+
| 'plan' // pre-activated by an executing plan's current frontier step (top-down prior)
|
|
23
|
+
| 'external' // a host-owned domain effector (move, attack, trade …) dispatched to the world
|
|
24
|
+
|
|
25
|
+
/** A body-state gate. If any precondition fails, the affordance is unavailable. */
|
|
26
|
+
export interface SchemaPrecondition {
|
|
27
|
+
metric: string
|
|
28
|
+
op: 'gt' | 'lt' | 'gte' | 'lte' | 'eq'
|
|
29
|
+
value: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** What kind of target a schema binds when it becomes an affordance. */
|
|
33
|
+
export type SchemaBinding = 'none' | 'entity' | 'percept'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A MotorSchema — a parameterized control program, not a flat effector row.
|
|
37
|
+
* `kind: 'primitive'` runs a body directly (an internal stance, a communication,
|
|
38
|
+
* or an external host invocation). `kind: 'composite'` expands into an ordered
|
|
39
|
+
* policy over sub-schemas (this is where a *created* skill actually executes).
|
|
40
|
+
*/
|
|
41
|
+
export interface MotorSchema {
|
|
42
|
+
id: string
|
|
43
|
+
kind: 'primitive' | 'composite'
|
|
44
|
+
source: AffordanceSource
|
|
45
|
+
/** Normalized intrinsic cost 0..1 (effort / energy demand). */
|
|
46
|
+
cost: number
|
|
47
|
+
/** What this schema binds to when afforded. */
|
|
48
|
+
binds: SchemaBinding
|
|
49
|
+
/** Body-state gates evaluated against live metrics. */
|
|
50
|
+
preconditions?: SchemaPrecondition[]
|
|
51
|
+
/** Sub-schema ids, in policy order — composite schemas only. */
|
|
52
|
+
composedOf?: string[]
|
|
53
|
+
/** Intrinsic affective prior (−1..1) before any learning has occurred. */
|
|
54
|
+
baseValence?: number
|
|
55
|
+
tags?: string[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* An Affordance — a possibility the current state affords *for this body now*,
|
|
60
|
+
* with its parameters already bound from the thing that evoked it. Parameters
|
|
61
|
+
* are never separated from the situation that supplies them, so there is no
|
|
62
|
+
* "empty params" failure mode. Affordances are transient: the field is rebuilt
|
|
63
|
+
* every tick from live perception.
|
|
64
|
+
*/
|
|
65
|
+
export interface Affordance {
|
|
66
|
+
id: string
|
|
67
|
+
/** The MotorSchema this would enact. */
|
|
68
|
+
schema: string
|
|
69
|
+
source: AffordanceSource
|
|
70
|
+
/** Parameters bound from the evoking context (e.g. focus text, target name). */
|
|
71
|
+
parameters: Record<string, unknown>
|
|
72
|
+
/** Bound target entity id, when the schema binds an entity. */
|
|
73
|
+
targetEntityId?: string
|
|
74
|
+
/** Entity id of the percept / known-entity that evoked this (provenance). */
|
|
75
|
+
evokedBy?: string
|
|
76
|
+
/** Anticipated affective outcome −1..1 (learned, falling back to schema prior). */
|
|
77
|
+
expectedValence: number
|
|
78
|
+
/** Learned value estimate 0..1 for this schema in like contexts. */
|
|
79
|
+
expectedReward: number
|
|
80
|
+
/** Normalized cost 0..1 in the current body state. */
|
|
81
|
+
cost: number
|
|
82
|
+
/** Proceduralization 0..1 of the underlying schema (0 = deliberate, 1 = habit). */
|
|
83
|
+
habitStrength: number
|
|
84
|
+
/** Preconditions satisfied in the current state. */
|
|
85
|
+
available: boolean
|
|
86
|
+
/** Schema tags, carried so selection can route drives/risk without the registry. */
|
|
87
|
+
tags: string[]
|
|
88
|
+
/**
|
|
89
|
+
* Top-down planning bias 0..1 — set when an executing plan's frontier step
|
|
90
|
+
* projected this affordance (source 'plan'). It lifts activation in the
|
|
91
|
+
* competition WITHOUT bypassing it (the plan biases; the field still decides).
|
|
92
|
+
*/
|
|
93
|
+
planBias?: number
|
|
94
|
+
/** Provenance: the plan whose frontier step projected this affordance. */
|
|
95
|
+
planId?: string
|
|
96
|
+
/** Provenance: the frontier step id — flows through to action.outcome so the plan advances. */
|
|
97
|
+
stepId?: string
|
|
98
|
+
tick: number
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* EfferenceCopy — the forward model's prediction of an action's own consequences,
|
|
103
|
+
* emitted *before* enaction so reafference has something to compare against.
|
|
104
|
+
*/
|
|
105
|
+
export interface EfferenceCopy {
|
|
106
|
+
expectedValence: number
|
|
107
|
+
expectedReward: number
|
|
108
|
+
/** Optional predicted metric deltas the schema expects to cause. */
|
|
109
|
+
predictedMetricDeltas?: Record<string, number>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* SchemaOutcome — reafference. The actual result of an enaction paired with what
|
|
114
|
+
* was predicted, yielding the prediction error that drives all learning.
|
|
115
|
+
*/
|
|
116
|
+
export interface SchemaOutcome {
|
|
117
|
+
schema: string
|
|
118
|
+
affordanceId: string
|
|
119
|
+
success: boolean
|
|
120
|
+
/** Actual outcome quality 0..1. */
|
|
121
|
+
outcomeQuality: number
|
|
122
|
+
predicted: EfferenceCopy
|
|
123
|
+
/** |predicted − actual| — the teaching signal. */
|
|
124
|
+
surprise: number
|
|
125
|
+
tick: number
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* LearnedSkill — the persisted competence unit. This, not the transient
|
|
130
|
+
* affordance field, is what travels in the PMA and makes a grown Will *act like
|
|
131
|
+
* itself* after re-embodiment.
|
|
132
|
+
*/
|
|
133
|
+
export interface LearnedSkill {
|
|
134
|
+
schema: string
|
|
135
|
+
/** Proceduralization 0..1 — rises with low-error success, decays with disuse. */
|
|
136
|
+
habitStrength: number
|
|
137
|
+
/** Expected reward 0..1 (EMA over outcomes). */
|
|
138
|
+
valueEstimate: number
|
|
139
|
+
/** Learned default parameters for this schema. */
|
|
140
|
+
paramPriors: Record<string, unknown>
|
|
141
|
+
enactments: number
|
|
142
|
+
successes: number
|
|
143
|
+
/** Rolling mean |prediction error|. */
|
|
144
|
+
avgPredictionError: number
|
|
145
|
+
lastEnactedTick: number
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** An affordance with its computed competition activation. */
|
|
149
|
+
export interface ScoredAffordance {
|
|
150
|
+
affordance: Affordance
|
|
151
|
+
activation: number
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* SelectionResult — the outcome of the competition. `needsDeliberation` is set
|
|
156
|
+
* when the field is ambiguous (high entropy) or the stakes are high, recruiting
|
|
157
|
+
* the executive (LLM); otherwise the winner is enacted directly (System 1).
|
|
158
|
+
*/
|
|
159
|
+
export interface SelectionResult {
|
|
160
|
+
winner: ScoredAffordance | null
|
|
161
|
+
field: ScoredAffordance[]
|
|
162
|
+
/** 0..1 ambiguity of the competition (1 = flat field, no clear winner). */
|
|
163
|
+
entropy: number
|
|
164
|
+
needsDeliberation: boolean
|
|
165
|
+
}
|