@andespindola/brainlink 0.1.0-beta.9 → 0.1.0-beta.91

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 (53) hide show
  1. package/AGENTS.md +8 -5
  2. package/CHANGELOG.md +26 -2
  3. package/CONTRIBUTING.md +2 -2
  4. package/COPYRIGHT.md +5 -0
  5. package/README.md +146 -17
  6. package/SECURITY.md +1 -1
  7. package/dist/application/analyze-vault.js +7 -7
  8. package/dist/application/build-context.js +56 -1
  9. package/dist/application/dedupe-notes.js +226 -0
  10. package/dist/application/frontend/client-css.js +154 -102
  11. package/dist/application/frontend/client-html.js +49 -40
  12. package/dist/application/frontend/client-js.js +3118 -167
  13. package/dist/application/frontend/client-worker-js.js +66 -0
  14. package/dist/application/get-graph-layout.js +18 -6
  15. package/dist/application/get-graph-node.js +12 -0
  16. package/dist/application/get-graph-summary.js +12 -0
  17. package/dist/application/get-graph.js +3 -3
  18. package/dist/application/import-legacy-sqlite.js +296 -0
  19. package/dist/application/index-vault.js +252 -19
  20. package/dist/application/list-agents.js +3 -3
  21. package/dist/application/list-links.js +5 -5
  22. package/dist/application/offline-pack-backup.js +44 -0
  23. package/dist/application/search-graph-node-ids.js +12 -0
  24. package/dist/application/search-knowledge.js +25 -10
  25. package/dist/application/server/routes.js +102 -1
  26. package/dist/application/start-server.js +75 -4
  27. package/dist/application/watch-vault.js +23 -2
  28. package/dist/benchmarks/large-vault.js +1 -1
  29. package/dist/cli/commands/agent-commands.js +20 -3
  30. package/dist/cli/commands/write-commands.js +818 -8
  31. package/dist/domain/context.js +53 -11
  32. package/dist/domain/embeddings.js +2 -1
  33. package/dist/domain/graph-layout.js +67 -16
  34. package/dist/domain/middle-out.js +18 -0
  35. package/dist/infrastructure/config.js +38 -0
  36. package/dist/infrastructure/file-index.js +358 -0
  37. package/dist/infrastructure/file-system-vault.js +15 -0
  38. package/dist/infrastructure/index-state.js +56 -0
  39. package/dist/infrastructure/private-pack-codec.js +134 -0
  40. package/dist/infrastructure/search-packs.js +452 -0
  41. package/dist/infrastructure/session-state.js +57 -2
  42. package/dist/mcp/server.js +11 -1
  43. package/dist/mcp/tools.js +215 -3
  44. package/docs/AGENT_USAGE.md +103 -16
  45. package/docs/ARCHITECTURE.md +25 -26
  46. package/docs/QUICKSTART.md +9 -1
  47. package/package.json +6 -4
  48. package/dist/infrastructure/sqlite/document-writer.js +0 -51
  49. package/dist/infrastructure/sqlite/graph-reader.js +0 -120
  50. package/dist/infrastructure/sqlite/schema.js +0 -111
  51. package/dist/infrastructure/sqlite/search-reader.js +0 -156
  52. package/dist/infrastructure/sqlite/types.js +0 -1
  53. package/dist/infrastructure/sqlite-index.js +0 -25
package/AGENTS.md CHANGED
@@ -6,19 +6,19 @@ This file tells coding agents and AI assistants how to use this repository.
6
6
 
7
7
  Brainlink is a local-first knowledge memory for agents.
8
8
 
9
- It reads a Markdown vault, extracts `[[wiki links]]` and `#tags`, builds a local SQLite full-text index, and returns compact context packages that agents can inject into prompts.
9
+ It reads a Markdown vault, extracts `[[wiki links]]` and `#tags`, builds a local file index at `.brainlink/index.json`, and returns compact context packages that agents can inject into prompts.
10
10
 
11
11
  ## Source Of Truth
12
12
 
13
13
  Markdown files are the source of truth.
14
14
 
15
- The SQLite database at `.brainlink/brainlink.db` is a derived index. It can be deleted and rebuilt with:
15
+ The JSON index at `.brainlink/index.json` is derived. It can be deleted and rebuilt with:
16
16
 
17
17
  ```bash
18
18
  npm run dev -- index --vault ./vault
19
19
  ```
20
20
 
21
- Do not store permanent knowledge only in SQLite.
21
+ Do not store permanent knowledge only in index artifacts.
22
22
 
23
23
  By default, the installed Brainlink CLI uses `$HOME/.brainlink/vault` as its vault. Passing `--vault` or setting `vault` in `brainlink.config.json` intentionally selects a custom vault such as `./vault`.
24
24
 
@@ -83,6 +83,9 @@ Use watch mode while editing notes:
83
83
  ```bash
84
84
  npm run dev -- server --vault ./vault --watch
85
85
  npm run dev -- watch --vault ./vault
86
+ npm run dev -- bench --vault ./vault
87
+ npm run dev -- bench --vault ./vault --watch
88
+ npm run dev -- pack-backup --vault ./vault
86
89
  ```
87
90
 
88
91
  Start MCP over stdio:
@@ -107,10 +110,10 @@ npm run dev -- doctor --vault ./vault
107
110
 
108
111
  - Keep domain rules in `src/domain`.
109
112
  - Keep use cases in `src/application`.
