@andespindola/brainlink 0.1.0-beta.99 → 1.0.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.
Files changed (48) hide show
  1. package/AGENTS.md +6 -6
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +198 -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/delete-note.js +80 -0
  10. package/dist/application/frontend/client-css.js +212 -42
  11. package/dist/application/frontend/client-html.js +42 -28
  12. package/dist/application/frontend/client-js.js +1294 -3222
  13. package/dist/application/frontend/client-render-worker-js.js +676 -0
  14. package/dist/application/get-graph-contexts.js +33 -0
  15. package/dist/application/get-graph-layout.js +62 -8
  16. package/dist/application/get-graph-stream-chunk.js +326 -0
  17. package/dist/application/get-graph-view.js +246 -0
  18. package/dist/application/graph-view-state.js +66 -0
  19. package/dist/application/import-legacy-sqlite.js +3 -33
  20. package/dist/application/index-vault.js +35 -22
  21. package/dist/application/migrate-context-links.js +79 -0
  22. package/dist/application/search-graph-node-ids.js +63 -3
  23. package/dist/application/server/routes.js +197 -12
  24. package/dist/cli/commands/read-commands.js +39 -3
  25. package/dist/cli/commands/vault-commands.js +182 -0
  26. package/dist/cli/commands/write-commands.js +172 -12
  27. package/dist/cli/main.js +2 -0
  28. package/dist/cli/runtime.js +10 -2
  29. package/dist/domain/context.js +1 -0
  30. package/dist/domain/graph-contexts.js +180 -0
  31. package/dist/domain/graph-layout.js +347 -21
  32. package/dist/domain/markdown.js +53 -9
  33. package/dist/infrastructure/config.js +105 -6
  34. package/dist/infrastructure/context-packs.js +122 -0
  35. package/dist/infrastructure/file-index.js +6 -3
  36. package/dist/infrastructure/file-system-vault.js +21 -1
  37. package/dist/infrastructure/index-state.js +2 -0
  38. package/dist/infrastructure/vault-migration-state.js +69 -0
  39. package/dist/infrastructure/volatile-memory.js +100 -0
  40. package/dist/mcp/http-server.js +97 -0
  41. package/dist/mcp/runtime.js +20 -0
  42. package/dist/mcp/server.js +41 -13
  43. package/dist/mcp/tools.js +226 -14
  44. package/docs/AGENT_USAGE.md +60 -5
  45. package/docs/ARCHITECTURE.md +11 -0
  46. package/docs/QUICKSTART.md +3 -1
  47. package/docs/RELEASE.md +4 -3
  48. 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,24 @@ 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.
540
+ - `brainlink_delete_note`: delete a durable Markdown note by title or path after explicit confirmation and reindex.
537
541
  - `brainlink_add_file`: ingest a local file as a note and reindex.
538
- - `brainlink_index`: rebuild the vault index.
542
+ - `brainlink_canonicalize_context_links`: ensure existing notes link to inferred context hubs.
543
+ - `brainlink_volatile_add`: write temporary agent-decided memory with TTL; volatile sections are included in context and never create durable graph edges.
544
+ - `brainlink_volatile_clear`: clear temporary memory for the current vault/agent namespace.
545
+ - `brainlink_index`: rebuild the vault index. Pass `full=true` for a complete source reindex.
539
546
  - `brainlink_stats`: read indexed vault statistics.
540
547
  - `brainlink_validate`: validate broken links and orphan notes.
541
548
  - `brainlink_sync`: run index, stats, validation, broken-link and orphan checks in one call.
542
549
  - `brainlink_graph`: read indexed graph nodes and weighted links.
550
+ - `brainlink_graph_contexts`: list the visual graph contexts used by the local server.
543
551
  - `brainlink_broken_links`: list unresolved wiki links.
544
552
  - `brainlink_orphans`: list disconnected notes.
545
553
 
@@ -550,8 +558,9 @@ By default, Brainlink enforces bootstrap and auto-runs it for read tools when se
550
558
  If you disable `autoBootstrapOnRead` through `brainlink_policy`, read tools return a preflight instruction with suggested `brainlink_bootstrap` arguments.
