@seanhogg/builderforce-memory-mcp 2026.7.14 → 2026.9.28

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.
Files changed (58) hide show
  1. package/README.md +47 -5
  2. package/dist/backend.d.ts +32 -0
  3. package/dist/backend.d.ts.map +1 -1
  4. package/dist/backends/memory-store.d.ts +32 -9
  5. package/dist/backends/memory-store.d.ts.map +1 -1
  6. package/dist/backends/memory-store.js +155 -33
  7. package/dist/backends/memory-store.js.map +1 -1
  8. package/dist/bin/http.d.ts +5 -0
  9. package/dist/bin/http.d.ts.map +1 -1
  10. package/dist/bin/http.js +11 -0
  11. package/dist/bin/http.js.map +1 -1
  12. package/dist/bin/install.js +3 -2
  13. package/dist/bin/install.js.map +1 -1
  14. package/dist/bin/stdio.d.ts +17 -7
  15. package/dist/bin/stdio.d.ts.map +1 -1
  16. package/dist/bin/stdio.js +24 -8
  17. package/dist/bin/stdio.js.map +1 -1
  18. package/dist/compaction.d.ts +76 -0
  19. package/dist/compaction.d.ts.map +1 -0
  20. package/dist/compaction.js +77 -0
  21. package/dist/compaction.js.map +1 -0
  22. package/dist/embedding/evermind-embedder.d.ts +119 -0
  23. package/dist/embedding/evermind-embedder.d.ts.map +1 -0
  24. package/dist/embedding/evermind-embedder.js +201 -0
  25. package/dist/embedding/evermind-embedder.js.map +1 -0
  26. package/dist/embedding/index.d.ts +38 -0
  27. package/dist/embedding/index.d.ts.map +1 -0
  28. package/dist/embedding/index.js +44 -0
  29. package/dist/embedding/index.js.map +1 -0
  30. package/dist/index.d.ts +8 -4
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +12 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/install/claude-hooks.d.ts +42 -2
  35. package/dist/install/claude-hooks.d.ts.map +1 -1
  36. package/dist/install/claude-hooks.js +35 -3
  37. package/dist/install/claude-hooks.js.map +1 -1
  38. package/dist/install/server-spec.d.ts +12 -0
  39. package/dist/install/server-spec.d.ts.map +1 -1
  40. package/dist/install/server-spec.js +18 -0
  41. package/dist/install/server-spec.js.map +1 -1
  42. package/dist/tools.d.ts.map +1 -1
  43. package/dist/tools.js +117 -6
  44. package/dist/tools.js.map +1 -1
  45. package/package.json +15 -4
  46. package/server.json +47 -0
  47. package/src/backend.ts +36 -0
  48. package/src/backends/memory-store.ts +184 -35
  49. package/src/bin/http.ts +12 -0
  50. package/src/bin/install.ts +3 -2
  51. package/src/bin/stdio.ts +25 -8
  52. package/src/compaction.ts +126 -0
  53. package/src/embedding/evermind-embedder.ts +276 -0
  54. package/src/embedding/index.ts +54 -0
  55. package/src/index.ts +18 -4
  56. package/src/install/claude-hooks.ts +40 -4
  57. package/src/install/server-spec.ts +25 -0
  58. package/src/tools.ts +133 -12
@@ -5,17 +5,27 @@
5
5
  * Env:
6
6
  * BUILDERFORCE_MEMORY_DB IndexedDB database name (default: MemoryStore default).
7
7
  * BUILDERFORCE_MEMORY_READONLY '1' to disable remember/forget tools.