110
- - Keep filesystem and SQLite details in `src/infrastructure`.
113
+ - Keep filesystem and index details in `src/infrastructure`.
111
114
  - Keep CLI concerns in `src/cli`.
112
115
  - Prefer pure functions for parsing, ranking, formatting, and transformation.
113
- - Do not make SQLite the canonical storage layer.
116
+ - Do not make index artifacts the canonical storage layer.
114
117
  - Do not add comments with emojis.
115
118
  - Keep JSON output backwards compatible where possible.
116
119
 
package/CHANGELOG.md CHANGED
@@ -22,6 +22,30 @@
22
22
  - Added short-lived hybrid search cache with automatic invalidation on index changes.
23
23
  - Added `stats --extended` observability output with storage, quality and latency probes.
24
24
  - Added `docs/QUICKSTART.md` and aligned README/agent docs with the latest CLI/MCP flows.
25
+ - Added middle-out context assembly so chunk selection expands around the strongest note chunk.
26
+ - Added compressed-space pack prefiltering (token bloom index) before `.blpk` decryption and scan.
27
+ - Improved graph UI auto-fit and viewport recovery so loaded nodes are re-centered when zoom/pan drifts to empty canvas.
28
+ - Added cross-platform native desktop GUI auto-open for `blink server` (macOS Swift/WebKit, Windows PowerShell WinForms, Linux Python GTK/WebKit2), with app-window/browser fallback.
29
+ - Changed Linux default UI launch to app-window/browser for lighter startup; Linux native GUI is now opt-in via `BRAINLINK_LINUX_NATIVE_GUI=1`.
30
+ - Added native GUI parent-process monitoring so GUI windows close automatically when `blink server` stops.
31
+ - Improved non-mac browser detection fallback to try installed Edge/Chrome/Firefox/Chromium candidates before system default open.
32
+ - Improved graph filter rendering to keep hub anchor nodes visible (`Memory Hub`/`MOC`/high-degree fallback) for coherent relationship context.
33
+ - Fixed graph modal content loading by correcting agent query parameter composition for `/api/graph-node` and `/api/graph-filter` requests.
34
+ - Improved 50k+ graph rendering performance with viewport-aware spatial node culling, cached render visibility, and node-adjacent edge selection to avoid full graph scans every frame.
35
+ - Added incremental vault indexing with file snapshots to reuse unchanged documents/chunks/embeddings, plus adaptive search-pack rebuild thresholds to avoid full re-compression on small edits.
36
+ - Reduced large-graph HTTP payload size with compact `/api/graph-layout` encoding for high-node vaults and capped transmitted edges to improve UI load responsiveness.
37
+ - Added aggressive graph LOD clustering when zoomed out, dynamic per-zoom edge render budgets, and a dedicated frontend worker for off-main-thread graph filter matching.
38
+ - Improved Linux browser fallback launch stability by auto-applying Chromium compatibility flags (`--ozone-platform=x11`, `--disable-gpu`, `--disable-features=Vulkan,VaapiVideoDecoder`, `--disable-background-networking`) for app-window/browser modes.
39
+ - Improved massive-graph UI responsiveness with stricter render budgets, adaptive heavy-graph frame throttling, reduced interaction hit-test frequency, and URL-first agent selection on initial graph load.
40
+ - Improved 50k+ graph LOD behavior so zoomed-out views render lightweight cluster overviews and progressively reveal nodes/edges only as zoom increases.
41
+ - Added `blink bench` with realtime index phase telemetry and per-run compressed-pack analysis (input/output bytes, ratio, saved space, rebuild reason and duration), including continuous watch mode.
42
+ - Added tunable single-stage search-pack compression settings (`searchPack.rowChunkSize`, `searchPack.compressionLevel`, `searchPack.useDictionary`).
43
+ - Added benchmark guardrails for compression savings and latency regression (`searchPack.guardrailMinSavingsPercent`, `searchPack.guardrailMaxLatencyRegressionPercent`), reported in `blink bench`.
44
+ - Added `blink pack-backup` for offline second-stage compression backups of encrypted `.blpk` packs, outside the online query path.
45
+ - Hardened Linux browser launch flags for Ubuntu 26 Chromium/Wayland compatibility (`--disable-vulkan`, `--use-gl=swiftshader`, `--ozone-platform-hint=x11`).
46
+ - Improved pack resilience by auto-repairing missing search-pack manifests from existing `.blpk` files, avoiding unnecessary full repacks on small incremental updates.
47
+ - Updated Linux graph auto-open behavior to prioritize the system default browser (`xdg-open`) before explicit browser fallbacks.
48
+ - Removed implicit Chromium dependency in Linux auto-open flow; app-window launch is now opt-in (`BRAINLINK_LINUX_APP_WINDOW=1`).
25
49
 
26
50
  ## 0.1.0-beta.3
27
51
 
@@ -49,8 +73,8 @@
49
73
  ## 0.1.0-alpha.0
50
74
 
51
75
  - Added local-first Markdown vault indexing.
52
- - Added SQLite FTS, local semantic retrieval, wiki links, backlinks and graph retrieval.
53
- - Added SQLite semantic bucket indexing to narrow vector candidates for larger vaults.
76
+ - Added local full-text indexing, local semantic retrieval, wiki links, backlinks and graph retrieval.
77
+ - Added semantic candidate bucket indexing to narrow vector candidates for larger vaults.
54
78
  - Optimized title/link resolution with precomputed agent-scoped title maps.
