@andespindola/brainlink 0.1.0-beta.98 → 1.0.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.
Files changed (46) hide show
  1. package/AGENTS.md +6 -6
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +186 -38
  4. package/dist/application/add-note.js +13 -44
  5. package/dist/application/analyze-vault.js +1 -1
  6. package/dist/application/auto-migrate-configured-vault.js +37 -0
  7. package/dist/application/build-context.js +119 -20
  8. package/dist/application/canonical-context-links.js +209 -0
  9. package/dist/application/frontend/client-css.js +212 -42
  10. package/dist/application/frontend/client-html.js +42 -28
  11. package/dist/application/frontend/client-js.js +1294 -3217
  12. package/dist/application/frontend/client-render-worker-js.js +676 -0
  13. package/dist/application/get-graph-contexts.js +33 -0
  14. package/dist/application/get-graph-layout.js +62 -8
  15. package/dist/application/get-graph-stream-chunk.js +326 -0
  16. package/dist/application/get-graph-view.js +246 -0
  17. package/dist/application/graph-view-state.js +66 -0
  18. package/dist/application/import-legacy-sqlite.js +3 -33
  19. package/dist/application/index-vault.js +35 -22
  20. package/dist/application/migrate-context-links.js +79 -0
  21. package/dist/application/search-graph-node-ids.js +63 -3
  22. package/dist/application/server/routes.js +197 -12
  23. package/dist/cli/commands/read-commands.js +39 -3
  24. package/dist/cli/commands/vault-commands.js +182 -0
  25. package/dist/cli/commands/write-commands.js +147 -12
  26. package/dist/cli/main.js +2 -0
  27. package/dist/cli/runtime.js +10 -2
  28. package/dist/domain/context.js +1 -0
  29. package/dist/domain/graph-contexts.js +180 -0
  30. package/dist/domain/graph-layout.js +347 -21
  31. package/dist/domain/markdown.js +53 -9
  32. package/dist/infrastructure/config.js +105 -6
  33. package/dist/infrastructure/context-packs.js +122 -0
  34. package/dist/infrastructure/file-index.js +6 -3
  35. package/dist/infrastructure/index-state.js +2 -0
  36. package/dist/infrastructure/vault-migration-state.js +69 -0
  37. package/dist/infrastructure/volatile-memory.js +100 -0
  38. package/dist/mcp/http-server.js +97 -0
  39. package/dist/mcp/runtime.js +20 -0
  40. package/dist/mcp/server.js +36 -13
  41. package/dist/mcp/tools.js +203 -14
  42. package/docs/AGENT_USAGE.md +50 -5
  43. package/docs/ARCHITECTURE.md +11 -0
  44. package/docs/QUICKSTART.md +3 -1
  45. package/docs/RELEASE.md +4 -3
  46. package/package.json +3 -1
package/AGENTS.md CHANGED
@@ -6,7 +6,7 @@ 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 file index at `.brainlink/index.json`, and returns compact context packages that agents can inject into prompts.
9
+ It reads a Markdown vault, extracts concise graph links from `## Context Links`, extracts `#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
 
@@ -27,15 +27,15 @@ By default, the installed Brainlink CLI uses `$HOME/.brainlink/vault` as its vau
27
27
  Use this loop when using Brainlink as memory:
28
28
 
29
29
  1. Write durable knowledge into Markdown notes.
30
- 2. Link related notes with explicit `[[Note Title]]` wiki links inside the note body.
30
+ 2. Link related notes with explicit `[[Note Title]]` wiki links inside a `## Context Links` section.
31
31
  3. Add explicit `#tags` for retrieval.
32
32
  4. Run `index` after writes.
33
33
  5. Run `context "<task or question>"` before answering.
34
34
  6. Use the returned sources as grounded context.
35
35
 
36
- `context` is read-only. It does not create notes, backlinks, graph edges or durable memory by itself. A relationship exists only when a Markdown note contains a `[[wiki link]]` to another note and the vault has been indexed after that write.
36
+ `context` is read-only. It does not create notes, backlinks, graph edges or durable memory by itself. A relationship exists only when a Markdown note contains a `[[wiki link]]` inside `## Context Links` and the vault has been indexed after that write.
37
37
 
38
- When an agent adds durable memory, it should connect the new note to at least one existing concept unless the note is intentionally a root concept. Prefer exact note titles in links, for example `[[Architecture]]`, and run `broken-links`, `orphans` or `validate` when the graph looks disconnected.
38
+ When an agent adds durable memory, it should add only the canonical relationships to `## Context Links`. Prefer exact note titles in links, for example `[[Architecture]]`, and run `broken-links`, `orphans` or `validate` when graph health matters.
39
39
 
40
40
  Agents can mark important relationships by placing priority hints on the same line as a wiki link, for example `[[Architecture]] priority: high`, `[[Incident Runbook]] #important` or `[[Incident Runbook]] #critical`. Indexed graph edges expose `weight` and `priority` so agents can sort related notes by importance.
41
41
 
@@ -78,10 +78,10 @@ http://127.0.0.1:4321/
78
78
  http://127.0.0.1:4321/api/graph
79
79
  ```
80
80
 
81
- Use watch mode while editing notes:
81
+ The graph server watches Markdown files by default while editing notes:
82
82
 
83
83
  ```bash
