brainllm 12.8.0 → 12.9.1

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 CHANGED
@@ -17,7 +17,7 @@ TRILIUM_ETAPI_TOKEN=
17
17
  TRILIUM_PASSWORD=
18
18
 
19
19
  # ── Tool surface ───────────────────────────────────────────────────────────────
20
- # core (default): the 44 brain-aware tools. full: adds the 33 raw ETAPI tools.
20
+ # core (default): the 45 brain-aware tools. full: adds the 33 raw ETAPI tools.
21
21
  # BRAINLLM_MODE=full
22
22
 
23
23
  # ── Timezone ───────────────────────────────────────────────────────────────────
package/README.md CHANGED
@@ -10,7 +10,7 @@ Self-hosted memory for LLMs in [TriliumNext Notes](https://github.com/TriliumNex
10
10
 
11
11
  [**Site**](https://miisodev.github.io/BrainLLM/) · [Connect to Claude](https://miisodev.github.io/BrainLLM/connect.html) · [How it works](https://miisodev.github.io/BrainLLM/how-it-works.html) · [Docs](https://miisodev.github.io/BrainLLM/docs.html) · [Privacy](https://miisodev.github.io/BrainLLM/privacy.html)
12
12
 
13
- [![Version](https://img.shields.io/badge/version-12.8.0-f59e0b?style=flat-square)](https://github.com/miisodev/BrainLLM/releases)
13
+ [![Version](https://img.shields.io/badge/version-12.9.1-f59e0b?style=flat-square)](https://github.com/miisodev/BrainLLM/releases)
14
14
  [![CI](https://img.shields.io/github/actions/workflow/status/miisodev/BrainLLM/ci.yml?branch=main&style=flat-square&label=CI&color=f59e0b)](https://github.com/miisodev/BrainLLM/actions/workflows/ci.yml)
15
15
  [![License: MIT](https://img.shields.io/badge/license-MIT-a1a1aa?style=flat-square)](./LICENSE)
16
16
  [![Runtime: Bun](https://img.shields.io/badge/runtime-Bun%20%E2%89%A5%201.0-a1a1aa?style=flat-square&logo=bun&logoColor=white)](https://bun.sh)
@@ -55,6 +55,8 @@ Placement, naming, labels, deduplication, relation bookkeeping, lifecycle aging,
55
55
  - **Resilient plumbing** — every backend call is timeout-bounded with retry on idempotent reads; routine core writes are idempotent or duplicate-guarded, while full-mode raw overwrites remain explicit; content surgery survives the editor's own HTML rewriting; renaming a domain cascades to everything inside it; the maintenance sweep heals drift it finds.
56
56
  - **Multi-agent by default** — all write-classified tools serialize behind a process-wide FIFO lock, so two agents (an interactive session and an automated run, say) writing through one hosted instance queue in arrival order instead of racing; reads stay fully parallel. Deletion catch-up in the daily log keeps a note deleted after its day's close from vanishing without a trace — and its window is configurable (`deletionCatchupDays` in the lifecycle policy), defaulting to Trilium's 7-day retention.
57
57
  - **Ask in prose, split on a seam** — `consistency(subject="…")` finds every note asserting about a fact however it is phrased, no regex guessing; `consistency(pattern, staleAfterDays=N)` surfaces figures held in exactly one untouched note, the ones that rot silently because nothing disagrees with them, and skips sessions and diaries, which are history rather than claims; `diff(since="today")` reviews every note a session changed in one call; `revise(find="Phase 2", within="tr")` edits, inserts or removes a table row named by a few words inside it; `read(ids=[…])` batches a multi-note orientation into one round trip; and `split(noteId, sections=[…], into="…")` is the write half of the oversized-note problem — it lifts whole sections into a new note and leaves a pointer back. Information notes can carry a `#mandate` marker so a scoped session finds the one note it must obey without reading every note's prose.
58
+ - **Registers close in one call** — `revise(find, within="tr", mode="remove", closure={thread, body})` removes a register row and records its closure in that thread's entry for the day; `within=` also accepts a row as a reader sees it (`"Phase 2 | Claude | Open"`), and a table row whose cells disagree with its header is flagged at write time and by `maintain(deep)`.
59
+ - **Records you can prove** — sessions, diary entries and dated thread entries are append-only, and now verifiably so: every `close()` seals its day's records into a digest chained to the previous day's, and `maintain(deep)` reports any sealed record that was rewritten afterwards. Appends stay legal; edits to history do not go unnoticed.
58
60
 
59
61
  ---
60
62
 
@@ -77,7 +79,7 @@ BrainLLM (#brainLlmRoot)
77
79
  | **Dated records** | diary, session, log | One per calendar day; every write lands as a timestamped addendum block — chronology is the point |
78
80
  | **Collections** | thread, user, information, domain | Titled notes, deduplicated by normalized title within their scope |
79
81
 
80
- A session follows an enforced protocol: `start()` orients (who you are, live threads, what changed since last time) → the model works, writing durable facts *as they surface* → `session()` → `addendum()` → `maintain()` → `remarks()` → `diary()` → `close()` commits the log, regenerates the daily change log, and triggers a DB backup. The pre-close gate is **enforced in code**: `close()` refuses until every step actually ran, in order — narrating "I did the steps" doesn't count, only tool calls do. The gate is *durable*, written to the session note rather than held in memory, so it survives a restart mid-session and behaves the same on stdio and HTTP.
82
+ A session follows an enforced protocol: `start()` orients (who you are, live threads, what changed since last time) → the model works, writing durable facts *as they surface* → `session()` → `addendum()` → `maintain()` → `remarks()` → `diary()` → `close()` commits the log, regenerates the daily change log, and triggers a DB backup. The pre-close gate is **enforced in code**: `close()` refuses until every step actually ran, in order — narrating "I did the steps" doesn't count, only tool calls do. The gate is *durable*, written to the session note rather than held in memory, so it survives a restart mid-session and behaves the same on stdio and HTTP. A session that wrote no content gets a **light close**: nothing to fold or sweep, so `close()` asks only for `session()`, `remarks()` and a short `diary()`.
81
83
 
82
84
  Orientation is deliberately cheap. `start()` returns your singletons as **section headings** — enough for the model to know what the brain holds — and it pulls the one section that matters. Serving the entire self-model on every session meant a one-line question cost the same as a day's work; `start(depth="full")` is still there for the sessions that genuinely need it.
83
85
 
package/SECURITY.md CHANGED
@@ -6,8 +6,8 @@ Security fixes ship on the latest release line. Older versions may remain useful
6
6
 
7
7
  | Version | Supported |
8
8
  |---|---|
9
- | 12.8.x | ✅ |
10
- | 12.7.x and earlier | ❌ |
9
+ | 12.9.x | ✅ |
10
+ | 12.8.x and earlier | ❌ |
11
11
 
12
12
  ## Reporting a vulnerability
13
13
 
@@ -28,6 +28,8 @@ Run the latest tagged release rather than an unreleased branch or a mutable depe
28
28
 
29
29
  Since 12.8.0 the OAuth store keeps refresh tokens only as SHA-256 hashes (a token issued earlier is accepted once and replaced by a hashed successor on its next refresh), MCP traffic is rate-limited per bearer token with a looser per-IP bound, and the consent screen shows where an approval will be sent and warns when that is the local machine.
30
30
 
31
+ Since 12.9.0 the body sanitizer keeps an unclosed `form`, `select`, `template` or similar opener as escaped, inert text instead of removing everything after it; unclosed `script`, `style`, `noscript` and `iframe` still take their tail with them, and closed active blocks are still removed whole. Each close also seals the day's records in a hash chain that `maintain(deep=true)` verifies, so a rewritten session, diary or thread entry is reported rather than silently trusted.
32
+
31
33
  For credential rotation, replace `MCP_AUTH_TOKEN` and the ETAPI token in the provider/Trilium, set a fresh `BRAINLLM_OAUTH_SECRET` (32+ characters) to invalidate existing OAuth tokens, and remove obsolete local `*.oauth.json` / `*.token` files. Never put recovered credentials in a repository, shared context note, Docker context, or release artifact.
32
34
 
33
35
  ## Scope