55
79
  - Added CLI, JSON output, HTTP API and graph UI.
56
80
  - Added vault diagnostics: stats, broken links, orphans, validation and doctor.
package/CONTRIBUTING.md CHANGED
@@ -22,7 +22,7 @@ npm run pack:smoke
22
22
  ## Design Rules
23
23
 
24
24
  - Markdown files are the source of truth.
25
- - SQLite is a derived index and must remain rebuildable.
25
+ - Local index artifacts are derived and must remain rebuildable.
26
26
  - Domain parsing, graph analysis and layout should stay pure and testable.
27
- - CLI, HTTP, filesystem and SQLite code are adapters around application use cases.
27
+ - CLI, HTTP, filesystem and index code are adapters around application use cases.
28
28
  - MCP integration should live outside this package by wrapping the CLI with `--json`.
package/COPYRIGHT.md ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2026 Substructa
2
+
3
+ This project is licensed under the MIT License.
4
+
5
+ See [LICENSE](./LICENSE) for full terms.
package/README.md CHANGED
@@ -52,11 +52,15 @@ LLMs do not have infinite context. Brainlink gives agents an external memory lay
52
52
  1. Durable knowledge is written as Markdown.
53
53
  2. Notes are connected with `[[wiki links]]`.
54
54
  3. Concepts are classified with `#tags`.
55
- 4. Brainlink builds a local SQLite index with FTS records and local embeddings.
55
+ 4. Brainlink builds a local JSON index (`.brainlink/index.json`) and private encrypted search packs.
56
56
  5. Agents query the index before responding.
57
57
  6. Brainlink returns compact, source-backed context.
58
58
 
59
- Markdown is the source of truth. `.brainlink/brainlink.db` is only a rebuildable index.
59
+ Markdown is the source of truth. `.brainlink/index.json` is a rebuildable index artifact.
60
+ After each index run, Brainlink also writes private encrypted search packs at `.brainlink/search-packs/*.blpk` to preserve fast retrieval and portable recovery.
61
+ Online retrieval always uses a single compression stage per pack; optional second-stage compression is reserved for offline backup artifacts only.
62
+ Pack decryption uses a Brainlink key from `$BRAINLINK_HOME/keys` or from `BRAINLINK_SEARCH_PACK_KEY` when explicitly configured.
63
+ Legacy `.jsonl.gz` packs are upgraded to `.blpk` automatically on first search/context access.
60
64
 
61
65
  ## Features
62
66
 
@@ -64,8 +68,12 @@ Markdown is the source of truth. `.brainlink/brainlink.db` is only a rebuildable
64
68
  - Obsidian-compatible `[[wiki links]]` and `#tags`.
65
69
  - Weighted graph edges so agents can rank relationship importance and priority.
66
70
  - Backlinks, broken-link reports, orphan detection and validation.
67
- - Full-text, semantic and hybrid retrieval modes.
68
- - SQLite-backed semantic candidate buckets for larger vaults.
71
+ - Full-text, semantic and hybrid retrieval on a local file index.
72
+ - Middle-out context assembly around the strongest chunk per document.
73
+ - In-process index and context caching with automatic invalidation on index updates.
74
+ - Compressed-space prefiltering for `.blpk` packs before decryption and scan.
75
+ - Incremental indexing that reprocesses only changed markdown files and reuses existing chunks/embeddings for unchanged notes.
76
+ - Adaptive compressed-pack rebuild policy to keep indexing fast during small edit batches.
69
77
  - Agent namespaces under `agents/<agent-id>/`.
70
78
  - S3-compatible bucket vaults through `s3://bucket/prefix` URIs.
71
79
  - CLI with machine-readable `--json` output.
@@ -73,6 +81,17 @@ Markdown is the source of truth. `.brainlink/brainlink.db` is only a rebuildable
73
81
  - Built-in MCP stdio server for agent tool integration.
74
82
  - Local HTTP API.
75
83
  - Realtime graph UI with agent selector and colored knowledge groups.
84
+ - Graph renderer optimized for large datasets with viewport-driven node culling and edge lookup by visible nodes.
85
+ - Canvas graph rendering uses the same batched node and edge pipeline for every graph size, reducing per-frame draw calls while keeping selected and hovered items highlighted.
86
+ - WebGL acceleration is used when available for dense node and edge drawing, with Canvas 2D preserved as the interaction and fallback layer.
87
+ - Graph zoom-out renders hierarchical ecosystem subgraphs only above 1000 notes: the memory hub stays centered, 1000-note groups stay as compact sand-like points, and focused groups gradually expand into smaller graph meshes before individual notes are rendered.
88
+ - Large graph layout API automatically uses compact payload encoding with link-coverage-aware edge selection to reduce initial client load without hiding major relationships.
89
+ - Large-segment layout spacing now grows logarithmically to keep initial visual density consistent between medium and very large vaults (for example, ~1k vs ~50k notes).
90
+ - Graph coordinates are visually compacted across graph sizes so reset starts from a stable macro mass and zoom-in progressively expands toward local detail.
91
+ - Zoomed-out graph LOD renders nested subgraphs and progressively expands only the focused cluster as zoom increases, including very large vaults.
92
+ - Graph reset starts in macro "galaxy" overview mode and progressively reveals nearby nodes as zoom increases, including smaller vaults.
93
+ - Graph filtering runs in a dedicated browser worker to keep the UI thread responsive during heavy datasets.
94
+ - Edge rendering budgets adapt to zoom level to prevent frame spikes on large graph panoramas.
76
95
 
