@seanhogg/builderforce-memory 2026.6.20 → 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,188 @@
1
+ /**
2
+ * LimbicSession.ts – high-level facade over the limbic affect model.
3
+ *
4
+ * Mirrors {@link MambaSession}: collapses GPU acquisition, model construction,
5
+ * checkpoint load, and the trainer into a single `LimbicSession.create()` call,
6
+ * with the same WebGPU-or-CPU-fallback contract. The agent runtime consumes
7
+ * this through `@seanhogg/builderforce-memory` to run the limbic system on a
8
+ * self-hosted node (GPU via @webgpu/node when present, CPU otherwise).
9
+ *
10
+ * const limbic = await LimbicSession.create({ gpuAdapter, checkpointBuffer });
11
+ * const { delta, reward } = await limbic.step(experienceEmbedding, state);
12
+ * await limbic.train(samples, { epochs: 30 });
13
+ * const bin = limbic.exportWeights({ fp16: true });
14
+ */
15
+ import { LimbicModel, LimbicTrainer, LIMBIC_AFFECT_WGSL, createStorageBuffer, createEmptyStorageBuffer, createUniformBuffer, createComputePipeline, createBindGroup, dispatchKernel, readBuffer, } from "@seanhogg/builderforce-memory-engine";
16
+ import { saveToIndexedDB, loadFromIndexedDB } from "../session/persistence.js";
17
+ export class LimbicSession {
18
+ model;
19
+ trainer;
20
+ device;
21
+ gpuMode;
22
+ _name;
23
+ _idbFactory;
24
+ // GPU step pipeline + buffers, allocated lazily on first GPU step.
25
+ _pipeline = null;
26
+ _dimsBuf = null;
27
+ _paramBufs = null;
28
+ _paramsDirty = true;
29
+ constructor(model, trainer, device, gpuMode, name, idbFactory) {
30
+ this.model = model;
31
+ this.trainer = trainer;
32
+ this.device = device;
33
+ this.gpuMode = gpuMode;
34
+ this._name = name;
35
+ this._idbFactory = idbFactory;
36
+ }
37
+ static async create(options = {}) {
38
+ let device = null;
39
+ let gpuMode = "cpu";
40
+ if (options.gpuAdapter != null) {
41
+ try {
42
+ device = await options.gpuAdapter.requestDevice();
43
+ gpuMode = "webgpu";
44
+ }
45
+ catch {
46
+ device = null;
47
+ gpuMode = "cpu";
48
+ }
49
+ }
50
+ else if (typeof navigator !== "undefined" && navigator.gpu) {
51
+ try {
52
+ const adapter = await navigator.gpu.requestAdapter({ powerPreference: "high-performance" });
53
+ if (adapter) {
54
+ device = await adapter.requestDevice();
55
+ gpuMode = "webgpu";
56
+ }
57
+ }
58
+ catch {
59
+ device = null;
60
+ }
61
+ if (!device && options.allowCpuFallback && typeof navigator !== "undefined" && navigator.gpu) {
62
+ try {
63
+ const fb = await navigator.gpu.requestAdapter({ forceFallbackAdapter: true });
64
+ if (fb) {
65
+ device = await fb.requestDevice();
66
+ gpuMode = "cpu-fallback";
67
+ }
68
+ }
69
+ catch {
70
+ device = null;
71
+ }
72
+ }
73
+ }
74
+ const model = new LimbicModel({ ...options.modelConfig, seed: options.seed });
75
+ if (options.checkpointBuffer) {
76
+ model.loadWeights(options.checkpointBuffer);
77
+ }
78
+ const trainer = new LimbicTrainer(model, device);
79
+ return new LimbicSession(model, trainer, device, gpuMode, options.name ?? "limbic-default", options.idbFactory);
80
+ }
81
+ /** One affect step. Uses the GPU kernel when a device is available, else CPU. */
82
+ async step(input, state, hidden) {
83
+ const h = hidden ?? this.model.initHidden();
84
+ if (!this.device)
85
+ return this.model.forward(input, h, state);
86
+ return this._stepGpu(input, h, state);
87
+ }
88
+ _ensureGpuStep() {
89
+ const device = this.device;
90
+ if (!this._pipeline) {
91
+ this._pipeline = createComputePipeline(device, LIMBIC_AFFECT_WGSL, "affect_step");
92
+ const { inputDim, hiddenDim, stateDim } = this.model.config;
93
+ const dims = new ArrayBuffer(16);
94
+ new Uint32Array(dims).set([inputDim, hiddenDim, stateDim, 0]);
95
+ this._dimsBuf = createUniformBuffer(device, dims);
96
+ }
97
+ if (this._paramsDirty || !this._paramBufs) {
98
+ this._destroyParamBufs();
99
+ this._paramBufs = {
100
+ win: createStorageBuffer(device, this.model.win, false),
101
+ ws: createStorageBuffer(device, this.model.ws, false),
102
+ aLogit: createStorageBuffer(device, this.model.aLogit, false),
103
+ woutState: createStorageBuffer(device, this.model.woutState, false),
104
+ boutState: createStorageBuffer(device, this.model.boutState, false),
105
+ };
106
+ this._paramsDirty = false;
107
+ }
108
+ return { pipeline: this._pipeline, params: this._paramBufs, dims: this._dimsBuf };
109
+ }
110
+ async _stepGpu(input, hPrev, sPrev) {
111
+ const device = this.device;
112
+ const { inputDim, hiddenDim, stateDim } = this.model.config;
113
+ const { pipeline, params, dims } = this._ensureGpuStep();
114
+ const xBuf = createStorageBuffer(device, Float32Array.from({ length: inputDim }, (_, i) => input[i] ?? 0), false);
115
+ const hBuf = createStorageBuffer(device, Float32Array.from({ length: hiddenDim }, (_, j) => hPrev[j] ?? 0), false);
116
+ const sBuf = createStorageBuffer(device, Float32Array.from({ length: stateDim }, (_, k) => sPrev[k] ?? 0), false);
117
+ const hOut = createEmptyStorageBuffer(device, hiddenDim * 4, true);
118
+ const dOut = createEmptyStorageBuffer(device, stateDim * 4, true);
119
+ const bg = createBindGroup(device, pipeline, [
120
+ dims,
121
+ params.win,
122
+ params.ws,
123
+ params.aLogit,
124
+ params.woutState,
125
+ params.boutState,
126
+ xBuf,
127
+ hBuf,
128
+ sBuf,
129
+ hOut,
130
+ dOut,
131
+ ]);
132
+ dispatchKernel(device, pipeline, bg, [1, 1, 1]);
133
+ const hidden = (await readBuffer(device, hOut, hiddenDim * 4)).subarray(0, hiddenDim);
134
+ const delta = (await readBuffer(device, dOut, stateDim * 4)).subarray(0, stateDim);
135
+ // Reward head is small — compute on CPU from the GPU-produced hidden state.
136
+ let reward = this.model.boutReward[0];
137
+ for (let j = 0; j < hiddenDim; j++)
138
+ reward += this.model.woutReward[j] * hidden[j];
139
+ xBuf.destroy();
140
+ hBuf.destroy();
141
+ sBuf.destroy();
142
+ hOut.destroy();
143
+ dOut.destroy();
144
+ return { hidden: Float32Array.from(hidden), delta: Float32Array.from(delta), reward };
145
+ }
146
+ /** Train the affect model on observed experiences. Marks GPU step buffers dirty. */
147
+ async train(samples, opts) {
148
+ const losses = await this.trainer.train(samples, opts);
149
+ this._paramsDirty = true; // weights changed → GPU step buffers must be re-uploaded
150
+ return losses;
151
+ }
152
+ evaluate(samples) {
153
+ return this.trainer.evaluate(samples);
154
+ }
155
+ exportWeights(opts) {
156
+ return this.model.exportWeights(opts);
157
+ }
158
+ /** Persist weights to IndexedDB under the session name. */
159
+ async save() {
160
+ await saveToIndexedDB(`limbic_${this._name}`, this.model.exportWeights({ fp16: true }), this._idbFactory);
161
+ }
162
+ /** Load weights from IndexedDB; returns true if a checkpoint was found. */
163
+ async load() {
164
+ const buf = await loadFromIndexedDB(`limbic_${this._name}`, this._idbFactory);
165
+ if (!buf)
166
+ return false;
167
+ this.model.loadWeights(buf);
168
+ this._paramsDirty = true;
169
+ return true;
170
+ }
171
+ _destroyParamBufs() {
172
+ if (this._paramBufs) {
173
+ this._paramBufs.win.destroy();
174
+ this._paramBufs.ws.destroy();
175
+ this._paramBufs.aLogit.destroy();
176
+ this._paramBufs.woutState.destroy();
177
+ this._paramBufs.boutState.destroy();
178
+ this._paramBufs = null;
179
+ }
180
+ }
181
+ destroy() {
182
+ this._destroyParamBufs();
183
+ this._dimsBuf?.destroy();
184
+ this._dimsBuf = null;
185
+ this._pipeline = null;
186
+ }
187
+ }
188
+ //# sourceMappingURL=LimbicSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LimbicSession.js","sourceRoot":"","sources":["../../src/limbic/LimbicSession.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanhogg/builderforce-memory",
3
- "version": "2026.6.20",
3
+ "version": "2026.6.27",
4
4
  "description": "BuilderForce Agent Memory — runtime layer. SSM execution, Transformer orchestration, online distillation, and persistent agent memory for BuilderForce.ai agents.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "homepage": "https://github.com/SeanHogg/builderforce-memory/tree/main/packages/memory#readme",
