AI second brain Phase 2: retriever seam in AiSurfaceService + brain helpers (0211) - #230
Merged
Conversation
…tier persistence - relationFieldsResolver/schemaRelationFields (schema.ts): derive the brain's relationFieldsOf straight off compiled schemas, so graph-walk works with the built-in registry without a hand-written map. - applyMemoryOp/rememberFact (memory-apply.ts): execute consolidateMemory's ADD/UPDATE/DELETE/NOOP decisions as governed MemoryItem node mutations. - saveVectorTier/loadVectorTier (persist.ts): persist the semantic index through a blob store; cold/corrupt tier reports false so the caller backfills lazily. - All structural/injected (no @xnetjs/data import); +17 tests (81 total). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…0211 Phase 2) Adds an optional retrieveContext to AiSurfaceServiceConfig that drives the query path of createContextPack — when present, context packs come from the injected hybrid GraphRAG retriever (@xnetjs/brain) instead of the built-in linear keyword scan; absent it, behavior is unchanged. Backward-compatible, +2 tests. Checks off the AiSurfaceService-wiring and vector-tier-persistence items in exploration 0211 (live apps/web injection remains the next step). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
|
✓ Changelog fragment found — thanks! |
Contributor
|
Preview removed for PR #230. |
crs48
added a commit
that referenced
this pull request
Jun 22, 2026
…(0211) (#232) Phase 3 of exploration [0211](docs/explorations/0211_[_]_AI_SECOND_BRAIN_GRAPHRAG_MEMORY_AND_TIERING.md) — makes the brain **live in the app**, building on the engine ([#228](#228)) and the `AiSurfaceService` seam ([#230](#230)). ## What ships The AI chat's context pack now comes from `@xnetjs/brain`'s `retrieve()` instead of a flat keyword scan. - **`apps/web/.../ai-graph-retriever.ts`** (new, 6 tests) — `createGraphContextRetriever` builds an `AiContextRetriever` over the local NodeStore: keyword entry search → **bounded expansion along typed relations** (resolved from the schema registry) → token budget → each hit carries a **readable provenance path**. - **`AiChatPanel`** injects it: `createAiSurfaceService({ store, schemas, retrieveContext: createGraphContextRetriever(store) })`. So `createContextPack({ query })` (already called per message) now walks the graph. - **Exports**: `AiContextRetriever` / `AiRetrievedNode` from the `@xnetjs/plugins` barrels; `@xnetjs/brain` added to `apps/web` deps. ### Deliberately model-free Entry search is **keyword-only** — no embedding-model download, no added bundle weight or startup cost (the [0204](docs/explorations/0204_[x]_FAST_LOCAL_FIRST_COLD_START_AND_CACHE_HYDRATION.md) cold-start constraint). The value-add here is the **graph-walk + budget + provenance**. The vector tier can swap in behind the same seam later without touching this call site. ## Verification - `@xnetjs/brain` exercises (browser runtime, via Vite module graph): `retrieve('inventory')` returned the entry node **plus** its 1-hop neighbor with path `"My inventory→ (items) Sword of testing"`. - App boots clean — **no console errors** with the new import in the bundle. - 6 retriever tests + 26 `ai-surface` tests green; `tsc --noEmit` clean for `@xnetjs/plugins` + `xnet-web` (40 turbo tasks); eslint + prettier clean. ## Still deferred (future enhancements, doc stays `[_]`) Swap the **vector tier** in behind the seam (wake `@xnetjs/vectors` in-app + backfill/persist), an `xnet_graph_expand` MCP tool, the `data-bridge` query-path placement of the locality planner, `WorkingSetPrewarm` consumption, and a managed `/ai/embed` hub route. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
crs48
added a commit
that referenced
this pull request
Jun 22, 2026
Follow-up to exploration [0211](docs/explorations/0211_[_]_AI_SECOND_BRAIN_GRAPHRAG_MEMORY_AND_TIERING.md) (after [#228](#228) / [#230](#230) / [#232](#232)) — ships the **just-in-time expansion** half of the anti-overwhelm design. ## What ships A built-in `AiSurfaceService` tool, **`xnet_graph_expand`**, that walks typed relation edges out from a node to its connected neighbors, bounded by `hops` (1–2) and a result `limit`. - Resolves relation fields from the schema registry; returns each neighbor with the **relation + direction** it came through (traceable answers). - **Auto-surfaced to MCP/CLI agents** via `getTools()` — and correctly **deferred** (JIT-discovered via the Tool Search Tool), not eagerly loaded into context, which is exactly the anti-overwhelm posture: the agent discovers and calls it only when it needs a node's connections. This pairs directly with the graph-aware retrieval shipped in #232: `createContextPack` hands the agent a budgeted slice plus expandable node ids; `xnet_graph_expand` is how it pulls a specific node's connections on demand instead of over-fetching the whole graph. ## Verification - 4 new tests (30 in `ai-surface.test.ts`): outbound walk, not-found, limit, tool-list presence. - `tsc --noEmit` clean for `@xnetjs/plugins` (turbo); eslint + prettier clean. - `fallow audit` (with coverage): *"✓ No issues in 2 changed files"* — the new functions add zero enforced complexity findings. - No new dependencies (uses the service's existing `store` + `schemas`). ## Still deferred (0211 doc stays `[_]`) The in-app **vector tier** (waking `@xnetjs/vectors` behind the retriever seam — a perf-sensitive change better suited to its own PR, possibly via a managed server-side embedding route given browser cost), the `data-bridge` query-path placement of the locality planner, `WorkingSetPrewarm` consumption, and a managed `/ai/embed` hub route. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
crs48
added a commit
that referenced
this pull request
Jun 22, 2026
…ff) (#236) The final piece of exploration [0211](docs/explorations/0211_[x]_AI_SECOND_BRAIN_GRAPHRAG_MEMORY_AND_TIERING.md) — wakes the dormant `@xnetjs/vectors` engine behind the retriever seam, making the **semantic half** of the AI second brain live. After #228 (engine), #230 (seam), #232 (graph-aware retrieval live), and #233 (`xnet_graph_expand`), this flips 0211 to **`[x]`**. ## What ships **`ai-vector-search.ts` — `createVectorEntrySearch`** (opt-in, lazy, fallback-safe): - The heavy `@xenova` model + `usearch` load via a **dynamic import only on the first search after opt-in** — zero boot/bundle cost when off (the [0204](docs/explorations/0204_[x]_FAST_LOCAL_FIRST_COLD_START_AND_CACHE_HYDRATION.md) constraint). - Once warm it **RRF-fuses vector + keyword**; until warm and on **any** failure it falls back to keyword — so enabling it can only ever match or improve results, **never break or slow** the chat. - **`ai-vector-storage.ts`** — IndexedDB `BlobStore` so the index **restores across sessions** instead of re-embedding the graph each time. - **`AiChatPanel`** — an opt-in toggle (off by default); the flag is read via a ref so toggling **never tears down the AI runtime** mid-conversation. ## Adversarial review caught 3 real bugs (fixed here) A 22-agent review (18 findings → 15 false positives killed by verification → 3 confirmed) surfaced: 1. `loadVectorTier` returned `false` on a *partial* restore but left the index half-populated → now **clears on failure** so "false ⇒ cold" is honest. 2. Persistence was never wired (no `storage`) → re-embedded every session and the realm-fix was dead in prod → now **wired via IndexedDB**. 3. Toggling the flag rebuilt `surface` → **reset the active thread** mid-conversation → now decoupled via a ref. Plus a realm-robustness fix in `brain/persist.ts`: a plain `instanceof Uint8Array` mangles bytes across the dynamic-import / IndexedDB structured-clone boundary; the `Symbol.toStringTag` brand holds. ## Verification - **End-to-end in the real browser**: warm → hybrid hit (`source: "hybrid"`); IndexedDB persist → restore (no re-embed) → query found the node. The cross-realm persist fix exercised under real production conditions. - 17 unit tests (vector tier lazy/fallback/persist + the persist clear-on-failure regression + graph retriever); `tsc` clean for `@xnetjs/brain` + `xnet-web`; eslint + prettier clean; `fallow audit` — *"✓ No issues in 10 changed files."* ## Justified defers (documented in the 0211 doc; none block the core) - **Managed `/ai/embed` route** — the server-side embedding upgrade path; dead infra until demand, so it follows demand. - **data-bridge locality** — *blocked*: `apps/web` never instantiates a `remoteNodeQueryClient`, so there's no remote read path to route to. - **`WorkingSetPrewarm` planner scores** — marginal until per-node frequency/pinned signals are tracked at boot (it already ranks by recency). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 2 of exploration 0211, building on the engine merged in #228. This connects the brain engine to the agent's context surface and fills the remaining package-level gaps so adoption is a few lines.
What ships
AiSurfaceServiceretriever seam (packages/plugins/src/ai-surface/service.ts):retrieveContextonAiSurfaceServiceConfig. When present, the query path ofcreateContextPackuses the injected graph-aware retriever (hybrid vector+keyword entry search + bounded graph expansion, budgeted) instead of the built-in linear keyword scan. Absent it, behavior is unchanged.@xnetjs/brain'sretrieve(mapping items →{ nodeId, pathLabel }). Decoupled — noplugins → brainimport.@xnetjs/brainhelpers (make the engine batteries-included; +17 tests, 81 total):relationFieldsResolver/schemaRelationFields— derive therelationFieldsOfresolver straight off compiled schemas, so graph-walk works with the built-in registry without a hand-written map.applyMemoryOp/rememberFact— executeconsolidateMemory's ADD/UPDATE/DELETE/NOOP decisions as governedMemoryItemnode mutations (closes the memory write-loop).saveVectorTier/loadVectorTier— persist the semantic index through a blob store (@xnetjs/storage); a cold/corrupt tier reportsfalseso the caller rebuilds lazily viareindexAll(the 0204 OPFS-eviction concern).@xnetjs/datadependency.Checks off (in the 0211 doc)
retrieve()intoAiSurfaceService✓ (the seam)@xnetjs/storage; rebuild lazily ✓Still deferred (next phase)
Live injection in
apps/web(constructing aSemanticSearch+ brain, embedding backfill + tier persistence on boot) — deserves its own perf-aware PR per [0204]; anxnet_graph_expandMCP tool; thedata-bridgequery-path placement of the locality planner;WorkingSetPrewarmconsumption; and a managed/ai/embedhub route.Verification
@xnetjs/brain: 81 tests green;ai-surface.test.ts: 26 green (incl. the 2 new).tsc --noEmitclean for@xnetjs/brain+@xnetjs/plugins(via turbo).fallow audit(with coverage): "✓ No issues in 9 changed files" (the flagged functions are pre-existing/inherited).No user-visible change yet (internal plumbing toward the capability already announced in #228's changelog), so this carries the
skip-changeloglabel.🤖 Generated with Claude Code