84
- npm run dev -- server --vault ./vault --watch
84
+ npm run dev -- server --vault ./vault
85
85
  npm run dev -- watch --vault ./vault
86
86
  npm run dev -- bench --vault ./vault
87
87
  npm run dev -- bench --vault ./vault --watch
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0
4
+
5
+ - Promoted Brainlink to the first stable functional release.
6
+ - Stabilized the local-first Markdown vault model with rebuildable indexes, graph links, tags, search packs and context packages.
7
+ - Stabilized the CLI memory workflow for init, add, index, search, context, graph inspection, validation, migration and vault management.
8
+ - Stabilized MCP tooling for bootstrap, policy, recommendations, RAG/CAG context, durable writes, volatile memory, graph reads and vault health checks.
9
+ - Added remote MCP server mode for centralized cluster access over Streamable HTTP with health/readiness probes and optional bearer authentication.
10
+ - Added dedicated `vaults` commands to list known vaults, choose the default vault and safely delete local filesystem vaults.
11
+ - Improved graph UI stability, dark theme behavior, large-graph rendering, node interaction and graph node visibility.
12
+ - Kept compatibility with the `0.1.0-beta` vault and index model; derived artifacts remain rebuildable from Markdown source.
13
+
3
14
  ## 0.1.0-beta.4
4
15
 
5
16
  - Added bootstrap session-state persistence in `$BRAINLINK_HOME/session-state.json` for vault/agent readiness tracking.
@@ -14,6 +25,9 @@
14
25
  - Added default MCP startup bootstrap behavior controlled by `brainlink_policy.autoBootstrapOnStartup`.
15
26
  - Added CLI MCP policy presets through `blink agent policy --preset fully-auto|strict`.
16
27
  - Added write-time non-orphan enforcement by auto-linking notes without wiki edges to agent hub notes.
28
+ - Changed graph indexing to keep every non-self Markdown wiki link as a weighted graph edge so the default star graph represents complete note connectivity.
29
+ - Added `blink index --full` and MCP `brainlink_index` `full=true` for complete source reindexing without clearing the existing index first.
30
+ - Improved index migration so stale graph link model metadata automatically triggers a complete source reindex.
17
31
  - Added MCP `brainlink_policy` presets (`fully-auto`, `strict`) for one-call policy switching.
18
32
  - Added MCP write connectivity metadata in `brainlink_add_note`/`brainlink_add_file` responses.
19
33
  - Added MCP `brainlink_recommendations` tool for plug-and-play workflow guidance.
package/README.md CHANGED
@@ -71,6 +71,8 @@ Legacy `.jsonl.gz` packs are upgraded to `.blpk` automatically on first search/c
71
71
  - Full-text, semantic and hybrid retrieval on a local file index.
72
72
  - Middle-out context assembly around the strongest chunk per document.
73
73
  - In-process index and context caching with automatic invalidation on index updates.
74
+ - Optional CAG context packs at `.brainlink/context-packs/*.json`, derived from the current index signature and reusable across repeated context calls.
75
+ - HTTP graph server caches generated frontend assets and graph-layout JSON payloads by signature, and skips layout serialization when ETag returns `304`.
74
76
  - Compressed-space prefiltering for `.blpk` packs before decryption and scan.
75
77
  - Incremental indexing that reprocesses only changed markdown files and reuses existing chunks/embeddings for unchanged notes.
76
78
  - Adaptive compressed-pack rebuild policy to keep indexing fast during small edit batches.
@@ -81,17 +83,17 @@ Legacy `.jsonl.gz` packs are upgraded to `.blpk` automatically on first search/c
81
83
  - Built-in MCP stdio server for agent tool integration.
82
84
  - Local HTTP API.
83
85
  - 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.
86
+ - Graph renderer uses a cauliflower-style hub layout: the primary hub stays centered, segment hubs anchor surrounding lobes, and visual edges are simplified to root hub -> segment hubs -> local context nodes.
87
+ - Real weighted `[[wiki link]]` edges stay preserved in the indexed graph APIs for backlinks, ranking and context traversal; the browser layout uses a separate visual edge layer for readability.
88
+ - Graph exploration uses stable chunk streaming (`/api/graph-stream`) with explicit node/edge budgets, returning the full mode-level scene while it fits the budget.
89
+ - Render pipeline uses WebGL in a dedicated worker through `OffscreenCanvas`, keeping the main thread focused on UI controls and details panels.
88
90
  - 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
91
  - 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.
92
+ - Graph coordinates are visually compacted across graph sizes so reset starts from a stable fitted scene and zoom-in progressively reveals local detail.
93
+ - Zoomed-out graph summarizes the scene as segment hub clusters, then progressively reveals individual nodes as the user zooms in.
94
+ - Graph reset fits the full graph scene instead of starting in a separate macro overview mode.
93
95
  - 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.
96
+ - Node titles are shown as the user zooms closer, while labels remain bounded to visible on-screen nodes in very large graphs.
95
97
 
96
98
  ## Install
97
99
 
@@ -209,19 +211,19 @@ Only store knowledge that is likely to matter later:
209
211
  ```bash
210
212
  blink add "Testing Policy" \
211
213
  --agent "$BLINK_AGENT" \
212
- --content "Run npm run check before final delivery. Related: [[Release Checklist]]. #testing #process"
214
+ --content $'Run npm run check before final delivery. #testing #process\n\n## Context Links\n\n- [[Release Checklist]]'
213
215
  ```
