@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.
- package/ARCHITECTURE.md +211 -0
- package/CHANGELOG.md +470 -0
- package/LICENSE +21 -0
- package/README.md +630 -0
- package/dist/adapters/base.d.ts +71 -0
- package/dist/adapters/base.js +187 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/bridges/antigravity.d.ts +53 -0
- package/dist/adapters/bridges/antigravity.js +106 -0
- package/dist/adapters/bridges/antigravity.js.map +1 -0
- package/dist/adapters/bridges/cdp.d.ts +82 -0
- package/dist/adapters/bridges/cdp.js +222 -0
- package/dist/adapters/bridges/cdp.js.map +1 -0
- package/dist/adapters/bridges/gui-chat.d.ts +113 -0
- package/dist/adapters/bridges/gui-chat.js +498 -0
- package/dist/adapters/bridges/gui-chat.js.map +1 -0
- package/dist/adapters/bridges/kiro.d.ts +19 -0
- package/dist/adapters/bridges/kiro.js +22 -0
- package/dist/adapters/bridges/kiro.js.map +1 -0
- package/dist/adapters/bridges/profiles.d.ts +82 -0
- package/dist/adapters/bridges/profiles.js +125 -0
- package/dist/adapters/bridges/profiles.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +26 -0
- package/dist/adapters/claude-code.js +209 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +51 -0
- package/dist/adapters/codex.js +291 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/echo.d.ts +20 -0
- package/dist/adapters/echo.js +79 -0
- package/dist/adapters/echo.js.map +1 -0
- package/dist/adapters/grok.d.ts +61 -0
- package/dist/adapters/grok.js +217 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.js +33 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/opencode.d.ts +64 -0
- package/dist/adapters/opencode.js +426 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/cli/common.d.ts +11 -0
- package/dist/cli/common.js +40 -0
- package/dist/cli/common.js.map +1 -0
- package/dist/cli/doctor.d.ts +12 -0
- package/dist/cli/doctor.js +181 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +641 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/tui-model.d.ts +73 -0
- package/dist/cli/tui-model.js +312 -0
- package/dist/cli/tui-model.js.map +1 -0
- package/dist/cli/tui.d.ts +15 -0
- package/dist/cli/tui.js +451 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/cli/ui.d.ts +10 -0
- package/dist/cli/ui.js +135 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/core/ades.d.ts +79 -0
- package/dist/core/ades.js +130 -0
- package/dist/core/ades.js.map +1 -0
- package/dist/core/baton.d.ts +37 -0
- package/dist/core/baton.js +92 -0
- package/dist/core/baton.js.map +1 -0
- package/dist/core/brain-extract.d.ts +111 -0
- package/dist/core/brain-extract.js +248 -0
- package/dist/core/brain-extract.js.map +1 -0
- package/dist/core/brain-index.d.ts +128 -0
- package/dist/core/brain-index.js +338 -0
- package/dist/core/brain-index.js.map +1 -0
- package/dist/core/brain.d.ts +214 -0
- package/dist/core/brain.js +425 -0
- package/dist/core/brain.js.map +1 -0
- package/dist/core/claude-cli.d.ts +12 -0
- package/dist/core/claude-cli.js +47 -0
- package/dist/core/claude-cli.js.map +1 -0
- package/dist/core/distill.d.ts +19 -0
- package/dist/core/distill.js +88 -0
- package/dist/core/distill.js.map +1 -0
- package/dist/core/eventlog.d.ts +43 -0
- package/dist/core/eventlog.js +211 -0
- package/dist/core/eventlog.js.map +1 -0
- package/dist/core/git.d.ts +203 -0
- package/dist/core/git.js +460 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/logbook.d.ts +59 -0
- package/dist/core/logbook.js +118 -0
- package/dist/core/logbook.js.map +1 -0
- package/dist/core/memory.d.ts +34 -0
- package/dist/core/memory.js +124 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/notify.d.ts +11 -0
- package/dist/core/notify.js +30 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/projection.d.ts +20 -0
- package/dist/core/projection.js +96 -0
- package/dist/core/projection.js.map +1 -0
- package/dist/core/registry.d.ts +76 -0
- package/dist/core/registry.js +147 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/router.d.ts +42 -0
- package/dist/core/router.js +99 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/routes.d.ts +93 -0
- package/dist/core/routes.js +399 -0
- package/dist/core/routes.js.map +1 -0
- package/dist/core/search.d.ts +63 -0
- package/dist/core/search.js +0 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/setup.d.ts +70 -0
- package/dist/core/setup.js +250 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/suggestions.d.ts +15 -0
- package/dist/core/suggestions.js +38 -0
- package/dist/core/suggestions.js.map +1 -0
- package/dist/core/worktree.d.ts +32 -0
- package/dist/core/worktree.js +83 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/daemon/app-page.d.ts +32 -0
- package/dist/daemon/app-page.js +6363 -0
- package/dist/daemon/app-page.js.map +1 -0
- package/dist/daemon/auth.d.ts +51 -0
- package/dist/daemon/auth.js +135 -0
- package/dist/daemon/auth.js.map +1 -0
- package/dist/daemon/board.d.ts +117 -0
- package/dist/daemon/board.js +202 -0
- package/dist/daemon/board.js.map +1 -0
- package/dist/daemon/brand-icons.d.ts +29 -0
- package/dist/daemon/brand-icons.js +44 -0
- package/dist/daemon/brand-icons.js.map +1 -0
- package/dist/daemon/client.d.ts +127 -0
- package/dist/daemon/client.js +237 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/geist-font.d.ts +10 -0
- package/dist/daemon/geist-font.js +783 -0
- package/dist/daemon/geist-font.js.map +1 -0
- package/dist/daemon/guards.d.ts +18 -0
- package/dist/daemon/guards.js +40 -0
- package/dist/daemon/guards.js.map +1 -0
- package/dist/daemon/linear.d.ts +59 -0
- package/dist/daemon/linear.js +117 -0
- package/dist/daemon/linear.js.map +1 -0
- package/dist/daemon/push.d.ts +22 -0
- package/dist/daemon/push.js +58 -0
- package/dist/daemon/push.js.map +1 -0
- package/dist/daemon/runtime.d.ts +299 -0
- package/dist/daemon/runtime.js +1016 -0
- package/dist/daemon/runtime.js.map +1 -0
- package/dist/daemon/server.d.ts +125 -0
- package/dist/daemon/server.js +1728 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/tasks.d.ts +153 -0
- package/dist/daemon/tasks.js +264 -0
- package/dist/daemon/tasks.js.map +1 -0
- package/dist/daemon/terminals.d.ts +82 -0
- package/dist/daemon/terminals.js +372 -0
- package/dist/daemon/terminals.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/sdk.d.ts +17 -0
- package/dist/sdk.js +17 -0
- package/dist/sdk.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/docs/SETUP.md +265 -0
- package/docs/adapters.md +132 -0
- package/docs/design-system.md +245 -0
- package/docs/img/board-linear.png +0 -0
- package/docs/img/board-projects.png +0 -0
- package/docs/img/brain.png +0 -0
- package/docs/img/settings.png +0 -0
- package/docs/img/workspace.png +0 -0
- package/docs/integration-notes.md +76 -0
- package/docs/proposals/brain.md +262 -0
- package/package.json +84 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 2 — learning from a turn.
|
|
3
|
+
*
|
|
4
|
+
* Phases 0 and 1 gave the brain units and retrieval, both deterministic. This is
|
|
5
|
+
* where memories start arriving on their own: when an agent finishes a turn, an
|
|
6
|
+
* ADE reads the transcript and proposes what to remember, reconciled against
|
|
7
|
+
* what's already known.
|
|
8
|
+
*
|
|
9
|
+
* The shape is mem0's, adapted (see docs/proposals/brain.md):
|
|
10
|
+
* - retrieve the candidates a new fact might collide with (phase 1);
|
|
11
|
+
* - show them to the extractor INTEGER-MAPPED, never by real id — a
|
|
12
|
+
* hallucinated "7" is detectable and droppable; a hallucinated uuid is
|
|
13
|
+
* silent corruption (mem0 main.py:889);
|
|
14
|
+
* - the extractor returns ADD / UPDATE / FORGET / NONE;
|
|
15
|
+
* - every ADD/UPDATE must quote the span it came from, and that span is
|
|
16
|
+
* verified IN CODE to appear in the turn — the cheapest hallucination guard
|
|
17
|
+
* there is, and the one mem0 doesn't have.
|
|
18
|
+
*
|
|
19
|
+
* The LLM is injected as an `engine` (a prompt → text function), so all of the
|
|
20
|
+
* logic here — mapping, parsing, evidence-checking, applying — is pure and
|
|
21
|
+
* tested without a model. The default engine is the logged-in Claude CLI, the
|
|
22
|
+
* same no-API-key path distill.ts already uses.
|
|
23
|
+
*/
|
|
24
|
+
import { MEMORY_KINDS } from "./brain.js";
|
|
25
|
+
import { retrieveFrom } from "./brain-index.js";
|
|
26
|
+
const MEMORY_KIND_SET = new Set(MEMORY_KINDS);
|
|
27
|
+
/** Whitespace-insensitive containment — the extractor may reflow what it quotes. */
|
|
28
|
+
function normalizeForMatch(s) {
|
|
29
|
+
return s.replace(/\s+/g, " ").trim().toLowerCase();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Does the quoted span actually appear in the turn?
|
|
33
|
+
*
|
|
34
|
+
* The whole anti-hallucination guarantee rests on this one check. Whitespace is
|
|
35
|
+
* normalised (the model reflows), but the words have to be there. A short quote
|
|
36
|
+
* is too easy to satisfy by accident, so anything under a few characters is
|
|
37
|
+
* rejected outright.
|
|
38
|
+
*/
|
|
39
|
+
export function evidenceInTurn(evidence, turn) {
|
|
40
|
+
const e = normalizeForMatch(evidence);
|
|
41
|
+
if (e.length < 8)
|
|
42
|
+
return false;
|
|
43
|
+
return normalizeForMatch(turn).includes(e);
|
|
44
|
+
}
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// The prompt
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
export const EXTRACT_SYSTEM = `You extract durable engineering memory from one agent turn in a software project.
|
|
49
|
+
|
|
50
|
+
Return ONLY facts that will still matter next week: constraints reality imposes, decisions and why, conventions this repo follows, plain facts about how things are, and — most valuable — failures (what was tried and did not work, so nobody burns the time again). Skip chit-chat, restated instructions, and anything true only for this minute.
|
|
51
|
+
|
|
52
|
+
You are given EXISTING memories, each under an integer id. Use them ONLY to avoid duplicates and to correct what's now wrong:
|
|
53
|
+
- ADD a new memory when the turn reveals something not already known.
|
|
54
|
+
- UPDATE an existing memory (by its integer id) when the turn shows it was incomplete or has changed. Keep the id.
|
|
55
|
+
- FORGET an existing memory (by its integer id) when the turn shows it is now false.
|
|
56
|
+
- Otherwise NONE.
|
|
57
|
+
|
|
58
|
+
Rules, strictly:
|
|
59
|
+
- Every ADD and UPDATE MUST include "evidence": a short verbatim quote FROM THE TURN that the memory is based on. If you can't quote it from the turn, don't write it.
|
|
60
|
+
- Each memory is ONE self-contained sentence that reads true with no surrounding context.
|
|
61
|
+
- Use ONLY the integer ids shown for UPDATE/FORGET. Never invent an id.
|
|
62
|
+
- kind is one of: constraint, decision, convention, fact, failure.
|
|
63
|
+
|
|
64
|
+
Respond with JSON only, no prose:
|
|
65
|
+
{"ops":[{"op":"ADD","text":"...","kind":"failure","evidence":"...","confidence":0.9},{"op":"UPDATE","id":"2","text":"...","evidence":"..."},{"op":"FORGET","id":"5","reason":"..."},{"op":"NONE"}]}`;
|
|
66
|
+
export function buildExtractionPrompt(turn, candidates) {
|
|
67
|
+
const existing = candidates.length
|
|
68
|
+
? candidates
|
|
69
|
+
.map((c) => ` ${JSON.stringify(c.intId)}: ${JSON.stringify(c.memory.text)}`)
|
|
70
|
+
.join("\n")
|
|
71
|
+
: " (none)";
|
|
72
|
+
return [
|
|
73
|
+
"EXISTING memories (integer id: text):",
|
|
74
|
+
existing,
|
|
75
|
+
"",
|
|
76
|
+
"THE TURN (an agent's work — the user's ask and the agent's actions/reply):",
|
|
77
|
+
turn.length > 12_000 ? turn.slice(0, 12_000) + "\n…(truncated)" : turn,
|
|
78
|
+
"",
|
|
79
|
+
"Extract memory as JSON per the system instructions.",
|
|
80
|
+
].join("\n");
|
|
81
|
+
}
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Parsing the engine's reply
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
function stripFences(s) {
|
|
86
|
+
return s.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
87
|
+
}
|
|
88
|
+
/** Pull the ops array out of the engine's reply, tolerantly. */
|
|
89
|
+
export function parseExtraction(response) {
|
|
90
|
+
const text = stripFences(response || "").trim();
|
|
91
|
+
if (!text)
|
|
92
|
+
return [];
|
|
93
|
+
let obj;
|
|
94
|
+
try {
|
|
95
|
+
obj = JSON.parse(text);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// The model wrapped the JSON in prose — grab the first {...} block.
|
|
99
|
+
const m = text.match(/\{[\s\S]*\}/);
|
|
100
|
+
if (!m)
|
|
101
|
+
return [];
|
|
102
|
+
try {
|
|
103
|
+
obj = JSON.parse(m[0]);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const rawOps = Array.isArray(obj)
|
|
110
|
+
? obj
|
|
111
|
+
: Array.isArray(obj?.ops)
|
|
112
|
+
? obj.ops
|
|
113
|
+
: [];
|
|
114
|
+
const ops = [];
|
|
115
|
+
for (const raw of rawOps) {
|
|
116
|
+
if (!raw || typeof raw !== "object")
|
|
117
|
+
continue;
|
|
118
|
+
const r = raw;
|
|
119
|
+
const op = String(r.op ?? "").toUpperCase();
|
|
120
|
+
if (op === "ADD" && typeof r.text === "string" && typeof r.evidence === "string") {
|
|
121
|
+
const kind = String(r.kind ?? "fact");
|
|
122
|
+
ops.push({
|
|
123
|
+
op: "ADD",
|
|
124
|
+
text: r.text,
|
|
125
|
+
kind: (MEMORY_KIND_SET.has(kind) ? kind : "fact"),
|
|
126
|
+
...(Array.isArray(r.entities) ? { entities: r.entities.map(String) } : {}),
|
|
127
|
+
...(typeof r.confidence === "number" ? { confidence: r.confidence } : {}),
|
|
128
|
+
evidence: r.evidence,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else if (op === "UPDATE" && r.id != null && typeof r.text === "string" && typeof r.evidence === "string") {
|
|
132
|
+
ops.push({
|
|
133
|
+
op: "UPDATE",
|
|
134
|
+
id: String(r.id),
|
|
135
|
+
text: r.text,
|
|
136
|
+
...(typeof r.confidence === "number" ? { confidence: r.confidence } : {}),
|
|
137
|
+
evidence: r.evidence,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else if (op === "FORGET" && r.id != null) {
|
|
141
|
+
ops.push({ op: "FORGET", id: String(r.id), reason: String(r.reason ?? "the turn showed it is no longer true") });
|
|
142
|
+
}
|
|
143
|
+
// NONE and anything malformed are simply ignored.
|
|
144
|
+
}
|
|
145
|
+
return ops;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Apply the extractor's ops to the brain, dropping anything that fails a guard.
|
|
149
|
+
*
|
|
150
|
+
* The guards, in order: an ADD/UPDATE with no verifiable evidence span is a
|
|
151
|
+
* hallucination and is dropped; an UPDATE/FORGET whose integer id isn't in the
|
|
152
|
+
* candidate set is a hallucinated reference and is dropped; a real id maps back
|
|
153
|
+
* to its uuid before touching the store.
|
|
154
|
+
*/
|
|
155
|
+
export function applyExtraction(brain, ops, turn, candidates, opts) {
|
|
156
|
+
const byInt = new Map(candidates.map((c) => [c.intId, c.memory]));
|
|
157
|
+
const res = { added: [], updated: [], forgotten: [], dropped: [] };
|
|
158
|
+
const prov = { agentId: opts.by, eventId: opts.eventId, ts: Date.now() };
|
|
159
|
+
for (const op of ops) {
|
|
160
|
+
if (op.op === "ADD") {
|
|
161
|
+
if (!evidenceInTurn(op.evidence, turn)) {
|
|
162
|
+
res.dropped.push({ op, reason: "evidence not found in the turn" });
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const { memory, created } = brain.add({
|
|
166
|
+
kind: op.kind,
|
|
167
|
+
text: op.text,
|
|
168
|
+
...(op.entities ? { entities: op.entities } : {}),
|
|
169
|
+
...(op.confidence !== undefined ? { confidence: op.confidence } : {}),
|
|
170
|
+
evidence: op.evidence,
|
|
171
|
+
...(opts.chat ? { scope: { chat: opts.chat } } : {}),
|
|
172
|
+
provenance: prov,
|
|
173
|
+
});
|
|
174
|
+
if (created)
|
|
175
|
+
res.added.push(memory);
|
|
176
|
+
else
|
|
177
|
+
res.dropped.push({ op, reason: "already known (hash match)" });
|
|
178
|
+
}
|
|
179
|
+
else if (op.op === "UPDATE") {
|
|
180
|
+
const target = byInt.get(op.id);
|
|
181
|
+
if (!target) {
|
|
182
|
+
res.dropped.push({ op, reason: `no candidate with id ${op.id}` });
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (!evidenceInTurn(op.evidence, turn)) {
|
|
186
|
+
res.dropped.push({ op, reason: "evidence not found in the turn" });
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const updated = brain.update(target.id, { text: op.text, ...(op.confidence !== undefined ? { confidence: op.confidence } : {}), evidence: op.evidence }, opts.by);
|
|
191
|
+
res.updated.push(updated);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
res.dropped.push({ op, reason: err instanceof Error ? err.message : String(err) });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else if (op.op === "FORGET") {
|
|
198
|
+
const target = byInt.get(op.id);
|
|
199
|
+
if (!target) {
|
|
200
|
+
res.dropped.push({ op, reason: `no candidate with id ${op.id}` });
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (brain.forget(target.id, op.reason, opts.by))
|
|
204
|
+
res.forgotten.push(target.id);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return res;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Learn from one turn: retrieve candidates, ask the engine, apply the ops.
|
|
211
|
+
*
|
|
212
|
+
* Non-throwing by contract — a broken or slow extractor must never be able to
|
|
213
|
+
* take down the turn that triggered it (the same rule distill.ts follows). Any
|
|
214
|
+
* failure returns an empty result.
|
|
215
|
+
*/
|
|
216
|
+
export async function extractFromTurn(brain, turn, opts) {
|
|
217
|
+
const empty = { added: [], updated: [], forgotten: [], dropped: [] };
|
|
218
|
+
if (!turn.trim())
|
|
219
|
+
return empty;
|
|
220
|
+
// Candidates: what a new fact from this turn might collide with. Query is the
|
|
221
|
+
// turn itself plus the files it touched — exactly what phase 1 ranks on.
|
|
222
|
+
const hits = retrieveFrom(brain.all(), {
|
|
223
|
+
query: turn.slice(0, 4000),
|
|
224
|
+
...(opts.files?.length ? { files: opts.files } : {}),
|
|
225
|
+
...(opts.chat ? { chat: opts.chat } : {}),
|
|
226
|
+
limit: opts.candidateLimit ?? 10,
|
|
227
|
+
});
|
|
228
|
+
const candidates = hits.map((h, i) => ({ intId: String(i), memory: h.memory }));
|
|
229
|
+
let reply;
|
|
230
|
+
try {
|
|
231
|
+
reply = await opts.engine({
|
|
232
|
+
system: EXTRACT_SYSTEM,
|
|
233
|
+
user: buildExtractionPrompt(turn, candidates),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return empty; // extractor unavailable — the turn is unaffected
|
|
238
|
+
}
|
|
239
|
+
const ops = parseExtraction(reply);
|
|
240
|
+
if (!ops.length)
|
|
241
|
+
return empty;
|
|
242
|
+
return applyExtraction(brain, ops, turn, candidates, {
|
|
243
|
+
by: opts.agentId,
|
|
244
|
+
eventId: opts.eventId ?? 0,
|
|
245
|
+
...(opts.chat ? { chat: opts.chat } : {}),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=brain-extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brain-extract.js","sourceRoot":"","sources":["../../src/core/brain-extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
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 type { Brain, Memory } from "./brain.js";
|
|
37
|
+
export interface RetrieveOpts {
|
|
38
|
+
/** Free text — a task description, a prompt, a turn. */
|
|
39
|
+
query?: string;
|
|
40
|
+
/** Files in play. From turn_diff, the file tree, or whatever the task names. */
|
|
41
|
+
files?: string[];
|
|
42
|
+
/** Only memories scoped to this agent (or to nobody). */
|
|
43
|
+
agent?: string;
|
|
44
|
+
/** Only memories scoped to this chat (or to nobody). */
|
|
45
|
+
chat?: string;
|
|
46
|
+
limit?: number;
|
|
47
|
+
/** Drop anything the extractor wasn't sure about. */
|
|
48
|
+
minConfidence?: number;
|
|
49
|
+
/** Show the arithmetic. For tests and for the Brain tab. */
|
|
50
|
+
explain?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface ScoreDetail {
|
|
53
|
+
bm25: number;
|
|
54
|
+
entity: number;
|
|
55
|
+
kindBias: number;
|
|
56
|
+
matchedEntities: string[];
|
|
57
|
+
final: number;
|
|
58
|
+
}
|
|
59
|
+
export interface Hit {
|
|
60
|
+
memory: Memory;
|
|
61
|
+
score: number;
|
|
62
|
+
detail?: ScoreDetail;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* An inverted index over a set of memories.
|
|
66
|
+
*
|
|
67
|
+
* Rebuilt from the fold rather than maintained incrementally. At project scale —
|
|
68
|
+
* hundreds to low thousands of memories — building it is well under a
|
|
69
|
+
* millisecond, and an index that can't drift from its source is worth more than
|
|
70
|
+
* one that's marginally faster to update. If that stops being true the shape
|
|
71
|
+
* here doesn't change; only when we rebuild does.
|
|
72
|
+
*/
|
|
73
|
+
export declare class MemoryIndex {
|
|
74
|
+
/** term → memory id → term frequency */
|
|
75
|
+
private postings;
|
|
76
|
+
/** entity (lowercased) → memory ids */
|
|
77
|
+
private entities;
|
|
78
|
+
private lengths;
|
|
79
|
+
private avgLen;
|
|
80
|
+
private docs;
|
|
81
|
+
constructor(memories: Memory[]);
|
|
82
|
+
private byId;
|
|
83
|
+
get size(): number;
|
|
84
|
+
/** Standard BM25. Returns id → score for every doc matching any term. */
|
|
85
|
+
bm25(query: string): Map<string, number>;
|
|
86
|
+
/**
|
|
87
|
+
* Entity postings, IDF-weighted the way mem0 does it (`main.py:1758`): an
|
|
88
|
+
* entity linked to n memories contributes 1/(1+0.001·(n-1)²). One memory →
|
|
89
|
+
* full weight; a hundred → nearly nothing. So `src/types.ts`, which half the
|
|
90
|
+
* memories will mention, stops drowning out the one that matters, without
|
|
91
|
+
* anyone maintaining a stoplist.
|
|
92
|
+
*
|
|
93
|
+
* Per-memory contributions take the max rather than the sum — matching two
|
|
94
|
+
* entities shouldn't outrank matching one that's far more specific.
|
|
95
|
+
*/
|
|
96
|
+
entityHits(terms: string[]): Map<string, {
|
|
97
|
+
score: number;
|
|
98
|
+
matched: string[];
|
|
99
|
+
}>;
|
|
100
|
+
memory(id: string): Memory | undefined;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Pull the entity-ish strings out of a query.
|
|
104
|
+
*
|
|
105
|
+
* Looser than brain.ts's extractEntities, and deliberately so: that one guards
|
|
106
|
+
* what gets *stored*, where a wrong entity poisons the index forever. This one
|
|
107
|
+
* only guards what gets *looked up*, where a wrong guess costs one failed Map
|
|
108
|
+
* lookup. Asymmetric stakes, asymmetric strictness.
|
|
109
|
+
*/
|
|
110
|
+
export declare function queryEntities(query: string, files?: string[]): string[];
|
|
111
|
+
/**
|
|
112
|
+
* Find the memories relevant to a piece of work.
|
|
113
|
+
*
|
|
114
|
+
* Pure over the memories handed in — the Brain owns the fold, this owns the
|
|
115
|
+
* ranking, and neither needs the other to be a class.
|
|
116
|
+
*/
|
|
117
|
+
export declare function retrieveFrom(memories: Memory[], opts: RetrieveOpts): Hit[];
|
|
118
|
+
/** The same, against a live Brain. */
|
|
119
|
+
export declare function retrieve(brain: Brain, opts: RetrieveOpts): Hit[];
|
|
120
|
+
/**
|
|
121
|
+
* Render retrieved memories into a brief for an agent taking the baton.
|
|
122
|
+
*
|
|
123
|
+
* Grouped by kind with constraints and failures first, because those are the
|
|
124
|
+
* ones that prevent damage — the reader should hit "don't do X, it breaks Y"
|
|
125
|
+
* before "the port is 7420". Returns "" for no memories, so a caller can append
|
|
126
|
+
* it unconditionally.
|
|
127
|
+
*/
|
|
128
|
+
export declare function compileBrief(memories: Memory[]): string;
|