@seanhogg/builderforce-memory-mcp 2026.10.3 → 2026.10.22

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 (72) hide show
  1. package/README.md +22 -5
  2. package/dist/backend.d.ts +26 -0
  3. package/dist/backend.d.ts.map +1 -1
  4. package/dist/backends/memory-store.d.ts +50 -2
  5. package/dist/backends/memory-store.d.ts.map +1 -1
  6. package/dist/backends/memory-store.js +88 -10
  7. package/dist/backends/memory-store.js.map +1 -1
  8. package/dist/bin/http.js +7 -1
  9. package/dist/bin/http.js.map +1 -1
  10. package/dist/bin/install.js +6 -3
  11. package/dist/bin/install.js.map +1 -1
  12. package/dist/bin/stdio.d.ts +9 -4
  13. package/dist/bin/stdio.d.ts.map +1 -1
  14. package/dist/bin/stdio.js +20 -5
  15. package/dist/bin/stdio.js.map +1 -1
  16. package/dist/compaction-tools.d.ts +15 -0
  17. package/dist/compaction-tools.d.ts.map +1 -0
  18. package/dist/compaction-tools.js +192 -0
  19. package/dist/compaction-tools.js.map +1 -0
  20. package/dist/compaction.d.ts +114 -8
  21. package/dist/compaction.d.ts.map +1 -1
  22. package/dist/compaction.js +161 -7
  23. package/dist/compaction.js.map +1 -1
  24. package/dist/index.d.ts +13 -6
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +11 -7
  27. package/dist/index.js.map +1 -1
  28. package/dist/install/claude-hooks.d.ts +5 -0
  29. package/dist/install/claude-hooks.d.ts.map +1 -1
  30. package/dist/install/claude-hooks.js +69 -9
  31. package/dist/install/claude-hooks.js.map +1 -1
  32. package/dist/install/server-spec.d.ts +33 -7
  33. package/dist/install/server-spec.d.ts.map +1 -1
  34. package/dist/install/server-spec.js +59 -9
  35. package/dist/install/server-spec.js.map +1 -1
  36. package/dist/model/recall-probe.d.ts +29 -0
  37. package/dist/model/recall-probe.d.ts.map +1 -0
  38. package/dist/model/recall-probe.js +72 -0
  39. package/dist/model/recall-probe.js.map +1 -0
  40. package/dist/persistence/fact-archive.d.ts +47 -0
  41. package/dist/persistence/fact-archive.d.ts.map +1 -0
  42. package/dist/persistence/fact-archive.js +85 -0
  43. package/dist/persistence/fact-archive.js.map +1 -0
  44. package/dist/persistence/shared-json-file.d.ts +24 -1
  45. package/dist/persistence/shared-json-file.d.ts.map +1 -1
  46. package/dist/persistence/shared-json-file.js +46 -1
  47. package/dist/persistence/shared-json-file.js.map +1 -1
  48. package/dist/restore.d.ts +56 -0
  49. package/dist/restore.d.ts.map +1 -0
  50. package/dist/restore.js +118 -0
  51. package/dist/restore.js.map +1 -0
  52. package/dist/tools.d.ts +13 -0
  53. package/dist/tools.d.ts.map +1 -1
  54. package/dist/tools.js +11 -90
  55. package/dist/tools.js.map +1 -1
  56. package/package.json +6 -6
  57. package/server.json +2 -2
  58. package/src/backend.ts +29 -0
  59. package/src/backends/memory-store.ts +141 -11
  60. package/src/bin/http.ts +7 -1
  61. package/src/bin/install.ts +7 -3
  62. package/src/bin/stdio.ts +20 -5
  63. package/src/compaction-tools.ts +206 -0
  64. package/src/compaction.ts +228 -9
  65. package/src/index.ts +55 -10
  66. package/src/install/claude-hooks.ts +72 -9
  67. package/src/install/server-spec.ts +74 -8
  68. package/src/model/recall-probe.ts +81 -0
  69. package/src/persistence/fact-archive.ts +101 -0
  70. package/src/persistence/shared-json-file.ts +54 -1
  71. package/src/restore.ts +144 -0
  72. package/src/tools.ts +24 -93
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The lossless compaction pair — `memory_compact` and `memory_restore` — split out
3
+ * of tools.ts the same way the gateway and experience families are.
4
+ *
5
+ * `memory_compact` stubs a fact only after archiving its original and proving the
6
+ * local Evermind absorbed it; `memory_restore` brings back the text of stubs that
7
+ * predate archiving (`contentLost`). The rules are compaction.ts / restore.ts; this
8
+ * module is only their I/O against a MemoryBackend.
9
+ */
10
+ import type { MemoryBackend } from "./backend.js";
11
+ import { type MemoryTool } from "./tool-core.js";
12
+ import type { MemoryToolsOptions } from "./tools.js";
13
+ /** Registers `memory_compact` and `memory_restore`. Caller checks the backend is writable. */
14
+ export declare function appendCompactionTools(tools: MemoryTool[], backend: MemoryBackend, opts: Pick<MemoryToolsOptions, "recallProbe" | "readRestoreFile">): void;
15
+ //# sourceMappingURL=compaction-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction-tools.d.ts","sourceRoot":"","sources":["../src/compaction-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,aAAa,EAA4B,MAAM,cAAc,CAAC;AAG5E,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAkCrD,8FAA8F;AAC9F,wBAAgB,qBAAqB,CACjC,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,GAAG,iBAAiB,CAAC,GAClE,IAAI,CAuJN"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * The lossless compaction pair — `memory_compact` and `memory_restore` — split out
3
+ * of tools.ts the same way the gateway and experience families are.
4
+ *
5
+ * `memory_compact` stubs a fact only after archiving its original and proving the
6
+ * local Evermind absorbed it; `memory_restore` brings back the text of stubs that
7
+ * predate archiving (`contentLost`). The rules are compaction.ts / restore.ts; this
8
+ * module is only their I/O against a MemoryBackend.
9
+ */
10
+ import { z } from "zod";
11
+ import { DEFAULT_STUB_CHARS, planLosslessCompaction } from "./compaction.js";
12
+ import { parseRestoreSource, planRestore, restoredArchive } from "./restore.js";
13
+ import { fail, ok } from "./tool-core.js";
14
+ const SKIP_REASONS = {
15
+ not_found: "not found",
16
+ already_compacted: "already compacted",
17
+ not_smaller: "already shorter than its stub",
18
+ no_probe: "no local Evermind model to prove it was absorbed (set BUILDERFORCE_MEMORY_MODEL)",
19
+ probe_failed: "the Evermind model could not answer a recall question about it — not absorbed yet",
20
+ no_archive: "this backend cannot archive the original, so stubbing would lose it",
21
+ };
22
+ /** One-line compaction receipt: what shrank, what it saved, and what was left alone. */
23
+ function renderCompaction(compacted, bytesSaved, skipped) {
24
+ const lines = [`Compacted ${compacted} memory(ies), ${bytesSaved} character(s) reclaimed.`];
25
+ for (const s of skipped)
26
+ lines.push(`• skipped ${s.key} — ${SKIP_REASONS[s.reason]}`);
27
+ return lines.join("\n");
28
+ }
29
+ /** `memory_restore` input: inline JSON always; a file path only where the host may read its disk. */
30
+ function restoreSchema(allowFile) {
31
+ const shape = {
32
+ json: z.string().optional().describe("The export itself, as JSON text (array, or JSON Lines)."),
33
+ };
34
+ if (allowFile)
35
+ shape["file"] = z.string().optional().describe("Path to a JSON export file to restore from.");
36
+ return shape;
37
+ }
38
+ /** Write a batch through the backend's batch path when it has one. */
39
+ async function writeAll(backend, writes) {
40
+ if (writes.length === 0)
41
+ return;
42
+ if (backend.rememberMany)
43
+ await backend.rememberMany(writes);
44
+ else
45
+ for (const w of writes)
46
+ await backend.remember(w);
47
+ }
48
+ /** Registers `memory_compact` and `memory_restore`. Caller checks the backend is writable. */
49
+ export function appendCompactionTools(tools, backend, opts) {
50
+ // memory_compact — the FIRST-CLASS compaction path. It writes through the live
51
+ // store (get → plan → archive → remember), so a durable backend's in-memory state
52
+ // and its on-disk snapshot agree afterwards. It is LOSSLESS: each original is
53
+ // archived before its body is stubbed, and only facts the local Evermind answers
54
+ // a recall probe about are stubbed at all (see planLosslessCompaction).
55
+ tools.push({
56
+ name: "memory_compact",
57
+ description: "Shrink memories that have already been absorbed into the local Evermind model down to " +
58
+ "a one-line pointer stub, reclaiming the context they were eating. Each fact is first " +
59
+ "checked with a recall question put to the model; facts it cannot answer stay as they " +
60
+ "are. The full original is archived and memory_recall / memory_get still return it, so " +
61
+ "nothing is lost. Already-compacted keys are skipped, so repeating the call is safe.",
62
+ inputSchema: {
63
+ keys: z.array(z.string()).min(1).describe("Exact keys of the memories to compact."),
64
+ stub: z
65
+ .string()
66
+ .optional()
67
+ .describe("Explicit replacement body applied to every listed key. Omit to generate " +
68
+ "'[absorbed→Evermind vN] <first line>' from each entry's own first line."),
69
+ version: z
70
+ .number()
71
+ .int()
72
+ .min(0)
73
+ .optional()
74
+ .describe("Version of the model that absorbed these facts; appears in the generated stub."),
75
+ maxChars: z
76
+ .number()
77
+ .int()
78
+ .min(20)
79
+ .max(500)
80
+ .optional()
81
+ .describe(`Max characters of the generated pointer line (default ${DEFAULT_STUB_CHARS}).`),
82
+ },
83
+ handler: async (args) => {
84
+ try {
85
+ const raw = args["keys"];
86
+ const keys = Array.isArray(raw) ? raw.map((k) => String(k)).filter(Boolean) : [];
87
+ if (keys.length === 0)
88
+ return fail("keys is required.");
89
+ // ONE read pass: the hit carries both the body the plan reasons about
90
+ // and the tags/importance the rewrite must preserve. A stub resolved to
91
+ // its archive reports the STORED stub in `hit.stub` — that is what the
92
+ // plan must see, or an archived fact would be compacted again.
93
+ const hits = new Map();
94
+ for (const key of keys) {
95
+ const hit = await backend.get(key);
96
+ if (hit)
97
+ hits.set(key, hit);
98
+ }
99
+ const plan = await planLosslessCompaction(keys.map((key) => {
100
+ const hit = hits.get(key);
101
+ return { key, content: hit ? (hit.stub ?? hit.content) : undefined };
102
+ }), {
103
+ stub: typeof args["stub"] === "string" ? args["stub"] : undefined,
104
+ version: args["version"] == null ? undefined : Number(args["version"]),
105
+ maxChars: args["maxChars"] == null ? undefined : Number(args["maxChars"]),
106
+ probe: opts.recallProbe ?? null,
107
+ canArchive: typeof backend.archive === "function",
108
+ });
109
+ if (plan.writes.length > 0) {
110
+ // Originals FIRST, durably — only then may the bodies become stubs.
111
+ await backend.archive(plan.writes.map((w) => w.archive));
112
+ // Preserve tags/importance — compaction shortens a body, it does not
113
+ // reclassify the fact.
114
+ await writeAll(backend, plan.writes.map((w) => {
115
+ const prev = hits.get(w.key);
116
+ return { key: w.key, content: w.content, tags: prev?.tags, importance: prev?.importance };
117
+ }));
118
+ }
119
+ return ok(renderCompaction(plan.writes.length, plan.bytesSaved, plan.skipped));
120
+ }
121
+ catch (err) {
122
+ return fail(`compact failed: ${String(err)}`);
123
+ }
124
+ },
125
+ });
126
+ // memory_restore — the way back for stubs whose original was never archived
127
+ // (`contentLost`). The text is matched to a stub by key, or by its first line
128
+ // against the stub's pointer, then archived beside the stub (the stub stays, so
129
+ // the token saving stays) — or written back as the body on a backend that
130
+ // cannot archive.
131
+ tools.push({
132
+ name: "memory_restore",
133
+ description: "Restore the full text of memories marked '(content lost)' — compaction stubs whose " +
134
+ "original was never kept — from an export. Accepts [{key, content}] (a memory export) " +
135
+ "or the Evermind coordinator's [{id, text}] record dump; records are matched by key, " +
136
+ "else by their first line against the stub. Restored memories recall in full again.",
137
+ inputSchema: restoreSchema(!!opts.readRestoreFile),
138
+ handler: async (args) => {
139
+ try {
140
+ const file = typeof args["file"] === "string" ? args["file"] : "";
141
+ let text = typeof args["json"] === "string" ? args["json"] : "";
142
+ if (file && opts.readRestoreFile)
143
+ text = await opts.readRestoreFile(file);
144
+ if (!text.trim())
145
+ return fail(opts.readRestoreFile ? "file or json is required." : "json is required.");
146
+ const records = parseRestoreSource(text);
147
+ if (records.length === 0)
148
+ return fail("no restorable records (need {key, content} or {id, text} rows).");
149
+ // Targets: the lost stubs (all of them when the backend can list them,
150
+ // else just the keys the records name).
151
+ const keys = new Set(backend.lostKeys ? await backend.lostKeys() : []);
152
+ for (const r of records)
153
+ if (r.key)
154
+ keys.add(r.key);
155
+ const targets = [];
156
+ const hits = new Map();
157
+ for (const key of keys) {
158
+ const hit = await backend.get(key);
159
+ if (!hit?.contentLost)
160
+ continue;
161
+ hits.set(key, hit);
162
+ targets.push({ key, stub: hit.content });
163
+ }
164
+ const plan = planRestore(records, targets);
165
+ if (plan.matches.length > 0) {
166
+ if (backend.archive) {
167
+ await backend.archive(plan.matches.map((m) => restoredArchive(m, hits.get(m.key).content)));
168
+ }
169
+ else {
170
+ await writeAll(backend, plan.matches.map((m) => {
171
+ const prev = hits.get(m.key);
172
+ return { key: m.key, content: m.content, tags: prev?.tags, importance: prev?.importance };
173
+ }));
174
+ }
175
+ }
176
+ const lines = [
177
+ `Restored ${plan.matches.length} memory(ies) of ${targets.length} with lost content.`,
178
+ ...plan.matches.map((m) => `• ${m.key} (matched by ${m.via})`),
179
+ ];
180
+ if (plan.ambiguous.length > 0)
181
+ lines.push(`Ambiguous (left alone): ${plan.ambiguous.join(", ")}`);
182
+ if (plan.unmatched > 0)
183
+ lines.push(`${plan.unmatched} record(s) matched no lost memory.`);
184
+ return ok(lines.join("\n"));
185
+ }
186
+ catch (err) {
187
+ return fail(`restore failed: ${String(err)}`);
188
+ }
189
+ },
190
+ });
191
+ }
192
+ //# sourceMappingURL=compaction-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction-tools.js","sourceRoot":"","sources":["../src/compaction-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAuB,MAAM,iBAAiB,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,eAAe,EAAsB,MAAM,cAAc,CAAC;AACpG,OAAO,EAAE,IAAI,EAAE,EAAE,EAAmB,MAAM,gBAAgB,CAAC;AAG3D,MAAM,YAAY,GAA6C;IAC3D,SAAS,EAAE,WAAW;IACtB,iBAAiB,EAAE,mBAAmB;IACtC,WAAW,EAAE,+BAA+B;IAC5C,QAAQ,EAAE,kFAAkF;IAC5F,YAAY,EAAE,mFAAmF;IACjG,UAAU,EAAE,qEAAqE;CACpF,CAAC;AAEF,wFAAwF;AACxF,SAAS,gBAAgB,CAAC,SAAiB,EAAE,UAAkB,EAAE,OAAyB;IACtF,MAAM,KAAK,GAAG,CAAC,aAAa,SAAS,iBAAiB,UAAU,0BAA0B,CAAC,CAAC;IAC5F,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,GAAG,MAAM,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,qGAAqG;AACrG,SAAS,aAAa,CAAC,SAAkB;IACrC,MAAM,KAAK,GAAkB;QACzB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yDAAyD,CAAC;KAClG,CAAC;IACF,IAAI,SAAS;QAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC,CAAC;IAC7G,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,sEAAsE;AACtE,KAAK,UAAU,QAAQ,CAAC,OAAsB,EAAE,MAAuB;IACnE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAChC,IAAI,OAAO,CAAC,YAAY;QAAE,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;;QACxD,KAAK,MAAM,CAAC,IAAI,MAAM;YAAE,MAAM,OAAO,CAAC,QAAS,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,qBAAqB,CACjC,KAAmB,EACnB,OAAsB,EACtB,IAAiE;IAEjE,+EAA+E;IAC/E,kFAAkF;IAClF,8EAA8E;IAC9E,iFAAiF;IACjF,wEAAwE;IACxE,KAAK,CAAC,IAAI,CAAC;QACP,IAAI,EAAE,gBAAgB;QACtB,WAAW,EACP,wFAAwF;YACxF,uFAAuF;YACvF,uFAAuF;YACvF,wFAAwF;YACxF,qFAAqF;QACzF,WAAW,EAAE;YACT,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,wCAAwC,CAAC;YACnF,IAAI,EAAE,CAAC;iBACF,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACL,0EAA0E;gBACtE,yEAAyE,CAChF;YACL,OAAO,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,EAAE;iBACV,QAAQ,CAAC,gFAAgF,CAAC;YAC/F,QAAQ,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,EAAE,CAAC;iBACP,GAAG,CAAC,GAAG,CAAC;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,yDAAyD,kBAAkB,IAAI,CAAC;SACjG;QACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACpB,IAAI,CAAC;gBACD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;gBACzB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,IAAI,CAAC,mBAAmB,CAAC,CAAC;gBAExD,sEAAsE;gBACtE,wEAAwE;gBACxE,uEAAuE;gBACvE,+DAA+D;gBAC/D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAqB,CAAC;gBAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACrB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACnC,IAAI,GAAG;wBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBAChC,CAAC;gBAED,MAAM,IAAI,GAAG,MAAM,sBAAsB,CACrC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;oBACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBAC1B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;gBACzE,CAAC,CAAC,EACF;oBACI,IAAI,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,MAAM,CAAY,CAAC,CAAC,CAAC,SAAS;oBAC7E,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACtE,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBACzE,KAAK,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;oBAC/B,UAAU,EAAE,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU;iBACpD,CACJ,CAAC;gBAEF,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACzB,oEAAoE;oBACpE,MAAM,OAAO,CAAC,OAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;oBAC1D,qEAAqE;oBACrE,uBAAuB;oBACvB,MAAM,QAAQ,CACV,OAAO,EACP,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;wBAClB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;wBAC7B,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;oBAC9F,CAAC,CAAC,CACL,CAAC;gBACN,CAAC;gBAED,OAAO,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YACnF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,mBAAmB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClD,CAAC;QACL,CAAC;KACJ,CAAC,CAAC;IAEH,4EAA4E;IAC5E,8EAA8E;IAC9E,gFAAgF;IAChF,0EAA0E;IAC1E,kBAAkB;IAClB,KAAK,CAAC,IAAI,CAAC;QACP,IAAI,EAAE,gBAAgB;QACtB,WAAW,EACP,qFAAqF;YACrF,uFAAuF;YACvF,sFAAsF;YACtF,oFAAoF;QACxF,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC;QAClD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACpB,IAAI,CAAC;gBACD,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,MAAM,CAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9E,IAAI,IAAI,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,MAAM,CAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5E,IAAI,IAAI,IAAI,IAAI,CAAC,eAAe;oBAAE,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;gBAC1E,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;oBAAE,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;gBAExG,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;gBACzC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,IAAI,CAAC,iEAAiE,CAAC,CAAC;gBAEzG,uEAAuE;gBACvE,wCAAwC;gBACxC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC/E,KAAK,MAAM,CAAC,IAAI,OAAO;oBAAE,IAAI,CAAC,CAAC,GAAG;wBAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBACpD,MAAM,OAAO,GAAoB,EAAE,CAAC;gBACpC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAqB,CAAC;gBAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACrB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACnC,IAAI,CAAC,GAAG,EAAE,WAAW;wBAAE,SAAS;oBAChC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;oBACnB,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC7C,CAAC;gBAED,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC3C,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC1B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;wBAClB,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBACjG,CAAC;yBAAM,CAAC;wBACJ,MAAM,QAAQ,CACV,OAAO,EACP,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;4BACnB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;4BAC7B,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;wBAC9F,CAAC,CAAC,CACL,CAAC;oBACN,CAAC;gBACL,CAAC;gBAED,MAAM,KAAK,GAAG;oBACV,YAAY,IAAI,CAAC,OAAO,CAAC,MAAM,mBAAmB,OAAO,CAAC,MAAM,qBAAqB;oBACrF,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,GAAG,GAAG,CAAC;iBACjE,CAAC;gBACF,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClG,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,oCAAoC,CAAC,CAAC;gBAC1F,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,mBAAmB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClD,CAAC;QACL,CAAC;KACJ,CAAC,CAAC;AACP,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Compaction — turning an ABSORBED memory into a terse pointer stub.
2
+ * Compaction — turning an ABSORBED memory into a terse pointer stub, LOSSLESSLY.
3
3
  *