214
216
 
215
- Brainlink does not infer durable graph relationships from generated context. A context result is only a read package for the model. To create a real link in the knowledge graph, the agent must write Markdown that contains an explicit `[[Note Title]]` wiki link.
217
+ Brainlink does not infer durable graph relationships from generated context. A context result is only a read package for the model. To create a real graph link, write a concise `## Context Links` section and put the canonical `[[Note Title]]` links there.
216
218
 
217
219
  Writes with `blink add` reindex the vault automatically by default. This can be disabled with `--no-auto-index` and controlled globally with `autoIndexOnWrite` in `brainlink.config.json`.
218
220
 
219
221
  When adding memory, follow this contract:
220
222
 
221
- - Link the new note to at least one existing note when there is a related concept.
223
+ - Link the new note to existing notes through `## Context Links` when there is a related concept.
222
224
  - Use the exact target note title inside `[[...]]`.
223
225
  - Add retrieval tags such as `#architecture`, `#decision`, `#runbook` or `#preference`.
224
- - Do not leave isolated notes unless they are intentionally root concepts.
226
+ - General wiki-link mentions outside `## Context Links` remain searchable Markdown content, but they do not become graph edges.
225
227
 
226
228
  If you disable auto-index, run `blink index` after batched writes.
227
229
 
@@ -264,7 +266,7 @@ blink search "jwt auth" --vault ./vault
264
266
 
265
267
  blink context "how does auth work?" --vault ./vault
266
268
 
267
- blink server --vault ./vault --watch
269
+ blink server --vault ./vault
268
270
  ```
269
271
 
270
272
  Open the graph UI:
@@ -528,18 +530,23 @@ Available tools:
528
530
 
529
531
  - `brainlink_bootstrap`: plug-and-play entrypoint that runs index + health checks and can return context in one call.
530
532
  - `brainlink_policy`: read or update bootstrap/context-first policy, including presets (`preset: "fully-auto" | "strict"`).
531
- - `brainlink_recommendations`: return an automatic action plan so agents can run Brainlink in the recommended order.
532
- - `brainlink_context`: read indexed context for a task or question.
533
+ - `brainlink_recommendations`: return an automatic action plan so agents can run Brainlink in the recommended order, including RAG/CAG context strategy guidance.
534
+ - `brainlink_context`: read indexed context for a task or question; pass `strategy: "rag"` for fresh retrieval assembly, `strategy: "cag"` for persisted context packs or `strategy: "auto"` for CAG hits with RAG fallback.
535
+ - `brainlink_context_packs`: list or clear persisted CAG context packs.
533
536
  - `brainlink_search`: search indexed notes.
534
537
  - `brainlink_dedupe`: detect duplicate candidates using exact hash + semantic similarity scores.
535
538
  - `brainlink_resolve_duplicate`: resolve duplicate pairs (`merge`, `link`, `ignore`) with connectivity-safe fallback edges.
536
539
  - `brainlink_add_note`: write durable Markdown memory and reindex.
537
540
  - `brainlink_add_file`: ingest a local file as a note and reindex.
538
- - `brainlink_index`: rebuild the vault index.
541
+ - `brainlink_canonicalize_context_links`: ensure existing notes link to inferred context hubs.
542
+ - `brainlink_volatile_add`: write temporary agent-decided memory with TTL; volatile sections are included in context and never create durable graph edges.
543
+ - `brainlink_volatile_clear`: clear temporary memory for the current vault/agent namespace.
544
+ - `brainlink_index`: rebuild the vault index. Pass `full=true` for a complete source reindex.
539
545
  - `brainlink_stats`: read indexed vault statistics.
540
546
  - `brainlink_validate`: validate broken links and orphan notes.
541
547
  - `brainlink_sync`: run index, stats, validation, broken-link and orphan checks in one call.
542
548
  - `brainlink_graph`: read indexed graph nodes and weighted links.
549
+ - `brainlink_graph_contexts`: list the visual graph contexts used by the local server.
543
550
  - `brainlink_broken_links`: list unresolved wiki links.
544
551
  - `brainlink_orphans`: list disconnected notes.
545
552
 
@@ -550,8 +557,9 @@ By default, Brainlink enforces bootstrap and auto-runs it for read tools when se
550
557
  If you disable `autoBootstrapOnRead` through `brainlink_policy`, read tools return a preflight instruction with suggested `brainlink_bootstrap` arguments.
551
558
  `brainlink_bootstrap`, `brainlink_policy` and preflight responses include structured `nextActions` so MCP clients can continue automatically without custom parsing.
552
559
  For one-call planning, use `brainlink_recommendations` to get the recommended tool sequence for the current vault/agent/query.
560
+ The MCP context tools are plug-and-play by default: omit `strategy` to use the configured default (`rag` unless changed), pass `strategy: "cag"` for repeated/stable task context, or pass `strategy: "auto"` so Brainlink chooses CAG on fresh pack hits and RAG otherwise. `brainlink_recommendations`, preflight responses and policy next actions include executable context arguments so clients can continue without custom parsing.
553
561
 
554
- The same linking rule applies through MCP: `brainlink_context` is read-only, and real graph links require Markdown notes with explicit `[[wiki links]]`. `brainlink_add_note` and `brainlink_add_file` reindex by default and include index + `writeConnectivity` metadata. Brainlink guarantees at least one edge per new note by auto-linking when needed.
562
+ The same linking rule applies through MCP: `brainlink_context` is read-only, and real graph links require Markdown notes with explicit `## Context Links` sections. `brainlink_add_note` and `brainlink_add_file` reindex by default and include index + `writeConnectivity` metadata. Brainlink does not auto-link new notes to fallback hubs.
555
563
 
