@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,62 @@
1
+ /**
2
+ * limbic_trainer.ts – LimbicTrainer: gradient-based training for the LimbicModel.
3
+ *
4
+ * Trains the affect head to predict affect deltas and reward from
5
+ * (experience embedding, current state) pairs, using full-batch gradient
6
+ * descent with AdamW. When a GPUDevice is supplied the optimiser step runs on
7
+ * the GPU via the shared WEIGHT_UPDATE_WGSL kernel (real WebGPU training); with
8
+ * no device it uses a numerically-equivalent CPU AdamW so training works
9
+ * everywhere (CI, Node without @webgpu/node, etc.).
10
+ *
11
+ * The objective is MSE (regression), not the cross-entropy used by the
12
+ * language-model {@link MambaTrainer} — a limbic experience has continuous
13
+ * targets, not a next-token distribution.
14
+ */
15
+ import type { LimbicModel } from "./limbic_model.js";
16
+ /** One training example: an experience and the affect change it should produce. */
17
+ export interface LimbicSample {
18
+ /** Experience embedding (length = model.inputDim). */
19
+ input: ArrayLike<number>;
20
+ /** Affective state at the time of the experience (length = model.stateDim). */
21
+ state: ArrayLike<number>;
22
+ /** Observed affect delta target in (-1, 1) per state dim (length = model.stateDim). */
23
+ deltaTarget: ArrayLike<number>;
24
+ /** Observed scalar reward for the experience. */
25
+ reward: number;
26
+ }
27
+ export interface LimbicTrainOptions {
28
+ learningRate?: number;
29
+ epochs?: number;
30
+ weightDecay?: number;
31
+ beta1?: number;
32
+ beta2?: number;
33
+ eps?: number;
34
+ /** Max global gradient L2 norm before the optimiser step. Default 1.0. */
35
+ maxGradNorm?: number;
36
+ onEpochEnd?: ((epoch: number, loss: number) => void) | null;
37
+ }
38
+ export declare class LimbicTrainer {
39
+ readonly model: LimbicModel;
40
+ readonly device: GPUDevice | null;
41
+ private _moments;
42
+ private _step;
43
+ private readonly _adamwPipeline;
44
+ constructor(model: LimbicModel, device?: GPUDevice | null);
45
+ /** Whether the optimiser step runs on the GPU. */
46
+ get gpuTraining(): boolean;
47
+ private _initMoments;
48
+ /**
49
+ * Train on a batch of samples for `epochs` passes. Returns the per-epoch mean
50
+ * loss (monotonically decreasing on a learnable mapping). Full-batch: grads
51
+ * accumulate across the whole sequence (recurrent hidden carried, reset per
52
+ * epoch), are averaged, clipped, then applied once per epoch.
53
+ */
54
+ train(samples: LimbicSample[], opts?: LimbicTrainOptions): Promise<number[]>;
55
+ /** Mean MSE loss over samples (no weight update). Hidden resets per sample. */
56
+ evaluate(samples: LimbicSample[]): number;
57
+ private _clipGradients;
58
+ private _adamwStepCpu;
59
+ /** AdamW on the GPU via the shared WEIGHT_UPDATE_WGSL kernel. Awaited per step. */
60
+ private _adamwStepGpu;
61
+ }
62
+ //# sourceMappingURL=limbic_trainer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limbic_trainer.d.ts","sourceRoot":"","sources":["../../src/limbic/limbic_trainer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,mBAAmB,CAAC;AAElE,mFAAmF;AACnF,MAAM,WAAW,YAAY;IAC3B,sDAAsD;IACtD,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACzB,+EAA+E;IAC/E,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACzB,uFAAuF;IACvF,WAAW,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7D;AAuBD,qBAAa,aAAa;IACxB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IAClC,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;gBAE/C,KAAK,EAAE,WAAW,EAAE,MAAM,GAAE,SAAS,GAAG,IAAW;IAQ/D,kDAAkD;IAClD,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,OAAO,CAAC,YAAY;IAQpB;;;;;OAKG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA6DtF,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM;IAkBzC,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,aAAa;IAqBrB,mFAAmF;YACrE,aAAa;CAkC5B"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * limbic_trainer.ts – LimbicTrainer: gradient-based training for the LimbicModel.
3
+ *
4
+ * Trains the affect head to predict affect deltas and reward from
5
+ * (experience embedding, current state) pairs, using full-batch gradient
6
+ * descent with AdamW. When a GPUDevice is supplied the optimiser step runs on
7
+ * the GPU via the shared WEIGHT_UPDATE_WGSL kernel (real WebGPU training); with
8
+ * no device it uses a numerically-equivalent CPU AdamW so training works
9
+ * everywhere (CI, Node without @webgpu/node, etc.).
10
+ *
11
+ * The objective is MSE (regression), not the cross-entropy used by the
12
+ * language-model {@link MambaTrainer} — a limbic experience has continuous
13
+ * targets, not a next-token distribution.
14
+ */
15
+ import { createUniformBuffer, createStorageBuffer, createComputePipeline, createBindGroup, dispatchKernel, readBuffer, cdiv, } from "../utils/gpu_utils.js";
16
+ import { WEIGHT_UPDATE_WGSL } from "../kernels/weight_update.js";
17
+ function packAdamParams(numElements, lr, beta1, beta2, eps, weightDecay, beta1_t, beta2_t) {
18
+ const buf = new ArrayBuffer(32);
19
+ new Uint32Array(buf, 0, 1).set([numElements]);
20
+ new Float32Array(buf, 4, 7).set([lr, beta1, beta2, eps, weightDecay, beta1_t, beta2_t]);
21
+ return buf;
22
+ }
23
+ export class LimbicTrainer {
24
+ model;
25
+ device;
26
+ _moments = null;
27
+ _step = 0;
28
+ _adamwPipeline;
29
+ constructor(model, device = null) {
30
+ this.model = model;
31
+ this.device = device;
32
+ this._adamwPipeline = device
33
+ ? createComputePipeline(device, WEIGHT_UPDATE_WGSL, "adamw_update")
34
+ : null;
35
+ }
36
+ /** Whether the optimiser step runs on the GPU. */
37
+ get gpuTraining() {
38
+ return this.device != null && this._adamwPipeline != null;
39
+ }
40
+ _initMoments() {
41
+ if (this._moments)
42
+ return;
43
+ this._moments = this.model.parameters().map((p) => ({
44
+ m: new Float32Array(p.numel),
45
+ v: new Float32Array(p.numel),
46
+ }));
47
+ }
48
+ /**
49
+ * Train on a batch of samples for `epochs` passes. Returns the per-epoch mean
50
+ * loss (monotonically decreasing on a learnable mapping). Full-batch: grads
51
+ * accumulate across the whole sequence (recurrent hidden carried, reset per
52
+ * epoch), are averaged, clipped, then applied once per epoch.
53
+ */
54
+ async train(samples, opts = {}) {
55
+ if (samples.length === 0)
56
+ throw new Error("LimbicTrainer.train: no samples");
57
+ const { learningRate = 0.05, epochs = 50, weightDecay = 0.0, beta1 = 0.9, beta2 = 0.999, eps = 1e-8, maxGradNorm = 1.0, onEpochEnd = null, } = opts;
58
+ this._initMoments();
59
+ const losses = [];
60
+ for (let epoch = 0; epoch < epochs; epoch++) {
61
+ this.model.zeroGrad();
62
+ let epochLoss = 0;
63
+ // Each sample is one experience appraisal: cross-experience memory flows
64
+ // through the affective state `s` (fed back by the runtime), not through
65
+ // the hidden scratch `h`, so the hidden state resets per sample. This also
66
+ // makes BPTT(1) exact — there is no truncated carry.
67
+ for (const s of samples) {
68
+ const { loss } = this.model.backwardStep(s.input, this.model.initHidden(), s.state, s.deltaTarget, s.reward);
69
+ epochLoss += loss;
70
+ }
71
+ // Average gradients over the batch.
72
+ const grads = this.model.gradients();
73
+ const invN = 1 / samples.length;
74
+ for (const g of grads) {
75
+ for (let i = 0; i < g.data.length; i++)
76
+ g.data[i] *= invN;
77
+ }
78
+ this._clipGradients(grads, maxGradNorm);
79
+ this._step++;
80
+ const beta1_t = Math.pow(beta1, this._step);
81
+ const beta2_t = Math.pow(beta2, this._step);
82
+ const hp = { learningRate, weightDecay, beta1, beta2, eps, beta1_t, beta2_t };
83
+ if (this.gpuTraining) {
84
+ await this._adamwStepGpu(grads, hp);
85
+ }
86
+ else {
87
+ this._adamwStepCpu(grads, hp);
88
+ }
89
+ const avg = epochLoss / samples.length;
90
+ losses.push(avg);
91
+ if (onEpochEnd)
92
+ onEpochEnd(epoch + 1, avg);
93
+ }
94
+ return losses;
95
+ }
96
+ /** Mean MSE loss over samples (no weight update). Hidden resets per sample. */
97
+ evaluate(samples) {
98
+ if (samples.length === 0)
99
+ return 0;
100
+ let total = 0;
101
+ const { rewardWeight } = this.model.config;
102
+ for (const s of samples) {
103
+ const f = this.model.forward(s.input, this.model.initHidden(), s.state);
104
+ let loss = 0;
105
+ for (let k = 0; k < f.delta.length; k++) {
106
+ const d = f.delta[k] - (s.deltaTarget[k] ?? 0);
107
+ loss += 0.5 * d * d;
108
+ }
109
+ const rd = f.reward - s.reward;
110
+ loss += 0.5 * rewardWeight * rd * rd;
111
+ total += loss;
112
+ }
113
+ return total / samples.length;
114
+ }
115
+ _clipGradients(grads, maxNorm) {
116
+ let normSq = 0;
117
+ for (const g of grads)
118
+ for (let i = 0; i < g.data.length; i++)
119
+ normSq += g.data[i] * g.data[i];
120
+ const norm = Math.sqrt(normSq);
121
+ if (norm > maxNorm && norm > 0) {
122
+ const scale = maxNorm / norm;
123
+ for (const g of grads)
124
+ for (let i = 0; i < g.data.length; i++)
125
+ g.data[i] *= scale;
126
+ }
127
+ }
128
+ _adamwStepCpu(grads, hp) {
129
+ const params = this.model.parameters();
130
+ const { learningRate: lr, weightDecay: wd, beta1, beta2, eps, beta1_t, beta2_t } = hp;
131
+ for (let pi = 0; pi < params.length; pi++) {
132
+ const p = params[pi].data;
133
+ const g = grads[pi].data;
134
+ const mom = this._moments[pi];
135
+ for (let i = 0; i < p.length; i++) {
136
+ const gi = g[i];
137
+ mom.m[i] = beta1 * mom.m[i] + (1 - beta1) * gi;
138
+ mom.v[i] = beta2 * mom.v[i] + (1 - beta2) * gi * gi;
139
+ const mHat = mom.m[i] / (1 - beta1_t);
140
+ const vHat = mom.v[i] / (1 - beta2_t);
141
+ p[i] = p[i] * (1 - lr * wd) - (lr * mHat) / (Math.sqrt(vHat) + eps);
142
+ }
143
+ }
144
+ }
145
+ /** AdamW on the GPU via the shared WEIGHT_UPDATE_WGSL kernel. Awaited per step. */
146
+ async _adamwStepGpu(grads, hp) {
147
+ const device = this.device;
148
+ const pipeline = this._adamwPipeline;
149
+ const params = this.model.parameters();
150
+ const { learningRate: lr, weightDecay: wd, beta1, beta2, eps, beta1_t, beta2_t } = hp;
151
+ for (let pi = 0; pi < params.length; pi++) {
152
+ const p = params[pi];
153
+ const mom = this._moments[pi];
154
+ const paramBuf = createStorageBuffer(device, p.data, true);
155
+ const gradBuf = createStorageBuffer(device, grads[pi].data, false);
156
+ const mBuf = createStorageBuffer(device, mom.m, true);
157
+ const vBuf = createStorageBuffer(device, mom.v, true);
158
+ const uni = createUniformBuffer(device, packAdamParams(p.numel, lr, beta1, beta2, eps, wd, beta1_t, beta2_t));
159
+ const bg = createBindGroup(device, pipeline, [uni, paramBuf, gradBuf, mBuf, vBuf]);
160
+ dispatchKernel(device, pipeline, bg, [cdiv(p.numel, 256), 1, 1]);
161
+ p.data.set((await readBuffer(device, paramBuf, p.numel * 4)).subarray(0, p.numel));
162
+ mom.m.set((await readBuffer(device, mBuf, p.numel * 4)).subarray(0, p.numel));
163
+ mom.v.set((await readBuffer(device, vBuf, p.numel * 4)).subarray(0, p.numel));
164
+ paramBuf.destroy();
165
+ gradBuf.destroy();
166
+ mBuf.destroy();
167
+ vBuf.destroy();
168
+ uni.destroy();
169
+ }
170
+ }
171
+ }
172
+ //# sourceMappingURL=limbic_trainer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limbic_trainer.js","sourceRoot":"","sources":["../../src/limbic/limbic_trainer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
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
+ /** The five modelled limbic regions. Hippocampus is reused, not re-modelled. */
24
+ export declare const REGION: {
25
+ readonly amygdala: "amygdala";
26
+ readonly hypothalamus: "hypothalamus";
27
+ readonly thalamus: "thalamus";
28
+ readonly basalGanglia: "basal_ganglia";
29
+ readonly hippocampus: "hippocampus";
30
+ };
31
+ export type Region = (typeof REGION)[keyof typeof REGION];
32
+ /**
33
+ * Canonical indices into the affective state vector. The vector is a dense
34
+ * Float32Array of length {@link LIMBIC_STATE_DIM}. Core affect (valence,
35
+ * arousal) is the 2D summary; the remaining dims are the per-region drives the
36
+ * agent's behaviour is modulated by.
37
+ */
38
+ export declare const LIMBIC_DIM: {
39
+ /** Core affect — pleasantness. Range [-1, +1] (negative .. positive). */
40
+ readonly valence: 0;
41
+ /** Core affect — activation. Range [0, 1] (calm .. activated). */
42
+ readonly arousal: 1;
43
+ /** Hypothalamus drive — appetite for novelty/exploration. Range [0, 1]. */
44
+ readonly driveCuriosity: 2;
45
+ /** Hypothalamus drive — appetite for safety/guardrails. Range [0, 1]. */
46
+ readonly driveCaution: 3;
47
+ /** Hypothalamus drive — available energy. Range [0, 1] (fatigued .. fresh). */
48
+ readonly driveEffort: 4;
49
+ /** Hypothalamus drive — appetite for communication/collaboration. Range [0, 1]. */
50
+ readonly driveSocial: 5;
51
+ /** Thalamus — attention gain on incoming signal. Range [0, 1]. */
52
+ readonly attention: 6;
53
+ /** Basal ganglia — explore(1) vs. exploit(0) action-selection bias. Range [0, 1]. */
54
+ readonly exploration: 7;
55
+ };
56
+ export type LimbicDimName = keyof typeof LIMBIC_DIM;
57
+ /** Length of the affective state vector. */
58
+ export declare const LIMBIC_STATE_DIM = 8;
59
+ /** Ordered dim names, index-aligned with {@link LIMBIC_DIM}. */
60
+ export declare const LIMBIC_DIM_NAMES: LimbicDimName[];
61
+ /** Inclusive [min, max] bounds per state dim, index-aligned. Valence is signed. */
62
+ export declare const LIMBIC_BOUNDS: ReadonlyArray<readonly [number, number]>;
63
+ /**
64
+ * Neutral resting state — the default homeostatic setpoint before personality
65
+ * pulls it anywhere. Calm, mildly positive, balanced drives, full attention,
66
+ * slightly exploit-biased.
67
+ */
68
+ export declare const NEUTRAL_STATE: ReadonlyArray<number>;
69
+ /** Clamp a single state dim to its bounds. */
70
+ export declare function clampDim(index: number, value: number): number;
71
+ /** Clamp a whole state vector in place and return it. */
72
+ export declare function clampState(state: Float32Array): Float32Array;
73
+ /** A fresh neutral state vector. */
74
+ export declare function neutralState(): Float32Array;
75
+ /** Build a labelled record from a dense state vector (for logging / transport). */
76
+ export declare function stateToRecord(state: ArrayLike<number>): Record<LimbicDimName, number>;
77
+ /** Build a dense state vector from a (possibly partial) labelled record. */
78
+ export declare function recordToState(rec: Partial<Record<LimbicDimName, number>>): Float32Array;
79
+ //# sourceMappingURL=regions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../../src/limbic/regions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,gFAAgF;AAChF,eAAO,MAAM,MAAM;;;;;;CAMT,CAAC;AACX,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,UAAU;IACrB,yEAAyE;;IAEzE,kEAAkE;;IAElE,2EAA2E;;IAE3E,yEAAyE;;IAEzE,+EAA+E;;IAE/E,mFAAmF;;IAEnF,kEAAkE;;IAElE,qFAAqF;;CAE7E,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,UAAU,CAAC;AAEpD,4CAA4C;AAC5C,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,gEAAgE;AAChE,eAAO,MAAM,gBAAgB,EAAE,aAAa,EAS3C,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CASlE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,MAAM,CAS/C,CAAC;AAEF,8CAA8C;AAC9C,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK7D;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAK5D;AAED,oCAAoC;AACpC,wBAAgB,YAAY,IAAI,YAAY,CAE3C;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAMrF;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GAAG,YAAY,CAOvF"}
@@ -0,0 +1,132 @@
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
+ /** The five modelled limbic regions. Hippocampus is reused, not re-modelled. */
24
+ export const REGION = {
25
+ amygdala: "amygdala",
26
+ hypothalamus: "hypothalamus",
27
+ thalamus: "thalamus",
28
+ basalGanglia: "basal_ganglia",
29
+ hippocampus: "hippocampus",
30
+ };
31
+ /**
32
+ * Canonical indices into the affective state vector. The vector is a dense
33
+ * Float32Array of length {@link LIMBIC_STATE_DIM}. Core affect (valence,
34
+ * arousal) is the 2D summary; the remaining dims are the per-region drives the
35
+ * agent's behaviour is modulated by.
36
+ */
37
+ export const LIMBIC_DIM = {
38
+ /** Core affect — pleasantness. Range [-1, +1] (negative .. positive). */
39
+ valence: 0,
40
+ /** Core affect — activation. Range [0, 1] (calm .. activated). */
41
+ arousal: 1,
42
+ /** Hypothalamus drive — appetite for novelty/exploration. Range [0, 1]. */
43
+ driveCuriosity: 2,
44
+ /** Hypothalamus drive — appetite for safety/guardrails. Range [0, 1]. */
45
+ driveCaution: 3,
46
+ /** Hypothalamus drive — available energy. Range [0, 1] (fatigued .. fresh). */
47
+ driveEffort: 4,
48
+ /** Hypothalamus drive — appetite for communication/collaboration. Range [0, 1]. */
49
+ driveSocial: 5,
50
+ /** Thalamus — attention gain on incoming signal. Range [0, 1]. */
51
+ attention: 6,
52
+ /** Basal ganglia — explore(1) vs. exploit(0) action-selection bias. Range [0, 1]. */
53
+ exploration: 7,
54
+ };
55
+ /** Length of the affective state vector. */
56
+ export const LIMBIC_STATE_DIM = 8;
57
+ /** Ordered dim names, index-aligned with {@link LIMBIC_DIM}. */
58
+ export const LIMBIC_DIM_NAMES = [
59
+ "valence",
60
+ "arousal",
61
+ "driveCuriosity",
62
+ "driveCaution",
63
+ "driveEffort",
64
+ "driveSocial",
65
+ "attention",
66
+ "exploration",
67
+ ];
68
+ /** Inclusive [min, max] bounds per state dim, index-aligned. Valence is signed. */
69
+ export const LIMBIC_BOUNDS = [
70
+ [-1, 1], // valence
71
+ [0, 1], // arousal
72
+ [0, 1], // driveCuriosity
73
+ [0, 1], // driveCaution
74
+ [0, 1], // driveEffort
75
+ [0, 1], // driveSocial
76
+ [0, 1], // attention
77
+ [0, 1], // exploration
78
+ ];
79
+ /**
80
+ * Neutral resting state — the default homeostatic setpoint before personality
81
+ * pulls it anywhere. Calm, mildly positive, balanced drives, full attention,
82
+ * slightly exploit-biased.
83
+ */
84
+ export const NEUTRAL_STATE = [
85
+ 0.0, // valence
86
+ 0.2, // arousal
87
+ 0.5, // driveCuriosity
88
+ 0.5, // driveCaution
89
+ 0.8, // driveEffort
90
+ 0.5, // driveSocial
91
+ 0.7, // attention
92
+ 0.5, // exploration (centred → resting state is behaviourally inert)
93
+ ];
94
+ /** Clamp a single state dim to its bounds. */
95
+ export function clampDim(index, value) {
96
+ const b = LIMBIC_BOUNDS[index];
97
+ if (!b)
98
+ return value;
99
+ if (Number.isNaN(value))
100
+ return b[0];
101
+ return Math.max(b[0], Math.min(b[1], value));
102
+ }
103
+ /** Clamp a whole state vector in place and return it. */
104
+ export function clampState(state) {
105
+ for (let i = 0; i < state.length && i < LIMBIC_STATE_DIM; i++) {
106
+ state[i] = clampDim(i, state[i]);
107
+ }
108
+ return state;
109
+ }
110
+ /** A fresh neutral state vector. */
111
+ export function neutralState() {
112
+ return Float32Array.from(NEUTRAL_STATE);
113
+ }
114
+ /** Build a labelled record from a dense state vector (for logging / transport). */
115
+ export function stateToRecord(state) {
116
+ const out = {};
117
+ for (let i = 0; i < LIMBIC_DIM_NAMES.length; i++) {
118
+ out[LIMBIC_DIM_NAMES[i]] = state[i] ?? 0;
119
+ }
120
+ return out;
121
+ }
122
+ /** Build a dense state vector from a (possibly partial) labelled record. */
123
+ export function recordToState(rec) {
124
+ const s = neutralState();
125
+ for (let i = 0; i < LIMBIC_DIM_NAMES.length; i++) {
126
+ const v = rec[LIMBIC_DIM_NAMES[i]];
127
+ if (typeof v === "number" && !Number.isNaN(v))
128
+ s[i] = clampDim(i, v);
129
+ }
130
+ return s;
131
+ }
132
+ //# sourceMappingURL=regions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regions.js","sourceRoot":"","sources":["../../src/limbic/regions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanhogg/builderforce-memory-engine",
3
- "version": "2026.6.19",
3
+ "version": "2026.6.27",
4
4
  "description": "BuilderForce Agent Memory — engine layer. TypeScript/WGSL Mamba SSM kernels, model blocks, autograd, training, and BPE tokenizer (zero runtime deps, WebGPU-native).",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