4
4
  * Once a fact has been folded into an Evermind (or any downstream learner) the
5
5
  * full body has done its job: keeping it verbatim in the store means every
@@ -7,10 +7,24 @@
7
7
  * Compaction replaces the body with a one-line `[absorbed→Evermind vN] <first
8
8
  * line>` pointer, so the fact stops filling context while it stays findable.
9
9
  *
10
+ * Two rules make that safe:
11
+ * 1. The full original is ARCHIVED before the body is stubbed — in a sidecar
12
+ * `<memory>.archive.json` beside the snapshot, never inside it, so it survives
13
+ * the snapshot round-trip, an external rewrite of the snapshot, and the
14
+ * store's entry cap / eviction. Recall resolves a stub back to its archive.
15
+ * 2. A fact is stubbed only after a RECALL PROBE passes: a question generated
16
+ * from the fact is put to the local Evermind model, and an answer that does
17
+ * not carry the fact (or no model at all) leaves the fact uncompacted —
18
+ * "absorbed" is proven, not asserted.
19
+ *
20
+ * A stub with no archive is a fact whose text is gone (compacted before rule 1
21
+ * existed): {@link isContentLost} names it, and every reader labels it with
22
+ * {@link CONTENT_LOST_LABEL} until `memory_restore` brings the text back.
23
+ *
10
24
  * The rules live here — not in the tool handler and not in a transport — because