556
564
  Agents can raise the importance of a relationship by putting priority markers on the same line as a wiki link:
557
565
 
@@ -560,17 +568,25 @@ Agents can raise the importance of a relationship by putting priority markers on
560
568
  Related: [[Incident Runbook]] #critical
561
569
  ```
562
570
 
563
- Indexed edges expose `weight` and `priority` (`low`, `normal`, `high`, `critical`) through CLI JSON, HTTP graph APIs and `brainlink_graph`.
571
+ Indexed edges expose `weight` and `priority` (`low`, `normal`, `high`, `critical`) through CLI JSON, HTTP graph APIs and `brainlink_graph`. Brainlink indexes every non-self `[[wiki link]]` inside `## Context Links` as a graph edge. Old indexes are rebuilt automatically when their graph link model version is missing or stale.
572
+
573
+ To migrate older vaults without deleting existing Markdown, generate concise context-link sections from current wiki-link mentions:
574
+
575
+ ```bash
576
+ blink migrate-context-links --vault ./vault --limit 5
577
+ blink index --vault ./vault --full
578
+ ```
564
579
 
565
580
  ## Graph UI
566
581
 
567
582
  Start the local frontend:
568
583
 
569
584
  ```bash
570
- blink server --host 127.0.0.1 --port 4321 --watch
585
+ blink server --host 127.0.0.1 --port 4321
571
586
  ```
572
587
 
573
588
  By default, the server uses `$HOME/.brainlink/vault`. Pass `--vault ./vault` only when you want to inspect a custom vault.
589
+ By default, the server watches Markdown files in local filesystem vaults and reindexes after note changes. Use `--no-watch` to disable realtime reindexing.
574
590
  By default, `blink server` tries to open the graph in a native desktop GUI window:
575
591
  - macOS: Swift + WebKit
576
592
  - Windows: PowerShell WinForms WebBrowser
@@ -586,23 +602,27 @@ When native GUI is used, the GUI window automatically closes when the `blink ser
586
602
  The graph UI shows:
587
603
 
588
604
  - notes as nodes
589
- - `[[wiki links]]` as weighted edges
590
- - details opened on node click (tags, outgoing links, backlinks, full Markdown content)
605
+ - all non-self `[[wiki links]]` inside `## Context Links` as weighted indexed edges
606
+ - default cauliflower-style visual layout centered on the primary hub, with segment hubs anchoring surrounding lobes and visual edges simplified to root hub -> segment hubs -> local context nodes
607
+ - details opened in a non-modal side panel (tags, outgoing links, backlinks, full Markdown content), so zoom and pan remain available while inspecting data
591
608
  - neutral graph nodes with segment/group metadata
592
609
  - agent selector (id-only labels) for isolated views
610
+ - context selector for segment-scoped cauliflower subgraphs derived from the visual graph context
593
611
  - graph filter matches title, path, tags and note content
594
612
  - graph filter keeps hub context nodes visible (`Memory Hub`/`MOC`/high-degree fallback) to preserve relationship readability
595
- - realtime refresh while `--watch` is enabled
613
+ - realtime refresh while watch mode is enabled
596
614
  - graph controls for zoom in, zoom out, fit visible nodes and reset-to-fit-all
597
615
  - wheel zoom (including `cmd+scroll` and `ctrl+scroll`) anchored to cursor position for faster navigation in large graphs
616
+ - wheel/button zoom updates immediately at the cursor anchor without delayed focus-transition interpolation
617
+ - Bloom-like scene navigation: reset fits the current graph scene, wheel zoom stays anchored to the cursor, and worker-driven WebGL rendering keeps pan/zoom interaction responsive
618
+ - zoom-out cluster mode that shows segment hub clusters first, then reveals local nodes as zoom increases
598
619
  - keyboard shortcuts: `+` zoom in, `-` zoom out, `0` reset fit
599
- - double-click on canvas zooms in at cursor position
620
+ - click on a node opens its details panel; double-click on empty canvas zooms in at cursor position
600
621
  - 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 one recursive model where each visible level targets up to 999 non-hub nodes, starts from a memory-hub-centered mesh, and each supernode can expand into another same-shape subgraph level (again up to 999 children) with latent fade-in, aggregated real links and local sibling mesh links so org-heavy and stress-50k follow the same structure at different depths
622
+ - graph rendering safeguards (batched GPU draw calls, lower redraw rate, zoom-aware interaction)
623
+ - adaptive CPU safeguards for large graphs: idle frame pacing, throttled background physics updates and cached viewport dimensions to reduce redraw/layout overhead while preserving interaction responsiveness
624
+ - worker-first WebGL rendering with Canvas fallback when `OffscreenCanvas` or worker rendering is unavailable
625
+ - large graph view keeps one indexed graph model across zoom levels, uses a stable visual hierarchy for rendering, uses segment clusters at high zoom-out, and shows node titles as zoom approaches readable scale
606
626
 
