feat(ai): give coding agents xNet's real retrieval, memory and a warm process (0415) - #667
Conversation
|
Warning Review limit reached
Next review available in: 1 minute Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (80)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Preview removed for PR #667. |
🖼️ UI changes in this PRNo visual differences detected in the changed UI. |
…and self-improvement Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Adds createWorkspaceRetrieval() — the single construction path that assembles the best retrieval a backend supports and reports the tier it settled for, so a degraded search can never render identically to an exhaustive one. authorize is required, not optional: graph expansion walks edges out of matched nodes and needs a gate no call site can forget. Also restores FTS to the two lanes that had lost it — the CLI's remote backend now routes searchText to /api/v1/ai/search, and the Electron store proxy asks the renderer (which owns nodes_fts) instead of scanning 500 nodes over IPC. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
createAgentRetrieval() is now the single call an agent lane makes. All three lanes use it — the CLI's services, xnet mcp serve, and the Electron bridge — plus the local API, whose /ai/context-pack was serving the same keyword scan to every HTTP client. The guard is the part that makes it stick: omitting a retriever is invisible in review, because the call reads fine and it is what it doesn't pass that costs you. scripts/guard-ai-surface-retrieval.mjs is a hard-zero gate with a reasoned allowlist, wired into CI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
runSearch now leads with a provenance line (tier, index, and the reason when degraded) in tsv and md, carries tier into json, and writes the notice to stderr for every format — stderr because a warning on stdout is one `| head` away from vanishing, and an agent that loses it reports "no such node" with total confidence. Writing the test against a real SQLite store surfaced a fourth degraded lane the exploration had rated half-equipped: createLocalAgentBackend never forwarded searchText either, so the --db path scanned 500 nodes while sitting on a working nodes_fts index. Fixed, and the doc corrected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…bbering The instruction files are the user's — in this repo CLAUDE.md is the entry point to the whole instruction tree — and connect used to write them wholesale. .mcp.json was carefully merged the whole time; these never got the same care. Writes are now fenced in a managed block: create when absent, replace between markers on a re-run, append when the file exists without them. Every existing byte survives. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
xnet recall and xnet_recall return a budgeted context pack: ranked hits each carrying the graph path they were reached by, plus the ids dropped for budget so the agent pulls them just-in-time rather than the CLI guessing. Both xnet_search and xnet_recall stand in the core tool set (0415 open question 2). They answer different questions — find the node called X versus answer this question — and a model with only recall would pay a graph walk to look up a title it already knew. xnet_recall registers only when the server has retrieval: a tool that exists to answer 'unavailable' still costs its definition tokens every turn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Holds the store, schema registry and FTS open behind a unix socket keyed on the invocation options, so a client finds it without paying the backend resolution the daemon exists to avoid. search and recall try it and fall back to a cold process when nothing is listening. Absent is silent; wrong is loud. A version mismatch and a connection that dies mid-request both throw named errors rather than falling back, because quietly retrying would discard the one signal saying the answer was bad — and a killed daemon must never read as 'no results'. Measured: the socket round-trip is p50 0.14ms / p95 0.26ms, but the CLI only improves 0.22s -> 0.19s because node's own boot dominates. The doc's 40ms target was aimed at the wrong thing and is restated against the round-trip; the real leverage is callers that are already processes. brain now ships a dist entry like every other dependency of plugins — source-only worked for bundled apps but not for the CLI build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…routes by cost The code-execution lane was the cheapest one and the most blinkered: a script could only see the slice the host preloaded. api.recall and api.graph now reach the whole workspace with the same graph walk and the same provenance paths the recall verb gets. The sandbox bans await on purpose, so these are synchronous and the host runs the script twice — a priming pass records the queries, the real pass answers them. A query the priming pass never saw throws; returning an empty array would be indistinguishable from 'nothing matches'. The skill now leads with the cost gap (2.7k vs 18k tokens for the same 15 tasks) and puts code execution above ad-hoc reads, tells the agent to read the tier line, and stays under its 1000-token standing budget — which the benchmark caught me exceeding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Loads the embedding + HNSW tier once in the warm process, persists it via brain's persist layer, and restores from the snapshot instead of re-embedding. A cold verb never pays the model load — that is the whole reason this is daemon-only. Every failure degrades honestly. No model, no usearch, a corrupt snapshot: each leaves the daemon serving bm25-graph and saying so. A first draft swallowed the backfill error and announced hybrid-graph over an index holding zero documents — this exploration's own thesis biting its implementation, now covered by a test. The ABI risk the doc rated medium has landed: transformers pulls in sharp, which this repo builds for Electron's ABI, so --vectors is honest and inert under system Node today. Recorded in the doc with follow-ups. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
consolidateMemory and applyMemoryOp existed and nothing called them, so every coding-agent session rediscovered the workspace from zero. xnet remember writes through the existing planner (a restatement consolidates rather than piling up); xnet memories lists what is stored; xnet forget deletes one, and refuses any node that is not a MemoryItem so a typo cannot destroy workspace data. xnet skill --memories appends a capped top-k preamble, so memory cannot re-bloat the context this exploration just trimmed. xnet distill reads AgentAction instructions that already exist — no new capture, no new consent surface. Three occurrences required, so a one-off task never becomes a standing preference, and redacted instructions contribute nothing rather than being reconstructed from their digest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
RetrievalProfile is four numbers, deliberately not a MemoryItem: facts about the user and tuning state obey different retention and privacy rules, and collapsing them turns 'make search better' into 'profile the user'. It is an ordinary private node — syncs, exports, deletes — and nothing in this module takes a network dependency. preferencePairs reads recall outcomes the session already produced; a rolled-back episode yields nothing rather than inverted pairs. proposeProfile nudges one knob by one step, because a real optimizer over four numbers and a few dozen observations would fit noise. ratchetProfile scores candidates against the PINNED corpus, never the behaviour that produced them — scoring a profile on its own training signal is how a retriever gets worse at everything the user hasn't done yet. The ratchet found something on its first run: hopDecay 0.2 beats the shipped 0.55 on every metric (all 0.81→0.85, graph 0.50→0.60, mrr 0.69→0.71). The default is NOT moved — the gap is one or two golden cases, which is this eval's resolution, the same argument 0394 made. Recorded in the doc with the numbers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…bers A memory the user cannot see is not a memory, it is a profile. The panel lists MemoryItem nodes ranked the way retrieval ranks them (recency-decayed salience, not creation order — a year-old 0.9 loses to yesterday's 0.6), marks which ones actually reach a session's preamble, shows the evidence a distilled memory came from, and lets any row be rewritten or removed. It also surfaces the adopted RetrievalProfile and the ratchet's reason for accepting it, so a tuning decision is something the user can read rather than something the retriever just did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Two bugs the end-to-end run caught that no unit test would have, both silent, both producing a lane that reported bm25-graph while doing much less: - FTS5 ANDs bare terms, so 'how do we roll back the Acme renewal cutover' required every one of those words in one node and matched nothing — recall's entire purpose defeated. queryVariants now tries the strict query first, then the content words OR'd together. - The two backends return different schema shapes (JSON-LD array from the CLI registry, keyed record from the Electron renderer). The relation resolver handled only the record, so on the CLI lane the graph stage found zero edges. relationFieldNames now reads both. Also adds getPort() to LocalAPIServer: port 0 could be listened on but never connected to, which made the app-running lane untestable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…nd self improvement Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
9acc0b8 to
b8d184d
Compare
Two CI failures, one mistake of mine: @xnetjs/plugins and @xnetjs/cli are published to npm, and I gave them dependencies on @xnetjs/brain (private) and @xnetjs/vectors (publish-ignored). Shipping that would break every npm consumer on install. - @xnetjs/brain is now published. It was private only because nothing outside the monorepo used it; it has zero runtime dependencies and is structural over whatever store you hand it, which is what makes that safe. Its own data/vectors deps were vestigial — the source imports neither — so data is dropped and vectors demoted to a devDependency used by one test. - @xnetjs/cli drops @xnetjs/vectors to a devDependency. The import in vector-tier.ts is already dynamic and optional: createVectorTier returns null when the engine cannot load, which is the tested behaviour. - The hub Dockerfile hand-lists the packages it COPYs, and brain joined the closure three packages away. Added, plus a guard: the stale list failed nothing until a six-minute image build died on 'Cannot find module', and that is the same build Railway runs, so the first symptom could have been a failed deploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Demoting vectors to a devDependency in the previous commit had a consequence I did not check: tsup externalizes dependencies but BUNDLES devDependencies, so it followed the dynamic import into transformers and then into sharp, whose native requires esbuild cannot resolve. The cli build failed, and editor-ux, typecheck, build-and-smoke-test, capture and deploy-preview all failed downstream of it. Marking it external is also what the design wants: the import is a runtime import() that is allowed to fail, and createVectorTier returns null when it does. sharp is now absent from the bundle entirely. The lesson is mine: I ran typecheck, lint and test locally but not 'pnpm build', which is the only thing that would have caught this. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…Y lists The previous fix added brain to the builder stage and the image got further — then died in the RUNTIME stage, which has its own two hand-maintained lists (manifests and dists) that I had not looked at. The guard I added alongside it checked exactly one of the four lists and reported a clean run, which is the false confidence a gate exists to remove — the same failure this whole PR is about, in my own tooling. It now checks all four, names which stage each one breaks, and fails loudly if a pattern matches nothing at all (the Dockerfile having been restructured under it). Verified by removing each of brain's four COPY lines in turn: each is caught independently, with the right list named. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
… workspace links
pnpm install --prod skips devDependencies but still RESOLVES every
workspace:* link it reads, so brain's devDependency on @xnetjs/vectors —
a package the hub image deliberately does not copy — failed the runtime
install with ERR_PNPM_WORKSPACE_PKG_NOT_FOUND. My guard modelled runtime
deps only and passed a Dockerfile that could not install.
Rather than copy vectors into an image that will never use it, brain no
longer depends on it at all. Its one consumer was an end-to-end test
using createSemanticSearch({ useMockModel: true }); that is now a local
fake, which is stronger coverage — brain's contract is that it is
structural over ANY conforming index, and testing it against the single
implementation we ship proved less. brain is now genuinely
zero-dependency, which is the property cited when making it publishable.
The guard now counts workspace:* devDependencies too, and its closure
grew from 19 to 20 as a result.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: xNet Test <test@xnet.dev>
Implements exploration 0415 — the coding-agent lane: retrieval, memory, speed and self-improvement.
The problem
Claude Code and Codex are xNet's primary AI interface, and they were running on the worst retrieval xNet owns.
@xnetjs/brain's hybrid GraphRAG stack was wired into exactly one consumer — the workbench — while every agent lane built its AI surface with no retriever at all and fell back to a substring scan over the first 500 nodes. Thedegraded: truenotice the AI surface computes was thrown away before the agent saw it, so a scan of 500 out of 40,000 nodes printedno resultsand sounded certain.It was worse than the exploration claimed. Three lanes had no
searchText, not two — the--dblane rated "half-equipped" turned out to drop it as well, sitting directly on top of a workingnodes_ftsindex.What landed
One retriever, every lane.
createAgentRetrieval()is now the single construction path, used by the CLI,xnet mcp serve, the Electron bridge and the local API.authorizeis required rather than optional — graph expansion walks edges out of matched nodes, and better retrieval widens whatever egress hole exists.scripts/guard-ai-surface-retrieval.mjsis a hard-zero CI gate, because omitting a retriever is invisible in review: the call reads fine, and it's what it doesn't pass that costs you.A search that can't lie.
runSearchleads with the tier it actually ran at and writes its notice to stderr — stderr because a warning on stdout is one| headaway from vanishing.xnet recallreplaces "search, then read eight nodes": a budgeted pack where each hit carries the graph path it was reached by, plus the ids dropped for budget.xnet servekeeps store, schemas and FTS warm behind a unix socket. Absent is silent; wrong is loud — a version mismatch or a mid-request death throws rather than falling back, because a killed daemon must never read as "no results".Memory and a learning loop.
remember/forget/memories/distill, a devtools console to see and edit what's stored, and aRetrievalProfiletuned from local traces that is only adopted when a pinned golden set doesn't regress. Nothing leaves the device; distillation readsAgentActioninstructions that already exist rather than capturing anything new, requires three occurrences, and never reconstructs a redacted instruction from its digest.Measurements
bench:agent-surfacesxnet searchThree things the implementation corrected in the doc
nodebooting the client. The socket itself answers in 0.26 ms. The target is restated against the round-trip; the leverage is callers that are already processes.@xenova/transformerspulls insharp, which this repo builds for Electron's ABI, soxnet serve --vectorsis honest and inert under system Node today. The tier is built and tested and reportshybrid-graphagainst an injected engine. A first draft swallowed the backfill error and announcedhybrid-graphover an index holding zero documents — this exploration's own thesis biting its implementation.recallquestion matched nothing; and the two backends return different schema shapes, so the CLI lane's graph stage found zero edges while still reportingbm25-graph. Both fixed, both now tested.Working end-to-end result on a workspace where the answer page contains none of the query's words:
Not done, deliberately
hopDecay 0.2beats the shipped 0.55 on every golden-set metric (all 0.81→0.85, graph 0.50→0.60, MRR 0.69→0.71). The default was not moved — the whole gap is one or two golden cases, which is this eval's resolution rather than a signal, and it's the same argument 0394 made. Recorded in the doc with the numbers so the next person can decide with a bigger corpus.🤖 Generated with Claude Code