11
25
  * 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.
26
+ * BuilderForce VS Code extension) must produce byte-identical stubs and the same
27
+ * archive; otherwise re-running either one re-stubs the other's work.
14
28
  *
15
29
  * Dependency-free and published as its own subpath
16
30
  * (`@seanhogg/builderforce-memory-mcp/compaction`), so an external compactor imports
@@ -31,23 +45,48 @@ export declare function isStub(content: string): boolean;
31
45
  export declare function firstLine(content: string, max?: number): string;
32
46
  /** Build the terse stub that replaces an absorbed entry's body. */
33
47
  export declare function memoryStub(content: string, version: number, maxChars?: number): string;
48
+ /** The pointer text inside a stub (after the `] `), without a trailing ellipsis. */
49
+ export declare function stubPointer(stub: string): string;
34
50
  /** One entry considered for compaction. */
35
51
  export interface CompactionCandidate {
36
52
  key: string;
37
53
  content: string;
38
54
  }
39
- /** A body rewrite the caller should apply. */
55
+ /**
56
+ * The full original of a compacted fact — what the archive keeps so a stub is
57
+ * never the only copy. Written BEFORE the stub replaces the body.
58
+ */
59
+ export interface ArchivedFact {
60
+ key: string;
61
+ /** The original, full body. */
62
+ content: string;
63
+ /** The stub that replaced it. */
64
+ stub: string;
65
+ /** Evermind version that absorbed it. */
66
+ version: number;
67
+ /** Unix ms the archive row was written. */
68
+ archivedAt: number;
69
+ }
70
+ /** A body rewrite the caller should apply — archive `archive` FIRST, then write `content`. */
40
71
  export interface CompactionWrite {
41
72
  key: string;
42
73
  /** The stub that replaces the entry's body. */
43
74
  content: string;
44
75
  /** Characters recovered by this rewrite. */
45
76
  bytesSaved: number;
77
+ /** The original body, to be archived before the stub is written. */
78
+ archive: ArchivedFact;
46
79
  }