607
627
  The server indexes before starting by default. Use `--no-index` to skip that step:
608
628
 
@@ -619,8 +639,11 @@ The server always refuses non-loopback hosts. Brainlink HTTP only runs on localh
619
639
  Routes:
620
640
 
621
641
  - `GET /api/agents`
642
+ - `GET /api/graph-contexts`
622
643
  - `GET /api/graph`
623
644
  - `GET /api/graph-layout`
645
+ - `GET /api/graph-view?x=<x>&y=<y>&w=<width>&h=<height>&scale=<scale>`
646
+ - `GET /api/graph-stream?x=<x>&y=<y>&w=<width>&h=<height>&scale=<scale>&nodeBudget=<n>&edgeBudget=<n>`
624
647
  - `GET /api/graph-node?id=<node-id>`
625
648
  - `GET /api/search?q=<query>&limit=10&mode=hybrid`
626
649
  - `GET /api/context?q=<query>&limit=12&tokens=2000&mode=hybrid`
@@ -635,6 +658,7 @@ Read routes accept `agent=<agent-id>`:
635
658
 
636
659
  ```txt
637
660
  /api/graph-layout?agent=coding-agent
661
+ /api/graph-layout?agent=coding-agent&context=Architecture
638
662
  /api/search?q=typescript&agent=coding-agent&mode=hybrid
639
663
  /api/context?q=module-boundaries&agent=coding-agent&mode=semantic
640
664
  ```
@@ -689,9 +713,25 @@ blink config set-vault "s3://my-memory-bucket/brainlink" --global
689
713
 
690
714
  `config set-vault` writes configuration through CLI (no manual file edits required).
691
715
  By default it writes local config (`./brainlink.config.json`), appends the vault to `allowedVaults`, and migrates Markdown memory from the current configured vault when the target is empty.
716
+ When the configured default vault is changed manually in config files, Brainlink also performs automatic migration on the next command that uses the configured vault (without explicit `--vault`).
692
717
  Use `--global` to write to `$BRAINLINK_HOME/brainlink.config.json`, `--no-migrate` to skip migration, and `--no-index` to skip post-migration indexing.
693
718
  `config doctor` is dry-run by default; use `--fix` to apply safe config normalization and allowlist fixes.
694
719
 
720
+ ### `vaults`
721
+
722
+ ```bash
723
+ blink vaults list
724
+ blink vaults list --json
725
+ blink vaults use /absolute/path/to/vault
726
+ blink vaults use /absolute/path/to/vault --global
727
+ blink vaults delete /absolute/path/to/vault --yes
728
+ blink vaults delete /absolute/path/to/vault --yes --prune-config
729
+ ```
730
+
731
+ Lists known vaults from the configured default, `allowedVaults`, and the built-in default at `$HOME/.brainlink/vault`.
732
+ `vaults use` chooses the default vault without migrating memory; use `migrate-vault` or `config set-vault --migrate-from` when you want to copy Markdown memory between vaults.
733
+ `vaults delete` only deletes local filesystem vaults, requires `--yes`, refuses bucket vaults, and refuses deleting the current default vault. Choose another default first with `vaults use`.
734
+
695
735
  ### `migrate-vault`
696
736
 
697
737
  ```bash
@@ -768,9 +808,12 @@ When action is not `merge`, Brainlink still creates a low-priority related edge
768
808
  ```bash
769
809
  blink index
770
810
  blink index --vault ./vault
811
+ blink index --vault ./vault --full
771
812
  ```
772
813
 
773
- Rebuilds the local index from Markdown files.
814
+ Rebuilds the local index from Markdown files. By default, unchanged notes reuse existing indexed chunks for speed.
815
+ Use `--full` to force a complete source reindex of every Markdown note. Full reindex builds the replacement index before persisting it, so existing context is not cleared first.
816
+ Brainlink also performs this complete source reindex automatically when it detects that the stored graph link model is older than the current model.
774
817
 
775
818
  ### `bench`
776
819
 
@@ -839,10 +882,15 @@ Context selection uses a middle-out strategy: it starts from the strongest chunk
839
882
  blink context "question" --vault ./vault --limit 12 --tokens 2000
840
883
  blink context "question" --vault ./vault --agent coding-agent --json
841
884
  blink context "question" --vault ./vault --agent coding-agent --mode hybrid --json
885
+ blink context "question" --vault ./vault --agent coding-agent --strategy cag --json
886
+ blink context "question" --vault ./vault --agent coding-agent --strategy auto --json
887
+ blink context-packs --vault ./vault --json
888
+ blink context-packs --vault ./vault --stale --clear
842
889
  ```
843
890
 
844
891
  Builds a compact context package for an agent.
845
892
  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.
893
+ The default strategy is configured by `defaultContextStrategy` and starts as `rag`, which retrieves and assembles context from the current index. `--strategy cag` enables cache-augmented context generation by reading or refreshing a persisted context pack under `.brainlink/context-packs`; `--strategy auto` uses CAG when a fresh pack exists and RAG otherwise, refreshing a pack for future calls. Context responses include `cache`, `metrics`, `requestedStrategy` and `recommendedStrategy` metadata. Packs are derived artifacts and become stale when the index or volatile memory signature changes.
846
894
 
847
895
  ### `links`
848
896
 
@@ -922,15 +970,26 @@ blink watch --vault ./vault
922
970
 