77
96
  ## Install
78
97
 
@@ -282,7 +301,7 @@ export BRAINLINK_S3_FORCE_PATH_STYLE=1
282
301
 
283
302
  Bucket vaults mirror Markdown into a local cache under
284
303
  `$BRAINLINK_HOME/bucket-cache`. The bucket remains canonical; the local
285
- `.brainlink/brainlink.db` stays a disposable index. Run `index` after remote
304
+ `.brainlink/index.json` stays a disposable index artifact. Run `index` after remote
286
305
  bucket changes before relying on `search`, `context`, graph or validation
287
306
  commands. Watch mode is only supported for local filesystem vaults.
288
307
 
@@ -298,7 +317,7 @@ vault/
298
317
  research-agent/
299
318
  source-review-policy.md
300
319
  .brainlink/
301
- brainlink.db
320
+ index.json
302
321
  ```
303
322
 
304
323
  Permanent data:
@@ -308,7 +327,7 @@ Permanent data:
308
327
 
309
328
  Rebuildable data:
310
329
 
311
- - `.brainlink/brainlink.db`
330
+ - `.brainlink/index.json`
312
331
  - full-text records
313
332
  - local embedding vectors
314
333
  - local embedding buckets
@@ -392,6 +411,7 @@ blink agent upgrade
392
411
  ```
393
412
 
394
413
  This configures `~/.codex/config.toml` with Brainlink MCP (`brainlink-mcp`) so Brainlink is available by default in agent sessions.
414
+ `agent install` and `agent upgrade` also apply the MCP `fully-auto` bootstrap policy by default (`enforceBootstrap`, `enforceContextFirst`, `autoBootstrapOnRead`, `autoBootstrapOnStartup` all enabled).
395
415
 
396
416
  If you are inside this repository and want plugin gallery setup too:
397
417
 
@@ -507,10 +527,12 @@ Restart the client after changing marketplace or MCP configuration so it reloads
507
527
  Available tools:
508
528
 
509
529
  - `brainlink_bootstrap`: plug-and-play entrypoint that runs index + health checks and can return context in one call.
510
- - `brainlink_policy`: read or update bootstrap enforcement policy, including presets (`preset: "fully-auto" | "strict"`).
530
+ - `brainlink_policy`: read or update bootstrap/context-first policy, including presets (`preset: "fully-auto" | "strict"`).
511
531
  - `brainlink_recommendations`: return an automatic action plan so agents can run Brainlink in the recommended order.
512
532
  - `brainlink_context`: read indexed context for a task or question.
513
533
  - `brainlink_search`: search indexed notes.
534
+ - `brainlink_dedupe`: detect duplicate candidates using exact hash + semantic similarity scores.
535
+ - `brainlink_resolve_duplicate`: resolve duplicate pairs (`merge`, `link`, `ignore`) with connectivity-safe fallback edges.
514
536
  - `brainlink_add_note`: write durable Markdown memory and reindex.
515
537
  - `brainlink_add_file`: ingest a local file as a note and reindex.
516
538
  - `brainlink_index`: rebuild the vault index.
@@ -522,6 +544,7 @@ Available tools:
522
544
  - `brainlink_orphans`: list disconnected notes.
523
545
 
524
546
  For the most automatic workflow, start MCP sessions with `brainlink_bootstrap` (optionally with `query`) and then continue with `brainlink_context`/`brainlink_add_note`.
547
+ By default, Brainlink enforces context-first for MCP reads (`enforceContextFirst=true`): non-context read tools return preflight until `brainlink_context` is called for the vault/agent session.
525
548
  By default, MCP startup already runs bootstrap on the configured default vault/agent (`autoBootstrapOnStartup=true`), so sessions begin warm.
526
549
  By default, Brainlink enforces bootstrap and auto-runs it for read tools when session state is missing or stale (`autoBootstrapOnRead=true`).
527
550
  If you disable `autoBootstrapOnRead` through `brainlink_policy`, read tools return a preflight instruction with suggested `brainlink_bootstrap` arguments.
