@seanhogg/builderforce-memory 2026.6.35 → 2026.6.39

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanhogg/builderforce-memory",
3
- "version": "2026.6.35",
3
+ "version": "2026.6.39",
4
4
  "description": "BuilderForce Agent Memory — runtime layer. SSM execution, Transformer orchestration, online distillation, hybrid RAG retrieval, and persistent agent memory for BuilderForce.ai agents.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "homepage": "https://github.com/SeanHogg/builderforce-memory/tree/main/packages/memory#readme",
55
55
  "peerDependencies": {
56
- "@seanhogg/builderforce-memory-engine": "^2026.6.35"
56
+ "@seanhogg/builderforce-memory-engine": "^2026.6.39"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@jest/globals": "^29.7.0",
@@ -67,7 +67,7 @@
67
67
  "ts-jest": "^29.2.0",
68
68
  "typescript": "^6.0.3",
69
69
  "@typescript/native-preview": "7.0.0-dev.20260627.2",
70
- "@seanhogg/builderforce-memory-engine": "2026.6.35"
70
+ "@seanhogg/builderforce-memory-engine": "2026.6.39"
71
71
  },
72
72
  "jest": {
73
73
  "preset": "ts-jest/presets/default-esm",
@@ -85,6 +85,7 @@
85
85
  "strict": false,
86
86
  "esModuleInterop": true,
87
87
  "skipLibCheck": true,
88
+ "rootDir": "..",
88
89
  "types": [
89
90
  "@webgpu/types",
90
91
  "jest"
@@ -94,12 +95,14 @@
94
95
  ]
95
96
  },
96
97
  "moduleNameMapper": {
98
+ "^@seanhogg/builderforce-memory-engine$": "<rootDir>/../memory-engine/src/index.ts",
97
99
  "^(\\.{1,2}/.*)\\.js$": "$1"
98
100
  },
99
101
  "collectCoverageFrom": [
100
102
  "src/**/*.ts",
101
103
  "!src/**/index.ts",
102
- "!src/session/**"
104
+ "!src/session/**",
105
+ "!src/limbic/LimbicSession.ts"
103
106
  ],