551
559
  `brainlink_bootstrap`, `brainlink_policy` and preflight responses include structured `nextActions` so MCP clients can continue automatically without custom parsing.
552
560
  For one-call planning, use `brainlink_recommendations` to get the recommended tool sequence for the current vault/agent/query.
561
+ 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
562
 
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.
563
+ 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
564
 
556
565
  Agents can raise the importance of a relationship by putting priority markers on the same line as a wiki link:
557
566
 
@@ -560,17 +569,25 @@ Agents can raise the importance of a relationship by putting priority markers on
560
569
  Related: [[Incident Runbook]] #critical
561
570
  ```
562
571
 
563
- Indexed edges expose `weight` and `priority` (`low`, `normal`, `high`, `critical`) through CLI JSON, HTTP graph APIs and `brainlink_graph`.
572
+ 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.
573
+
574
+ To migrate older vaults without deleting existing Markdown, generate concise context-link sections from current wiki-link mentions:
575
+
576
+ ```bash
577
+ blink migrate-context-links --vault ./vault --limit 5
578
+ blink index --vault ./vault --full
579
+ ```
564
580
 
565
581
  ## Graph UI
566
582
 
567
583
  Start the local frontend:
568
584
 
569
585
  ```bash
570
- blink server --host 127.0.0.1 --port 4321 --watch
586
+ blink server --host 127.0.0.1 --port 4321
571
587
  ```
572
588
 
573
589
  By default, the server uses `$HOME/.brainlink/vault`. Pass `--vault ./vault` only when you want to inspect a custom vault.
590
+ By default, the server watches Markdown files in local filesystem vaults and reindexes after note changes. Use `--no-watch` to disable realtime reindexing.
574
591
  By default, `blink server` tries to open the graph in a native desktop GUI window:
575
592
  - macOS: Swift + WebKit
576
593
  - Windows: PowerShell WinForms WebBrowser
@@ -586,23 +603,27 @@ When native GUI is used, the GUI window automatically closes when the `blink ser
586
603
  The graph UI shows:
587
604
 
588
605
  - notes as nodes
589
- - `[[wiki links]]` as weighted edges
590
- - details opened on node click (tags, outgoing links, backlinks, full Markdown content)
606
+ - all non-self `[[wiki links]]` inside `## Context Links` as weighted indexed edges
607
+ - 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
608
+ - 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
609
  - neutral graph nodes with segment/group metadata
592
610
  - agent selector (id-only labels) for isolated views
611
+ - context selector for segment-scoped cauliflower subgraphs derived from the visual graph context
593
612
  - graph filter matches title, path, tags and note content
594
613
  - graph filter keeps hub context nodes visible (`Memory Hub`/`MOC`/high-degree fallback) to preserve relationship readability
595
- - realtime refresh while `--watch` is enabled
614
+ - realtime refresh while watch mode is enabled
596
615
  - graph controls for zoom in, zoom out, fit visible nodes and reset-to-fit-all
597
616
  - wheel zoom (including `cmd+scroll` and `ctrl+scroll`) anchored to cursor position for faster navigation in large graphs
617
+ - wheel/button zoom updates immediately at the cursor anchor without delayed focus-transition interpolation
618
+ - 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
619
+ - zoom-out cluster mode that shows segment hub clusters first, then reveals local nodes as zoom increases
598
620
  - keyboard shortcuts: `+` zoom in, `-` zoom out, `0` reset fit
599
- - double-click on canvas zooms in at cursor position
621
+ - click on a node opens its details panel; double-click on empty canvas zooms in at cursor position
600
622
  - 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; for massive graphs the first expansion starts much deeper in zoom and low-size child levels use slower easing so the view stays as one compact graph longer
623
+ - graph rendering safeguards (batched GPU draw calls, lower redraw rate, zoom-aware interaction)
624
+ - 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
625
+ - worker-first WebGL rendering with Canvas fallback when `OffscreenCanvas` or worker rendering is unavailable
626
+ - 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
627
 
607
628
  The server indexes before starting by default. Use `--no-index` to skip that step:
608
629
 
@@ -619,8 +640,11 @@ The server always refuses non-loopback hosts. Brainlink HTTP only runs on localh
619
640
  Routes:
620
641
 
621
642
  - `GET /api/agents`
643
+ - `GET /api/graph-contexts`
622
644
  - `GET /api/graph`
623
645
  - `GET /api/graph-layout`
646
+ - `GET /api/graph-view?x=<x>&y=<y>&w=<width>&h=<height>&scale=<scale>`
647
+ - `GET /api/graph-stream?x=<x>&y=<y>&w=<width>&h=<height>&scale=<scale>&nodeBudget=<n>&edgeBudget=<n>`
624
648
  - `GET /api/graph-node?id=<node-id>`
625
649
  - `GET /api/search?q=<query>&limit=10&mode=hybrid`
626
650
  - `GET /api/context?q=<query>&limit=12&tokens=2000&mode=hybrid`
@@ -635,6 +659,7 @@ Read routes accept `agent=<agent-id>`:
635
659
 
636
660
  ```txt
637
661
  /api/graph-layout?agent=coding-agent
662
+ /api/graph-layout?agent=coding-agent&context=Architecture
638
663
  /api/search?q=typescript&agent=coding-agent&mode=hybrid
639
664
  /api/context?q=module-boundaries&agent=coding-agent&mode=semantic
640
665
  ```
@@ -689,9 +714,36 @@ blink config set-vault "s3://my-memory-bucket/brainlink" --global
689
714
 
690
715
  `config set-vault` writes configuration through CLI (no manual file edits required).
691
716
  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.
717
+ 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
718
  Use `--global` to write to `$BRAINLINK_HOME/brainlink.config.json`, `--no-migrate` to skip migration, and `--no-index` to skip post-migration indexing.
693
719
  `config doctor` is dry-run by default; use `--fix` to apply safe config normalization and allowlist fixes.
694
720
 
721
+ ### `vaults`
722
+
723
+ ```bash
724
+ blink vaults list
725
+ blink vaults list --json
726
+ blink vaults use /absolute/path/to/vault
727
+ blink vaults use /absolute/path/to/vault --global
728
+ blink vaults delete /absolute/path/to/vault --yes
729
+ blink vaults delete /absolute/path/to/vault --yes --prune-config
730
+ ```
731
+
732
+ Lists known vaults from the configured default, `allowedVaults`, and the built-in default at `$HOME/.brainlink/vault`.
733
+ `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.
734
+ `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`.
735
+
736
+ ### `delete-note`
737
+
738
+ ```bash
739
+ blink delete-note --title "Architecture" --yes
740
+ blink delete-note --path agents/shared/architecture.md --yes
741
+ blink delete-note --title "Architecture" --agent coding-agent --yes
742
+ blink delete-note --path agents/shared/architecture.md --yes --no-auto-index
743
+ ```
744
+
745
+ Deletes a durable Markdown note from the selected vault. The command requires `--yes`, accepts exactly one selector (`--title` or `--path`), refuses non-Markdown/protected paths, and reindexes by default. Bucket vault note deletion is intentionally refused until provider-native object deletion is implemented safely.
746
+
695
747
  ### `migrate-vault`
696
748
 
697
749
  ```bash
@@ -768,9 +820,12 @@ When action is not `merge`, Brainlink still creates a low-priority related edge
768
820
  ```bash
769
821
  blink index
770
822
  blink index --vault ./vault
823
+ blink index --vault ./vault --full
771
824
  ```
772
825
 
773
- Rebuilds the local index from Markdown files.
826
+ Rebuilds the local index from Markdown files. By default, unchanged notes reuse existing indexed chunks for speed.
827
+ 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.
828
+ Brainlink also performs this complete source reindex automatically when it detects that the stored graph link model is older than the current model.
774
829
 
775
830
  ### `bench`
776
831
 
@@ -839,10 +894,15 @@ Context selection uses a middle-out strategy: it starts from the strongest chunk
839
894
  blink context "question" --vault ./vault --limit 12 --tokens 2000