package/src/index.ts CHANGED
@@ -108,6 +108,37 @@ export { COMPLEX_SSD_FORWARD_WGSL, COMPLEX_SSD_BACKWARD_WGSL }
108
108
  export { ATTENTION_FORWARD_WGSL, ATTENTION_BACKWARD_WGSL, SOFTMAX_WGSL }
109
109
  from './kernels/attention.js';
110
110
 
111
+ // ── Limbic system (trainable affective dynamics) ──────────────────────────────
112
+
113
+ export {
114
+ REGION,
115
+ LIMBIC_DIM,
116
+ LIMBIC_DIM_NAMES,
117
+ LIMBIC_STATE_DIM,
118
+ LIMBIC_BOUNDS,
119
+ NEUTRAL_STATE,
120
+ clampDim,
121
+ clampState,
122
+ neutralState,
123
+ stateToRecord,
124
+ recordToState,
125
+ LimbicModel,
126
+ DEFAULT_LIMBIC_CONFIG,
127
+ DEFAULT_LIMBIC_SEED,
128
+ LimbicTrainer,
129
+ } from './limbic/index.js';
130
+ export type {
131
+ Region,
132
+ LimbicDimName,
133
+ LimbicModelConfig,
134
+ LimbicForward,
135
+ LimbicParam,
136
+ LimbicSample,
137
+ LimbicTrainOptions,
138
+ } from './limbic/index.js';
139
+
140
+ export { LIMBIC_AFFECT_WGSL } from './kernels/limbic_affect.js';
141
+
111
142
  // ── Version ───────────────────────────────────────────────────────────────────