923
971
  Watches Markdown files and rebuilds the index when notes change.
924
972
 
973
+ ### `canonicalize-context-links`
974
+
975
+ ```bash
976
+ blink canonicalize-context-links --vault ./vault --dry-run
977
+ blink canonicalize-context-links --vault ./vault
978
+ ```
979
+
980
+ Ensures existing notes have canonical `## Context Links` entries to inferred context hubs such as `User Preferences Hub`, `GitHub Repositories Hub` or `Brainlink Hub`. The command is idempotent, creates missing hub notes by default, and fully reindexes after writes unless `--no-index` is passed.
981
+
925
982
  ### `server`
926
983
 
927
984
  ```bash
928
- blink server --watch
929
- blink server --vault ./vault --watch
930
- blink server --vault ./vault --watch --no-open
985
+ blink server
986
+ blink server --vault ./vault
987
+ blink server --vault ./vault --no-open
988
+ blink server --vault ./vault --no-watch
931
989
  ```
932
990
 
933
991
  Starts the local read-only graph UI and HTTP API.
992
+ Watch mode is enabled by default for Markdown changes in local filesystem vaults. Use `--no-watch` to run without the watcher.
934
993
  By default, it tries to open a native desktop GUI window for the graph URL.
935
994
  On Linux, native GUI is disabled by default; enable it with `BRAINLINK_LINUX_NATIVE_GUI=1`.
936
995
  If native GUI launch is unavailable, it falls back to dedicated app-window mode and then browser open.
@@ -971,8 +1030,10 @@ If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HO
971
1030
  "allowedVaults": [".brainlink-vault"],
972
1031
  "defaultAgent": "shared",
973
1032
  "autoIndexOnWrite": true,
1033
+ "autoCanonicalContextLinks": true,
974
1034
  "defaultSearchLimit": 10,
975
1035
  "defaultContextTokens": 2000,
1036
+ "defaultContextStrategy": "rag",
976
1037
  "embeddingProvider": "local",
977
1038
  "defaultSearchMode": "hybrid",
978
1039
  "chunkSize": 1200,
@@ -987,7 +1048,8 @@ If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HO
987
1048
  "coding-agent": {
988
1049
  "defaultSearchMode": "semantic",
989
1050
  "defaultSearchLimit": 8,
990
- "defaultContextTokens": 2400
1051
+ "defaultContextTokens": 2400,
1052
+ "defaultContextStrategy": "auto"
991
1053
  },