47
80
  /** Why a candidate was left alone. */
48
81
  export interface CompactionSkip {
49
82
  key: string;
50
- reason: "not_found" | "already_compacted" | "not_smaller";
83
+ reason: "not_found" | "already_compacted" | "not_smaller"
84
+ /** No local model to probe with — the fact cannot be proven absorbed. */
85
+ | "no_probe"
86
+ /** The model could not answer the probe question generated from the fact. */
87
+ | "probe_failed"
88
+ /** The caller cannot archive the original, so stubbing would lose it. */
89
+ | "no_archive";
51
90
  }
52
91
  /** The decided plan: what to write, what to leave, and the total recovered. */
53
92
  export interface CompactionPlan {
@@ -63,14 +102,81 @@ export interface CompactionOptions {
63
102
  version?: number;
64
103
  /** Max characters of a generated pointer line. Default {@link DEFAULT_STUB_CHARS}. */
65
104
  maxChars?: number;
105
+ /** Clock for {@link ArchivedFact.archivedAt}. Default `Date.now()`. */
106
+ now?: number;
66
107
  }
67
108
  /**
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.
109
+ * Decide the STRUCTURAL compaction for a set of candidates: what is already a
110
+ * stub, what would not shrink. Pure — no I/O — and NOT the whole contract: a
111
+ * caller must still archive each write's `archive` before applying it, and prove
112
+ * absorption — {@link planLosslessCompaction} runs this and then the probe.
113
+ * `undefined` content means the key was not found.
71
114
  */
72
115
  export declare function planCompaction(candidates: Array<CompactionCandidate | {
73
116
  key: string;
74
117
  content: undefined;
75
118
  }>, opts?: CompactionOptions): CompactionPlan;
119
+ /**
120
+ * The local model compaction asks before it stubs a fact. `answer` returns the
121
+ * model's text for a question, or null when it cannot answer at all.
122
+ */
123
+ export interface RecallProbe {
124
+ answer(question: string): Promise<string | null>;
125
+ }
126
+ /** A question generated from one fact, and the token a correct answer must carry. */
127
+ export interface ProbeQuestion {
128
+ question: string;
129
+ expected: string;
130
+ }
131
+ /**
132
+ * A cloze question over the fact's pointer line: its most identifying token is
133
+ * blanked and the model must supply it. Deterministic, so the same fact always
134
+ * gets the same question. Null when the line has nothing worth blanking.
135
+ */
136
+ export declare function probeQuestionFor(key: string, content: string): ProbeQuestion | null;
137
+ /** True when the model's answer carries the blanked token (case-insensitive). */
138
+ export declare function probePasses(answer: string | null | undefined, expected: string): boolean;
139
+ /** Options for {@link planLosslessCompaction}. */
140
+ export interface LosslessCompactionOptions extends CompactionOptions {
141
+ /** The local model to prove absorption with. Null/absent → nothing is stubbed. */
142
+ probe?: RecallProbe | null;
143
+ /** False when the caller cannot archive originals → nothing is stubbed. Default true. */
144
+ canArchive?: boolean;
145
+ }
146
+ /**
147
+ * The full compaction contract: {@link planCompaction}'s structural rules, then a
148
+ * recall probe per surviving write. A write is kept only when the model answers
149
+ * the question generated from its fact; otherwise the fact stays uncompacted and
150
+ * the reason is recorded. The caller then archives every write's `archive` BEFORE
151
+ * applying its stub.
152
+ */
153
+ export declare function planLosslessCompaction(candidates: Array<CompactionCandidate | {
154
+ key: string;
155
+ content: undefined;
156
+ }>, opts?: LosslessCompactionOptions): Promise<CompactionPlan>;
157
+ /**
158
+ * Label every reader attaches to a stub whose original text no longer exists
159
+ * anywhere (compacted before archiving existed).
160
+ */
161
+ export declare const CONTENT_LOST_LABEL = "(content lost \u2014 re-verify before relying on it)";
162
+ /**
163
+ * Where the archive lives: beside the snapshot, under the same name — the same
164
+ * convention as the vector cache (`memory.json` → `memory.archive.json`).
165
+ */
166
+ export declare function defaultArchiveFile(memoryFile: string): string;
167
+ /** True when `content` is a stub AND no archived original exists for it. */
168
+ export declare function isContentLost(content: string, archived: ArchivedFact | undefined): boolean;
169
+ /** On-disk archive: key → the latest archived original for that key. Never pruned. */
170
+ export interface ArchiveSnapshot {
171
+ version: 1;
172
+ facts: Record<string, ArchivedFact>;
173
+ }
174
+ /** Parse archive text; an absent/corrupt file is an empty archive. */
175
+ export declare function parseArchive(text: string | null | undefined): Record<string, ArchivedFact>;
176
+ /**
177
+ * Merge `facts` into archive text, returning the next text. Rows are only ever
178
+ * added or replaced by a newer archive of the same key — never removed — so a
179
+ * forgotten or evicted stub keeps its original.
180
+ */
181
+ export declare function mergeArchive(text: string | null | undefined, facts: ArchivedFact[]): string;
76
182
  //# sourceMappingURL=compaction.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../src/compaction.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Compaction — turning an ABSORBED memory into a terse pointer stub.
2
+ * Compaction — turning an ABSORBED memory into a terse pointer stub, LOSSLESSLY.
3
3
  *
4
4
  * Once a fact has been folded into an Evermind (or any downstream learner) the
5
5
  * full body has done its job: keeping it verbatim in the store means every
@@ -7,10 +7,24 @@
7
7
  * Compaction replaces the body with a one-line `[absorbed→Evermind vN] <first
8
8
  * line>` pointer, so the fact stops filling context while it stays findable.
9
9
  *
10
+ * Two rules make that safe:
11
+ * 1. The full original is ARCHIVED before the body is stubbed — in a sidecar
12
+ * `<memory>.archive.json` beside the snapshot, never inside it, so it survives
13
+ * the snapshot round-trip, an external rewrite of the snapshot, and the
14
+ * store's entry cap / eviction. Recall resolves a stub back to its archive.
15
+ * 2. A fact is stubbed only after a RECALL PROBE passes: a question generated
16
+ * from the fact is put to the local Evermind model, and an answer that does
17
+ * not carry the fact (or no model at all) leaves the fact uncompacted —
18
+ * "absorbed" is proven, not asserted.
19
+ *
20
+ * A stub with no archive is a fact whose text is gone (compacted before rule 1
21
+ * existed): {@link isContentLost} names it, and every reader labels it with
22
+ * {@link CONTENT_LOST_LABEL} until `memory_restore` brings the text back.
23
+ *
10
24
  * The rules live here — not in the tool handler and not in a transport — because
11
25
  * 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.
26
+ * BuilderForce VS Code extension) must produce byte-identical stubs and the same
27
+ * archive; otherwise re-running either one re-stubs the other's work.
14
28
  *
