@loompad/cli 0.1.0

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 (177) hide show
  1. package/ARCHITECTURE.md +211 -0
  2. package/CHANGELOG.md +470 -0
  3. package/LICENSE +21 -0
  4. package/README.md +630 -0
  5. package/dist/adapters/base.d.ts +71 -0
  6. package/dist/adapters/base.js +187 -0
  7. package/dist/adapters/base.js.map +1 -0
  8. package/dist/adapters/bridges/antigravity.d.ts +53 -0
  9. package/dist/adapters/bridges/antigravity.js +106 -0
  10. package/dist/adapters/bridges/antigravity.js.map +1 -0
  11. package/dist/adapters/bridges/cdp.d.ts +82 -0
  12. package/dist/adapters/bridges/cdp.js +222 -0
  13. package/dist/adapters/bridges/cdp.js.map +1 -0
  14. package/dist/adapters/bridges/gui-chat.d.ts +113 -0
  15. package/dist/adapters/bridges/gui-chat.js +498 -0
  16. package/dist/adapters/bridges/gui-chat.js.map +1 -0
  17. package/dist/adapters/bridges/kiro.d.ts +19 -0
  18. package/dist/adapters/bridges/kiro.js +22 -0
  19. package/dist/adapters/bridges/kiro.js.map +1 -0
  20. package/dist/adapters/bridges/profiles.d.ts +82 -0
  21. package/dist/adapters/bridges/profiles.js +125 -0
  22. package/dist/adapters/bridges/profiles.js.map +1 -0
  23. package/dist/adapters/claude-code.d.ts +26 -0
  24. package/dist/adapters/claude-code.js +209 -0
  25. package/dist/adapters/claude-code.js.map +1 -0
  26. package/dist/adapters/codex.d.ts +51 -0
  27. package/dist/adapters/codex.js +291 -0
  28. package/dist/adapters/codex.js.map +1 -0
  29. package/dist/adapters/echo.d.ts +20 -0
  30. package/dist/adapters/echo.js +79 -0
  31. package/dist/adapters/echo.js.map +1 -0
  32. package/dist/adapters/grok.d.ts +61 -0
  33. package/dist/adapters/grok.js +217 -0
  34. package/dist/adapters/grok.js.map +1 -0
  35. package/dist/adapters/index.d.ts +9 -0
  36. package/dist/adapters/index.js +33 -0
  37. package/dist/adapters/index.js.map +1 -0
  38. package/dist/adapters/opencode.d.ts +64 -0
  39. package/dist/adapters/opencode.js +426 -0
  40. package/dist/adapters/opencode.js.map +1 -0
  41. package/dist/cli/common.d.ts +11 -0
  42. package/dist/cli/common.js +40 -0
  43. package/dist/cli/common.js.map +1 -0
  44. package/dist/cli/doctor.d.ts +12 -0
  45. package/dist/cli/doctor.js +181 -0
  46. package/dist/cli/doctor.js.map +1 -0
  47. package/dist/cli/index.d.ts +8 -0
  48. package/dist/cli/index.js +641 -0
  49. package/dist/cli/index.js.map +1 -0
  50. package/dist/cli/tui-model.d.ts +73 -0
  51. package/dist/cli/tui-model.js +312 -0
  52. package/dist/cli/tui-model.js.map +1 -0
  53. package/dist/cli/tui.d.ts +15 -0
  54. package/dist/cli/tui.js +451 -0
  55. package/dist/cli/tui.js.map +1 -0
  56. package/dist/cli/ui.d.ts +10 -0
  57. package/dist/cli/ui.js +135 -0
  58. package/dist/cli/ui.js.map +1 -0
  59. package/dist/core/ades.d.ts +79 -0
  60. package/dist/core/ades.js +130 -0
  61. package/dist/core/ades.js.map +1 -0
  62. package/dist/core/baton.d.ts +37 -0
  63. package/dist/core/baton.js +92 -0
  64. package/dist/core/baton.js.map +1 -0
  65. package/dist/core/brain-extract.d.ts +111 -0
  66. package/dist/core/brain-extract.js +248 -0
  67. package/dist/core/brain-extract.js.map +1 -0
  68. package/dist/core/brain-index.d.ts +128 -0
  69. package/dist/core/brain-index.js +338 -0
  70. package/dist/core/brain-index.js.map +1 -0
  71. package/dist/core/brain.d.ts +214 -0
  72. package/dist/core/brain.js +425 -0
  73. package/dist/core/brain.js.map +1 -0
  74. package/dist/core/claude-cli.d.ts +12 -0
  75. package/dist/core/claude-cli.js +47 -0
  76. package/dist/core/claude-cli.js.map +1 -0
  77. package/dist/core/distill.d.ts +19 -0
  78. package/dist/core/distill.js +88 -0
  79. package/dist/core/distill.js.map +1 -0
  80. package/dist/core/eventlog.d.ts +43 -0
  81. package/dist/core/eventlog.js +211 -0
  82. package/dist/core/eventlog.js.map +1 -0
  83. package/dist/core/git.d.ts +203 -0
  84. package/dist/core/git.js +460 -0
  85. package/dist/core/git.js.map +1 -0
  86. package/dist/core/logbook.d.ts +59 -0
  87. package/dist/core/logbook.js +118 -0
  88. package/dist/core/logbook.js.map +1 -0
  89. package/dist/core/memory.d.ts +34 -0
  90. package/dist/core/memory.js +124 -0
  91. package/dist/core/memory.js.map +1 -0
  92. package/dist/core/notify.d.ts +11 -0
  93. package/dist/core/notify.js +30 -0
  94. package/dist/core/notify.js.map +1 -0
  95. package/dist/core/projection.d.ts +20 -0
  96. package/dist/core/projection.js +96 -0
  97. package/dist/core/projection.js.map +1 -0
  98. package/dist/core/registry.d.ts +76 -0
  99. package/dist/core/registry.js +147 -0
  100. package/dist/core/registry.js.map +1 -0
  101. package/dist/core/router.d.ts +42 -0
  102. package/dist/core/router.js +99 -0
  103. package/dist/core/router.js.map +1 -0
  104. package/dist/core/routes.d.ts +93 -0
  105. package/dist/core/routes.js +399 -0
  106. package/dist/core/routes.js.map +1 -0
  107. package/dist/core/search.d.ts +63 -0
  108. package/dist/core/search.js +0 -0
  109. package/dist/core/search.js.map +1 -0
  110. package/dist/core/setup.d.ts +70 -0
  111. package/dist/core/setup.js +250 -0
  112. package/dist/core/setup.js.map +1 -0
  113. package/dist/core/suggestions.d.ts +15 -0
  114. package/dist/core/suggestions.js +38 -0
  115. package/dist/core/suggestions.js.map +1 -0
  116. package/dist/core/worktree.d.ts +32 -0
  117. package/dist/core/worktree.js +83 -0
  118. package/dist/core/worktree.js.map +1 -0
  119. package/dist/daemon/app-page.d.ts +32 -0
  120. package/dist/daemon/app-page.js +6363 -0
  121. package/dist/daemon/app-page.js.map +1 -0
  122. package/dist/daemon/auth.d.ts +51 -0
  123. package/dist/daemon/auth.js +135 -0
  124. package/dist/daemon/auth.js.map +1 -0
  125. package/dist/daemon/board.d.ts +117 -0
  126. package/dist/daemon/board.js +202 -0
  127. package/dist/daemon/board.js.map +1 -0
  128. package/dist/daemon/brand-icons.d.ts +29 -0
  129. package/dist/daemon/brand-icons.js +44 -0
  130. package/dist/daemon/brand-icons.js.map +1 -0
  131. package/dist/daemon/client.d.ts +127 -0
  132. package/dist/daemon/client.js +237 -0
  133. package/dist/daemon/client.js.map +1 -0
  134. package/dist/daemon/geist-font.d.ts +10 -0
  135. package/dist/daemon/geist-font.js +783 -0
  136. package/dist/daemon/geist-font.js.map +1 -0
  137. package/dist/daemon/guards.d.ts +18 -0
  138. package/dist/daemon/guards.js +40 -0
  139. package/dist/daemon/guards.js.map +1 -0
  140. package/dist/daemon/linear.d.ts +59 -0
  141. package/dist/daemon/linear.js +117 -0
  142. package/dist/daemon/linear.js.map +1 -0
  143. package/dist/daemon/push.d.ts +22 -0
  144. package/dist/daemon/push.js +58 -0
  145. package/dist/daemon/push.js.map +1 -0
  146. package/dist/daemon/runtime.d.ts +299 -0
  147. package/dist/daemon/runtime.js +1016 -0
  148. package/dist/daemon/runtime.js.map +1 -0
  149. package/dist/daemon/server.d.ts +125 -0
  150. package/dist/daemon/server.js +1728 -0
  151. package/dist/daemon/server.js.map +1 -0
  152. package/dist/daemon/tasks.d.ts +153 -0
  153. package/dist/daemon/tasks.js +264 -0
  154. package/dist/daemon/tasks.js.map +1 -0
  155. package/dist/daemon/terminals.d.ts +82 -0
  156. package/dist/daemon/terminals.js +372 -0
  157. package/dist/daemon/terminals.js.map +1 -0
  158. package/dist/index.d.ts +20 -0
  159. package/dist/index.js +20 -0
  160. package/dist/index.js.map +1 -0
  161. package/dist/sdk.d.ts +17 -0
  162. package/dist/sdk.js +17 -0
  163. package/dist/sdk.js.map +1 -0
  164. package/dist/types.d.ts +236 -0
  165. package/dist/types.js +18 -0
  166. package/dist/types.js.map +1 -0
  167. package/docs/SETUP.md +265 -0
  168. package/docs/adapters.md +132 -0
  169. package/docs/design-system.md +245 -0
  170. package/docs/img/board-linear.png +0 -0
  171. package/docs/img/board-projects.png +0 -0
  172. package/docs/img/brain.png +0 -0
  173. package/docs/img/settings.png +0 -0
  174. package/docs/img/workspace.png +0 -0
  175. package/docs/integration-notes.md +76 -0
  176. package/docs/proposals/brain.md +262 -0
  177. package/package.json +84 -0