112
143
 
113
144
  export const VERSION = '2.0.0';
@@ -0,0 +1,74 @@
1
+ // Limbic Affect WGSL Kernel
2
+ //
3
+ // One forward step of the limbic model's recurrent affect core, run on the GPU.
4
+ // Given the previous hidden state, previous affective state, and an experience
5
+ // embedding, it produces the next hidden state and a bounded affect *delta*.
6
+ //
7
+ // This mirrors the CPU reference in limbic/limbic_model.ts exactly (same math):
8
+ // pre[j] = Σ_i Win[j,i]·x[i] + Σ_k Ws[j,k]·s[k]
9
+ // a[j] = sigmoid(A[j]) // per-channel recurrence gate
10
+ // h'[j] = a[j]·h[j] + (1-a[j])·tanh(pre[j]) // SSM-style leak/input
11
+ // Δ[k] = tanh( Σ_j Wout[k,j]·h'[j] + b[k] ) // bounded affect change
12
+ //
13
+ // hidden_dim is assumed ≤ 64 (the limbic head is tiny — the heavy lifting is in
14
+ // the hippocampus SSM that produces the experience embedding). Reward is NOT
15
+ // computed here: it is only needed during training, which runs the CPU backward
16
+ // path; the per-turn inference step only needs Δ and the next hidden state.
17
+
18
+ export const LIMBIC_AFFECT_WGSL: string = /* wgsl */ `
19
+
20
+ struct Dims {
21
+ input_dim : u32,
22
+ hidden_dim : u32,
23
+ state_dim : u32,
24
+ _pad : u32,
25
+ };
26
+
27
+ @group(0) @binding(0) var<uniform> dims : Dims;
28
+ @group(0) @binding(1) var<storage, read> win : array<f32>; // hidden*input
29
+ @group(0) @binding(2) var<storage, read> ws : array<f32>; // hidden*state
30
+ @group(0) @binding(3) var<storage, read> a_logit : array<f32>; // hidden
31
+ @group(0) @binding(4) var<storage, read> wout_state : array<f32>; // state*hidden
32
+ @group(0) @binding(5) var<storage, read> bout_state : array<f32>; // state
33
+ @group(0) @binding(6) var<storage, read> x_in : array<f32>; // input
34
+ @group(0) @binding(7) var<storage, read> h_prev : array<f32>; // hidden
35
+ @group(0) @binding(8) var<storage, read> s_prev : array<f32>; // state
36
+ @group(0) @binding(9) var<storage, read_write> h_out : array<f32>; // hidden
37
+ @group(0) @binding(10) var<storage, read_write> delta_out : array<f32>; // state
38
+
39
+ var<workgroup> hbuf : array<f32, 64>;
40
+
41
+ // Single-workgroup dispatch: (1, 1, 1) with workgroup_size 64.
42
+ @compute @workgroup_size(64, 1, 1)
43
+ fn affect_step(
44
+ @builtin(local_invocation_id) lid : vec3<u32>,
45
+ ) {
46
+ let j = lid.x;
47
+
48
+ // Pass 1: recurrent hidden update (one thread per hidden channel).
49
+ if (j < dims.hidden_dim) {
50
+ var pre : f32 = 0.0;
51
+ for (var i : u32 = 0u; i < dims.input_dim; i = i + 1u) {
52
+ pre = pre + win[j * dims.input_dim + i] * x_in[i];
53
+ }
54
+ for (var k : u32 = 0u; k < dims.state_dim; k = k + 1u) {
55
+ pre = pre + ws[j * dims.state_dim + k] * s_prev[k];
56
+ }
57
+ let a = 1.0 / (1.0 + exp(-a_logit[j]));
58
+ let hn = a * h_prev[j] + (1.0 - a) * tanh(pre);
59
+ hbuf[j] = hn;
60
+ h_out[j] = hn;
61
+ }
62
+
63
+ workgroupBarrier();
64
+
65
+ // Pass 2: bounded affect delta (one thread per state dim).
66
+ if (j < dims.state_dim) {
67
+ var acc : f32 = bout_state[j];
68
+ for (var m : u32 = 0u; m < dims.hidden_dim; m = m + 1u) {
69
+ acc = acc + wout_state[j * dims.hidden_dim + m] * hbuf[m];
70
+ }
71
+ delta_out[j] = tanh(acc);
72
+ }
73
+ }
74
+ `;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Limbic system – trainable affective/motivational dynamics for agents.
3
+ *
4
+ * The dynamic counterpart to the (static) psychometric personality: where
5
+ * personality sets the homeostatic setpoints, the limbic model learns — in
6
+ * WebGPU — how an agent's affective state moves in response to experience.
7
+ */
8
+
9
+ export {
10
+ REGION,
11
+ LIMBIC_DIM,
12
+ LIMBIC_DIM_NAMES,
13
+ LIMBIC_STATE_DIM,
14
+ LIMBIC_BOUNDS,
15
+ NEUTRAL_STATE,
16
+ clampDim,
17
+ clampState,
18
+ neutralState,
19
+ stateToRecord,
20
+ recordToState,
21
+ } from "./regions.js";
22
+ export type { Region, LimbicDimName } from "./regions.js";
23
+
24
+ export { LimbicModel, DEFAULT_LIMBIC_CONFIG, DEFAULT_LIMBIC_SEED } from "./limbic_model.js";
25
+ export type { LimbicModelConfig, LimbicForward, LimbicParam } from "./limbic_model.js";
26
+
27
+ export { LimbicTrainer } from "./limbic_trainer.js";
28
+ export type { LimbicSample, LimbicTrainOptions } from "./limbic_trainer.js";