@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,425 @@
|
|
|
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 crypto from "node:crypto";
|
|
29
|
+
export const MEMORY_KINDS = [
|
|
30
|
+
"constraint",
|
|
31
|
+
"decision",
|
|
32
|
+
"convention",
|
|
33
|
+
"fact",
|
|
34
|
+
"failure",
|
|
35
|
+
"task",
|
|
36
|
+
];
|
|
37
|
+
/** Injected memories are load-bearing, so they have to clear a bar. */
|
|
38
|
+
export const CONFIDENCE_FLOOR = 0.6;
|
|
39
|
+
export function memoryHash(text) {
|
|
40
|
+
return crypto.createHash("md5").update(text.trim().toLowerCase()).digest("hex");
|
|
41
|
+
}
|
|
42
|
+
export function newMemoryId() {
|
|
43
|
+
return crypto.randomBytes(8).toString("hex");
|
|
44
|
+
}
|
|
45
|
+
const STOPWORDS = new Set([
|
|
46
|
+
"a", "an", "and", "are", "as", "at", "be", "been", "but", "by", "can", "did", "do", "does",
|
|
47
|
+
"for", "from", "had", "has", "have", "how", "i", "if", "in", "is", "it", "its", "of", "on",
|
|
48
|
+
"or", "our", "out", "so", "than", "that", "the", "their", "them", "then", "there", "these",
|
|
49
|
+
"they", "this", "to", "was", "we", "were", "what", "when", "which", "who", "will", "with",
|
|
50
|
+
"you", "your",
|
|
51
|
+
]);
|
|
52
|
+
/**
|
|
53
|
+
* Poor man's lemmatiser. mem0 runs spaCy for this; we are not shipping a 500MB
|
|
54
|
+
* model to stem a hundred sentences, and for identifier-heavy text the crude
|
|
55
|
+
* rules do nearly as well.
|
|
56
|
+
*
|
|
57
|
+
* Deliberately keeps the raw token alongside the stem when they differ, which
|
|
58
|
+
* is the same trick mem0 plays for -ing forms (`lemmatization.py:47`): "running"
|
|
59
|
+
* should match both a query for "run" and a query for "running", and we can't
|
|
60
|
+
* tell which the user meant. Keeping both costs one token in the index.
|
|
61
|
+
*/
|
|
62
|
+
export function lemmatize(text) {
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const raw of text.toLowerCase().split(/[^a-z0-9_/.@#-]+/)) {
|
|
65
|
+
const tok = raw.replace(/^[-.]+|[-.]+$/g, "");
|
|
66
|
+
if (!tok || tok.length < 2 || STOPWORDS.has(tok))
|
|
67
|
+
continue;
|
|
68
|
+
out.push(tok);
|
|
69
|
+
const stem = stemOf(tok);
|
|
70
|
+
if (stem !== tok)
|
|
71
|
+
out.push(stem);
|
|
72
|
+
}
|
|
73
|
+
return out.join(" ");
|
|
74
|
+
}
|
|
75
|
+
function stemOf(tok) {
|
|
76
|
+
// Only touch plain words. Identifiers (paths, dotted names, snake_case) are
|
|
77
|
+
// exact strings and stemming them turns `useState` into garbage.
|
|
78
|
+
if (!/^[a-z]+$/.test(tok))
|
|
79
|
+
return tok;
|
|
80
|
+
for (const [suffix, min] of [
|
|
81
|
+
["ingly", 6], ["edly", 6], ["ing", 5], ["ies", 4], ["ied", 4], ["es", 4], ["ed", 4], ["ly", 5], ["s", 3],
|
|
82
|
+
]) {
|
|
83
|
+
if (tok.length >= min && tok.endsWith(suffix)) {
|
|
84
|
+
const base = tok.slice(0, -suffix.length);
|
|
85
|
+
if (suffix === "ies" || suffix === "ied")
|
|
86
|
+
return base + "y";
|
|
87
|
+
if (suffix === "ing" || suffix === "ed")
|
|
88
|
+
return undouble(base);
|
|
89
|
+
return base;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return tok;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Porter's step-1b tail: dropping -ing off "running" leaves "runn", which
|
|
96
|
+
* matches nothing. Undouble the consonant — but not l, s or z, or "calling"
|
|
97
|
+
* stems to "cal" and stops matching "call".
|
|
98
|
+
*/
|
|
99
|
+
function undouble(base) {
|
|
100
|
+
const n = base.length;
|
|
101
|
+
if (n < 3)
|
|
102
|
+
return base;
|
|
103
|
+
const last = base[n - 1];
|
|
104
|
+
if (last !== base[n - 2])
|
|
105
|
+
return base;
|
|
106
|
+
if (last === "l" || last === "s" || last === "z")
|
|
107
|
+
return base;
|
|
108
|
+
return base.slice(0, -1);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Entities we can find without a model, because in a codebase they're literal.
|
|
112
|
+
*
|
|
113
|
+
* Paths, dotted/slashed identifiers, CamelCase symbols, SCREAMING_CASE error
|
|
114
|
+
* codes, #123 issue refs. Deliberately conservative: a wrong entity is worse
|
|
115
|
+
* than a missing one, because entities are the high-precision retrieval channel
|
|
116
|
+
* and polluting them poisons it.
|
|
117
|
+
*/
|
|
118
|
+
export function extractEntities(text) {
|
|
119
|
+
const found = new Set();
|
|
120
|
+
const add = (s) => {
|
|
121
|
+
const t = s.trim().replace(/[.,;:)\]}"']+$/, "");
|
|
122
|
+
if (t.length >= 2 && t.length <= 120)
|
|
123
|
+
found.add(t);
|
|
124
|
+
};
|
|
125
|
+
// file paths and dotted module names: src/core/git.ts, package.json, a.b.c
|
|
126
|
+
for (const m of text.matchAll(/\b[\w@.-]+(?:\/[\w@.-]+)+\b/g))
|
|
127
|
+
add(m[0]);
|
|
128
|
+
for (const m of text.matchAll(/\b[\w-]+\.(?:ts|tsx|js|jsx|json|md|py|rs|go|sh|yml|yaml|toml|lock)\b/g))
|
|
129
|
+
add(m[0]);
|
|
130
|
+
// CamelCase / PascalCase symbols: AgentBase, useState
|
|
131
|
+
for (const m of text.matchAll(/\b[A-Za-z][a-z0-9]+(?:[A-Z][a-z0-9]+)+\b/g))
|
|
132
|
+
add(m[0]);
|
|
133
|
+
// SCREAMING_SNAKE: EADDRINUSE, MAX_IMPORT_CHARS
|
|
134
|
+
for (const m of text.matchAll(/\b[A-Z][A-Z0-9]{2,}(?:_[A-Z0-9]+)*\b/g))
|
|
135
|
+
add(m[0]);
|
|
136
|
+
// issue / PR refs
|
|
137
|
+
for (const m of text.matchAll(/#\d+\b/g))
|
|
138
|
+
add(m[0]);
|
|
139
|
+
// backticked spans — someone marked it as code, believe them
|
|
140
|
+
for (const m of text.matchAll(/`([^`\n]{2,60})`/g))
|
|
141
|
+
if (m[1])
|
|
142
|
+
add(m[1]);
|
|
143
|
+
return [...found];
|
|
144
|
+
}
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// The fold
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
const MEMORY_KINDS_SET = new Set(MEMORY_KINDS);
|
|
149
|
+
function isMemoryKind(v) {
|
|
150
|
+
return typeof v === "string" && MEMORY_KINDS_SET.has(v);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Rebuild current state from the log.
|
|
154
|
+
*
|
|
155
|
+
* Order matters and is the log's, not ours: an update to a forgotten memory is
|
|
156
|
+
* a no-op, and a memory added twice keeps the first id. Both fall out of
|
|
157
|
+
* replaying in id order, which is why this reads the log rather than keeping a
|
|
158
|
+
* parallel Map that could disagree with it.
|
|
159
|
+
*/
|
|
160
|
+
export function foldMemories(events) {
|
|
161
|
+
const byId = new Map();
|
|
162
|
+
for (const e of events) {
|
|
163
|
+
if (e.kind === "memory_add") {
|
|
164
|
+
const m = e.payload.memory;
|
|
165
|
+
if (m?.id && isMemoryKind(m.kind) && typeof m.text === "string") {
|
|
166
|
+
// Last writer wins on a re-add of the same id — an idempotent replay
|
|
167
|
+
// shouldn't fork.
|
|
168
|
+
byId.set(m.id, { ...m });
|
|
169
|
+
}
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (e.kind === "memory_update") {
|
|
173
|
+
const id = e.payload.id;
|
|
174
|
+
const cur = id ? byId.get(id) : undefined;
|
|
175
|
+
if (!cur)
|
|
176
|
+
continue; // update to something forgotten or never added
|
|
177
|
+
const patch = e.payload.patch;
|
|
178
|
+
if (!patch)
|
|
179
|
+
continue;
|
|
180
|
+
const next = { ...cur, updatedAt: e.ts };
|
|
181
|
+
if (typeof patch.text === "string" && patch.text.trim()) {
|
|
182
|
+
next.text = patch.text;
|
|
183
|
+
next.lemmas = lemmatize(patch.text);
|
|
184
|
+
next.hash = memoryHash(patch.text);
|
|
185
|
+
}
|
|
186
|
+
if (isMemoryKind(patch.kind))
|
|
187
|
+
next.kind = patch.kind;
|
|
188
|
+
if (Array.isArray(patch.entities))
|
|
189
|
+
next.entities = patch.entities;
|
|
190
|
+
if (typeof patch.confidence === "number")
|
|
191
|
+
next.confidence = clamp01(patch.confidence);
|
|
192
|
+
if (typeof patch.evidence === "string")
|
|
193
|
+
next.evidence = patch.evidence;
|
|
194
|
+
if (patch.expiresAt !== undefined)
|
|
195
|
+
next.expiresAt = patch.expiresAt;
|
|
196
|
+
byId.set(cur.id, next);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (e.kind === "memory_forget") {
|
|
200
|
+
const id = e.payload.id;
|
|
201
|
+
if (id)
|
|
202
|
+
byId.delete(id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return byId;
|
|
206
|
+
}
|
|
207
|
+
function clamp01(n) {
|
|
208
|
+
if (!Number.isFinite(n))
|
|
209
|
+
return 0;
|
|
210
|
+
return Math.min(1, Math.max(0, n));
|
|
211
|
+
}
|
|
212
|
+
export function isExpired(m, now = Date.now()) {
|
|
213
|
+
return m.expiresAt !== undefined && m.expiresAt <= now;
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Brain
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
/**
|
|
219
|
+
* A project's memory. Reads and writes through the event log; holds a folded
|
|
220
|
+
* cache so `list`/`retrieve` don't replay history on every call, and keeps the
|
|
221
|
+
* cache honest by subscribing to the log rather than by trusting its own writes.
|
|
222
|
+
*/
|
|
223
|
+
export class Brain {
|
|
224
|
+
log;
|
|
225
|
+
cache = null;
|
|
226
|
+
unsubscribe = null;
|
|
227
|
+
constructor(log) {
|
|
228
|
+
this.log = log;
|
|
229
|
+
// The cache is invalidated by the log, not by our own methods, so a write
|
|
230
|
+
// from anywhere — another Brain, a replay, the CLI — is seen too.
|
|
231
|
+
this.unsubscribe = this.log.onEvent((e) => {
|
|
232
|
+
if (e.kind === "memory_add" || e.kind === "memory_update" || e.kind === "memory_forget") {
|
|
233
|
+
this.cache = null;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
close() {
|
|
238
|
+
this.unsubscribe?.();
|
|
239
|
+
this.unsubscribe = null;
|
|
240
|
+
}
|
|
241
|
+
state() {
|
|
242
|
+
if (!this.cache) {
|
|
243
|
+
this.cache = foldMemories(this.log.list({ kinds: ["memory_add", "memory_update", "memory_forget"] }));
|
|
244
|
+
}
|
|
245
|
+
return this.cache;
|
|
246
|
+
}
|
|
247
|
+
/** Every live memory, newest first. */
|
|
248
|
+
all() {
|
|
249
|
+
return [...this.state().values()].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
250
|
+
}
|
|
251
|
+
get(id) {
|
|
252
|
+
return this.state().get(id);
|
|
253
|
+
}
|
|
254
|
+
/** Is this already known? Cheap enough to call before spending an LLM turn. */
|
|
255
|
+
findByHash(hash) {
|
|
256
|
+
for (const m of this.state().values())
|
|
257
|
+
if (m.hash === hash)
|
|
258
|
+
return m;
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
list(opts = {}) {
|
|
262
|
+
const now = Date.now();
|
|
263
|
+
let out = this.all();
|
|
264
|
+
if (opts.kind)
|
|
265
|
+
out = out.filter((m) => m.kind === opts.kind);
|
|
266
|
+
if (opts.chat)
|
|
267
|
+
out = out.filter((m) => !m.scope.chat || m.scope.chat === opts.chat);
|
|
268
|
+
if (opts.agent)
|
|
269
|
+
out = out.filter((m) => !m.scope.agent || m.scope.agent === opts.agent);
|
|
270
|
+
if (!opts.includeExpired)
|
|
271
|
+
out = out.filter((m) => !isExpired(m, now));
|
|
272
|
+
if (opts.minConfidence !== undefined) {
|
|
273
|
+
out = out.filter((m) => m.confidence >= opts.minConfidence);
|
|
274
|
+
}
|
|
275
|
+
return opts.limit ? out.slice(0, opts.limit) : out;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Learn something.
|
|
279
|
+
*
|
|
280
|
+
* Deduplicates on hash before writing: the same fact arriving twice is not
|
|
281
|
+
* two memories. Returns the existing one when that happens, so a caller can
|
|
282
|
+
* tell "already knew that" from "learned that" — which is exactly the signal
|
|
283
|
+
* phase 2 needs to skip an extraction call.
|
|
284
|
+
*/
|
|
285
|
+
add(input) {
|
|
286
|
+
const text = input.text.trim();
|
|
287
|
+
if (!text)
|
|
288
|
+
throw new Error("a memory needs text");
|
|
289
|
+
if (!MEMORY_KINDS_SET.has(input.kind))
|
|
290
|
+
throw new Error(`unknown memory kind: ${input.kind}`);
|
|
291
|
+
const hash = memoryHash(text);
|
|
292
|
+
const existing = this.findByHash(hash);
|
|
293
|
+
if (existing)
|
|
294
|
+
return { memory: existing, created: false };
|
|
295
|
+
const ts = Date.now();
|
|
296
|
+
const memory = {
|
|
297
|
+
id: newMemoryId(),
|
|
298
|
+
kind: input.kind,
|
|
299
|
+
text,
|
|
300
|
+
// Entities the caller names are additive to the ones we can see, because
|
|
301
|
+
// an extractor knows things the regex can't (that "the daemon" means
|
|
302
|
+
// src/daemon/server.ts) and the regex sees things it forgot to mention.
|
|
303
|
+
entities: [...new Set([...(input.entities ?? []), ...extractEntities(text)])],
|
|
304
|
+
scope: input.scope ?? {},
|
|
305
|
+
provenance: input.provenance,
|
|
306
|
+
confidence: clamp01(input.confidence ?? 1),
|
|
307
|
+
...(input.evidence ? { evidence: input.evidence } : {}),
|
|
308
|
+
lemmas: lemmatize(text),
|
|
309
|
+
hash,
|
|
310
|
+
createdAt: ts,
|
|
311
|
+
updatedAt: ts,
|
|
312
|
+
...(input.expiresAt !== undefined ? { expiresAt: input.expiresAt } : {}),
|
|
313
|
+
};
|
|
314
|
+
this.log.append({
|
|
315
|
+
kind: "memory_add",
|
|
316
|
+
agentId: input.provenance.agentId,
|
|
317
|
+
...(input.scope?.chat ? { chat: input.scope.chat } : {}),
|
|
318
|
+
payload: { memory },
|
|
319
|
+
});
|
|
320
|
+
return { memory, created: true };
|
|
321
|
+
}
|
|
322
|
+
/** Correct something we already believe. */
|
|
323
|
+
update(id, patch, by) {
|
|
324
|
+
const cur = this.state().get(id);
|
|
325
|
+
if (!cur)
|
|
326
|
+
throw new Error(`no such memory: ${id}`);
|
|
327
|
+
if (patch.text !== undefined && !patch.text.trim()) {
|
|
328
|
+
throw new Error("a memory needs text");
|
|
329
|
+
}
|
|
330
|
+
if (patch.kind !== undefined && !MEMORY_KINDS_SET.has(patch.kind)) {
|
|
331
|
+
throw new Error(`unknown memory kind: ${patch.kind}`);
|
|
332
|
+
}
|
|
333
|
+
// Re-derive entities when the text moves, or they describe the old text.
|
|
334
|
+
const full = patch.text !== undefined && patch.entities === undefined
|
|
335
|
+
? { ...patch, entities: [...new Set(extractEntities(patch.text))] }
|
|
336
|
+
: patch;
|
|
337
|
+
this.log.append({
|
|
338
|
+
kind: "memory_update",
|
|
339
|
+
agentId: by,
|
|
340
|
+
...(cur.scope.chat ? { chat: cur.scope.chat } : {}),
|
|
341
|
+
payload: { id, patch: full, by },
|
|
342
|
+
});
|
|
343
|
+
const next = this.state().get(id);
|
|
344
|
+
if (!next)
|
|
345
|
+
throw new Error(`no such memory: ${id}`); // unreachable: we just appended
|
|
346
|
+
return next;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Forget something.
|
|
350
|
+
*
|
|
351
|
+
* The memory leaves the fold; the log keeps every byte. That asymmetry is the
|
|
352
|
+
* point — "why did it ever think that" stays answerable after the answer is
|
|
353
|
+
* "it doesn't any more". A reason is required, because a tombstone with no
|
|
354
|
+
* reason is how you end up re-learning the same wrong thing next week.
|
|
355
|
+
*/
|
|
356
|
+
forget(id, reason, by) {
|
|
357
|
+
const cur = this.state().get(id);
|
|
358
|
+
if (!cur)
|
|
359
|
+
return false;
|
|
360
|
+
if (!reason.trim())
|
|
361
|
+
throw new Error("forgetting needs a reason");
|
|
362
|
+
this.log.append({
|
|
363
|
+
kind: "memory_forget",
|
|
364
|
+
agentId: by,
|
|
365
|
+
...(cur.scope.chat ? { chat: cur.scope.chat } : {}),
|
|
366
|
+
payload: { id, reason: reason.trim(), by, text: cur.text },
|
|
367
|
+
});
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Everything that ever happened to one memory, including its death.
|
|
372
|
+
*
|
|
373
|
+
* This is a filter over the same events the fold reads. mem0 keeps a whole
|
|
374
|
+
* second SQLite database for it (`storage.py`), and pays for the two to
|
|
375
|
+
* disagree — its batch path drops actor_id and role that its single path
|
|
376
|
+
* records.
|
|
377
|
+
*/
|
|
378
|
+
history(id) {
|
|
379
|
+
const out = [];
|
|
380
|
+
for (const e of this.log.list({ kinds: ["memory_add", "memory_update", "memory_forget"] })) {
|
|
381
|
+
if (e.kind === "memory_add") {
|
|
382
|
+
const m = e.payload.memory;
|
|
383
|
+
if (m?.id !== id)
|
|
384
|
+
continue;
|
|
385
|
+
out.push({ op: "add", eventId: e.id, ts: e.ts, by: m.provenance.agentId, text: m.text });
|
|
386
|
+
}
|
|
387
|
+
else if (e.kind === "memory_update") {
|
|
388
|
+
if (e.payload.id !== id)
|
|
389
|
+
continue;
|
|
390
|
+
const patch = e.payload.patch;
|
|
391
|
+
out.push({
|
|
392
|
+
op: "update",
|
|
393
|
+
eventId: e.id,
|
|
394
|
+
ts: e.ts,
|
|
395
|
+
by: String(e.payload.by ?? e.agentId ?? "unknown"),
|
|
396
|
+
...(patch?.text ? { text: patch.text } : {}),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
else if (e.kind === "memory_forget") {
|
|
400
|
+
if (e.payload.id !== id)
|
|
401
|
+
continue;
|
|
402
|
+
out.push({
|
|
403
|
+
op: "forget",
|
|
404
|
+
eventId: e.id,
|
|
405
|
+
ts: e.ts,
|
|
406
|
+
by: String(e.payload.by ?? e.agentId ?? "unknown"),
|
|
407
|
+
reason: String(e.payload.reason ?? ""),
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return out;
|
|
412
|
+
}
|
|
413
|
+
stats() {
|
|
414
|
+
const now = Date.now();
|
|
415
|
+
const byKind = {};
|
|
416
|
+
let expired = 0;
|
|
417
|
+
for (const m of this.state().values()) {
|
|
418
|
+
byKind[m.kind] = (byKind[m.kind] ?? 0) + 1;
|
|
419
|
+
if (isExpired(m, now))
|
|
420
|
+
expired++;
|
|
421
|
+
}
|
|
422
|
+
return { total: this.state().size, byKind, expired };
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
//# sourceMappingURL=brain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brain.js","sourceRoot":"","sources":["../../src/core/brain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal helper for one-shot Claude calls (headless `claude -p`, JSON out).
|
|
3
|
+
* Used by the dynamic-route router and the LLM projection distiller — small
|
|
4
|
+
* internal reasoning jobs, never user-facing turns (those go through the
|
|
5
|
+
* claude-code adapter with sessions and streaming).
|
|
6
|
+
*/
|
|
7
|
+
export interface ClaudeCliOptions {
|
|
8
|
+
/** Model alias or id; small+fast is right for internal jobs. */
|
|
9
|
+
model?: string;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function claudeText(prompt: string, opts?: ClaudeCliOptions): Promise<string>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal helper for one-shot Claude calls (headless `claude -p`, JSON out).
|
|
3
|
+
* Used by the dynamic-route router and the LLM projection distiller — small
|
|
4
|
+
* internal reasoning jobs, never user-facing turns (those go through the
|
|
5
|
+
* claude-code adapter with sessions and streaming).
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { agentEnv } from "../adapters/base.js";
|
|
9
|
+
export function claudeText(prompt, opts = {}) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const child = spawn("claude", [
|
|
12
|
+
"-p",
|
|
13
|
+
prompt,
|
|
14
|
+
"--output-format",
|
|
15
|
+
"json",
|
|
16
|
+
"--model",
|
|
17
|
+
opts.model ?? "haiku",
|
|
18
|
+
"--no-session-persistence",
|
|
19
|
+
], { stdio: ["ignore", "pipe", "ignore"], env: agentEnv() });
|
|
20
|
+
let out = "";
|
|
21
|
+
const timer = setTimeout(() => {
|
|
22
|
+
child.kill("SIGKILL");
|
|
23
|
+
reject(new Error("claude call timed out"));
|
|
24
|
+
}, opts.timeoutMs ?? 45_000);
|
|
25
|
+
timer.unref?.();
|
|
26
|
+
child.stdout.on("data", (d) => (out += d.toString()));
|
|
27
|
+
child.on("error", (err) => {
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
reject(err);
|
|
30
|
+
});
|
|
31
|
+
child.on("close", (code) => {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
if (code !== 0)
|
|
34
|
+
return reject(new Error(`claude exited ${code}`));
|
|
35
|
+
try {
|
|
36
|
+
const wrapper = JSON.parse(out);
|
|
37
|
+
if (wrapper.is_error)
|
|
38
|
+
return reject(new Error("claude returned an error result"));
|
|
39
|
+
resolve(String(wrapper.result ?? out));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
resolve(out);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=claude-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-cli.js","sourceRoot":"","sources":["../../src/core/claude-cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAQ/C,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,OAAyB,EAAE;IACpE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CACjB,QAAQ,EACR;YACE,IAAI;YACJ,MAAM;YACN,iBAAiB;YACjB,MAAM;YACN,SAAS;YACT,IAAI,CAAC,KAAK,IAAI,OAAO;YACrB,0BAA0B;SAC3B,EACD,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,CACzD,CAAC;QACF,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtB,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAC7C,CAAC,EAAE,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;QAC7B,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC9D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,IAAI,KAAK,CAAC;gBAAE,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,CAAC;YAClE,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4C,CAAC;gBAC3E,IAAI,OAAO,CAAC,QAAQ;oBAAE,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAAC;gBAClF,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection rendering — template (default) or LLM-synthesized.
|
|
3
|
+
*
|
|
4
|
+
* The LLM path asks a small Claude model to distill the recent log into a
|
|
5
|
+
* dense handoff document (mission, state, decisions, risks, next moves).
|
|
6
|
+
* It sits behind the SAME interface as the template and falls back to it on
|
|
7
|
+
* any failure — a missing/broken/slow Claude must never break a handoff.
|
|
8
|
+
*
|
|
9
|
+
* Enable per project in .loom/config.json:
|
|
10
|
+
* "projection": { "mode": "llm", "model": "haiku" }
|
|
11
|
+
*/
|
|
12
|
+
import type { ProjectionConfig } from "../types.js";
|
|
13
|
+
import { type ProjectionInput } from "./projection.js";
|
|
14
|
+
export interface RenderedProjection {
|
|
15
|
+
content: string;
|
|
16
|
+
mode: "template" | "llm";
|
|
17
|
+
}
|
|
18
|
+
/** Render a projection per the project's configured mode, falling back to the template. */
|
|
19
|
+
export declare function renderProjection(input: ProjectionInput, cfg?: ProjectionConfig): Promise<RenderedProjection>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection rendering — template (default) or LLM-synthesized.
|
|
3
|
+
*
|
|
4
|
+
* The LLM path asks a small Claude model to distill the recent log into a
|
|
5
|
+
* dense handoff document (mission, state, decisions, risks, next moves).
|
|
6
|
+
* It sits behind the SAME interface as the template and falls back to it on
|
|
7
|
+
* any failure — a missing/broken/slow Claude must never break a handoff.
|
|
8
|
+
*
|
|
9
|
+
* Enable per project in .loom/config.json:
|
|
10
|
+
* "projection": { "mode": "llm", "model": "haiku" }
|
|
11
|
+
*/
|
|
12
|
+
import { claudeText } from "./claude-cli.js";
|
|
13
|
+
import { buildProjection } from "./projection.js";
|
|
14
|
+
const MAX_TRANSCRIPT_EVENTS = 60;
|
|
15
|
+
const MAX_LINE_CHARS = 300;
|
|
16
|
+
function managedHeader(input) {
|
|
17
|
+
return [
|
|
18
|
+
`# Loom shared context — ${input.projectName}`,
|
|
19
|
+
"",
|
|
20
|
+
"> Managed by Loom. Do not edit — regenerated on every baton handoff.",
|
|
21
|
+
`> You are \`${input.targetAgentId}\`. This is the shared state of the project across all agents.`,
|
|
22
|
+
"",
|
|
23
|
+
].join("\n");
|
|
24
|
+
}
|
|
25
|
+
function transcriptDigest(events) {
|
|
26
|
+
const interesting = events.filter((e) => ["message", "decision", "handoff", "file_edit", "route_started", "route_completed"].includes(e.kind));
|
|
27
|
+
return interesting
|
|
28
|
+
.slice(-MAX_TRANSCRIPT_EVENTS)
|
|
29
|
+
.map((e) => {
|
|
30
|
+
const who = e.agentId ?? String(e.payload.author ?? "user");
|
|
31
|
+
if (e.kind === "message") {
|
|
32
|
+
return `${who}: ${String(e.payload.text ?? "").replace(/\s+/g, " ").slice(0, MAX_LINE_CHARS)}`;
|
|
33
|
+
}
|
|
34
|
+
if (e.kind === "decision")
|
|
35
|
+
return `DECISION: ${String(e.payload.text ?? "")}`;
|
|
36
|
+
if (e.kind === "file_edit")
|
|
37
|
+
return `EDIT: ${String(e.payload.path ?? "")}`;
|
|
38
|
+
if (e.kind === "handoff") {
|
|
39
|
+
return `HANDOFF: ${String(e.payload.from ?? "—")} -> ${String(e.payload.to ?? "—")}`;
|
|
40
|
+
}
|
|
41
|
+
return `${e.kind.toUpperCase()}: ${String(e.payload.task ?? "")}`;
|
|
42
|
+
})
|
|
43
|
+
.join("\n");
|
|
44
|
+
}
|
|
45
|
+
function distillPrompt(input) {
|
|
46
|
+
const roles = input.config.agents.map((a) => `- ${a.id} (${a.kind}) — ${a.role}`).join("\n");
|
|
47
|
+
return [
|
|
48
|
+
"You are Loom's memory distiller. Multiple coding agents share one project; the baton",
|
|
49
|
+
`is being handed to agent "${input.targetAgentId}". Distill the transcript below into the`,
|
|
50
|
+
"handoff document that agent needs. Be dense and concrete; no fluff, no preamble.",
|
|
51
|
+
"",
|
|
52
|
+
"Write EXACTLY these markdown sections:",
|
|
53
|
+
"## Mission — what the humans actually want (1-3 sentences)",
|
|
54
|
+
"## Current state — what has been done / where things stand",
|
|
55
|
+
"## Decisions — every standing decision, one bullet each (keep ALL of them)",
|
|
56
|
+
"## Open questions & risks — unresolved threads the next agent must know",
|
|
57
|
+
"## Next moves — what the incoming agent should do first",
|
|
58
|
+
"",
|
|
59
|
+
"Under 500 words total. Base it ONLY on the transcript; do not invent facts.",
|
|
60
|
+
"",
|
|
61
|
+
"Agents & roles:",
|
|
62
|
+
roles,
|
|
63
|
+
"",
|
|
64
|
+
"Transcript (oldest first):",
|
|
65
|
+
transcriptDigest(input.events),
|
|
66
|
+
].join("\n");
|
|
67
|
+
}
|
|
68
|
+
/** Render a projection per the project's configured mode, falling back to the template. */
|
|
69
|
+
export async function renderProjection(input, cfg) {
|
|
70
|
+
if (cfg?.mode !== "llm") {
|
|
71
|
+
return { content: buildProjection(input), mode: "template" };
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const body = await claudeText(distillPrompt(input), {
|
|
75
|
+
model: cfg.model ?? "haiku",
|
|
76
|
+
timeoutMs: cfg.timeoutMs ?? 45_000,
|
|
77
|
+
});
|
|
78
|
+
const trimmed = body.trim();
|
|
79
|
+
if (trimmed.length < 80 || !/##\s/.test(trimmed)) {
|
|
80
|
+
throw new Error("distillation came back malformed");
|
|
81
|
+
}
|
|
82
|
+
return { content: managedHeader(input) + trimmed + "\n", mode: "llm" };
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return { content: buildProjection(input), mode: "template" };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=distill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distill.js","sourceRoot":"","sources":["../../src/core/distill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAwB,MAAM,iBAAiB,CAAC;AAExE,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACjC,MAAM,cAAc,GAAG,GAAG,CAAC;AAO3B,SAAS,aAAa,CAAC,KAAsB;IAC3C,OAAO;QACL,2BAA2B,KAAK,CAAC,WAAW,EAAE;QAC9C,EAAE;QACF,sEAAsE;QACtE,eAAe,KAAK,CAAC,aAAa,gEAAgE;QAClG,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAmB;IAC3C,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACtC,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,CAAC,CAAC,QAAQ,CAC1F,CAAC,CAAC,IAAI,CACP,CACF,CAAC;IACF,OAAO,WAAW;SACf,KAAK,CAAC,CAAC,qBAAqB,CAAC;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;QAC5D,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,EAAE,CAAC;QACjG,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU;YAAE,OAAO,aAAa,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9E,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW;YAAE,OAAO,SAAS,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QAC3E,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,YAAY,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC;QACvF,CAAC;QACD,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;IACpE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,KAAsB;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7F,OAAO;QACL,sFAAsF;QACtF,6BAA6B,KAAK,CAAC,aAAa,0CAA0C;QAC1F,kFAAkF;QAClF,EAAE;QACF,wCAAwC;QACxC,4DAA4D;QAC5D,4DAA4D;QAC5D,4EAA4E;QAC5E,yEAAyE;QACzE,yDAAyD;QACzD,EAAE;QACF,6EAA6E;QAC7E,EAAE;QACF,iBAAiB;QACjB,KAAK;QACL,EAAE;QACF,4BAA4B;QAC5B,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC;KAC/B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAsB,EACtB,GAAsB;IAEtB,IAAI,GAAG,EAAE,IAAI,KAAK,KAAK,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE;YAClD,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,OAAO;YAC3B,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,MAAM;SACnC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC/D,CAAC;AACH,CAAC"}
|