992
1054
  "*": {
993
1055
  "defaultSearchMode": "hybrid"
@@ -997,9 +1059,93 @@ If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HO
997
1059
  ```
998
1060
 
999
1061
  `defaultAgent` is optional. When set, CLI and MCP calls that omit `--agent`/`agent` use this value automatically. If not set, behavior remains as before.
1000
- `agentProfiles` is optional. When present, CLI and MCP resolve `mode`, `limit` and `tokens` per agent automatically, then fallback to global defaults.
1062
+ `agentProfiles` is optional. When present, CLI and MCP resolve `mode`, `limit`, `tokens` and context `strategy` per agent automatically, then fallback to global defaults.
1001
1063
 
1002
1064
  `autoIndexOnWrite` is optional and defaults to `true`. Set it to `false` to defer indexing after writes.
1065
+ `autoCanonicalContextLinks` is optional and defaults to `true`. When enabled, `blink add`, `brainlink_add_note` and `brainlink_add_file` add a canonical `## Context Links` entry to the inferred context hub, creating that hub when needed.
1066
+
1067
+ ## Remote MCP Server
1068
+
1069
+ Brainlink can run as a centralized MCP service for clustered workloads. This keeps one shared memory service inside the cluster while applications and agents connect to the MCP endpoint over Streamable HTTP.
1070
+
1071
+ ```bash
1072
+ BRAINLINK_MCP_TOKEN="change-me" brainlink mcp-server \
1073
+ --vault /data/vault \
1074
+ --host 0.0.0.0 \
1075
+ --port 3333 \
1076
+ --path /mcp
1077
+ ```
1078
+
1079
+ The server exposes:
1080
+
1081
+ ```txt
1082
+ POST /mcp MCP Streamable HTTP endpoint
1083
+ GET /healthz liveness probe
1084
+ GET /readyz readiness probe
1085
+ ```
1086
+
1087
+ When `BRAINLINK_MCP_TOKEN` or `--token` is set, MCP requests must include:
1088
+
1089
+ ```txt
1090
+ Authorization: Bearer <token>
1091
+ ```
1092
+
1093
+ For Kubernetes, run Brainlink as a central `Deployment` with a `Service` and a mounted vault volume:
1094
+
1095
+ ```yaml
1096
+ apiVersion: apps/v1
1097
+ kind: Deployment
1098
+ metadata:
1099
+ name: brainlink-mcp
1100
+ spec:
1101
+ replicas: 1
1102
+ selector:
1103
+ matchLabels:
1104
+ app: brainlink-mcp
1105
+ template:
1106
+ metadata:
1107
+ labels:
1108
+ app: brainlink-mcp
1109
+ spec:
1110
+ containers:
1111
+ - name: brainlink
1112
+ image: brainlink:latest
1113
+ args: ["brainlink", "mcp-server", "--vault", "/data/vault", "--host", "0.0.0.0", "--port", "3333"]
1114
+ env:
1115
+ - name: BRAINLINK_MCP_TOKEN
1116
+ valueFrom:
1117
+ secretKeyRef:
1118
+ name: brainlink-mcp
1119
+ key: token
1120
+ ports:
1121
+ - containerPort: 3333
1122
+ readinessProbe:
1123
+ httpGet:
1124
+ path: /readyz
1125
+ port: 3333
1126
+ livenessProbe:
1127
+ httpGet:
1128
+ path: /healthz
1129
+ port: 3333
1130
+ volumeMounts:
1131
+ - name: vault
1132
+ mountPath: /data/vault
1133
+ volumes:
1134
+ - name: vault
1135
+ persistentVolumeClaim:
1136
+ claimName: brainlink-vault
1137
+ ---
1138
+ apiVersion: v1
1139
+ kind: Service
1140
+ metadata:
1141
+ name: brainlink-mcp
1142
+ spec:
1143
+ selector:
1144
+ app: brainlink-mcp
1145
+ ports:
1146
+ - port: 3333
1147
+ targetPort: 3333
1148
+ ```
1003
1149
 
1004
1150
  Use `"embeddingProvider": "none"` when you want FTS-only indexing.
1005
1151
 
@@ -1078,7 +1224,7 @@ Local CLI:
1078
1224
 
1079
1225
  ```bash
1080
1226
  npm run dev -- --help
1081
- npm run dev -- server --vault .brainlink-vault --watch
1227
+ npm run dev -- server --vault .brainlink-vault
1082
1228
  ```
1083
1229
 
1084
1230
  Package smoke test:
@@ -1111,9 +1257,9 @@ Detailed notes:
1111
1257
  - HTTP API is local and unauthenticated.
1112
1258
  - Watch mode depends on the platform filesystem watcher.
1113
1259
 
1114
- ## Beta Scope
1260
+ ## Stable Scope
1115
1261
 
1116
- The `0.1.0-beta` line is intended to stabilize the local-first memory loop:
1262
+ The `1.0.0` line is the first stable functional release of the local-first memory loop:
1117
1263
 
1118
1264
  - Markdown as durable memory.
1119
1265
  - Rebuildable file index plus local embeddings and encrypted pack exports.
@@ -1121,8 +1267,10 @@ The `0.1.0-beta` line is intended to stabilize the local-first memory loop:
1121
1267
  - HTTP graph API and frontend as inspection tools.
1122
1268
  - Agent namespaces to avoid context mixing.
1123
1269
  - MCP tools for context retrieval, durable memory writes and graph maintenance.
1270
+ - Remote MCP server mode for centralized cluster access.
1271
+ - Vault management commands for listing, selecting and safely deleting local vaults.
1124
1272
 
1125
- The beta includes local semantic retrieval. Remote embedding providers, remote auth, advanced deduplication and graph editing are future milestones.
1273
+ The stable release includes local semantic retrieval, local-first storage, remote MCP transport and operational vault management. Remote embedding providers and deeper graph editing remain future milestones.
1126
1274
 
1127
1275
  ## Security
1128
1276
 
@@ -1,20 +1,14 @@
1
- import { access } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
1
  import { writeMarkdownFile } from '../infrastructure/file-system-vault.js';
4
2
  import { sanitizeAgentId, sharedAgentId } from '../domain/agents.js';
5
- import { extractWikiLinks } from '../domain/markdown.js';
6
3
  import { validateNoteInput } from '../domain/note-safety.js';
7
4
  import { ensureVault } from '../infrastructure/file-system-vault.js';
5
+ import { addCanonicalContextLinkToContent, ensureCanonicalContextHub } from './canonical-context-links.js';
8
6
  const slugify = (title) => title
9
7
  .normalize('NFKD')
10
8
  .replace(/[\u0300-\u036f]/g, '')
11
9
  .toLowerCase()
12
10
  .replace(/[^a-z0-9]+/g, '-')
13
11
  .replace(/^-+|-+$/g, '');
14
- const systemHubTitle = 'Memory Hub';
15
- const systemRootTitle = 'Knowledge Root';
16
- const normalizeTitle = (title) => title.trim().replace(/\.md$/i, '').toLowerCase();
17
- const noteFilename = (agentId, title) => `agents/${agentId}/${slugify(title) || 'untitled'}.md`;
18
12
  const buildNote = (title, content, agentId) => [
19
13
  `---`,
20
14
  `title: "${title.replaceAll('"', '\\"')}"`,
@@ -26,38 +20,6 @@ const buildNote = (title, content, agentId) => [
26
20
  content.trim(),
27
21
  ''
28
22
  ].join('\n');
29
- const ensureSystemNote = async (vaultPath, absoluteVaultPath, agentId, title, content) => {
30
- const filename = noteFilename(agentId, title);
31
- const absolutePath = join(absoluteVaultPath, filename);
32
- try {
33
- await access(absolutePath);
34
- return;
35
- }
36
- catch { }
37
- await writeMarkdownFile(vaultPath, filename, buildNote(title, content, agentId));
38
- };
39
- const ensureNonOrphanContent = async (vaultPath, absoluteVaultPath, title, content, agentId) => {
40
- const links = extractWikiLinks(content).filter((link) => normalizeTitle(link) !== normalizeTitle(title));
41
- if (links.length > 0) {
42
- return {
43
- content: content.trim(),
44
- autoLinked: false,
45
- linkTarget: null
46
- };
47
- }
48
- const fallbackTitle = normalizeTitle(title) === normalizeTitle(systemHubTitle) ? systemRootTitle : systemHubTitle;
49
- if (fallbackTitle === systemRootTitle) {
50
- await ensureSystemNote(vaultPath, absoluteVaultPath, agentId, systemRootTitle, `Entry point for agent memory. [[${systemHubTitle}]] #memory #root`);
51
- }
52
- else {
53
- await ensureSystemNote(vaultPath, absoluteVaultPath, agentId, systemHubTitle, 'Central memory index for this agent namespace. #memory #hub');
54
- }
55
- return {
56
- content: `${content.trim()}\n\nRelated: [[${fallbackTitle}]]`,
57
- autoLinked: true,
58
- linkTarget: fallbackTitle
59
- };
60
- };
61
23
  export const addNoteWithMetadata = async (vaultPath, title, content, agentId = sharedAgentId, options = {}) => {
62
24
  validateNoteInput({
63
25
  title,
@@ -65,15 +27,22 @@ export const addNoteWithMetadata = async (vaultPath, title, content, agentId = s
65
27
  allowSensitive: options.allowSensitive
66
28
  });
67
29
  const sanitizedAgentId = sanitizeAgentId(agentId);
68
- const absoluteVaultPath = await ensureVault(vaultPath);
69
30
  const filename = `agents/${sanitizedAgentId}/${slugify(title) || 'untitled'}.md`;
70
- const linkedContent = await ensureNonOrphanContent(vaultPath, absoluteVaultPath, title, content, sanitizedAgentId);
71
- const note = buildNote(title, linkedContent.content, sanitizedAgentId);
31
+ await ensureVault(vaultPath);
32
+ const canonical = options.autoContextLinks === false
33
+ ? null
34
+ : addCanonicalContextLinkToContent(title, content.trim());
35
+ const hub = canonical?.changed
36
+ ? await ensureCanonicalContextHub(vaultPath, canonical.context, sanitizedAgentId)
37
+ : null;
38
+ const note = buildNote(title, canonical?.content ?? content.trim(), sanitizedAgentId);
72
39
  const path = await writeMarkdownFile(vaultPath, filename, note);
73
40
  return {
74
41
  path,
75
- autoLinked: linkedContent.autoLinked,
76
- linkTarget: linkedContent.linkTarget
42
+ autoLinked: canonical?.changed ?? false,
43
+ linkTarget: canonical?.changed ? canonical.hubTitle : null,
44
+ context: canonical?.context ?? null,
45
+ hubCreated: hub?.created ?? false
77
46
  };
78
47
  };
79
48
  export const addNote = async (vaultPath, title, content, agentId = sharedAgentId, options = {}) => (await addNoteWithMetadata(vaultPath, title, content, agentId, options)).path;
@@ -50,7 +50,7 @@ export const getExtendedStats = async (vaultPath, agentId) => {
50
50
  await searchKnowledge(absoluteVaultPath, probeQuery, Math.min(defaults.defaultSearchLimit, 8), agentId, 'hybrid');
51
51
  const searchLatency = performance.now() - searchStart;
52
52
  const contextStart = performance.now();
53
- await buildContextPackage(absoluteVaultPath, probeQuery, Math.min(defaults.defaultSearchLimit, 8), defaults.defaultContextTokens, agentId, 'hybrid');
53
+ await buildContextPackage(absoluteVaultPath, probeQuery, Math.min(defaults.defaultSearchLimit, 8), defaults.defaultContextTokens, agentId, 'hybrid', undefined, defaults.defaultContextCacheTtlMs);
54
54
  const contextLatency = performance.now() - contextStart;
55
55
  return {
56
56
  stats,
@@ -0,0 +1,37 @@
1
+ import { indexVault } from './index-vault.js';
2
+ import { migrateVaultContent } from './migrate-vault.js';
3
+ import { getLastConfiguredVaultForKey, setLastConfiguredVaultForKey } from '../infrastructure/vault-migration-state.js';
4
+ export const autoMigrateConfiguredVaultIfChanged = async (input) => {
5
+ const configKey = input.configKey.trim();
6
+ const configuredVault = input.configuredVault.trim();
7
+ if (configKey.length === 0 || configuredVault.length === 0) {
8
+ return {
9
+ changed: false,
10
+ migrated: false
11
+ };
12
+ }
13
+ const previousVault = await getLastConfiguredVaultForKey(configKey);
14
+ if (!previousVault) {
15
+ await setLastConfiguredVaultForKey(configKey, configuredVault);
16
+ return {
17
+ changed: false,
18
+ migrated: false
19
+ };
20
+ }
21
+ if (previousVault === configuredVault) {
22
+ return {
23
+ changed: false,
24
+ migrated: false
25
+ };
26
+ }
27
+ const migration = await migrateVaultContent(previousVault, configuredVault);
28
+ const shouldIndex = migration.copied + migration.conflicted > 0;
29
+ if (shouldIndex) {
30
+ await indexVault(configuredVault);
31
+ }
32
+ await setLastConfiguredVaultForKey(configKey, configuredVault);
33
+ return {
34
+ changed: true,
35
+ migrated: shouldIndex
36
+ };
37
+ };