@@ -548,16 +571,38 @@ blink server --host 127.0.0.1 --port 4321 --watch
548
571
  ```
549
572
 
550
573
  By default, the server uses `$HOME/.brainlink/vault`. Pass `--vault ./vault` only when you want to inspect a custom vault.
574
+ By default, `blink server` tries to open the graph in a native desktop GUI window:
575
+ - macOS: Swift + WebKit
576
+ - Windows: PowerShell WinForms WebBrowser
577
+ - Linux: optional Python GTK + WebKit2 (requires `python3` + `gi` + `WebKit2`)
578
+
579
+ On Linux, native GUI is disabled by default for better startup performance. Enable it with `BRAINLINK_LINUX_NATIVE_GUI=1`.
580
+ If native GUI launch is unavailable on your system, it falls back to dedicated app-window mode and then to the default browser.
581
+ For Chromium-family browsers on Linux (`chromium`, `chromium-browser`, `google-chrome`, `microsoft-edge`, `brave-browser`), Brainlink now auto-applies compatibility flags during launch (`--ozone-platform=x11`, `--ozone-platform-hint=x11`, `--disable-gpu`, `--disable-vulkan`, `--use-gl=swiftshader`, `--disable-features=Vulkan,VaapiVideoDecoder`, `--disable-background-networking`) to avoid common Wayland/Vulkan/VAAPI startup issues.
582
+ On Linux, Brainlink opens the graph through the system default browser first (`xdg-open`), then `$BROWSER`/detected browsers as fallback. Chromium-family app-window mode is optional via `BRAINLINK_LINUX_APP_WINDOW=1`.
583
+ Use `--no-open` to keep it headless.
584
+ When native GUI is used, the GUI window automatically closes when the `blink server` process stops.
551
585
 
552
586
  The graph UI shows:
553
587
 
554
588
  - notes as nodes
555
589
  - `[[wiki links]]` as weighted edges
556
- - backlinks and outgoing links
557
- - full Markdown content for the selected note
590
+ - details opened on node click (tags, outgoing links, backlinks, full Markdown content)
558
591
  - neutral graph nodes with segment/group metadata
559
- - agent selector for isolated views
592
+ - agent selector (id-only labels) for isolated views
593
+ - graph filter matches title, path, tags and note content
594
+ - graph filter keeps hub context nodes visible (`Memory Hub`/`MOC`/high-degree fallback) to preserve relationship readability
560
595
  - realtime refresh while `--watch` is enabled
596
+ - graph controls for zoom in, zoom out, fit visible nodes and reset-to-fit-all
597
+ - wheel zoom (including `cmd+scroll` and `ctrl+scroll`) anchored to cursor position for faster navigation in large graphs
598
+ - keyboard shortcuts: `+` zoom in, `-` zoom out, `0` reset fit
599
+ - double-click on canvas zooms in at cursor position
600
+ - floating graph totals (notes, links, tags) below the Brainlink title
601
+ - graph rendering safeguards (batched canvas drawing across graph sizes, edge draw caps, lower redraw rate, zoom-aware interaction)
602
+ - WebGL node and edge acceleration when supported, falling back to Canvas 2D without changing graph behavior
603
+ - compact macro-to-micro density progression so reset keeps the graph mass oriented and zoom-in separates local neighborhoods progressively
604
+ - graph camera treats hub-centered navigation as structural only when the hub is dominant; diffuse stress graphs reset and zoom around the full graph mass
605
+ - graph LOD progression: graphs up to 1000 notes render directly; larger graphs use a compact memory-hub-centered mesh of connected 1000-note points, zoom-in continuously spreads and fades only focused clusters through 500-note, 250-note, 125-note and 60-note subgraphs with aggregated real links plus local sibling mesh links, keeps parent points during expansion to avoid visual jumps, then progressively raises the focused node budget so local areas keep nearby notes and links visible
561
606
 
562
607
  The server indexes before starting by default. Use `--no-index` to skip that step:
563
608
 
@@ -576,6 +621,7 @@ Routes:
576
621
  - `GET /api/agents`
577
622
  - `GET /api/graph`
578
623
  - `GET /api/graph-layout`
624
+ - `GET /api/graph-node?id=<node-id>`
579
625
  - `GET /api/search?q=<query>&limit=10&mode=hybrid`
580
626
  - `GET /api/context?q=<query>&limit=12&tokens=2000&mode=hybrid`
581
627
  - `GET /api/links`
@@ -605,6 +651,7 @@ blink agent install --self-test
605
651
  blink agent upgrade
606
652
  blink agent policy --preset fully-auto
607
653
  blink agent policy --preset strict
654
+ blink agent policy --enforce-context-first false
608
655
  blink agent install --plugin-path ./plugins/brainlink
609
656
  blink agent install --mcp-only --allowed-vaults "/absolute/vault,/absolute/team-vault"
610
657
  blink agent status
@@ -615,6 +662,7 @@ When plugin files are available, it also links Brainlink plugin files into `~/pl
615
662
  With `--self-test`, install also validates MCP block presence, command wiring and local plugin registration signals.
616
663
  Use `agent upgrade` on legacy installations to reapply current defaults and run the same self-test diagnostics.
617
664
  Use `agent policy --preset fully-auto` for plug-and-play defaults, or `agent policy --preset strict` to require explicit bootstrap calls.
665
+ Both presets keep `enforceContextFirst=true` so Brainlink stays the primary context source for MCP sessions.
618
666
 
619
667
  ### `quickstart`
620
668
 
@@ -656,6 +704,18 @@ blink migrate-vault --from ~/.brainlink/vault --to ./team-vault --report ./migra
656
704
  Runs explicit markdown migration between vaults while preserving conflicts as `.conflict-<timestamp>` files.
657
705
  Use `--dry-run` to preview `copied`, `conflicted` and `unchanged` counts before writing.
658
706
 
707
+ ### `db-import`
708
+
709
+ ```bash
710
+ blink db-import --vault ./team-vault
711
+ blink db-import --vault ./team-vault --db ./legacy/brainlink.db
712
+ blink db-import --vault ./team-vault --db ./legacy/brainlink.db --table legacy_notes --dry-run
713
+ ```
714
+
715
+ Imports durable memory from a legacy SQLite database into Markdown notes (`agents/<agent-id>/*.md`) and reindexes by default.
716
+ When `--db` is omitted, Brainlink auto-detects common legacy paths such as `<vault>/.brainlink/brainlink.db`.
717
+ Use `--agent <id>` to force all imported rows into one namespace, `--limit` for incremental imports, `--dry-run` to preview without writing files, and `--no-index` to defer reindexing.
718
+
659
719
  ### `init`