@@ -0,0 +1,338 @@
1
+ /**
2
+ * Retrieval — finding the memories a turn is actually about.
3
+ *
4
+ * Two channels, unioned:
5
+ *
6
+ * entities — exact matches on the literal strings a codebase is made of:
7
+ * src/core/git.ts, AgentBase, EADDRINUSE, #42. High precision,
8
+ * zero cost, and available because our domain's entities are
9
+ * strings that appear verbatim rather than noun phrases someone
10
+ * has to infer.
11
+ * BM25 — ordinary lexical scoring over the lemmatised text, for the
12
+ * memories nobody thought to tag.
13
+ *
14
+ * The union is the whole point, and it's where this consciously parts company
15
+ * with the reference. mem0 builds its candidate set from the dense results only
16
+ * (`main.py:1622`) and then lets BM25 and entity boosts re-rank *within* it —
17
+ * so a memory BM25 matches perfectly but vector search missed is unreachable at
18
+ * any score. That isn't hybrid retrieval, it's dense recall wearing a hybrid
19
+ * costume. With no dense channel at all, our recall IS entities ∪ BM25; if
20
+ * either finds something, it's a candidate.
21
+ *
22
+ * Two more things worth taking from mem0, and two worth refusing:
23
+ *
24
+ * take: memory_count_weight (`main.py:1758`) — an inverse-quadratic IDF
25
+ * surrogate. An entity linked to one memory weighs 1.0; to a hundred,
26
+ * 0.09. Common entities suppress themselves. Ten lines, no corpus
27
+ * statistics, no tuning.
28
+ * take: over-fetch before the cut, so scoring has room to reorder.
29
+ * refuse: its query-dependent divisor (`scoring.py:97`), which makes a score
30
+ * mean different things in different queries — a memory scoring 0.9 on
31
+ * semantics alone drops to 0.45 the moment some *other* memory in the
32
+ * same query happens to match a keyword. Ours is fixed.
33
+ * refuse: filtering expiry after the top-k cut (`main.py:1626`), where dead
34
+ * rows silently eat the recall budget. Ours are gone before we rank.
35
+ */
36
+ import { isExpired, lemmatize } from "./brain.js";
37
+ /** BM25 constants. The literature's defaults; we have no corpus to tune on. */
38
+ const K1 = 1.2;
39
+ const B = 0.75;
40
+ /** How much a matched entity can contribute, before IDF weighting. */
41
+ const ENTITY_WEIGHT = 0.5;
42
+ /** Fixed, so scores mean the same thing across queries. */
43
+ const MAX_SCORE = 1 + ENTITY_WEIGHT;
44
+ /**
45
+ * Constraints and failures outrank facts at equal score. Getting burned by the
46
+ * same footgun twice is worse than not knowing a detail, so when the ranking is
47
+ * a coin flip, break it toward the memory that prevents damage.
48
+ */
49
+ const KIND_BIAS = {
50
+ constraint: 0.06,
51
+ failure: 0.06,
52
+ decision: 0.03,
53
+ convention: 0.02,
54
+ fact: 0,
55
+ task: 0,
56
+ };
57
+ function tokens(text) {
58
+ return lemmatize(text).split(" ").filter(Boolean);
59
+ }
60
+ /** Entities are matched case-insensitively; everything else is exact. */
61
+ function entKey(s) {
62
+ return s.trim().toLowerCase();
63
+ }
64
+ /**
65
+ * The keys one entity is findable under.
66
+ *
67
+ * A path gets its basename too, because people say "the backtick thing in
68
+ * app-page.ts" and mean src/daemon/app-page.ts. Without this the precision
69
+ * channel only fires when someone types the whole path, which is exactly when
70
+ * they least need help finding it.
71
+ *
72
+ * The basename is a separate key with its own posting list, so its IDF weight
73
+ * is computed on its own merits: if four files are named index.ts, "index.ts"
74
+ * suppresses itself and the full paths stay sharp.
75
+ */
76
+ function entAliases(e) {
77
+ const k = entKey(e);
78
+ if (!k)
79
+ return [];
80
+ if (!k.includes("/"))
81
+ return [k];
82
+ const base = k.split("/").pop();
83
+ return base && base !== k ? [k, base] : [k];
84
+ }
85
+ /**
86
+ * An inverted index over a set of memories.
87
+ *
88
+ * Rebuilt from the fold rather than maintained incrementally. At project scale —
89
+ * hundreds to low thousands of memories — building it is well under a
90
+ * millisecond, and an index that can't drift from its source is worth more than
91
+ * one that's marginally faster to update. If that stops being true the shape
92
+ * here doesn't change; only when we rebuild does.
93
+ */
94
+ export class MemoryIndex {
95
+ /** term → memory id → term frequency */
96
+ postings = new Map();
97
+ /** entity (lowercased) → memory ids */
98
+ entities = new Map();
99
+ lengths = new Map();
100
+ avgLen = 0;
101
+ docs = [];
102
+ constructor(memories) {
103
+ let total = 0;
104
+ for (const m of memories) {
105
+ this.docs.push(m);
106
+ const toks = m.lemmas ? m.lemmas.split(" ").filter(Boolean) : tokens(m.text);
107
+ this.lengths.set(m.id, toks.length);
108
+ total += toks.length;
109
+ for (const t of toks) {
110
+ let p = this.postings.get(t);
111
+ if (!p)
112
+ this.postings.set(t, (p = new Map()));
113
+ p.set(m.id, (p.get(m.id) ?? 0) + 1);
114
+ }
115
+ for (const e of m.entities) {
116
+ for (const k of entAliases(e)) {
117
+ let s = this.entities.get(k);
118
+ if (!s)
119
+ this.entities.set(k, (s = new Set()));
120
+ s.add(m.id);
121
+ }
122
+ }
123
+ }
124
+ this.avgLen = memories.length ? total / memories.length : 0;
125
+ this.byId = new Map(memories.map((m) => [m.id, m]));
126
+ }
127
+ byId;
128
+ get size() {
129
+ return this.docs.length;
130
+ }
131
+ /** Standard BM25. Returns id → score for every doc matching any term. */
132
+ bm25(query) {
133
+ const scores = new Map();
134
+ const N = this.docs.length;
135
+ if (!N)
136
+ return scores;
137
+ for (const term of new Set(tokens(query))) {
138
+ const posting = this.postings.get(term);
139
+ if (!posting)
140
+ continue;
141
+ const df = posting.size;
142
+ // +0.5/+1 smoothing keeps idf positive for a term in every document,
143
+ // which would otherwise go negative and *penalise* a match.
144
+ const idf = Math.log(1 + (N - df + 0.5) / (df + 0.5));
145
+ for (const [id, tf] of posting) {
146
+ const len = this.lengths.get(id) ?? 0;
147
+ const norm = this.avgLen > 0 ? 1 - B + B * (len / this.avgLen) : 1;
148
+ const score = idf * ((tf * (K1 + 1)) / (tf + K1 * norm));
149
+ scores.set(id, (scores.get(id) ?? 0) + score);
150
+ }
151
+ }
152
+ return scores;
153
+ }
154
+ /**
155
+ * Entity postings, IDF-weighted the way mem0 does it (`main.py:1758`): an
156
+ * entity linked to n memories contributes 1/(1+0.001·(n-1)²). One memory →
157
+ * full weight; a hundred → nearly nothing. So `src/types.ts`, which half the
158
+ * memories will mention, stops drowning out the one that matters, without
159
+ * anyone maintaining a stoplist.
160
+ *
161
+ * Per-memory contributions take the max rather than the sum — matching two
162
+ * entities shouldn't outrank matching one that's far more specific.
163
+ */
164
+ entityHits(terms) {
165
+ const out = new Map();
166
+ for (const term of new Set(terms.map(entKey).filter(Boolean))) {
167
+ const ids = this.entities.get(term);
168
+ if (!ids)
169
+ continue;
170
+ const n = ids.size;
171
+ const weight = 1 / (1 + 0.001 * (n - 1) ** 2);
172
+ const score = ENTITY_WEIGHT * weight;
173
+ for (const id of ids) {
174
+ const cur = out.get(id);
175
+ if (!cur)
176
+ out.set(id, { score, matched: [term] });
177
+ else {
178
+ cur.matched.push(term);
179
+ cur.score = Math.max(cur.score, score);
180
+ }
181
+ }
182
+ }
183
+ return out;
184
+ }
185
+ memory(id) {
186
+ return this.byId.get(id);
187
+ }
188
+ }
189
+ /**
190
+ * Pull the entity-ish strings out of a query.
191
+ *
192
+ * Looser than brain.ts's extractEntities, and deliberately so: that one guards
193
+ * what gets *stored*, where a wrong entity poisons the index forever. This one
194
+ * only guards what gets *looked up*, where a wrong guess costs one failed Map
195
+ * lookup. Asymmetric stakes, asymmetric strictness.
196
+ */
197
+ export function queryEntities(query, files = []) {
198
+ const out = new Set(files.map((f) => f.trim()).filter(Boolean));
199
+ for (const m of query.matchAll(/\b[\w@.-]+(?:\/[\w@.-]+)+\b/g))
200
+ out.add(m[0]);
201
+ for (const m of query.matchAll(/\b[\w-]+\.(?:ts|tsx|js|jsx|json|md|py|rs|go|sh|yml|yaml|toml)\b/g))
202
+ out.add(m[0]);
203
+ for (const m of query.matchAll(/\b[A-Za-z][a-z0-9]+(?:[A-Z][a-z0-9]+)+\b/g))
204
+ out.add(m[0]);
205
+ for (const m of query.matchAll(/\b[A-Z][A-Z0-9]{2,}(?:_[A-Z0-9]+)*\b/g))
206
+ out.add(m[0]);
207
+ for (const m of query.matchAll(/#\d+\b/g))
208
+ out.add(m[0]);
209
+ for (const m of query.matchAll(/`([^`\n]{2,60})`/g))
210
+ if (m[1])
211
+ out.add(m[1]);
212
+ // A file's basename is worth trying too: "git.ts" should find memories about
213
+ // src/core/git.ts even when the query never spells the path out.
214
+ for (const f of files) {
215
+ const base = f.split("/").pop();
216
+ if (base && base !== f)
217
+ out.add(base);
218
+ }
219
+ return [...out];
220
+ }
221
+ /**
222
+ * Find the memories relevant to a piece of work.
223
+ *
224
+ * Pure over the memories handed in — the Brain owns the fold, this owns the
225
+ * ranking, and neither needs the other to be a class.
226
+ */
227
+ export function retrieveFrom(memories, opts) {
228
+ const now = Date.now();
229
+ const limit = opts.limit ?? 12;
230
+ // Filter FIRST — scope, confidence, expiry — so dead and out-of-scope rows
231
+ // never consume the ranking budget. mem0 does this after its over-fetch and
232
+ // silently loses recall to rows that were never eligible.
233
+ const live = memories.filter((m) => {
234
+ if (isExpired(m, now))
235
+ return false;
236
+ if (opts.minConfidence !== undefined && m.confidence < opts.minConfidence)
237
+ return false;
238
+ if (opts.agent && m.scope.agent && m.scope.agent !== opts.agent)
239
+ return false;
240
+ if (opts.chat && m.scope.chat && m.scope.chat !== opts.chat)
241
+ return false;
242
+ return true;
243
+ });
244
+ if (!live.length)
245
+ return [];
246
+ const index = new MemoryIndex(live);
247
+ const query = opts.query ?? "";
248
+ const ents = queryEntities(query, opts.files ?? []);
249
+ const bm = query.trim() ? index.bm25(query) : new Map();
250
+ const eh = ents.length ? index.entityHits(ents) : new Map();
251
+ // The union. Either channel alone is enough to be a candidate.
252
+ const ids = new Set([...bm.keys(), ...eh.keys()]);
253
+ if (!ids.size)
254
+ return [];
255
+ // BM25 is unbounded, so normalise against the best hit in this query to get
256
+ // it onto the same 0..1 footing as the entity channel. Relative-to-best is
257
+ // honest here in a way mem0's adaptive divisor isn't: it rescales the axis,
258
+ // it doesn't change what the total can mean.
259
+ let maxBm = 0;
260
+ for (const s of bm.values())
261
+ if (s > maxBm)
262
+ maxBm = s;
263
+ const hits = [];
264
+ for (const id of ids) {
265
+ const memory = index.memory(id);
266
+ if (!memory)
267
+ continue;
268
+ const bmNorm = maxBm > 0 ? (bm.get(id) ?? 0) / maxBm : 0;
269
+ const ent = eh.get(id);
270
+ const bias = KIND_BIAS[memory.kind] ?? 0;
271
+ const raw = bmNorm + (ent?.score ?? 0);
272
+ const final = Math.min(1, raw / MAX_SCORE + bias);
273
+ hits.push({
274
+ memory,
275
+ score: final,
276
+ ...(opts.explain
277
+ ? {
278
+ detail: {
279
+ bm25: round(bmNorm),
280
+ entity: round(ent?.score ?? 0),
281
+ kindBias: bias,
282
+ matchedEntities: ent?.matched ?? [],
283
+ final: round(final),
284
+ },
285
+ }
286
+ : {}),
287
+ });
288
+ }
289
+ hits.sort((a, b) => b.score - a.score || b.memory.updatedAt - a.memory.updatedAt);
290
+ return hits.slice(0, limit);
291
+ }
292
+ function round(n) {
293
+ return Math.round(n * 1000) / 1000;
294
+ }
295
+ /** The same, against a live Brain. */
296
+ export function retrieve(brain, opts) {
297
+ return retrieveFrom(brain.all(), opts);
298
+ }
299
+ const KIND_LABEL = {
300
+ constraint: "Constraints reality imposes",
301
+ failure: "Known failures — do not repeat these",
302
+ decision: "Decisions and why",
303
+ convention: "How this project does things",
304
+ fact: "Facts about the project",
305
+ task: "Task notes",
306
+ };
307
+ /**
308
+ * Render retrieved memories into a brief for an agent taking the baton.
309
+ *
310
+ * Grouped by kind with constraints and failures first, because those are the
311
+ * ones that prevent damage — the reader should hit "don't do X, it breaks Y"
312
+ * before "the port is 7420". Returns "" for no memories, so a caller can append
313
+ * it unconditionally.
314
+ */
315
+ export function compileBrief(memories) {
316
+ if (!memories.length)
317
+ return "";
318
+ const order = ["constraint", "failure", "decision", "convention", "fact", "task"];
319
+ const byKind = new Map();
320
+ for (const m of memories) {
321
+ const arr = byKind.get(m.kind);
322
+ if (arr)
323
+ arr.push(m);
324
+ else
325
+ byKind.set(m.kind, [m]);
326
+ }
327
+ const lines = ["## What this project has learned (relevant to the work at hand)"];
328
+ for (const kind of order) {
329
+ const ms = byKind.get(kind);
330
+ if (!ms?.length)
331
+ continue;
332
+ lines.push("", `### ${KIND_LABEL[kind]}`);
333
+ for (const m of ms)
334
+ lines.push(`- ${m.text}`);
335
+ }
336
+ return lines.join("\n");
337
+ }
338
+ //# sourceMappingURL=brain-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-index.js","sourceRoot":"","sources":["../../src/core/brain-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * The brain — memory as units instead of a sliding window.
3
+ *
4
+ * What was here before (and still is, next door in memory.ts) concatenates:
5
+ * every ADE's CLAUDE.md, truncated at 8k, plus the last 40 decisions, plus the
6
+ * last 30 messages, glued together and handed to whoever takes the baton. That
7
+ * is a recency window, not a memory. Nothing is learned from a turn, nothing is
8
+ * reconciled when two facts disagree, nothing is retrieved because it's
9
+ * relevant, and nothing is ever forgotten — growth is bounded by truncation,
10
+ * which throws away the durable architectural facts first and keeps the last
11
+ * thirty lines of chatter.
12
+ *
13
+ * This module is the other thing: addressable units that can be added, updated,
14
+ * forgotten, and looked up by what they're about.
15
+ *
16
+ * There is no new store. A memory IS three event kinds in the log that already
17
+ * exists — memory_add / memory_update / memory_forget. Current state is a fold
18
+ * over them; history is a filter over the same bytes; and the two cannot drift
19
+ * apart, because they are the same bytes. mem0 needs a second SQLite database
20
+ * to get the history that falls out of this for free. types.ts has said from
21
+ * the beginning that the log is the source of truth and everything else is a
22
+ * projection of it — this is that, taken literally.
23
+ *
24
+ * Phase 0 (this file) and phase 1 (retrieval, brain-index.ts) are deterministic
25
+ * and LLM-free by design: prove the store and the recall before spending a
26
+ * token on extraction. See docs/proposals/brain.md.
27
+ */
28
+ import type { EventLog } from "./eventlog.js";
29
+ import type { LoomEvent } from "../types.js";
30
+ /**
31
+ * The taxonomy is where this has to diverge hardest from mem0, whose categories
32
+ * are personal — preferences, relationships, health. These are engineering.
33
+ *
34
+ * `failure` is the one nobody builds and the one that pays for the rest.
35
+ * Negative knowledge is the most expensive kind to re-derive and the least
36
+ * likely to get written down: every hour lost to "we already tried that" is a
37
+ * failure memory that didn't exist. This repo's own history is full of them.
38
+ */
39
+ export type MemoryKind = "constraint" | "decision" | "convention" | "fact" | "failure" | "task";
40
+ export declare const MEMORY_KINDS: MemoryKind[];
41
+ /** Injected memories are load-bearing, so they have to clear a bar. */
42
+ export declare const CONFIDENCE_FLOOR = 0.6;
43
+ export interface MemoryProvenance {
44
+ /** Who learned it. "user" when a human typed it. */
45
+ agentId: string;
46
+ /** The event it came from — one click back to the turn that produced it. */
47
+ eventId: number;
48
+ ts: number;
49
+ }
50
+ export interface Memory {
51
+ id: string;
52
+ kind: MemoryKind;
53
+ /** One self-contained sentence. Must read true with no surrounding context. */
54
+ text: string;
55
+ /**
56
+ * Literal strings this is about: file paths, symbols, agent ids, error codes.
57
+ * mem0 has to run spaCy NER over prose and then fuzzy-match embeddings at 0.95
58
+ * to decide two restaurant names are the same place. Ours are exact strings
59
+ * that appear verbatim in the repo, so an index is a Map and a lookup is a
60
+ * `get`. That is the whole reason lexical retrieval is enough here.
61
+ */
62
+ entities: string[];
63
+ scope: {
64
+ chat?: string;
65
+ agent?: string;
66
+ task?: string;
67
+ };
68
+ provenance: MemoryProvenance;
69
+ /** Below CONFIDENCE_FLOOR: visible in the Brain tab, never injected. */
70
+ confidence: number;
71
+ /**
72
+ * The verbatim span this was extracted from. Phase 2 verifies it appears in
73
+ * the source turn and drops the memory if it doesn't — the cheapest
74
+ * hallucination guard there is, and it costs one `includes`.
75
+ */
76
+ evidence?: string;
77
+ /** Lemmatised copy for BM25. Never shown to anyone. */
78
+ lemmas: string;
79
+ /** md5(text) — lets us skip an LLM call for something we already know. */
80
+ hash: string;
81
+ createdAt: number;
82
+ updatedAt: number;
83
+ /** `task` memories die with their run; constraints and failures never do. */
84
+ expiresAt?: number;
85
+ }
86
+ /** What a caller hands us. The derived fields are ours to compute. */
87
+ export interface NewMemory {
88
+ kind: MemoryKind;
89
+ text: string;
90
+ entities?: string[];
91
+ scope?: {
92
+ chat?: string;
93
+ agent?: string;
94
+ task?: string;
95
+ };
96
+ provenance: MemoryProvenance;
97
+ confidence?: number;
98
+ evidence?: string;
99
+ expiresAt?: number;
100
+ }
101
+ export interface MemoryPatch {
102
+ text?: string;
103
+ kind?: MemoryKind;
104
+ entities?: string[];
105
+ confidence?: number;
106
+ evidence?: string;
107
+ expiresAt?: number;
108
+ }
109
+ /** One line of a memory's life, straight out of the log. */
110
+ export interface MemoryHistoryEntry {
111
+ op: "add" | "update" | "forget";
112
+ eventId: number;
113
+ ts: number;
114
+ by: string;
115
+ text?: string;
116
+ reason?: string;
117
+ }
118
+ export interface ListOpts {
119
+ kind?: MemoryKind;
120
+ chat?: string;
121
+ agent?: string;
122
+ /** Include expired memories. Off by default — they're dead to retrieval. */
123
+ includeExpired?: boolean;
124
+ minConfidence?: number;
125
+ limit?: number;
126
+ }
127
+ export declare function memoryHash(text: string): string;
128
+ export declare function newMemoryId(): string;
129
+ /**
130
+ * Poor man's lemmatiser. mem0 runs spaCy for this; we are not shipping a 500MB
131
+ * model to stem a hundred sentences, and for identifier-heavy text the crude
132
+ * rules do nearly as well.
133
+ *
134
+ * Deliberately keeps the raw token alongside the stem when they differ, which
135
+ * is the same trick mem0 plays for -ing forms (`lemmatization.py:47`): "running"
136
+ * should match both a query for "run" and a query for "running", and we can't
137
+ * tell which the user meant. Keeping both costs one token in the index.
138
+ */
139
+ export declare function lemmatize(text: string): string;
140
+ /**
141
+ * Entities we can find without a model, because in a codebase they're literal.
142
+ *
143
+ * Paths, dotted/slashed identifiers, CamelCase symbols, SCREAMING_CASE error
144
+ * codes, #123 issue refs. Deliberately conservative: a wrong entity is worse
145
+ * than a missing one, because entities are the high-precision retrieval channel
146
+ * and polluting them poisons it.
147
+ */
148
+ export declare function extractEntities(text: string): string[];
149
+ /**
150
+ * Rebuild current state from the log.
151
+ *
152
+ * Order matters and is the log's, not ours: an update to a forgotten memory is
153
+ * a no-op, and a memory added twice keeps the first id. Both fall out of
154
+ * replaying in id order, which is why this reads the log rather than keeping a
155
+ * parallel Map that could disagree with it.
156
+ */
157
+ export declare function foldMemories(events: LoomEvent[]): Map<string, Memory>;
158
+ export declare function isExpired(m: Memory, now?: number): boolean;
159
+ /**
160
+ * A project's memory. Reads and writes through the event log; holds a folded
161
+ * cache so `list`/`retrieve` don't replay history on every call, and keeps the
162
+ * cache honest by subscribing to the log rather than by trusting its own writes.
163
+ */
164
+ export declare class Brain {
165
+ private log;
166
+ private cache;
167
+ private unsubscribe;
168
+ constructor(log: EventLog);
169
+ close(): void;
170
+ private state;
171
+ /** Every live memory, newest first. */
172
+ all(): Memory[];
173
+ get(id: string): Memory | undefined;
174
+ /** Is this already known? Cheap enough to call before spending an LLM turn. */
175
+ findByHash(hash: string): Memory | undefined;
176
+ list(opts?: ListOpts): Memory[];
177
+ /**
178
+ * Learn something.
179
+ *
180
+ * Deduplicates on hash before writing: the same fact arriving twice is not
181
+ * two memories. Returns the existing one when that happens, so a caller can
182
+ * tell "already knew that" from "learned that" — which is exactly the signal
183
+ * phase 2 needs to skip an extraction call.
184
+ */
185
+ add(input: NewMemory): {
186
+ memory: Memory;
187
+ created: boolean;
188
+ };
189
+ /** Correct something we already believe. */
190
+ update(id: string, patch: MemoryPatch, by: string): Memory;
191
+ /**
192
+ * Forget something.
193
+ *
194
+ * The memory leaves the fold; the log keeps every byte. That asymmetry is the
195
+ * point — "why did it ever think that" stays answerable after the answer is
196
+ * "it doesn't any more". A reason is required, because a tombstone with no
197
+ * reason is how you end up re-learning the same wrong thing next week.
198
+ */
199
+ forget(id: string, reason: string, by: string): boolean;
200
+ /**
201
+ * Everything that ever happened to one memory, including its death.
202
+ *
203
+ * This is a filter over the same events the fold reads. mem0 keeps a whole
204
+ * second SQLite database for it (`storage.py`), and pays for the two to
205
+ * disagree — its batch path drops actor_id and role that its single path
206
+ * records.
207
+ */
208
+ history(id: string): MemoryHistoryEntry[];
209
+ stats(): {
210
+ total: number;
211
+ byKind: Record<string, number>;
212
+ expired: number;
213
+ };
214
+ }