15
29
  * Dependency-free and published as its own subpath
16
30
  * (`@seanhogg/builderforce-memory-mcp/compaction`), so an external compactor imports
@@ -41,13 +55,23 @@ export function firstLine(content, max = DEFAULT_STUB_CHARS) {
41
55
  export function memoryStub(content, version, maxChars = DEFAULT_STUB_CHARS) {
42
56
  return `${STUB_PREFIX} v${version}] ${firstLine(content, maxChars)}`;
43
57
  }
58
+ /** The pointer text inside a stub (after the `] `), without a trailing ellipsis. */
59
+ export function stubPointer(stub) {
60
+ const s = stub.trim();
61
+ const close = s.indexOf("] ");
62
+ const pointer = isStub(s) && close >= 0 ? s.slice(close + 2) : s;
63
+ return pointer.replace(/…$/, "").trim();
64
+ }
44
65
  /**
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.
66
+ * Decide the STRUCTURAL compaction for a set of candidates: what is already a
67
+ * stub, what would not shrink. Pure — no I/O — and NOT the whole contract: a
68
+ * caller must still archive each write's `archive` before applying it, and prove
69
+ * absorption — {@link planLosslessCompaction} runs this and then the probe.
70
+ * `undefined` content means the key was not found.
48
71
  */
49
72
  export function planCompaction(candidates, opts = {}) {
50
73
  const version = opts.version ?? 0;
74
+ const now = opts.now ?? Date.now();
51
75
  const maxChars = Math.max(20, opts.maxChars ?? DEFAULT_STUB_CHARS);
52
76
  const writes = [];
53
77
  const skipped = [];
@@ -69,9 +93,139 @@ export function planCompaction(candidates, opts = {}) {
69
93
  continue;
70
94
  }
71
95
  const saved = content.length - stub.length;
72
- writes.push({ key: c.key, content: stub, bytesSaved: saved });
96
+ writes.push({
97
+ key: c.key,
98
+ content: stub,
99
+ bytesSaved: saved,
100
+ archive: { key: c.key, content, stub, version, archivedAt: now },
101
+ });
73
102
  bytesSaved += saved;
74
103
  }
75
104
  return { writes, skipped, bytesSaved };
76
105
  }