660
720
 
661
721
  ```bash
@@ -680,6 +740,28 @@ blink add "Note Title" --vault ./vault --content-file ./notes.md --no-auto-index
680
740
 
681
741
  Creates a Markdown note under `agents/<agent-id>/`. Common secret patterns are blocked by default; use `--allow-sensitive` only for an intentionally protected vault.
682
742
  To avoid disconnected memory, Brainlink auto-adds a fallback wiki edge when a note is written without links, creating agent hub notes when needed.
743
+ `add` also returns `possibleDuplicates` (exact hash + semantic candidates) so agents can resolve duplicate memory right after writes.
744
+
745
+ ### `dedupe`
746
+
747
+ ```bash
748
+ blink dedupe --vault ./vault --json
749
+ blink dedupe --vault ./vault --agent coding-agent --limit 20 --min-score 0.92 --json
750
+ blink dedupe --vault ./vault --no-semantic --json
751
+ ```
752
+
753
+ Detects `possibleDuplicate` pairs using exact content hashes and optional semantic similarity.
754
+
755
+ ### `dedupe-resolve`
756
+
757
+ ```bash
758
+ blink dedupe-resolve --vault ./vault --left agents/shared/a.md --right agents/shared/b.md --action merge --json
759
+ blink dedupe-resolve --vault ./vault --left agents/shared/a.md --right agents/shared/b.md --action link --json
760
+ blink dedupe-resolve --vault ./vault --left agents/shared/a.md --right agents/shared/b.md --action ignore --json
761
+ ```
762
+
763
+ Resolves a duplicate pair with `merge`, `link` or `ignore`.
764
+ When action is not `merge`, Brainlink still creates a low-priority related edge (`#related-to`) so notes remain connected.
683
765
 
684
766
  ### `index`
685
767
 
@@ -690,6 +772,38 @@ blink index --vault ./vault
690
772
 
691
773
  Rebuilds the local index from Markdown files.
692
774
 
775
+ ### `bench`
776
+
777
+ ```bash
778
+ blink bench --vault ./vault
779
+ blink bench --vault ./vault --watch
780
+ blink bench --vault ./vault --watch --debounce 500
781
+ blink bench --vault ./vault --json
782
+ ```
783
+
784
+ Runs indexing with realtime phase telemetry (`start`, `scan`, `parse`, `embed`, `persist`, `packs`, `complete`) and prints a benchmark summary at the end of each run.
785
+
786
+ Summary includes compression behavior for `.blpk` packs when rebuild happens:
787
+ - pack rebuild reason
788
+ - pack count and pack build duration
789
+ - uncompressed input bytes vs compressed output bytes
790
+ - saved percentage
791
+ - objective guardrails (minimum savings and maximum latency regression thresholds)
792
+
793
+ Use `--watch` to keep benchmarking incremental reindex runs after Markdown changes (local filesystem vaults only).
794
+ When `.brainlink/search-packs/manifest.json` is missing but `.blpk` files exist, Brainlink repairs the manifest first and avoids unnecessary full pack rebuild on small edits.
795
+
796
+ ### `pack-backup`
797
+
798
+ ```bash
799
+ blink pack-backup --vault ./vault
800
+ blink pack-backup --vault ./vault --output ./vault/.brainlink/backups/custom.blpkbak.gz
801
+ blink pack-backup --vault ./vault --json
802
+ ```
803
+
804
+ Creates an offline backup artifact of encrypted search packs with a second compression pass.
805
+ This is intentionally outside the online retrieval path (`index`, `search`, `context`).
806
+
693
807
  ### `agents`
694
808
 
695
809
  ```bash
@@ -712,11 +826,12 @@ If `--mode` or `--limit` is omitted, Brainlink resolves values from the current
712
826
 
713
827
  Modes:
714
828
 
715
- - `hybrid`: default; combines SQLite FTS with local embedding similarity.
716
- - `fts`: exact lexical retrieval through SQLite FTS.
829
+ - `hybrid`: default; combines lexical matching with local embedding similarity.
830
+ - `fts`: exact lexical retrieval from the file index.
717
831
  - `semantic`: local deterministic embedding similarity only.
718
832
 
719
833
  Hybrid results are cached in-memory for a short TTL and invalidated automatically when the local index file changes.
834
+ Context selection uses a middle-out strategy: it starts from the strongest chunk in a note and expands to neighboring chunks while respecting token budget.
720
835
 
721
836
  ### `context`
722
837
 
@@ -727,6 +842,7 @@ blink context "question" --vault ./vault --agent coding-agent --mode hybrid --js
727
842
  ```
728
843
 
729
844
  Builds a compact context package for an agent.
845
+ Repeated calls with the same vault, agent, query, mode and token/limit settings are served from a short in-memory cache while the index is unchanged.
730
846
 
731
847
  ### `links`
732
848
 
@@ -811,9 +927,15 @@ Watches Markdown files and rebuilds the index when notes change.
811
927
  ```bash
812
928
  blink server --watch
813
929
  blink server --vault ./vault --watch
930
+ blink server --vault ./vault --watch --no-open
814
931
  ```
815
932
 
816
933
  Starts the local read-only graph UI and HTTP API.