8
- * BUILDERFORCE_MEMORY_FILE Absolute path to a JSON snapshot. When set, memory
9
- * persists across process restarts — REQUIRED for an
10
- * MCP client that respawns this server each session
11
- * (otherwise fake-indexeddb loses everything on exit).
8
+ * BUILDERFORCE_MEMORY_FILE Absolute path to a JSON snapshot. Defaults to the
9
+ * shared per-machine store (~/.builderforce-memory/
10
+ * memory.json) so memory survives the respawn every
11
+ * MCP client does between sessions — without a
12
+ * snapshot fake-indexeddb loses everything on exit.
13
+ * BUILDERFORCE_MEMORY_MODEL Absolute path to a `.evermind` package (an
14
+ * `evermind-lm`). When set, recall is ranked by SSM
15
+ * embedding cosine FUSED with the lexical ranking
16
+ * instead of lexically alone. Pure CPU — no GPU is
17
+ * involved; see src/embedding/evermind-embedder.ts.
18
+ * BUILDERFORCE_MEMORY_TOKENIZER Tokenizer JSON matching that checkpoint. Defaults
19
+ * to `<model>.tokenizer.json`.
20
+ * BUILDERFORCE_MEMORY_VECTORS Persistent vector-cache path. Defaults beside the
21
+ * snapshot, so a respawned subprocess does not
22
+ * re-embed the whole store on its first recall.
12
23
  * BUILDERFORCE_GATEWAY_URL Gateway base URL (default https://api.builderforce.ai).
13
24
  * BUILDERFORCE_API_KEY `bfk_*` tenant key. When set, exposes the cost tools
14
25
  * (token_usage, model_efficiency).
15
26
  *
16
- * Recall is lexical (Jaccard) here — this headless binary does not stand up the
17
- * SSM runtime/GPU. For SSM-embedding recall, embed the package in-process and
18
- * pass `runtime` to createLocalMemoryStoreBackend (see README).
27
+ * With no model configured recall ranks lexically (BM25) and the tool result SAYS
28
+ * so ("Ranked by: lexical recall (fallback …)"), so a degrade is never silent.
19
29
  */
20
30
  export {};
21
31
  //# sourceMappingURL=stdio.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/bin/stdio.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG"}
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/bin/stdio.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG"}
package/dist/bin/stdio.js CHANGED
@@ -5,23 +5,39 @@
5
5
  * Env:
6
6
  * BUILDERFORCE_MEMORY_DB IndexedDB database name (default: MemoryStore default).
7
7
  * BUILDERFORCE_MEMORY_READONLY '1' to disable remember/forget tools.
8
- * BUILDERFORCE_MEMORY_FILE Absolute path to a JSON snapshot. When set, memory
9
- * persists across process restarts — REQUIRED for an
10
- * MCP client that respawns this server each session
11
- * (otherwise fake-indexeddb loses everything on exit).
8
+ * BUILDERFORCE_MEMORY_FILE Absolute path to a JSON snapshot. Defaults to the
9
+ * shared per-machine store (~/.builderforce-memory/
10
+ * memory.json) so memory survives the respawn every
11
+ * MCP client does between sessions — without a
12
+ * snapshot fake-indexeddb loses everything on exit.
13
+ * BUILDERFORCE_MEMORY_MODEL Absolute path to a `.evermind` package (an
14
+ * `evermind-lm`). When set, recall is ranked by SSM
15
+ * embedding cosine FUSED with the lexical ranking
16
+ * instead of lexically alone. Pure CPU — no GPU is
17
+ * involved; see src/embedding/evermind-embedder.ts.
18
+ * BUILDERFORCE_MEMORY_TOKENIZER Tokenizer JSON matching that checkpoint. Defaults
19
+ * to `<model>.tokenizer.json`.
20
+ * BUILDERFORCE_MEMORY_VECTORS Persistent vector-cache path. Defaults beside the
21
+ * snapshot, so a respawned subprocess does not
22
+ * re-embed the whole store on its first recall.
12
23
  * BUILDERFORCE_GATEWAY_URL Gateway base URL (default https://api.builderforce.ai).
13
24
  * BUILDERFORCE_API_KEY `bfk_*` tenant key. When set, exposes the cost tools
14
25
  * (token_usage, model_efficiency).
15
26
  *
16
- * Recall is lexical (Jaccard) here — this headless binary does not stand up the
17
- * SSM runtime/GPU. For SSM-embedding recall, embed the package in-process and
18
- * pass `runtime` to createLocalMemoryStoreBackend (see README).
27
+ * With no model configured recall ranks lexically (BM25) and the tool result SAYS
28
+ * so ("Ranked by: lexical recall (fallback …)"), so a degrade is never silent.
19
29
  */
20
30
  import { createLocalMemoryStoreBackend } from "../backends/memory-store.js";
31
+ import { createRecallEmbedder } from "../embedding/index.js";
32
+ import { resolveMemoryFile } from "../install/server-spec.js";
21
33
  import { runStdio } from "../transports/stdio.js";
34
+ const memoryFile = resolveMemoryFile();
22
35
  const backend = await createLocalMemoryStoreBackend({
23
36
  dbName: process.env["BUILDERFORCE_MEMORY_DB"],
24
- persistFile: process.env["BUILDERFORCE_MEMORY_FILE"],
37
+ persistFile: memoryFile,
38
+ // Null when no model is configured (or it fails to load) — recall then stays
39
+ // lexical rather than the server refusing to start over an optional model.
40
+ runtime: (await createRecallEmbedder({ memoryFile })) ?? undefined,
25
41
  });
26
42
  await runStdio(backend, {
27
43
  writable: process.env["BUILDERFORCE_MEMORY_READONLY"] !== "1",
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/bin/stdio.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,MAAM,OAAO,GAAG,MAAM,6BAA6B,CAAC;IAChD,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IAC7C,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,QAAQ,CAAC,OAAO,EAAE;IACpB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,KAAK,GAAG;IAC7D,2EAA2E;IAC3E,0EAA0E;IAC1E,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,IAAI,6BAA6B;IACpF,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;CACrD,CAAC,CAAC"}
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/bin/stdio.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC;AAEvC,MAAM,OAAO,GAAG,MAAM,6BAA6B,CAAC;IAChD,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IAC7C,WAAW,EAAE,UAAU;IACvB,6EAA6E;IAC7E,2EAA2E;IAC3E,OAAO,EAAE,CAAC,MAAM,oBAAoB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,IAAI,SAAS;CACrE,CAAC,CAAC;AAEH,MAAM,QAAQ,CAAC,OAAO,EAAE;IACpB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,KAAK,GAAG;IAC7D,2EAA2E;IAC3E,0EAA0E;IAC1E,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,IAAI,6BAA6B;IACpF,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;CACrD,CAAC,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Compaction — turning an ABSORBED memory into a terse pointer stub.
3
+ *
4
+ * Once a fact has been folded into an Evermind (or any downstream learner) the
5
+ * full body has done its job: keeping it verbatim in the store means every
6
+ * subsequent recall pays for text the model already carries in its weights.
7
+ * Compaction replaces the body with a one-line `[absorbed→Evermind vN] <first
8
+ * line>` pointer, so the fact stops filling context while it stays findable.
9
+ *
10
+ * The rules live here — not in the tool handler and not in a transport — because
11
+ * BOTH the in-process `memory_compact` tool and any external compactor (the
12
+ * BuilderForce VS Code extension rewrites the snapshot directly) must produce
13
+ * byte-identical stubs; otherwise re-running either one re-stubs the other's work.
14
+ *
15
+ * Dependency-free and published as its own subpath
16
+ * (`@seanhogg/builderforce-memory-mcp/compaction`), so an external compactor imports
17
+ * these rules instead of re-implementing them — the marker below is the shared
18
+ * idempotency contract between the two processes.
19
+ */
20
+ /**
21
+ * Marker that opens every compacted stub — also the idempotency guard. An entry
22
+ * whose content already starts with this is skipped, so compacting twice (or
23
+ * from two processes) never double-stubs.
24
+ */
25
+ export declare const STUB_PREFIX = "[absorbed\u2192Evermind";
26
+ /** Default cap on the pointer line carried inside a stub. */
27
+ export declare const DEFAULT_STUB_CHARS = 140;
28
+ /** True when a body is already a compaction stub. */
29
+ export declare function isStub(content: string): boolean;
30
+ /** The first non-empty line of a body, trimmed to `max` chars — the stub's pointer text. */
31
+ export declare function firstLine(content: string, max?: number): string;
32
+ /** Build the terse stub that replaces an absorbed entry's body. */
33
+ export declare function memoryStub(content: string, version: number, maxChars?: number): string;
34
+ /** One entry considered for compaction. */
35
+ export interface CompactionCandidate {
36
+ key: string;
37
+ content: string;
38
+ }
39
+ /** A body rewrite the caller should apply. */
40
+ export interface CompactionWrite {
41
+ key: string;
42
+ /** The stub that replaces the entry's body. */
43
+ content: string;
44
+ /** Characters recovered by this rewrite. */
45
+ bytesSaved: number;
46
+ }
47
+ /** Why a candidate was left alone. */
48
+ export interface CompactionSkip {
49
+ key: string;
50
+ reason: "not_found" | "already_compacted" | "not_smaller";
51
+ }
52
+ /** The decided plan: what to write, what to leave, and the total recovered. */
53
+ export interface CompactionPlan {
54
+ writes: CompactionWrite[];
55
+ skipped: CompactionSkip[];
56
+ bytesSaved: number;
57
+ }
58
+ /** Options controlling how a stub body is produced. */
59
+ export interface CompactionOptions {
60
+ /** Explicit replacement body. Omit to generate a pointer from each entry's own first line. */
61
+ stub?: string;
62
+ /** Evermind version that absorbed the facts; appears in a generated stub. Default 0. */
63
+ version?: number;
64
+ /** Max characters of a generated pointer line. Default {@link DEFAULT_STUB_CHARS}. */
65
+ maxChars?: number;
66
+ }
67
+ /**
68
+ * Decide the compaction for a set of candidates. Pure — no I/O — so the same
69
+ * rules are unit-testable and reusable by any caller that can read and write
70
+ * memories. `undefined` content means the key was not found.
71
+ */
72
+ export declare function planCompaction(candidates: Array<CompactionCandidate | {
73
+ key: string;
74
+ content: undefined;
75
+ }>, opts?: CompactionOptions): CompactionPlan;
76
+ //# sourceMappingURL=compaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../src/compaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,4BAAuB,CAAC;AAEhD,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,qDAAqD;AACrD,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAqB,GAAG,MAAM,CAO3E;AAED,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,SAAqB,GAAG,MAAM,CAElG;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,8CAA8C;AAC9C,MAAM,WAAW,eAAe;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,GAAG,mBAAmB,GAAG,aAAa,CAAC;CAC7D;AAED,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,uDAAuD;AACvD,MAAM,WAAW,iBAAiB;IAC9B,8FAA8F;IAC9F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC1B,UAAU,EAAE,KAAK,CAAC,mBAAmB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC,EAC5E,IAAI,GAAE,iBAAsB,GAC7B,cAAc,CA6BhB"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Compaction — turning an ABSORBED memory into a terse pointer stub.
3
+ *
4
+ * Once a fact has been folded into an Evermind (or any downstream learner) the
5
+ * full body has done its job: keeping it verbatim in the store means every
6
+ * subsequent recall pays for text the model already carries in its weights.
7
+ * Compaction replaces the body with a one-line `[absorbed→Evermind vN] <first
8
+ * line>` pointer, so the fact stops filling context while it stays findable.
9
+ *
10
+ * The rules live here — not in the tool handler and not in a transport — because
11
+ * BOTH the in-process `memory_compact` tool and any external compactor (the
12
+ * BuilderForce VS Code extension rewrites the snapshot directly) must produce
13
+ * byte-identical stubs; otherwise re-running either one re-stubs the other's work.
14
+ *
15
+ * Dependency-free and published as its own subpath
16
+ * (`@seanhogg/builderforce-memory-mcp/compaction`), so an external compactor imports
17
+ * these rules instead of re-implementing them — the marker below is the shared
18
+ * idempotency contract between the two processes.
19
+ */
20
+ /**
21
+ * Marker that opens every compacted stub — also the idempotency guard. An entry
22
+ * whose content already starts with this is skipped, so compacting twice (or
23
+ * from two processes) never double-stubs.
24
+ */
25
+ export const STUB_PREFIX = "[absorbed→Evermind";
26
+ /** Default cap on the pointer line carried inside a stub. */
27
+ export const DEFAULT_STUB_CHARS = 140;
28
+ /** True when a body is already a compaction stub. */
29
+ export function isStub(content) {
30
+ return content.trimStart().startsWith(STUB_PREFIX);
31
+ }
32
+ /** The first non-empty line of a body, trimmed to `max` chars — the stub's pointer text. */
33
+ export function firstLine(content, max = DEFAULT_STUB_CHARS) {
34
+ const line = content
35
+ .split(/\r?\n/)
36
+ .map((l) => l.trim())
37
+ .find((l) => l.length > 0) ?? "";
38
+ return line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;
39
+ }
40
+ /** Build the terse stub that replaces an absorbed entry's body. */
41
+ export function memoryStub(content, version, maxChars = DEFAULT_STUB_CHARS) {
42
+ return `${STUB_PREFIX} v${version}] ${firstLine(content, maxChars)}`;
43
+ }
44
+ /**
45
+ * Decide the compaction for a set of candidates. Pure — no I/O — so the same
46
+ * rules are unit-testable and reusable by any caller that can read and write
47
+ * memories. `undefined` content means the key was not found.
48
+ */
49
+ export function planCompaction(candidates, opts = {}) {
50
+ const version = opts.version ?? 0;
51
+ const maxChars = Math.max(20, opts.maxChars ?? DEFAULT_STUB_CHARS);
52
+ const writes = [];
53
+ const skipped = [];
54
+ let bytesSaved = 0;
55
+ for (const c of candidates) {
56
+ const content = c.content;
57
+ if (typeof content !== "string" || !content.trim()) {
58
+ skipped.push({ key: c.key, reason: "not_found" });
59
+ continue;
60
+ }
61
+ if (isStub(content)) {
62
+ skipped.push({ key: c.key, reason: "already_compacted" });
63
+ continue;
64
+ }
65
+ const stub = opts.stub ?? memoryStub(content, version, maxChars);
66
+ // Never grow an entry — a "compaction" that costs tokens is a regression.
67
+ if (stub.length >= content.length) {
68
+ skipped.push({ key: c.key, reason: "not_smaller" });
69
+ continue;
70
+ }
71
+ const saved = content.length - stub.length;
72
+ writes.push({ key: c.key, content: stub, bytesSaved: saved });
73
+ bytesSaved += saved;
74
+ }
75
+ return { writes, skipped, bytesSaved };
76
+ }
77
+ //# sourceMappingURL=compaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../src/compaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * evermind-embedder — SSM-embedding recall for a HEADLESS host, with no GPU.
3
+ *
4
+ * The stdio/HTTP servers used to rank recall by word overlap because standing up
5
+ * the embedding model was assumed to mean standing up WebGPU. It does not:
6
+ * `EvermindLM` is a pure-CPU model and `EvermindLM.embed()` mean-pools + L2-
7
+ * normalises the same hidden state the GPU `HybridMambaModel.embed()` does, to the
8
+ * same contract. So a headless Node process can embed — it just needs the
9
+ * checkpoint and the tokenizer that produced its token ids.
10
+ *
11
+ * This module is the loader for that pair, plus the ONE thing a per-session
12
+ * subprocess needs to make embedding affordable: a vector cache that OUTLIVES the
13
+ * process. Recall embeds the query plus every stored memory; recomputing 200
14
+ * memory embeddings on the first recall of every session would cost more than the
15
+ * lexical ranking it replaces. The cache is stamped with the model's fingerprint,
16
+ * so an adapted checkpoint invalidates it wholesale rather than silently mixing
17
+ * vectors from two different models.
18
+ *
19
+ * It mirrors how this package already persists memories: a JSON file alongside the
20
+ * memory snapshot, written by the process that owns it, tolerant of a corrupt or
21
+ * absent file (drop it and recompute — a cache is never the source of truth).
22
+ */
23
+ /** The seam `MemoryStore.recallRanked(query, k, runtime)` consumes. */
24
+ export interface TextEmbedderLike {
25
+ embed(text: string): Promise<Float32Array>;
26
+ /** Vector width. */
27
+ readonly dimensions: number;
28
+ /** Model identity — vectors are only comparable within one fingerprint. */
29
+ readonly fingerprint: string;
30
+ }
31
+ /** A text embedder that also owns a persistent cache. */
32
+ export interface PersistentTextEmbedder extends TextEmbedderLike {
33
+ /** Write any pending vectors to disk now. Safe to call repeatedly. */
34
+ flush(): void;
35
+ }
36
+ export interface EvermindEmbedderOptions {
37
+ /**
38
+ * Absolute path to a `.evermind` package holding an `evermind-lm` checkpoint.
39
+ * This is the model whose hidden state becomes the embedding.
40
+ */
41
+ modelFile: string;
42
+ /**
43
+ * Absolute path to the tokenizer JSON that produced the checkpoint's token ids
44
+ * (`BPETokenizer.toObject()`, or a Hugging Face `tokenizer.json`).
45
+ *
46
+ * Only needed for a package that does NOT embed its own tokenizer. Token ids are
47
+ * meaningless without the exact vocabulary behind them, so an `.evermind` built
48
+ * with `PackageMeta.tokenizer` carries one and is self-contained — that is
49
+ * preferred over any file, because a file can be the WRONG vocabulary while the
50
+ * embedded one is checksummed and vocab-size-checked against the checkpoint.
51
+ * Resolution order: the package's own tokenizer, then this path, then a sibling
52
+ * `<modelFile>.tokenizer.json`.
53
+ */
54
+ tokenizerFile?: string;
55
+ /**
56
+ * Absolute path for the persistent vector cache. Omit to disable persistence
57
+ * (vectors are still cached in memory for the life of the process).
58
+ */
59
+ cacheFile?: string;
60
+ /** Max vectors retained in the cache (LRU past this). Default 4000. */
61
+ maxEntries?: number;
62
+ /** Debounce before a dirty cache is written back, ms. Default 500. */
63
+ flushDelayMs?: number;
64
+ }
65
+ type FsLike = {
66
+ readFileSync(path: string, enc: "utf8"): string;
67
+ readFileSync(path: string): Uint8Array;
68
+ writeFileSync(path: string, data: string): void;
69
+ existsSync(path: string): boolean;
70
+ };
71
+ /**
72
+ * Build a CPU text embedder from a `.evermind` package + its tokenizer, wrapped in
73
+ * a persistent vector cache.
74
+ *
75
+ * Returns `null` — never throws — when the engine peer is absent, a file is
76
+ * missing, or the package is not an `evermind-lm`. Recall then stays on the
77
+ * lexical ranking and says so, which is the correct degrade: a memory server that
78
+ * refuses to start because an OPTIONAL model is unavailable is worse than one that
79
+ * ranks by word overlap.
80
+ */
81
+ export declare function createEvermindEmbedder(opts: EvermindEmbedderOptions): Promise<PersistentTextEmbedder | null>;
82
+ /**
83
+ * Wraps a text embedder with an LRU vector cache mirrored to a JSON file.
84
+ *
85
+ * Exported for hosts that already hold an embedder (a GPU runtime, say) and want
86
+ * the same cross-process cache — there is one implementation of "cache these
87
+ * vectors on disk", not one per embedder.
88
+ */
89
+ export declare class CachedTextEmbedder implements PersistentTextEmbedder {
90
+ private readonly inner;
91
+ private readonly fs;
92
+ readonly dimensions: number;
93
+ readonly fingerprint: string;
94
+ /** text → vector. Map insertion order is the LRU order. */
95
+ private readonly cache;
96
+ private readonly file;
97
+ private readonly maxEntries;
98
+ private readonly flushDelayMs;
99
+ private dirty;
100
+ private timer;
101
+ constructor(inner: TextEmbedderLike, fs: FsLike, opts: Pick<EvermindEmbedderOptions, "cacheFile" | "maxEntries" | "flushDelayMs">);
102
+ embed(text: string): Promise<Float32Array>;
103
+ /** Write pending vectors now, cancelling any scheduled write. */
104
+ flush(): void;
105
+ private put;
106
+ /**
107
+ * Debounce the write-back: one recall embeds the query plus every new memory,
108
+ * and that burst should cost ONE file write, not one per vector.
109
+ */
110
+ private schedule;
111
+ /**
112
+ * Read the cache back. A cache stamped with a DIFFERENT fingerprint is from a
113
+ * different (or adapted) model, so its vectors are not comparable with this
114
+ * model's — it is dropped whole rather than mixed in.
115
+ */
116
+ private hydrate;
117
+ }
118
+ export {};
119
+ //# sourceMappingURL=evermind-embedder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evermind-embedder.d.ts","sourceRoot":"","sources":["../../src/embedding/evermind-embedder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC3C,oBAAoB;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAChC;AAED,yDAAyD;AACzD,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC5D,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACpC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAkBD,KAAK,MAAM,GAAG;IACV,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAChD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IACvC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACrC,CAAC;AA+BF;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CACxC,IAAI,EAAE,uBAAuB,GAC9B,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAgCxC;AAED;;;;;;GAMG;AACH,qBAAa,kBAAmB,YAAW,sBAAsB;IAazD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,EAAE;IAbvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmC;IACzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,KAAK,CAA8C;gBAGtC,KAAK,EAAE,gBAAgB,EACvB,EAAE,EAAE,MAAM,EAC3B,IAAI,EAAE,IAAI,CAAC,uBAAuB,EAAE,WAAW,GAAG,YAAY,GAAG,cAAc,CAAC;IAU9E,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAahD,iEAAiE;IACjE,KAAK,IAAI,IAAI;IAmBb,OAAO,CAAC,GAAG;IAUX;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAUhB;;;;OAIG;IACH,OAAO,CAAC,OAAO;CAelB"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * evermind-embedder — SSM-embedding recall for a HEADLESS host, with no GPU.
3
+ *
4
+ * The stdio/HTTP servers used to rank recall by word overlap because standing up
5
+ * the embedding model was assumed to mean standing up WebGPU. It does not:
6
+ * `EvermindLM` is a pure-CPU model and `EvermindLM.embed()` mean-pools + L2-
7
+ * normalises the same hidden state the GPU `HybridMambaModel.embed()` does, to the
8
+ * same contract. So a headless Node process can embed — it just needs the
9
+ * checkpoint and the tokenizer that produced its token ids.
10
+ *
11
+ * This module is the loader for that pair, plus the ONE thing a per-session
12
+ * subprocess needs to make embedding affordable: a vector cache that OUTLIVES the
13
+ * process. Recall embeds the query plus every stored memory; recomputing 200
14
+ * memory embeddings on the first recall of every session would cost more than the
15
+ * lexical ranking it replaces. The cache is stamped with the model's fingerprint,
16
+ * so an adapted checkpoint invalidates it wholesale rather than silently mixing
17
+ * vectors from two different models.
18
+ *
19
+ * It mirrors how this package already persists memories: a JSON file alongside the
20
+ * memory snapshot, written by the process that owns it, tolerant of a corrupt or
21
+ * absent file (drop it and recompute — a cache is never the source of truth).
22
+ */
23
+ const DEFAULT_MAX_ENTRIES = 4000;
24
+ const DEFAULT_FLUSH_DELAY_MS = 500;
25
+ /** Indirect import so the optional engine peer is never statically resolved. */
26
+ function dynamicImport(m) {
27
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
28
+ return new Function("m", "return import(m)")(m);
29
+ }
30
+ /**
31
+ * Load the tokenizer spec from `file`, accepting either `BPETokenizer.toObject()`
32
+ * output or a Hugging Face `tokenizer.json`.
33
+ */
34
+ function loadCodec(engine, fs, file) {
35
+ const spec = JSON.parse(fs.readFileSync(file, "utf8"));
36
+ const tok = new engine.BPETokenizer();
37
+ if (spec.vocab && spec.merges)
38
+ tok.loadFromSpec(spec);
39
+ else
40
+ tok.loadHuggingFace(spec);
41
+ return tok;
42
+ }
43
+ /**
44
+ * Build a CPU text embedder from a `.evermind` package + its tokenizer, wrapped in
45
+ * a persistent vector cache.
46
+ *
47
+ * Returns `null` — never throws — when the engine peer is absent, a file is
48
+ * missing, or the package is not an `evermind-lm`. Recall then stays on the
49
+ * lexical ranking and says so, which is the correct degrade: a memory server that
50
+ * refuses to start because an OPTIONAL model is unavailable is worse than one that
51
+ * ranks by word overlap.
52
+ */
53
+ export async function createEvermindEmbedder(opts) {
54
+ try {
55
+ const engine = (await dynamicImport("@seanhogg/builderforce-memory-engine"));
56
+ if (!engine?.EvermindModelPackage || !engine.BPETokenizer || !engine.EvermindTextEmbedder)
57
+ return null;
58
+ const fs = (await dynamicImport("node:fs"));
59
+ if (!fs.existsSync(opts.modelFile))
60
+ return null;
61
+ const bytes = fs.readFileSync(opts.modelFile);
62
+ const blob = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
63
+ const pkg = engine.EvermindModelPackage.fromBlob(blob);
64
+ if (pkg.manifest.modelType !== "evermind-lm")
65
+ return null;
66
+ // Prefer the package's OWN tokenizer. It is checksummed and cross-checked
67
+ // against the checkpoint's vocab size at package time, so it cannot be the
68
+ // wrong vocabulary — which a separate file silently can be. Falling back to a
69
+ // file keeps every package written before the section existed working.
70
+ const embedded = pkg.loadTokenizer?.() ?? null;
71
+ let codec;
72
+ if (embedded) {
73
+ codec = embedded;
74
+ }
75
+ else {
76
+ const tokenizerFile = opts.tokenizerFile ?? `${opts.modelFile}.tokenizer.json`;
77
+ if (!fs.existsSync(tokenizerFile))
78
+ return null;
79
+ codec = loadCodec(engine, fs, tokenizerFile);
80
+ }
81
+ const inner = new engine.EvermindTextEmbedder(pkg.loadLM(), codec);
82
+ return new CachedTextEmbedder(inner, fs, opts);
83
+ }
84
+ catch {
85
+ // Any failure to stand the model up is a degrade, not a fault.
86
+ return null;
87
+ }
88
+ }
89
+ /**
90
+ * Wraps a text embedder with an LRU vector cache mirrored to a JSON file.
91
+ *
92
+ * Exported for hosts that already hold an embedder (a GPU runtime, say) and want
93
+ * the same cross-process cache — there is one implementation of "cache these
94
+ * vectors on disk", not one per embedder.
95
+ */
96
+ export class CachedTextEmbedder {
97
+ inner;
98
+ fs;
99
+ dimensions;
100
+ fingerprint;
101
+ /** text → vector. Map insertion order is the LRU order. */
102
+ cache = new Map();
103
+ file;
104
+ maxEntries;
105
+ flushDelayMs;
106
+ dirty = false;
107
+ timer = null;
108
+ constructor(inner, fs, opts) {
109
+ this.inner = inner;
110
+ this.fs = fs;
111
+ this.dimensions = inner.dimensions;
112
+ this.fingerprint = inner.fingerprint;
113
+ this.file = opts.cacheFile;
114
+ this.maxEntries = Math.max(1, opts.maxEntries ?? DEFAULT_MAX_ENTRIES);
115
+ this.flushDelayMs = Math.max(0, opts.flushDelayMs ?? DEFAULT_FLUSH_DELAY_MS);
116
+ this.hydrate();
117
+ }
118
+ async embed(text) {
119
+ const hit = this.cache.get(text);
120
+ if (hit) {
121
+ // LRU touch — re-insert so this entry becomes most-recently-used.
122
+ this.cache.delete(text);
123
+ this.cache.set(text, hit);
124
+ return hit;
125
+ }
126
+ const vec = await this.inner.embed(text);
127
+ this.put(text, vec);
128
+ return vec;
129
+ }
130
+ /** Write pending vectors now, cancelling any scheduled write. */
131
+ flush() {
132
+ if (this.timer) {
133
+ clearTimeout(this.timer);
134
+ this.timer = null;
135
+ }
136
+ if (!this.dirty || !this.file)
137
+ return;
138
+ const payload = {
139
+ fingerprint: this.fingerprint,
140
+ dimensions: this.dimensions,
141
+ vectors: [...this.cache.entries()].map(([text, v]) => [text, Array.from(v)]),
142
+ };
143
+ try {
144
+ this.fs.writeFileSync(this.file, JSON.stringify(payload));
145
+ this.dirty = false;
146
+ }
147
+ catch {
148
+ // A cache that cannot be written is still a working in-memory cache.
149
+ }
150
+ }
151
+ put(text, vec) {
152
+ if (this.cache.size >= this.maxEntries) {
153
+ const lru = this.cache.keys().next().value;
154
+ if (lru !== undefined)
155
+ this.cache.delete(lru);
156
+ }
157
+ this.cache.set(text, vec);
158
+ this.dirty = true;
159
+ this.schedule();
160
+ }
161
+ /**
162
+ * Debounce the write-back: one recall embeds the query plus every new memory,
163
+ * and that burst should cost ONE file write, not one per vector.
164
+ */
165
+ schedule() {
166
+ if (!this.file || this.timer)
167
+ return;
168
+ this.timer = setTimeout(() => {
169
+ this.timer = null;
170
+ this.flush();
171
+ }, this.flushDelayMs);
172
+ // Never hold the process open for a cache write.
173
+ this.timer.unref?.();
174
+ }
175
+ /**
176
+ * Read the cache back. A cache stamped with a DIFFERENT fingerprint is from a
177
+ * different (or adapted) model, so its vectors are not comparable with this
178
+ * model's — it is dropped whole rather than mixed in.
179
+ */
180
+ hydrate() {
181
+ if (!this.file || !this.fs.existsSync(this.file))
182
+ return;
183
+ let parsed;
184
+ try {
185
+ parsed = JSON.parse(this.fs.readFileSync(this.file, "utf8"));
186
+ }
187
+ catch {
188
+ return;
189
+ }
190
+ if (!parsed || parsed.fingerprint !== this.fingerprint || !Array.isArray(parsed.vectors))
191
+ return;
192
+ for (const row of parsed.vectors) {
193
+ if (!Array.isArray(row) || typeof row[0] !== "string" || !Array.isArray(row[1]))
194
+ continue;
195
+ if (row[1].length !== this.dimensions)
196
+ continue;
197
+ this.cache.set(row[0], Float32Array.from(row[1]));
198
+ }
199
+ }
200
+ }
201
+ //# sourceMappingURL=evermind-embedder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evermind-embedder.js","sourceRoot":"","sources":["../../src/embedding/evermind-embedder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * embedding — stand up the recall embedder for a headless server.
3
+ *
4
+ * Both out-of-process bins (stdio and HTTP) need the SAME decision: is an
5
+ * embedding model configured, and if so where do its vectors get cached? That
6
+ * decision lives here once, so the two servers cannot drift into ranking recall
7
+ * differently from one another.
8
+ */
9
+ import { type PersistentTextEmbedder } from "./evermind-embedder.js";
10
+ export { createEvermindEmbedder, CachedTextEmbedder } from "./evermind-embedder.js";
11
+ export type { EvermindEmbedderOptions, PersistentTextEmbedder, TextEmbedderLike } from "./evermind-embedder.js";
12
+ /** Absolute path to a `.evermind` package (an `evermind-lm`) to embed recall with. */
13
+ export declare const MODEL_FILE_ENV = "BUILDERFORCE_MEMORY_MODEL";
14
+ /** Absolute path to the tokenizer JSON matching that checkpoint. */
15
+ export declare const TOKENIZER_FILE_ENV = "BUILDERFORCE_MEMORY_TOKENIZER";
16
+ /** Absolute path for the persistent vector cache; defaults beside the snapshot. */
17
+ export declare const VECTOR_CACHE_ENV = "BUILDERFORCE_MEMORY_VECTORS";
18
+ /**
19
+ * Where vectors are cached by default: beside the memory snapshot, under the same
20
+ * name. They belong together — the cache is only meaningful for the memories in
21
+ * that snapshot, and pointing a second store at the same vectors would be a cache
22
+ * for the wrong corpus.
23
+ */
24
+ export declare function defaultVectorCacheFile(memoryFile: string): string;
25
+ export interface RecallEmbedderEnv {
26
+ env?: Record<string, string | undefined>;
27
+ /** The snapshot path in effect — used to site the default vector cache. */
28
+ memoryFile: string;
29
+ }
30
+ /**
31
+ * The embedder this server should rank recall with, or `null` when none is
32
+ * configured (recall then ranks lexically and says so).
33
+ *
34
+ * Opt-in by design: no model path, no model. A memory server must start and serve
35
+ * recall on a host that has never downloaded a checkpoint.
36
+ */
37
+ export declare function createRecallEmbedder(opts: RecallEmbedderEnv): Promise<PersistentTextEmbedder | null>;
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/embedding/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAE7F,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACpF,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEhH,sFAAsF;AACtF,eAAO,MAAM,cAAc,8BAA8B,CAAC;AAC1D,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,kCAAkC,CAAC;AAClE,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,gCAAgC,CAAC;AAE9D;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,MAAM,WAAW,iBAAiB;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAS1G"}