840
895
  blink context "question" --vault ./vault --agent coding-agent --json
841
896
  blink context "question" --vault ./vault --agent coding-agent --mode hybrid --json
897
+ blink context "question" --vault ./vault --agent coding-agent --strategy cag --json
898
+ blink context "question" --vault ./vault --agent coding-agent --strategy auto --json
899
+ blink context-packs --vault ./vault --json
900
+ blink context-packs --vault ./vault --stale --clear
842
901
  ```
843
902
 
844
903
  Builds a compact context package for an agent.
845
904
  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.
905
+ 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
906
 
847
907
  ### `links`
848
908
 
@@ -922,15 +982,26 @@ blink watch --vault ./vault
922
982
 
923
983
  Watches Markdown files and rebuilds the index when notes change.
924
984
 
985
+ ### `canonicalize-context-links`
986
+
987
+ ```bash
988
+ blink canonicalize-context-links --vault ./vault --dry-run
989
+ blink canonicalize-context-links --vault ./vault
990
+ ```
991
+
992
+ 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.
993
+
925
994
  ### `server`
926
995
 
927
996
  ```bash
928
- blink server --watch
929
- blink server --vault ./vault --watch
930
- blink server --vault ./vault --watch --no-open
997
+ blink server
998
+ blink server --vault ./vault
999
+ blink server --vault ./vault --no-open
1000
+ blink server --vault ./vault --no-watch
931
1001
  ```
932
1002
 
933
1003
  Starts the local read-only graph UI and HTTP API.
1004
+ Watch mode is enabled by default for Markdown changes in local filesystem vaults. Use `--no-watch` to run without the watcher.
934
1005
  By default, it tries to open a native desktop GUI window for the graph URL.
935
1006
  On Linux, native GUI is disabled by default; enable it with `BRAINLINK_LINUX_NATIVE_GUI=1`.
936
1007
  If native GUI launch is unavailable, it falls back to dedicated app-window mode and then browser open.
@@ -971,8 +1042,10 @@ If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HO
971
1042
  "allowedVaults": [".brainlink-vault"],
972
1043
  "defaultAgent": "shared",
973
1044
  "autoIndexOnWrite": true,
1045
+ "autoCanonicalContextLinks": true,
974
1046
  "defaultSearchLimit": 10,
975
1047
  "defaultContextTokens": 2000,
1048
+ "defaultContextStrategy": "rag",
976
1049
  "embeddingProvider": "local",
977
1050
  "defaultSearchMode": "hybrid",
978
1051
  "chunkSize": 1200,
@@ -987,7 +1060,8 @@ If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HO
987
1060
  "coding-agent": {
988
1061
  "defaultSearchMode": "semantic",
989
1062
  "defaultSearchLimit": 8,
990
- "defaultContextTokens": 2400
1063
+ "defaultContextTokens": 2400,
1064
+ "defaultContextStrategy": "auto"
991
1065
  },
992
1066
  "*": {
993
1067
  "defaultSearchMode": "hybrid"
@@ -997,9 +1071,93 @@ If no `vault` is configured and no `--vault` flag is passed, Brainlink uses `$HO
997
1071
  ```
998
1072
 
999
1073
  `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.
1074
+ `agentProfiles` is optional. When present, CLI and MCP resolve `mode`, `limit`, `tokens` and context `strategy` per agent automatically, then fallback to global defaults.
1001
1075
 
1002
1076
  `autoIndexOnWrite` is optional and defaults to `true`. Set it to `false` to defer indexing after writes.