51
51
  "peerDependencies": {
52
- "@seanhogg/builderforce-memory-engine": "^2026.6.20"
52
+ "@seanhogg/builderforce-memory-engine": "^2026.6.27"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@jest/globals": "^29.7.0",
@@ -62,7 +62,7 @@
62
62
  "jest": "^29.7.0",
63
63
  "ts-jest": "^29.2.0",
64
64
  "typescript": "^5.0.0",
65
- "@seanhogg/builderforce-memory-engine": "2026.6.20"
65
+ "@seanhogg/builderforce-memory-engine": "2026.6.27"
66
66
  },
67
67
  "jest": {
68
68
  "preset": "ts-jest/presets/default-esm",
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Evermind — Write-Through Cognition engine.
3
+ *
4
+ * `commit()` is the model-knowledge analogue of a write-through cache write with
5
+ * a conflict resolver: Canonicalize (stable subject key) → Recall incumbent →
6
+ * Evaluate evidence → Reconcile (augment | confirm | supersede | reject) →
7
+ * write-through. `recall()` is the read side, served through a version-token
8
+ * cache that invalidates for free whenever knowledge changes.
9
+ *
10
+ * This is the layer the append-only knowledge loop skipped: it derives a STABLE
11
+ * subject key (not a per-run id) and replaces-on-write, so beliefs never drift
12
+ * into a manual reconciliation step.
13
+ */
14
+
15
+ import type {
16
+ Claim,
17
+ CognitionFactStore,
18
+ CommitResult,
19
+ EvidenceGatherer,
20
+ EvidenceResult,
21
+ Verdict,
22
+ } from './types.js';
23
+
24
+ export interface EvermindCognitionOptions {
25
+ store: CognitionFactStore;
26
+ /** Default evidence gatherer used when `commit()` is not given one. */
27
+ gather?: EvidenceGatherer;
28
+ /**
29
+ * Optional embedding-capable runtime (e.g. SSMRuntime) forwarded to the
30
+ * store's `recallSimilar` so recall sharpens as the model adapts.
31
+ */
32
+ runtime?: unknown;
33
+ }
34
+
35
+ /** Subset of a store that can rank facts by semantic similarity. */
36
+ interface SimilarityCapableStore {
37
+ recallSimilar(query: string, topK: number, runtime?: unknown): Promise<Array<{ content: string }>>;
38
+ }
39
+
40
+ function hasRecallSimilar(store: unknown): store is SimilarityCapableStore {
41
+ return typeof (store as SimilarityCapableStore).recallSimilar === 'function';
42
+ }
43
+
44
+ export class EvermindCognition {
45
+ private readonly _store: CognitionFactStore;
46
+ private readonly _defaultGather?: EvidenceGatherer;
47
+ private readonly _runtime?: unknown;
48
+
49
+ /**
50
+ * Knowledge-generation token. Bumped on every change to the fact set
51
+ * (augment / supersede). Doubles as the recall-cache namespace so a write
52
+ * invalidates cached reads for free — the "invalidate on write" rule applied
53
+ * to model knowledge.
54
+ */
55
+ private _version = 0;
56
+
57
+ /**
58
+ * L1 recall cache, namespaced by `_version`. After any knowledge change the
59
+ * version moves, so prior-generation entries are never read again (and are
60
+ * cleared to bound growth) — not an ad-hoc TTL map; a version-token cache.
61
+ */
62
+ private readonly _recallCache = new Map<string, string[]>();
63
+
64
+ constructor(opts: EvermindCognitionOptions) {
65
+ this._store = opts.store;
66
+ this._defaultGather = opts.gather;
67
+ this._runtime = opts.runtime;
68
+ }
69
+
70
+ /** Current knowledge-generation token. */
71
+ get version(): number {
72
+ return this._version;
73
+ }
74
+
75
+ /**
76
+ * Commit a candidate fact write-through. Evidence decides conflicts; a
77
+ * supersede replaces the incumbent under the same stable key and invalidates
78
+ * recall. Never appends a competing belief for the same subject.
79
+ */
80
+ async commit(claim: Claim, gather: EvidenceGatherer | undefined = this._defaultGather): Promise<CommitResult> {
81
+ const incumbent = await this._store.recall(claim.subjectKey);
82
+ const audit: string[] = [];
83
+
84
+ // ── Brand-new subject ────────────────────────────────────────────────
85
+ if (!incumbent) {
86
+ if (gather && claim.requireEvidence) {
87
+ const e = await gather({ claim });
88
+ audit.push(...e.notes);
89
+ if (!e.supportsNew) {
90
+ return this._result('reject', claim.subjectKey, claim.content, undefined, audit);
91
+ }
92
+ }
93
+ await this._write(claim, claim.importance ?? 0.6);
94
+ this._bumpVersion();
95
+ return this._result('augment', claim.subjectKey, claim.content, undefined, audit);
96
+ }
97
+
98
+ // ── Identical incumbent — confirm (refresh confidence, no replace) ────
99
+ if (incumbent.content === claim.content) {
100
+ await this._write(claim, Math.min(1, (claim.importance ?? 0.6) + 0.1));
101
+ return this._result('confirm', claim.subjectKey, claim.content, undefined, audit);
102
+ }
103
+
104
+ // ── Conflict — evidence decides ──────────────────────────────────────
105
+ const e: EvidenceResult = gather
106
+ ? await gather({ claim, incumbent: incumbent.content })
107
+ : { supportsNew: true, notes: ['no evidence gatherer supplied; trusting newer observation'] };
108
+ audit.push(...e.notes);
109
+
110
+ if (e.supportsNew) {
111
+ await this._write(claim, Math.max(claim.importance ?? 0.6, 0.9));
112
+ this._bumpVersion();
113
+ return this._result('supersede', claim.subjectKey, claim.content, incumbent.content, audit);
114
+ }
115
+ return this._result('reject', claim.subjectKey, incumbent.content, undefined, audit);
116
+ }
117
+
118
+ /**
119
+ * Write-through recall: the top-K most relevant facts for `query`, served
120
+ * from a version-namespaced cache that invalidates on the next knowledge
121
+ * change. Falls back to an empty set when the store can't rank similarity.
122
+ */
123
+ async recall(query: string, topK = 5): Promise<string[]> {
124
+ const cacheKey = `${this._version}:${topK}:${query}`;
125
+ const cached = this._recallCache.get(cacheKey);
126
+ if (cached) return cached;
127
+
128
+ const facts = hasRecallSimilar(this._store)
129
+ ? (await this._store.recallSimilar(query, topK, this._runtime)).map((e) => e.content)
130
+ : [];
131
+
132
+ this._recallCache.set(cacheKey, facts);
133
+ return facts;
134
+ }
135
+
136
+ // ── internals ────────────────────────────────────────────────────────────
137
+
138
+ private async _write(claim: Claim, importance: number): Promise<void> {
139
+ await this._store.remember(claim.subjectKey, claim.content, { tags: claim.tags, importance });
140
+ }
141
+
142
+ private _bumpVersion(): void {
143
+ this._version++;
144
+ this._recallCache.clear();
145
+ }
146
+
147
+ private _result(
148
+ verdict: Verdict,
149
+ subjectKey: string,
150
+ content: string,
151
+ superseded: string | undefined,
152
+ evidence: string[],
153
+ ): CommitResult {
154
+ return { verdict, subjectKey, content, superseded, evidence, version: this._version };
155
+ }
156
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Evermind — reusable evidence gatherers.
3
+ *
4
+ * Concrete, surface-agnostic evidence rules. The presence rule is the one the
5
+ * IDE self-correction loop uses (ground a claim by listing the workspace), kept
6
+ * here so the IDE, the proof harness, and tests share one implementation.
7
+ */
8
+
9
+ import type { EvidenceGatherer } from './types.js';
10
+
11
+ export interface WorkspacePresenceRule {
12
+ /** Lists the workspace (e.g. the IDE `list_files('.')` control tool). */
13
+ list: () => Promise<string[]>;
14
+ /** Entries that MUST be present for the new claim to hold. */
15
+ mustExist?: string[];
16
+ /** Entries that MUST be absent for the new claim to hold. */
17
+ mustBeAbsent?: string[];
18
+ }
19
+
20
+ /**
21
+ * Evidence rule: the new claim is supported iff every `mustExist` entry is
22
+ * present and every `mustBeAbsent` entry is gone from the listing.
23
+ */
24
+ export function workspacePresenceGatherer(rule: WorkspacePresenceRule): EvidenceGatherer {
25
+ const mustExist = rule.mustExist ?? [];
26
+ const mustBeAbsent = rule.mustBeAbsent ?? [];
27
+ return async () => {
28
+ const listing = await rule.list();
29
+ const present = mustExist.filter((d) => listing.includes(d));
30
+ const absent = mustBeAbsent.filter((d) => !listing.includes(d));
31
+ const supportsNew = present.length === mustExist.length && absent.length === mustBeAbsent.length;
32
+ return {
33
+ supportsNew,
34
+ notes: [
35
+ `present: ${present.join(', ') || '—'}`,
36
+ `absent (as expected): ${absent.join(', ') || '—'}`,
37
+ ],
38
+ };
39
+ };
40
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Evermind — Write-Through Cognition.
3
+ *
4
+ * The layer that keeps model knowledge current without a reconciliation step:
5
+ * stable-subject-key beliefs, evidence-gated conflict resolution, replace-on-write.
6
+ */
7
+
8
+ export { EvermindCognition } from './EvermindCognition.js';
9
+ export type { EvermindCognitionOptions } from './EvermindCognition.js';
10
+ export { workspacePresenceGatherer } from './gatherers.js';
11
+ export type { WorkspacePresenceRule } from './gatherers.js';
12
+ export type {
13
+ Claim,
14
+ CognitionFactStore,
15
+ CommitResult,
16
+ EvidenceContext,
17
+ EvidenceGatherer,
18
+ EvidenceResult,
19
+ Verdict,
20
+ } from './types.js';
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Evermind — Write-Through Cognition types.
3
+ *
4
+ * The cognition layer is what lets the model's KNOWLEDGE stay current without a
5
+ * reconciliation step: every incoming fact is evaluated against EVIDENCE and the
6
+ * incumbent belief, then committed write-through (replace-on-write by a STABLE
7
+ * subject key) instead of being appended under a fresh per-run key. These types
8
+ * are intentionally store- and surface-agnostic so the same logic runs in the
9
+ * IDE, on-prem, cloud, and the browser.
10
+ */
11
+
12
+ /** Outcome of evaluating a candidate fact against its incumbent + evidence. */
13
+ export type Verdict =
14
+ /** No incumbent on this subject — stored as a new belief. */
15
+ | 'augment'
16
+ /** Incumbent identical — recency/confidence refreshed, nothing replaced. */
17
+ | 'confirm'
18
+ /** Incumbent conflicted and evidence favoured the new fact — replaced. */
19
+ | 'supersede'
20
+ /** Incumbent conflicted but evidence did NOT favour the new fact — dropped. */
21
+ | 'reject';
22
+
23
+ /**
24
+ * Minimal write-through fact store the cognition layer needs. `MemoryStore`
25
+ * satisfies this structurally (no adapter required) — kept narrow so cloud
26
+ * (Postgres) / browser (IndexedDB) backends can satisfy it too.
27
+ */
28
+ export interface CognitionFactStore {
29
+ remember(
30
+ key: string,
31
+ content: string,
32
+ opts?: { tags?: string[]; importance?: number; ttlMs?: number },
33
+ ): Promise<void>;
34
+ recall(key: string): Promise<{ content: string } | undefined>;
35
+ forget(key: string): Promise<void>;
36
+ }
37
+
38
+ /** A fact asserted about a subject, keyed by a STABLE canonical key. */
39
+ export interface Claim {
40
+ /**
41
+ * STABLE canonical key naming the SUBJECT of the fact (e.g. `pkg:ssm-stack`).
42
+ * This is the anti-drift fix: logically-superseding facts collide on this key
43
+ * and replace, instead of accumulating under per-run keys.
44
+ */
45
+ subjectKey: string;
46
+ /** The asserted fact content. */
47
+ content: string;
48
+ tags?: string[];
49
+ importance?: number;
50
+ /**
51
+ * When true, a brand-new subject is only stored if the gatherer's evidence
52
+ * supports it (guards against recording unverified first-observations).
53
+ */
54
+ requireEvidence?: boolean;
55
+ }
56
+
57
+ /** The verdict of an evidence probe: does ground truth favour the new claim? */
58
+ export interface EvidenceResult {
59
+ supportsNew: boolean;
60
+ /** Audit-readable lines describing what was checked and found. */
61
+ notes: string[];
62
+ }
63
+
64
+ export interface EvidenceContext {
65
+ claim: Claim;
66
+ /** Incumbent belief content for this subject, when one exists. */
67
+ incumbent?: string;
68
+ }
69
+
70
+ /**
71
+ * Gathers ground-truth evidence for a claim. Injected by the caller because the
72
+ * evidence source is surface-specific (IDE file tools, cloud DB, HTTP probe…).
73
+ */
74
+ export type EvidenceGatherer = (ctx: EvidenceContext) => Promise<EvidenceResult>;
75
+
76
+ /** Result of committing a claim through the cognition pipeline. */
77
+ export interface CommitResult {
78
+ verdict: Verdict;
79
+ subjectKey: string;
80
+ /** The belief now held for this subject (incumbent's content on reject). */
81
+ content: string;
82
+ /** Prior content, present only when `verdict === 'supersede'`. */
83
+ superseded?: string;
84
+ /** Evidence audit trail. */
85
+ evidence: string[];
86
+ /** Knowledge-generation token after this commit (bumps on augment/supersede). */
87
+ version: number;
88
+ }
package/src/index.ts CHANGED
@@ -40,6 +40,34 @@ export type {
40
40
  Tokenizer,
41
41
  } from './session/index.js';
42
42
 
43
+ // ── Limbic system (trainable affective dynamics) ──────────────────────────────
44
+ export { LimbicSession } from './limbic/LimbicSession.js';
45
+ export type { LimbicSessionOptions, LimbicGpuMode } from './limbic/LimbicSession.js';
46
+ // Re-export the limbic engine primitives so consumers can use the model/trainer
47
+ // and the region schema directly from @seanhogg/builderforce-memory.
48
+ export {
49
+ LimbicModel,
50
+ LimbicTrainer,
51
+ LIMBIC_DIM,
52
+ LIMBIC_DIM_NAMES,
53
+ LIMBIC_STATE_DIM,
54
+ LIMBIC_BOUNDS,
55
+ NEUTRAL_STATE,
56
+ REGION,
57
+ clampState,
58
+ neutralState,
59
+ stateToRecord,
60
+ recordToState,
61
+ } from '@seanhogg/builderforce-memory-engine';
62
+ export type {
63
+ LimbicModelConfig,
64
+ LimbicForward,
65
+ LimbicSample,
66
+ LimbicTrainOptions,
67
+ LimbicDimName,
68
+ Region,
69
+ } from '@seanhogg/builderforce-memory-engine';
70
+
43
71
  // ── Runtime ───────────────────────────────────────────────────────────────────
44
72
  export { SSMRuntime } from './runtime/SSMRuntime.js';
45
73
  export type { SSMRuntimeOptions, GenerateOptions } from './runtime/SSMRuntime.js';
@@ -92,6 +120,20 @@ export type {
92
120
  FactType,
93
121
  } from './memory/MemoryStore.js';
94
122
 
123
+ // ── Cognition (Evermind — Write-Through Cognition) ────────────────────────────
124
+ export { EvermindCognition, workspacePresenceGatherer } from './cognition/index.js';
125
+ export type {
126
+ EvermindCognitionOptions,
127
+ WorkspacePresenceRule,
128
+ Claim,
129
+ CognitionFactStore,
130
+ CommitResult,
131
+ EvidenceContext,
132
+ EvidenceGatherer,
133
+ EvidenceResult,
134
+ Verdict,
135
+ } from './cognition/index.js';
136
+
95
137
  // ── Distillation ──────────────────────────────────────────────────────────────
96
138
  export { DistillationEngine } from './distillation/DistillationEngine.js';
97
139
  export type {