@seanhogg/builderforce-memory-engine 2026.6.19 → 2026.6.27

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.
@@ -0,0 +1,141 @@
1
+ /**
2
+ * regions.ts – Limbic-system region map and affective state schema.
3
+ *
4
+ * The limbic model is the *dynamic* affective/motivational layer that rides on
5
+ * top of the (static) psychometric personality. Where the hippocampus
6
+ * (HybridMambaModel + MemoryStore) holds *what the agent knows*, the limbic
7
+ * model holds *how the agent currently feels and what it is driven toward* —
8
+ * and learns the dynamics of that, in WebGPU, from experience.
9
+ *
10
+ * This file is the single source of truth for the affective state vector that
11
+ * every other limbic module (model, trainer, runtime service) indexes into.
12
+ * Keep the dimension ids in sync with the runtime compiler in
13
+ * `agent-runtime/src/builderforce/limbic.ts` — the two are coupled solely by
14
+ * these string ids and the {@link LIMBIC_DIM} indices.
15
+ *
16
+ * Region → state mapping (mirrors the labelled diagram):
17
+ * • Amygdala → salience / threat appraisal → drives valence + arousal
18
+ * • Hypothalamus → homeostatic drives (curiosity, caution, effort, social)
19
+ * • Thalamus → attention gate (how much incoming signal is admitted)
20
+ * • Basal ganglia → action selection bias (explore vs. exploit)
21
+ * • Hippocampus → reused (existing SSM memory); feeds the experience input
22
+ */
23
+
24
+ /** The five modelled limbic regions. Hippocampus is reused, not re-modelled. */
25
+ export const REGION = {
26
+ amygdala: "amygdala",
27
+ hypothalamus: "hypothalamus",
28
+ thalamus: "thalamus",
29
+ basalGanglia: "basal_ganglia",
30
+ hippocampus: "hippocampus",
31
+ } as const;
32
+ export type Region = (typeof REGION)[keyof typeof REGION];
33
+
34
+ /**
35
+ * Canonical indices into the affective state vector. The vector is a dense
36
+ * Float32Array of length {@link LIMBIC_STATE_DIM}. Core affect (valence,
37
+ * arousal) is the 2D summary; the remaining dims are the per-region drives the
38
+ * agent's behaviour is modulated by.
39
+ */
40
+ export const LIMBIC_DIM = {
41
+ /** Core affect — pleasantness. Range [-1, +1] (negative .. positive). */
42
+ valence: 0,
43
+ /** Core affect — activation. Range [0, 1] (calm .. activated). */
44
+ arousal: 1,
45
+ /** Hypothalamus drive — appetite for novelty/exploration. Range [0, 1]. */
46
+ driveCuriosity: 2,
47
+ /** Hypothalamus drive — appetite for safety/guardrails. Range [0, 1]. */
48
+ driveCaution: 3,
49
+ /** Hypothalamus drive — available energy. Range [0, 1] (fatigued .. fresh). */
50
+ driveEffort: 4,
51
+ /** Hypothalamus drive — appetite for communication/collaboration. Range [0, 1]. */
52
+ driveSocial: 5,
53
+ /** Thalamus — attention gain on incoming signal. Range [0, 1]. */
54
+ attention: 6,
55
+ /** Basal ganglia — explore(1) vs. exploit(0) action-selection bias. Range [0, 1]. */
56
+ exploration: 7,
57
+ } as const;
58
+ export type LimbicDimName = keyof typeof LIMBIC_DIM;
59
+
60
+ /** Length of the affective state vector. */
61
+ export const LIMBIC_STATE_DIM = 8;
62
+
63
+ /** Ordered dim names, index-aligned with {@link LIMBIC_DIM}. */
64
+ export const LIMBIC_DIM_NAMES: LimbicDimName[] = [
65
+ "valence",
66
+ "arousal",
67
+ "driveCuriosity",
68
+ "driveCaution",
69
+ "driveEffort",
70
+ "driveSocial",
71
+ "attention",
72
+ "exploration",
73
+ ];
74
+
75
+ /** Inclusive [min, max] bounds per state dim, index-aligned. Valence is signed. */
76
+ export const LIMBIC_BOUNDS: ReadonlyArray<readonly [number, number]> = [
77
+ [-1, 1], // valence
78
+ [0, 1], // arousal
79
+ [0, 1], // driveCuriosity
80
+ [0, 1], // driveCaution
81
+ [0, 1], // driveEffort
82
+ [0, 1], // driveSocial
83
+ [0, 1], // attention
84
+ [0, 1], // exploration
85
+ ];
86
+
87
+ /**
88
+ * Neutral resting state — the default homeostatic setpoint before personality
89
+ * pulls it anywhere. Calm, mildly positive, balanced drives, full attention,
90
+ * slightly exploit-biased.
91
+ */
92
+ export const NEUTRAL_STATE: ReadonlyArray<number> = [
93
+ 0.0, // valence
94
+ 0.2, // arousal
95
+ 0.5, // driveCuriosity
96
+ 0.5, // driveCaution
97
+ 0.8, // driveEffort
98
+ 0.5, // driveSocial
99
+ 0.7, // attention
100
+ 0.5, // exploration (centred → resting state is behaviourally inert)
101
+ ];
102
+
103
+ /** Clamp a single state dim to its bounds. */
104
+ export function clampDim(index: number, value: number): number {
105
+ const b = LIMBIC_BOUNDS[index];
106
+ if (!b) return value;
107
+ if (Number.isNaN(value)) return b[0];
108
+ return Math.max(b[0], Math.min(b[1], value));
109
+ }
110
+
111
+ /** Clamp a whole state vector in place and return it. */
112
+ export function clampState(state: Float32Array): Float32Array {
113
+ for (let i = 0; i < state.length && i < LIMBIC_STATE_DIM; i++) {
114
+ state[i] = clampDim(i, state[i]!);
115
+ }
116
+ return state;
117
+ }
118
+
119
+ /** A fresh neutral state vector. */
120
+ export function neutralState(): Float32Array {
121
+ return Float32Array.from(NEUTRAL_STATE);
122
+ }
123
+
124
+ /** Build a labelled record from a dense state vector (for logging / transport). */
125
+ export function stateToRecord(state: ArrayLike<number>): Record<LimbicDimName, number> {
126
+ const out = {} as Record<LimbicDimName, number>;
127
+ for (let i = 0; i < LIMBIC_DIM_NAMES.length; i++) {
128
+ out[LIMBIC_DIM_NAMES[i]!] = state[i] ?? 0;
129
+ }
130
+ return out;
131
+ }
132
+
133
+ /** Build a dense state vector from a (possibly partial) labelled record. */
134
+ export function recordToState(rec: Partial<Record<LimbicDimName, number>>): Float32Array {
135
+ const s = neutralState();
136
+ for (let i = 0; i < LIMBIC_DIM_NAMES.length; i++) {
137
+ const v = rec[LIMBIC_DIM_NAMES[i]!];
138
+ if (typeof v === "number" && !Number.isNaN(v)) s[i] = clampDim(i, v);
139
+ }
140
+ return s;
141
+ }