934
+ By default, it tries to open a native desktop GUI window for the graph URL.
935
+ On Linux, native GUI is disabled by default; enable it with `BRAINLINK_LINUX_NATIVE_GUI=1`.
936
+ If native GUI launch is unavailable, it falls back to dedicated app-window mode and then browser open.
937
+ When fallback opens Chromium-family browsers on Linux, Brainlink automatically uses compatibility launch flags for stable rendering on Ubuntu/Wayland setups.
938
+ Use `--no-open` to skip that behavior.
817
939
 
818
940
  The HTTP server only binds to loopback hosts such as `127.0.0.1`, `localhost` or `::1`.
819
941
 
@@ -854,6 +976,13 @@ If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HO
854
976
  "embeddingProvider": "local",
855
977
  "defaultSearchMode": "hybrid",
856
978
  "chunkSize": 1200,
979
+ "searchPack": {
980
+ "rowChunkSize": 5000,
981
+ "compressionLevel": 5,
982
+ "useDictionary": true,
983
+ "guardrailMinSavingsPercent": 8,
984
+ "guardrailMaxLatencyRegressionPercent": 5
985
+ },
857
986
  "agentProfiles": {
858
987
  "coding-agent": {
859
988
  "defaultSearchMode": "semantic",
@@ -965,7 +1094,7 @@ src/
965
1094
  application/ use cases
966
1095
  cli/ command-line adapter
967
1096
  domain/ pure knowledge rules
968
- infrastructure/ filesystem and SQLite adapters
1097
+ infrastructure/ filesystem and index adapters
969
1098
  ```
970
1099
 
971
1100
  Detailed notes:
@@ -977,7 +1106,6 @@ Detailed notes:
977
1106
  ## Current Limits
978
1107
 
979
1108
  - Semantic search uses deterministic local embeddings, not a remote model provider.
980
- - Semantic search uses SQLite embedding buckets to narrow candidates before cosine scoring.
981
1109
  - `embeddingProvider` currently supports `local` and `none`.
982
1110
  - Link resolution is title-based inside each agent namespace, with `shared` as fallback.
983
1111
  - HTTP API is local and unauthenticated.
@@ -988,7 +1116,7 @@ Detailed notes:
988
1116
  The `0.1.0-beta` line is intended to stabilize the local-first memory loop:
989
1117
 
990
1118
  - Markdown as durable memory.
991
- - SQLite FTS plus local embeddings and semantic buckets as rebuildable retrieval index.
1119
+ - Rebuildable file index plus local embeddings and encrypted pack exports.
992
1120
  - CLI as the primary agent interface.
993
1121
  - HTTP graph API and frontend as inspection tools.
994
1122
  - Agent namespaces to avoid context mixing.
@@ -1004,7 +1132,7 @@ Brainlink is local-first by default.
1004
1132
  - Brainlink HTTP is localhost-only and refuses non-loopback hosts.
1005
1133
  - Brainlink blocks common secret patterns by default when adding notes. Use `--allow-sensitive` only for intentional, protected vaults.
1006
1134
  - Do not store secrets, credentials, API keys or regulated personal data unless the vault is protected by your own storage controls.
1007
- - Treat `.brainlink/brainlink.db` as disposable derived data.
1135
+ - Treat `.brainlink/index.json` and `.brainlink/search-packs/` as disposable derived artifacts.
1008
1136
 
1009
1137
  See [SECURITY.md](SECURITY.md).
1010
1138
 
@@ -1015,6 +1143,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
1015
1143
  ## License
1016
1144
 
1017
1145
  MIT. See [LICENSE](LICENSE).
1146
+ Copyright (c) 2026 Substructa. See [COPYRIGHT.md](COPYRIGHT.md).
1018
1147
 
1019
1148
  ### Memory Optimization Loop (1-7)
1020
1149
 
package/SECURITY.md CHANGED
@@ -7,7 +7,7 @@ Brainlink is local-first.
7
7
  - The HTTP server binds to `127.0.0.1` by default.
8
8
  - The HTTP server always refuses non-loopback hosts.
9
9
  - The HTTP server is read-only and does not expose note creation, indexing or update routes.
10
- - The SQLite database is a derived local index.
10
+ - Local index artifacts (`.brainlink/index.json` and `.brainlink/search-packs/`) are derived data.
11
11
  - Markdown files are user-owned source data.
12
12
  - Brainlink-created Markdown files use `0600` permissions.
13
13
  - Brainlink-created directories and `.brainlink` use `0700` permissions.
@@ -3,19 +3,19 @@ import { performance } from 'node:perf_hooks';
3
3
  import { validateGraph, getBrokenLinks, getOrphanNodes, getVaultStats } from '../domain/graph-analysis.js';
4
4
  import { ensureVault, listVaultFiles, readMarkdownFiles } from '../infrastructure/file-system-vault.js';
5
5
  import { resolveAgentRuntimeDefaults } from '../infrastructure/config.js';
6
- import { getGraph } from './get-graph.js';
6
+ import { getGraphSummary } from './get-graph-summary.js';
7
7
  import { buildContextPackage } from './build-context.js';
8
8
  import { indexVault } from './index-vault.js';
9
9
  import { searchKnowledge } from './search-knowledge.js';
10
10
  import { loadBrainlinkConfig } from '../infrastructure/config.js';
11
- export const getStats = async (vaultPath, agentId) => getVaultStats(await getGraph(vaultPath, agentId));
12
- export const getBrokenLinksReport = async (vaultPath, agentId) => getBrokenLinks(await getGraph(vaultPath, agentId));
13
- export const getOrphansReport = async (vaultPath, agentId) => getOrphanNodes(await getGraph(vaultPath, agentId));
14
- export const validateVault = async (vaultPath, agentId) => validateGraph(await getGraph(vaultPath, agentId));
11
+ export const getStats = async (vaultPath, agentId) => getVaultStats(await getGraphSummary(vaultPath, agentId));
12
+ export const getBrokenLinksReport = async (vaultPath, agentId) => getBrokenLinks(await getGraphSummary(vaultPath, agentId));
13
+ export const getOrphansReport = async (vaultPath, agentId) => getOrphanNodes(await getGraphSummary(vaultPath, agentId));
14
+ export const validateVault = async (vaultPath, agentId) => validateGraph(await getGraphSummary(vaultPath, agentId));
15
15
  const toRatio = (part, total) => total === 0 ? 0 : Number((part / total).toFixed(4));
16
16
  export const getExtendedStats = async (vaultPath, agentId) => {
17
17
  const absoluteVaultPath = await ensureVault(vaultPath);
18
- const graph = await getGraph(absoluteVaultPath, agentId);
18
+ const graph = await getGraphSummary(absoluteVaultPath, agentId);
19
19
  const stats = getVaultStats(graph);
20
20
  const markdownFiles = await readMarkdownFiles(absoluteVaultPath);
21
21
  const allFiles = await listVaultFiles(absoluteVaultPath);
@@ -92,7 +92,7 @@ const createCheck = (name, ok, message) => ({
92
92
  export const doctorVault = async (vaultPath) => {
93
93
  const absoluteVaultPath = await ensureVault(vaultPath);
94
94
  const files = await readMarkdownFiles(absoluteVaultPath);
95
- const graph = await getGraph(absoluteVaultPath);
95
+ const graph = await getGraphSummary(absoluteVaultPath);
96
96
  const validation = validateGraph(graph);
97
97
  const checks = [
98
98
  createCheck('vault', true, `Vault ready at ${absoluteVaultPath}`),
@@ -1,13 +1,68 @@
1
+ import { stat } from 'node:fs/promises';
1
2
  import { formatContextPackage, selectContextSections } from '../domain/context.js';
3
+ import { indexStoragePath } from '../infrastructure/file-index.js';
2
4
  import { searchKnowledge } from './search-knowledge.js';
5
+ const contextCacheTtlMs = 45_000;
6
+ const contextCacheMaxEntries = 200;
7
+ const contextCache = new Map();
8
+ const readIndexMtimeMs = async (vaultPath) => {
9
+ try {
10
+ return (await stat(indexStoragePath(vaultPath))).mtimeMs;
11
+ }
12
+ catch {
13
+ return 0;
14
+ }
15
+ };
16
+ const toCacheKey = (vaultPath, query, limit, maxTokens, agentId, mode) => JSON.stringify({
17
+ vaultPath,
18
+ query: query.trim().toLowerCase(),
19
+ limit,
20
+ maxTokens,
21
+ agentId: agentId?.trim().toLowerCase() ?? '*',
22
+ mode: mode ?? 'default'
23
+ });
24
+ const contextCacheGet = (key, indexMtimeMs) => {
25
+ const entry = contextCache.get(key);
26
+ if (!entry) {
27
+ return undefined;
28
+ }
29
+ const fresh = Date.now() - entry.createdAt <= contextCacheTtlMs && entry.indexMtimeMs === indexMtimeMs;
30
+ if (!fresh) {
31
+ contextCache.delete(key);
32
+ return undefined;
33
+ }
34
+ return entry.context;
35
+ };
36
+ const contextCacheSet = (entry) => {
37
+ contextCache.set(entry.key, entry);
38
+ if (contextCache.size <= contextCacheMaxEntries) {
39
+ return;
40
+ }
41
+ const overflow = contextCache.size - contextCacheMaxEntries;
42
+ const keys = Array.from(contextCache.keys()).slice(0, overflow);
43
+ keys.forEach((key) => contextCache.delete(key));
44
+ };
3
45
  export const buildContextPackage = async (vaultPath, query, limit, maxTokens, agentId, mode) => {
46
+ const cacheKey = toCacheKey(vaultPath, query, limit, maxTokens, agentId, mode);
47
+ const indexMtimeMs = await readIndexMtimeMs(vaultPath);
48
+ const cached = contextCacheGet(cacheKey, indexMtimeMs);
49
+ if (cached) {
50
+ return cached;
51
+ }
4
52
  const results = await searchKnowledge(vaultPath, query, limit, agentId, mode);
5
53
  const sections = selectContextSections(results, maxTokens);
6
- return {
54
+ const context = {
7
55
  query,
8
56
  sections,
9
57
  content: formatContextPackage(query, sections)
10
58
  };
59
+ contextCacheSet({
60
+ key: cacheKey,
61
+ createdAt: Date.now(),
62
+ indexMtimeMs,
63
+ context
64
+ });
65
+ return context;
11
66
  };
12
67
  export const buildContext = async (vaultPath, query, limit, maxTokens, agentId, mode) => {
13
68
  const contextPackage = await buildContextPackage(vaultPath, query, limit, maxTokens, agentId, mode);