104
107
  "coverageThreshold": {
105
108
  "global": {
package/src/index.ts CHANGED
@@ -217,9 +217,12 @@ export {
217
217
  BUILTIN_STEPS,
218
218
  AGENTIC_SEVEN_LAYER,
219
219
  TRAIN_LLM,
220
+ TEACH_CODE,
220
221
  WORKFLOW_TEMPLATES,
221
222
  getTemplate,
222
223
  cloneTemplate,
224
+ analyzeCode,
225
+ runJsCases,
223
226
  } from './workflow/index.js';
224
227
  export type {
225
228
  WorkflowConfig,
@@ -228,6 +231,9 @@ export type {
228
231
  StepFactory,
229
232
  WorkflowValidationError,
230
233
  RunWorkflowOptions,
234
+ CodeAnalysis,
235
+ CodeCase,
236
+ CodeEvalResult,
231
237
  } from './workflow/index.js';
232
238
 
233
239
  // ── Publish (ship an engine model export to HF / local folder) ──────────────────
@@ -24,10 +24,15 @@ import {
24
24
  createBindGroup,
25
25
  dispatchKernel,
26
26
  readBuffer,
27
+ personalitySetpoint,
28
+ clampState,
29
+ neutralState,
30
+ LIMBIC_STATE_DIM,
27
31
  type LimbicModelConfig,
28
32
  type LimbicForward,
29
33
  type LimbicSample,
30
34
  type LimbicTrainOptions,
35
+ type PersonalityTraits,
31
36
  } from "@seanhogg/builderforce-memory-engine";
32
37
 
33
38
  import { saveToIndexedDB, loadFromIndexedDB } from "../session/persistence.js";
@@ -49,6 +54,18 @@ export interface LimbicSessionOptions {
49
54
  modelConfig?: Partial<LimbicModelConfig>;
50
55
  /** Deterministic init seed. */
51
56
  seed?: number;
57
+ /**
58
+ * The agent's static personality traits (0..100). Mapped to the resting affective
59
+ * SETPOINT the learned dynamics ride on top of, so training + relaxation happen
60
+ * around the personality baseline instead of neutral ("personality = setpoints,
61
+ * limbic = dynamics"). Ignored when {@link personalitySetpoint} is given.
62
+ */
63
+ personalityTraits?: PersonalityTraits;
64
+ /**
65
+ * An explicit 8-dim resting setpoint (e.g. one already derived by the runtime's
66
+ * `deriveLimbicSetpoints`). Overrides {@link personalityTraits}. Clamped to bounds.
67
+ */
68
+ personalitySetpoint?: ArrayLike<number>;
52
69
  }
53
70
 
54
71
  interface StepBuffers {
@@ -64,6 +81,11 @@ export class LimbicSession {
64
81
  readonly trainer: LimbicTrainer;
65
82
  readonly device: GPUDevice | null;
66
83
  readonly gpuMode: LimbicGpuMode;
84
+ /** The personality-conditioned resting setpoint (8-dim). NEUTRAL when no
85
+ * personality was supplied. The homeostatic baseline the learned dynamics
86
+ * settle toward — see {@link baselineState}. Mutable via {@link setSetpoint} so a
87
+ * long-lived service can re-point it when the active personas change. */
88
+ setpoint: Float32Array;
67
89
  private readonly _name: string;
68
90
  private readonly _idbFactory: IDBFactory | undefined;
69
91
 
@@ -80,6 +102,7 @@ export class LimbicSession {
80
102
  gpuMode: LimbicGpuMode,
81
103
  name: string,
82
104
  idbFactory: IDBFactory | undefined,
105
+ setpoint: Float32Array,
83
106
  ) {
84
107
  this.model = model;
85
108
  this.trainer = trainer;
@@ -87,6 +110,32 @@ export class LimbicSession {
87
110
  this.gpuMode = gpuMode;
88
111
  this._name = name;
89
112
  this._idbFactory = idbFactory;
113
+ this.setpoint = setpoint;
114
+ }
115
+
116
+ /**
117
+ * A fresh copy of the personality-conditioned resting state. Seed training-sample
118
+ * states and initial affect from this so the learned dynamics move AROUND the
119
+ * agent's personality baseline rather than a fixed neutral. NEUTRAL when the
120
+ * session was created with no personality.
121
+ */
122
+ baselineState(): Float32Array {
123
+ return Float32Array.from(this.setpoint);
124
+ }
125
+
126
+ /**
127
+ * Re-point the resting setpoint after construction — e.g. a long-lived service
128
+ * recomputing it when the active personas change. An explicit 8-dim vector wins;
129
+ * else the traits are mapped; else neutral. Clamped to bounds.
130
+ */
131
+ setSetpoint(traits?: PersonalityTraits, explicit?: ArrayLike<number>): void {
132
+ if (explicit && explicit.length >= LIMBIC_STATE_DIM) {
133
+ this.setpoint = clampState(Float32Array.from(explicit));
134
+ } else if (traits) {
135
+ this.setpoint = personalitySetpoint(traits);
136
+ } else {
137
+ this.setpoint = neutralState();
138
+ }
90
139
  }
91
140
 
92
141
  static async create(options: LimbicSessionOptions = {}): Promise<LimbicSession> {
@@ -129,7 +178,18 @@ export class LimbicSession {
129
178
  model.loadWeights(options.checkpointBuffer);
130
179
  }
131
180
  const trainer = new LimbicTrainer(model, device);
132
- return new LimbicSession(model, trainer, device, gpuMode, options.name ?? "limbic-default", options.idbFactory);
181
+ // Resolve the resting setpoint: an explicit 8-dim vector wins; else map the
182
+ // personality traits; else neutral. This is where the STATIC personality enters
183
+ // the (dynamic, trainable) limbic system.
184
+ let setpoint: Float32Array;
185
+ if (options.personalitySetpoint && options.personalitySetpoint.length >= LIMBIC_STATE_DIM) {
186
+ setpoint = clampState(Float32Array.from(options.personalitySetpoint));
187
+ } else if (options.personalityTraits) {
188
+ setpoint = personalitySetpoint(options.personalityTraits);
189
+ } else {
190
+ setpoint = neutralState();
191
+ }
192
+ return new LimbicSession(model, trainer, device, gpuMode, options.name ?? "limbic-default", options.idbFactory, setpoint);
133
193
  }
134
194
 
135
195
  /** One affect step. Uses the GPU kernel when a device is available, else CPU. */
@@ -0,0 +1,143 @@
1
+ /**
2
+ * workflow/code-eval.ts — dependency-free code-validity + execution-reward checks.
3
+ *
4
+ * These power the code-specific BUILD diagnostics that grade what Evermind's
5
+ * generator produces when we teach it to program:
6
+ * • {@link analyzeCode} — structural validity (balanced delimiters/strings) +
7
+ * an optional JS parse. The portable "does this look
8
+ * like valid code" gate.
9
+ * • {@link runJsCases} — execution-grounded reward: run generated JS against
10
+ * test cases and score the pass-rate. Code's signal is
11
+ * that it RUNS — this turns that into a number.
12
+ *
13
+ * Both are zero-dependency (no AST library) so they run in the same WebGPU/TS
14
+ * runtime as the rest of the engine. Execution uses the `Function` constructor —
15
+ * a heuristic reward sandbox, NOT a security boundary; only feed it code from a
16
+ * trusted teacher / the model under training.
17
+ */
18
+
19
+ const OPEN: Record<string, string> = { ")": "(", "]": "[", "}": "{" };
20
+
21
+ export interface CodeAnalysis {
22
+ /** 0..1 structural-validity score (1 = delimiters and strings all balanced). */
23
+ score: number;
24
+ /** True when every bracket/brace/paren and string literal closes cleanly. */
25
+ balanced: boolean;
26
+ /** For language="js": whether the source parses (Function constructor). undefined otherwise. */
27
+ jsParse?: boolean;
28
+ }
29
+
30
+ /**
31
+ * Score how structurally valid `code` is: walk the source tracking string state
32
+ * (so brackets inside strings are ignored) and a delimiter stack. A clean parse
33
+ * scores 1; each unmatched/mismatched delimiter or unterminated string lowers it.
34
+ */
35
+ export function analyzeCode(code: string, language = "js"): CodeAnalysis {
36
+ const stack: string[] = [];
37
+ let mismatches = 0;
38
+ let delims = 0;
39
+ let inString: string | null = null; // the opening quote char, or null
40
+ let escaped = false;
41
+
42
+ for (let i = 0; i < code.length; i++) {
43
+ const ch = code[i]!;
44
+ if (inString) {
45
+ if (escaped) { escaped = false; continue; }
46
+ if (ch === "\\") { escaped = true; continue; }
47
+ if (ch === inString) inString = null;
48
+ continue;
49
+ }
50
+ if (ch === '"' || ch === "'" || ch === "`") { inString = ch; continue; }
51
+ if (ch === "(" || ch === "[" || ch === "{") { stack.push(ch); delims++; continue; }
52
+ if (ch === ")" || ch === "]" || ch === "}") {
53
+ delims++;
54
+ if (stack.pop() !== OPEN[ch]) mismatches++;
55
+ }
56
+ }
57
+ // Anything left open is an unmatched delimiter; an open string is one more fault.
58
+ mismatches += stack.length;
59
+ const faults = mismatches + (inString ? 1 : 0);
60
+ const denom = Math.max(1, delims + (inString ? 1 : 0));
61
+ const balanced = faults === 0;
62
+ const score = balanced ? 1 : Math.max(0, 1 - faults / denom);
63
+
64
+ const analysis: CodeAnalysis = { score, balanced };
65
+ if (language === "js" || language === "ts" || language === "javascript" || language === "typescript") {
66
+ analysis.jsParse = jsParses(code);
67
+ }
68
+ return analysis;
69
+ }
70
+
71
+ /** True when `code` parses as JS (Function constructor parses but does not run). */
72
+ function jsParses(code: string): boolean {
73
+ try {
74
+ // eslint-disable-next-line no-new-func
75
+ new Function(code);
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ export interface CodeCase {
83
+ /** An expression to evaluate after the code is loaded, e.g. "add(2, 3)". */
84
+ call: string;
85
+ /** The expected result (deep-equal compared). */
86
+ expect: unknown;
87
+ }
88
+
89
+ export interface CodeEvalResult {
90
+ passed: number;
91
+ total: number;
92
+ /** passed / total (0 when there are no cases). */
93
+ passRate: number;
94
+ /** A whole-program error (e.g. syntax error) that failed every case, if any. */
95
+ error?: string;
96
+ }
97
+
98
+ /**
99
+ * Load `code` (which defines functions) and evaluate each case's `call`
100
+ * expression, comparing the result to `expect`. A syntax error in `code` fails
101
+ * every case. Per-case runtime errors fail only that case.
102
+ *
103
+ * NOTE: heuristic reward sandbox, not a security boundary — see file header.
104
+ */
105
+ export function runJsCases(code: string, cases: CodeCase[]): CodeEvalResult {
106
+ const total = cases.length;
107
+ if (total === 0) return { passed: 0, total: 0, passRate: 0 };
108
+
109
+ let passed = 0;
110
+ for (const c of cases) {
111
+ try {
112
+ // Code defines functions (hoisted); the trailing return evaluates the call.
113
+ // eslint-disable-next-line no-new-func
114
+ const fn = new Function(`${code}\n;return (${c.call});`);
115
+ const actual = fn();
116
+ if (deepEqual(actual, c.expect)) passed++;
117
+ } catch (err) {
118
+ // A syntax error throws at construction for ALL cases — report it once.
119
+ if (err instanceof SyntaxError) {
120
+ return { passed: 0, total, passRate: 0, error: err.message };
121
+ }
122
+ // Runtime error → this case simply fails.
123
+ }
124
+ }
125
+ return { passed, total, passRate: passed / total };
126
+ }
127
+
128
+ /** Structural deep equality good enough for JSON-shaped test expectations. */
129
+ function deepEqual(a: unknown, b: unknown): boolean {
130
+ if (a === b) return true;
131
+ if (typeof a !== typeof b || a === null || b === null) return false;
132
+ if (typeof a !== "object") return Object.is(a, b);
133
+ if (Array.isArray(a) || Array.isArray(b)) {
134
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
135
+ return a.every((v, i) => deepEqual(v, b[i]));
136
+ }
137
+ const ao = a as Record<string, unknown>;
138
+ const bo = b as Record<string, unknown>;
139
+ const ak = Object.keys(ao);
140
+ const bk = Object.keys(bo);
141
+ if (ak.length !== bk.length) return false;
142
+ return ak.every((k) => deepEqual(ao[k], bo[k]));
143
+ }
@@ -6,14 +6,22 @@
6
6
 
7
7
  export type { WorkflowConfig, WorkflowStepConfig, StepTypeInfo, StepFactory } from "./types.js";
8
8
  export { StepTypeRegistry, createDefaultRegistry, defaultStepRegistry } from "./registry.js";
9
- export { BUILTIN_STEPS } from "./steps.js";
9
+ export { BUILTIN_STEPS, selectBestCandidate, stripCodeFences } from "./steps.js";
10
10
  export {
11
11
  AGENTIC_SEVEN_LAYER,
12
12
  TRAIN_LLM,
13
+ TEACH_CODE,
13
14
  WORKFLOW_TEMPLATES,
14
15
  getTemplate,
15
16
  cloneTemplate,
16
17
  } from "./templates.js";
18
+ export {
19
+ analyzeCode,
20
+ runJsCases,
21
+ type CodeAnalysis,
22
+ type CodeCase,
23
+ type CodeEvalResult,
24
+ } from "./code-eval.js";
17
25
  export {
18
26
  compileWorkflow,
19
27
  validateWorkflow,