1077
+ `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.
1078
+
1079
+ ## Remote MCP Server
1080
+
1081
+ 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.
1082
+
1083
+ ```bash
1084
+ BRAINLINK_MCP_TOKEN="change-me" brainlink mcp-server \
1085
+ --vault /data/vault \
1086
+ --host 0.0.0.0 \
1087
+ --port 3333 \
1088
+ --path /mcp
1089
+ ```
1090
+
1091
+ The server exposes:
1092
+
1093
+ ```txt
1094
+ POST /mcp MCP Streamable HTTP endpoint
1095
+ GET /healthz liveness probe
1096
+ GET /readyz readiness probe
1097
+ ```
1098
+
1099
+ When `BRAINLINK_MCP_TOKEN` or `--token` is set, MCP requests must include:
1100
+
1101
+ ```txt
1102
+ Authorization: Bearer <token>
1103
+ ```
1104
+
1105
+ For Kubernetes, run Brainlink as a central `Deployment` with a `Service` and a mounted vault volume:
1106
+
1107
+ ```yaml
1108
+ apiVersion: apps/v1
1109
+ kind: Deployment
1110
+ metadata:
1111
+ name: brainlink-mcp
1112
+ spec:
1113
+ replicas: 1
1114
+ selector:
1115
+ matchLabels:
1116
+ app: brainlink-mcp
1117
+ template:
1118
+ metadata:
1119
+ labels:
1120
+ app: brainlink-mcp
1121
+ spec:
1122
+ containers:
1123
+ - name: brainlink
1124
+ image: brainlink:latest
1125
+ args: ["brainlink", "mcp-server", "--vault", "/data/vault", "--host", "0.0.0.0", "--port", "3333"]
1126
+ env:
1127
+ - name: BRAINLINK_MCP_TOKEN
1128
+ valueFrom:
1129
+ secretKeyRef:
1130
+ name: brainlink-mcp
1131
+ key: token
1132
+ ports:
1133
+ - containerPort: 3333
1134
+ readinessProbe:
1135
+ httpGet:
1136
+ path: /readyz
1137
+ port: 3333
1138
+ livenessProbe:
1139
+ httpGet:
1140
+ path: /healthz
1141
+ port: 3333
1142
+ volumeMounts:
1143
+ - name: vault
1144
+ mountPath: /data/vault
1145
+ volumes:
1146
+ - name: vault
1147
+ persistentVolumeClaim:
1148
+ claimName: brainlink-vault
1149
+ ---
1150
+ apiVersion: v1
1151
+ kind: Service
1152
+ metadata:
1153
+ name: brainlink-mcp
1154
+ spec:
1155
+ selector:
1156
+ app: brainlink-mcp
1157
+ ports:
1158
+ - port: 3333
1159
+ targetPort: 3333
1160
+ ```
1003
1161
 
1004
1162
  Use `"embeddingProvider": "none"` when you want FTS-only indexing.
1005
1163
 
@@ -1078,7 +1236,7 @@ Local CLI:
1078
1236
 
1079
1237
  ```bash
1080
1238
  npm run dev -- --help
1081
- npm run dev -- server --vault .brainlink-vault --watch
1239
+ npm run dev -- server --vault .brainlink-vault
1082
1240
  ```
1083
1241
 
1084
1242
  Package smoke test:
@@ -1111,9 +1269,9 @@ Detailed notes:
1111
1269
  - HTTP API is local and unauthenticated.
1112
1270
  - Watch mode depends on the platform filesystem watcher.
1113
1271
 
1114
- ## Beta Scope
1272
+ ## Stable Scope
1115
1273
 
1116
- The `0.1.0-beta` line is intended to stabilize the local-first memory loop:
1274
+ The `1.0.0` line is the first stable functional release of the local-first memory loop:
1117
1275
 
1118
1276
  - Markdown as durable memory.
1119
1277
  - Rebuildable file index plus local embeddings and encrypted pack exports.
@@ -1121,8 +1279,10 @@ The `0.1.0-beta` line is intended to stabilize the local-first memory loop:
1121
1279
  - HTTP graph API and frontend as inspection tools.
1122
1280
  - Agent namespaces to avoid context mixing.
1123
1281
  - MCP tools for context retrieval, durable memory writes and graph maintenance.
1282
+ - Remote MCP server mode for centralized cluster access.
1283
+ - Vault management commands for listing, selecting and safely deleting local vaults.
1124
1284
 
1125
- The beta includes local semantic retrieval. Remote embedding providers, remote auth, advanced deduplication and graph editing are future milestones.
1285
+ 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
1286
 
1127
1287
  ## Security
1128
1288
 
@@ -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
+ };