106
+ const PROBE_BLANK = "____";
107
+ /** Tokens of a line that a cloze question can blank: alphanumeric, 4+ chars. */
108
+ function probeTokens(line) {
109
+ return (line.match(/[A-Za-z0-9][A-Za-z0-9_.-]*[A-Za-z0-9]/g) ?? []).filter((t) => t.length >= 4);
110
+ }
111
+ /** How identifying a token is: digits and capitals beat length (a name/version IS the fact). */
112
+ function salience(token) {
113
+ return (/[0-9]/.test(token) ? 100 : 0) + (/[A-Z]/.test(token) ? 50 : 0) + token.length;
114
+ }
115
+ /**
116
+ * A cloze question over the fact's pointer line: its most identifying token is
117
+ * blanked and the model must supply it. Deterministic, so the same fact always
118
+ * gets the same question. Null when the line has nothing worth blanking.
119
+ */
120
+ export function probeQuestionFor(key, content) {
121
+ const line = firstLine(content, 400);
122
+ const tokens = probeTokens(line);
123
+ if (tokens.length === 0)
124
+ return null;
125
+ let expected = tokens[0];
126
+ for (const t of tokens)
127
+ if (salience(t) > salience(expected))
128
+ expected = t;
129
+ const at = line.indexOf(expected);
130
+ const cloze = `${line.slice(0, at)}${PROBE_BLANK}${line.slice(at + expected.length)}`;
131
+ return {
132
+ question: `Stored memory "${key}": ${cloze}\nThe missing word (${PROBE_BLANK}) is:`,
133
+ expected,
134
+ };
135
+ }
136
+ /** True when the model's answer carries the blanked token (case-insensitive). */
137
+ export function probePasses(answer, expected) {
138
+ if (typeof answer !== "string" || !expected)
139
+ return false;
140
+ return answer.toLowerCase().includes(expected.toLowerCase());
141
+ }
142
+ /**
143
+ * The full compaction contract: {@link planCompaction}'s structural rules, then a
144
+ * recall probe per surviving write. A write is kept only when the model answers
145
+ * the question generated from its fact; otherwise the fact stays uncompacted and
146
+ * the reason is recorded. The caller then archives every write's `archive` BEFORE
147
+ * applying its stub.
148
+ */
149
+ export async function planLosslessCompaction(candidates, opts = {}) {
150
+ const structural = planCompaction(candidates, opts);
151
+ const writes = [];
152
+ const skipped = [...structural.skipped];
153
+ for (const w of structural.writes) {
154
+ if (opts.canArchive === false) {
155
+ skipped.push({ key: w.key, reason: "no_archive" });
156
+ continue;
157
+ }
158
+ if (!opts.probe) {
159
+ skipped.push({ key: w.key, reason: "no_probe" });
160
+ continue;
161
+ }
162
+ const q = probeQuestionFor(w.key, w.archive.content);
163
+ let answer = null;
164
+ if (q) {
165
+ try {
166
+ answer = await opts.probe.answer(q.question);
167
+ }
168
+ catch {
169
+ answer = null;
170
+ }
171
+ }
172
+ if (!q || !probePasses(answer, q.expected)) {
173
+ skipped.push({ key: w.key, reason: "probe_failed" });
174
+ continue;
175
+ }
176
+ writes.push(w);
177
+ }
178
+ return { writes, skipped, bytesSaved: writes.reduce((n, w) => n + w.bytesSaved, 0) };
179
+ }
180
+ // ── Archive + lost content ───────────────────────────────────────────────────
181
+ /**
182
+ * Label every reader attaches to a stub whose original text no longer exists
183
+ * anywhere (compacted before archiving existed).
184
+ */
185
+ export const CONTENT_LOST_LABEL = "(content lost — re-verify before relying on it)";
186
+ /**
187
+ * Where the archive lives: beside the snapshot, under the same name — the same
188
+ * convention as the vector cache (`memory.json` → `memory.archive.json`).
189
+ */
190
+ export function defaultArchiveFile(memoryFile) {
191
+ return memoryFile.replace(/\.json$/i, "") + ".archive.json";
192
+ }
193
+ /** True when `content` is a stub AND no archived original exists for it. */
194
+ export function isContentLost(content, archived) {
195
+ return isStub(content) && !archived;
196
+ }
197
+ /** Parse archive text; an absent/corrupt file is an empty archive. */
198
+ export function parseArchive(text) {
199
+ if (!text)
200
+ return {};
201
+ let parsed;
202
+ try {
203
+ parsed = JSON.parse(text);
204
+ }
205
+ catch {
206
+ return {};
207
+ }
208
+ const facts = parsed && typeof parsed === "object" ? parsed.facts : undefined;
209
+ if (!facts || typeof facts !== "object")
210
+ return {};
211
+ // Null prototype: a memory key is arbitrary text, "__proto__" included.
212
+ const out = Object.create(null);
213
+ for (const [key, f] of Object.entries(facts)) {
214
+ if (f && typeof f.content === "string")
215
+ out[key] = { ...f, key };
216
+ }
217
+ return out;
218
+ }
219
+ /**
220
+ * Merge `facts` into archive text, returning the next text. Rows are only ever
221
+ * added or replaced by a newer archive of the same key — never removed — so a
222
+ * forgotten or evicted stub keeps its original.
223
+ */
224
+ export function mergeArchive(text, facts) {
225
+ const all = parseArchive(text);
226
+ for (const f of facts)
227
+ all[f.key] = f;
228
+ const snap = { version: 1, facts: all };
229
+ return `${JSON.stringify(snap, null, 2)}\n`;
230
+ }
77
231
  //# sourceMappingURL=compaction.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../src/compaction.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../src/compaction.ts"],"names":[],"mappings":"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"}