brainllm 12.4.2 → 12.6.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/.env.example +7 -0
- package/README.md +6 -3
- package/SECURITY.md +2 -2
- package/dist/index.js +606 -602
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/brainllm/SKILL.md +144 -308
- package/skills/brainllm/references/domains.md +30 -46
- package/skills/brainllm/references/fullmode.md +1 -1
- package/skills/brainllm/references/taxonomy.md +3 -2
- package/skills/brainllm/references/troubleshooting.md +4 -3
|
@@ -1,66 +1,50 @@
|
|
|
1
1
|
# Domain Knowledge Lifecycle
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Read this before creating a domain, adding a sub-category note, or refreshing domain content, then follow the one protocol that matches. Never skip the sources gate and never manufacture knowledge.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## The shape of a domain
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A domain is **one Sources note plus one information note per sub-category**, each a maintained document revised in place.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
| Note | Holds |
|
|
10
|
+
|---|---|
|
|
11
|
+
| **Sources** | every source, marked ❇️ discovered / ✅ used, grouped under h3s; a **Revision** table (Source · Marker · Date) |
|
|
12
|
+
| **Current State** | the domain's **measured state** — what is live, built, deployed, counted — latest value only, with the date it was measured. The only information note that holds state |
|
|
13
|
+
| any other information note | **timeless knowledge**: how the thing works, its rules, its architecture, its constraints. No state, versions, dates, incidents or decision history |
|
|
14
|
+
| a `#mandate` note (optional) | a standing brief a session must follow (e.g. a founder brief) — `remember(..., mandate=true)` |
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
**History and decisions never live in a domain.** "What run N found" and "what Miiso decided on a date" go to the venture's thread entries; the domain holds the resulting truth. If you are about to write a date into a non-Current-State note, it belongs in Current State (a measurement) or in a thread entry (an event).
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
Markers are just the emoji on each source; their dates live only in the Revision table. `Last updated` lines are server-maintained. Titles are ≤ 4 words with no dates or run numbers; a sub-category that needs more words is two sub-categories. Read a sibling before writing and match it; improve a pattern across every domain or not at all.
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
2. `Last updated - <date>` (h4) — server-maintained
|
|
17
|
-
3. **Sources** (h2) — the ❇️/✅ legend line, then the full, complete source list: **every** source (URL, doc, file, dataset, …) listed and marked individually with just its emoji; related sources grouped under h3 subheadings
|
|
18
|
-
4. **Revision** (h2) — a `Source | Marker | Date` table recording each source's current marker and the date it earned it
|
|
20
|
+
## The Sources note
|
|
19
21
|
|
|
20
|
-
`remember(kind="sources", domain=…)`
|
|
22
|
+
Creating a domain creates the book and its Sources note together. `remember(kind="sources", domain=…)` merges into the Sources section; pass `revision=[{source, marker, date?}]` to upsert Revision rows by source name (the name must match the list exactly).
|
|
21
23
|
|
|
22
|
-
## 1. Creating a
|
|
24
|
+
## 1. Creating a domain
|
|
23
25
|
|
|
24
26
|
1. Propose the title.
|
|
25
|
-
2.
|
|
26
|
-
3.
|
|
27
|
-
4.
|
|
28
|
-
5. Read the approved sources
|
|
29
|
-
6. Flip
|
|
30
|
-
7.
|
|
27
|
+
2. Any domain-scoped `remember()` creates the book and its Sources note.
|
|
28
|
+
3. Discover sources and record each as ❇️, grouped.
|
|
29
|
+
4. Agree the learning scope with the user.
|
|
30
|
+
5. Read the approved sources; write the information notes, starting with **Current State**.
|
|
31
|
+
6. Flip used sources to ✅ with their Revision dates.
|
|
32
|
+
7. Wire relations (`connect=` on the `remember` calls).
|
|
31
33
|
|
|
32
|
-
If
|
|
34
|
+
If every source is rejected, create no information note — an unsourced note corrupts the brain.
|
|
33
35
|
|
|
34
|
-
## 2. Adding a sub-category
|
|
36
|
+
## 2. Adding a sub-category
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
For a source the Sources note already covers: `remember(kind="information", domain, title, body, mustCreate=true)`, then wire it.
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
2. Create the `information` note directly (`remember(kind="information", domain=…, title=…)`).
|
|
40
|
-
3. `connect()` and wire relations.
|
|
41
|
-
|
|
42
|
-
## 3. Maintaining domain knowledge (periodic refresh)
|
|
40
|
+
## 3. Refreshing a domain
|
|
43
41
|
|
|
44
|
-
1. Read
|
|
45
|
-
2.
|
|
46
|
-
3.
|
|
47
|
-
4.
|
|
48
|
-
5.
|
|
42
|
+
1. Read ❇️ sources not yet used; fold what they teach into the right notes.
|
|
43
|
+
2. Re-check ✅ sources older than a month; update Sources if they moved.
|
|
44
|
+
3. Re-measure **Current State** against live sources; replace values in place, then run `consistency()` on anything recorded twice.
|
|
45
|
+
4. Spot-check each other note for correctness and for state or history that has crept in (`maintain(deep)` flags dated prose).
|
|
46
|
+
5. Update markers and Revision dates; wire relations.
|
|
49
47
|
|
|
50
48
|
## Renames
|
|
51
49
|
|
|
52
|
-
Retitling a domain book
|
|
53
|
-
|
|
54
|
-
## The shape of a domain's surface
|
|
55
|
-
|
|
56
|
-
A domain's Knowledge surface is exactly: **one maintained Sources note, plus a small set of consolidated, current-state information notes — one per sub-category, never one per day.** It holds what's true now, not a changelog of what was true on each date it was checked.
|
|
57
|
-
|
|
58
|
-
Chronological, run-by-run history belongs in Memory/Threads — a thread is the right place for "here's what Run N found"; a domain information note is the right place for "here's what's actually true about this sub-category," kept current by revision, not accumulation. If you find yourself naming a new information note after today's date or a run number, stop — that finding either updates an existing note (revise it) or doesn't belong in Domains at all.
|
|
59
|
-
|
|
60
|
-
The title rule is the same here as everywhere: **four words maximum**, no dates, no run numbers. Sub-category titles are where it's most often stretched, and a title that won't fit is telling you something — a sub-category needing eight words to name is usually two sub-categories, so split the content rather than the title. Merge behaviour follows from the same place: Sources and information notes are maintained current-state documents, so new findings fold into the relevant section (`section=`, `find=`) and never arrive as a dated addendum block.
|
|
61
|
-
|
|
62
|
-
## Standing briefs vs current-state facts — the mandate marker
|
|
63
|
-
|
|
64
|
-
A domain holds two things that read alike and behave differently: a **standing brief** (instructions a future session must follow — the operating mandate of a venture) and a **current-state fact** (what's true right now). Both are `kind="information"`, and until now telling them apart meant reading each note's prose. A scoped autonomous session that needs "the thing I must obey" should not have to skim every sub-category note to find it.
|
|
65
|
-
|
|
66
|
-
Mark a brief with `remember(kind="information", domain=…, title=…, body=…, mandate=true)` — it adds a `#mandate` flag, surfaced as `mandate: true` in `domain()` group rows and in `knowledge_recall` stubs. A session looking for its brief reads the domain and the mandate-flagged note is the one carrying instructions rather than facts. `label(noteId, "mandate")` / `label(noteId, "mandate", remove=true)` sets or clears the marker on an existing note. One note per mandate, titled by the brief it is ("Founder Brief", "Operating Mandate") — the four-word rule still applies.
|
|
50
|
+
Retitling a domain book cascades its `#domain` slug to every child automatically.
|
|
@@ -20,7 +20,7 @@ BrainLLM memories are typed text notes; `code` / `file` / `image` notes aren't a
|
|
|
20
20
|
- **Code / structured text** → embed it as a fenced block in an `information` note via core `remember` (fully conformant and full-text searchable), or
|
|
21
21
|
- **Binary (image, PDF, file)** → core `attach()` it onto the relevant typed note (`role=image` / `file`) — pass standard base64 with `encoding="base64"`; read back with `attach(noteId, title)`, which returns the base64 envelope and MIME.
|
|
22
22
|
|
|
23
|
-
Create a standalone `type=code` / `file` note only when you specifically need Trilium's native handling of that type. If you do: label it (`add_label noteType <closest kind>`) so `recall` can see it, give it the same `domain` / `topic` labels as its anchor, and `connect` it to a typed note — otherwise it's an orphan
|
|
23
|
+
Create a standalone `type=code` / `file` note only when you specifically need Trilium's native handling of that type. If you do: label it (`add_label noteType <closest kind>`) so `recall` can see it, give it the same `domain` / `topic` labels as its anchor, and `connect` it to a typed note — otherwise it's an untyped orphan, exactly what `maintain(deep=true)` flags.
|
|
24
24
|
|
|
25
25
|
### Use-case → tool
|
|
26
26
|
|
|
@@ -36,7 +36,8 @@ Written by the server — you never set `#noteType`, `#status`, `#created`, `#up
|
|
|
36
36
|
|
|
37
37
|
| Label | Values | Purpose |
|
|
38
38
|
|---|---|---|
|
|
39
|
-
| `#noteType` | `biography` `goals` `preferences` `responsibilities` `protocols` `selfcorrection` `diary` `session` `thread` `threadEntry` `user` `domain` `information` `sources` `claim` `log` | Kind — exactly one per note, set at creation and immutable once present; a missing kind may be repaired explicitly. `threadEntry` is
|
|
39
|
+
| `#noteType` | `biography` `goals` `preferences` `responsibilities` `protocols` `selfcorrection` `diary` `session` `thread` `threadEntry` `user` `domain` `information` `sources` `claim` `log` | Kind — exactly one per note, set at creation and immutable once present; a missing kind may be repaired explicitly. `threadEntry` is a dated thread's day-child (created by appending) or a collection thread's titled entry (`remember(kind="threadEntry", thread=)`); `claim` is managed by `claim()` |
|
|
40
|
+
| `#threadShape` | `collection` | On a thread book: children are titled, maintained entries rather than dated days. Absent = dated |
|
|
40
41
|
| `#status` | `active` `dormant` `resolved` `superseded` `eternal` | Lifecycle state — threads age; `resolve()` sets terminal. `eternal` = lifecycle-exempt, never aged by the sweep — for user-curated permanent threads |
|
|
41
42
|
| `#created` | ISO date | Set at creation (the user's local day) |
|
|
42
43
|
| `#updated` | ISO date | Updated on every write |
|
|
@@ -47,6 +48,6 @@ Written by the server — you never set `#noteType`, `#status`, `#created`, `#up
|
|
|
47
48
|
| `#mandate` | (flag) | Information notes only — marks a **standing brief** (instructions a future session must follow) as distinct from current-state fact. Set via `remember(…, mandate=true)` or `label(id, "mandate")`; surfaced in `domain()` and `knowledge_recall`. See `references/domains.md` |
|
|
48
49
|
| `#reviewed` | content blob id | Set by `maintain(ack=[…])` — this note's current content was reviewed and its maintenance findings accepted. Suppresses them until the body actually changes, then all of them return |
|
|
49
50
|
| `#brainLlmRoot` | (flag) | Marks the brain root — used by auto-discovery |
|
|
50
|
-
| `#iconClass` | `bx …` | Display icon —
|
|
51
|
+
| `#iconClass` | `bx …` | Display icon — required on every note except logs. Kind default at creation (entries inherit their thread's), sweep backfills; choose one with `icon=` on `remember`/`revise`/`diary`/`close` |
|
|
51
52
|
|
|
52
53
|
**Searching by label:** `recall()` accepts `kinds=[]`, `domain=`, `includeArchived=`. Raw queries via `search_notes()`: `#status=active`, `#noteType=thread`, `#topic=infra`, `#archived` (presence), `note.dateModified < 'YYYY-MM-DD'`. Combine with `AND`/`OR` (space = AND).
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
|---|---|
|
|
29
29
|
| BrainLLM tools time out or return connection errors | The backend is unreachable, not the tool — every ETAPI call is bounded at 30 s with one retry on idempotent reads, so a hang surfaces as a failed tool call rather than a stuck session. Check the instance `TRILIUM_BASE_URL` names: a hosted deploy should answer at its own URL, a local desktop install needs the Trilium app running. Then retry. |
|
|
30
30
|
| `start` → `uninitialized` | `bootstrap` |
|
|
31
|
-
| Deep maintenance flags the same items every session | Act on them
|
|
31
|
+
| Deep maintenance flags the same items every session | Act on them, or `maintain(ack=[…])` the ones you reviewed and found correct |
|
|
32
32
|
| `revise(find=)` returns `replaced: 0` | Read the hint — it names the cause. Escaped search string (`<h3>` against stored real tags): pass the tag literally. Spans a block boundary (`</h3>` then `<ol>`): anchor inside one element, or use `section=` with `mode="before"/"after"`. Otherwise it was already replaced, or the text genuinely differs — check with `inspect(noteId, find="<shorter substring>")` |
|
|
33
33
|
| `revise(section=)` returns `matched: false` | No heading matched, so a NEW section was written — at the note's own level, with the note's real headings in `available[]`. Re-target from that list; `outline(noteId)` gives the same tree plus levels and occurrence indices before you write |
|
|
34
34
|
| Two sections legitimately share a heading | `revise(section=…, occurrence=2)`. Repeating a closing heading under every category is the consistency rule working, not drift — `occurrence=` is how you reach the second one without rewriting the whole section |
|
|
35
|
-
| Note body
|
|
35
|
+
| Note body shows literal `<p>` text in Trilium | Entity-corrupted: `maintain(repair=[noteId])` unwinds one level in place |
|
|
36
36
|
| Unsure whether a note is still structurally sound after several edits | `outline(noteId)` — duplicate headings, unbalanced tags and size in one call, no body read. `maintain(deep=true)` runs the same check across the brain |
|
|
37
37
|
| `recall` returns odd results | It already filters untyped notes; if it persists, `maintain(deep=true)` then retry |
|
|
38
|
-
| Deep maintenance flags something
|
|
38
|
+
| Deep maintenance flags something unfamiliar | Every finding names its own fix in the flag text — read it rather than guessing |
|
|
39
39
|
| A session was interrupted and you are resuming it | The pre-close gate is durable (a `#gate` label on today's session note), so steps you already ran still count. Re-run only what `close()` says is missing. |
|
|
40
40
|
| `start()` did not give you the singleton content you expected | By design — singletons arrive as section headings. Pull one with `master(which)`/`llm(which)`, or re-run `start(depth="full")` if the session genuinely needs the whole self-model. |
|
|
41
41
|
| A keyword search should have matched and did not | Try `recall(query, fuzzy=true)` for typo tolerance, or `recall(regex="…")` when the question is structural (a pattern in the stored HTML) rather than lexical. |
|
|
@@ -46,3 +46,4 @@
|
|
|
46
46
|
| Config IDs stale after restructuring in Trilium | `bootstrap` re-discovers and rewrites `brainllm.json` |
|
|
47
47
|
| `bootstrap` created a duplicate brain tree | Caused by a transient network/auth error during the existence check — the old catch-all fell through to fresh create. Fixed: only a confirmed 404 (root deleted) now triggers a new tree. To recover: identify the newer duplicate by `dateCreated`, then `forget(rootId, hard=true)` its entire subtree or delete it directly in Trilium. |
|
|
48
48
|
| Hosted deploy: `ENOENT` on startup / auto-discovery fails with a file-path error | `BRAINLLM_CONFIG` is set to a directory path instead of a file path (e.g. `/home/node/trilium-data` instead of `/vol/brainllm.json`). Mount a persistent volume on the BrainLLM MCP service (not the Trilium service) and set `BRAINLLM_CONFIG` to a file path inside it, or remove `BRAINLLM_CONFIG` entirely to rely on auto-discovery each startup. |
|
|
49
|
+
| `revise`/`remember` on a thread returns `collection_thread` | It is a collection thread: add entries with `remember(kind="threadEntry", thread=, title=)`, edit them with `revise(<entry id>)` |
|