feat(miner): add read-only chat grounding over the existing miner MCP tools - #6570
Conversation
… tools Adds POST /api/chat, a streaming read-only conversational endpoint for the miner-ui chat rail. Answers are grounded exclusively in the 11 existing read-only loopover_miner_* MCP tools -- their implementations are called directly via the miner's own stdio MCP server, never reimplemented here. Backend only: no chat UI, no action-dispatch, no new tool, and no conversation store (the caller supplies full history per request). chat-grounding.ts resolves the provider through driver-factory.ts rather than reading MINER_CODING_AGENT_* directly, and drives the agent-sdk query() loop behind an injectable ChatQueryFn seam so every test runs a fake async-iterable and CI never makes a real model call. Fails closed on provider: claude-cli/codex-cli are single-turn, buffered coding drivers rather than a conversational streaming tool-calling loop, so either of those (or nothing configured) emits exactly one error event -- chat_requires_agent_sdk_provider / no_coding_agent_configured -- followed by done, never a partial, mock, or echoed answer. Privacy is enforced twice. A conversational surface adds a leak vector the tools themselves lack: a user can simply ask for a trust score and an ungrounded model could hallucinate one. The system prompt instructs the model to decline those terms, and because a prompt is not enforcement every outgoing text chunk is checked against track-record-summary.ts's PUBLIC_FIELD_BLOCKLIST and redacted on a hit. That constant is now exported and reused rather than copied, so instruction and enforcement cannot drift. vite-chat-api.ts is transport only -- route match, body validation, and re-emitting the engine's events as text/event-stream frames. It is registered after authPlugin(), so the existing session-cookie gate covers it with no new auth mechanism. An invariant test pins the session allowlist to exactly the 11 read-only tool names, so an accidental 12th (or a write-capable one) fails the suite. Closes JSONbored#6517
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6570 +/- ##
=======================================
Coverage 93.60% 93.61%
=======================================
Files 675 676 +1
Lines 67764 67835 +71
Branches 18627 18654 +27
=======================================
+ Hits 63431 63502 +71
Misses 3360 3360
Partials 973 973
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-16 14:21:47 UTC
Review summary Nits — 2 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy · Diff highlights exactly what changed. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|



Summary
POST /api/chat, a streaming, read-only conversational endpoint for the miner-ui chat rail. It answers natural-language questions about the miner's own local state, grounded exclusively in the 11 existing read-onlyloopover_miner_*MCP tools. Backend only — no chat UI, no action-dispatch, no new tool, no conversation store.packages/loopover-engine/src/miner/chat-grounding.ts(new): resolves the provider throughdriver-factory.ts(never readingMINER_CODING_AGENT_*directly), drives@anthropic-ai/claude-agent-sdk'squery()against the miner's own stdio MCP server so the 11 tools' implementations are called rather than reimplemented, and exposes an injectableChatQueryFnseam so every test drives a fake async-iterable and CI never makes a real model call.claude-cli/codex-cliare single-turn, buffered coding drivers, not a conversational streaming tool-calling loop; when the resolved provider is either of those (or nothing is configured) the stream emits exactly oneerrorevent (chat_requires_agent_sdk_provider/no_coding_agent_configured) thendone— never a partial, mock, or echoed answer.textchunk is checked againsttrack-record-summary.ts'sPUBLIC_FIELD_BLOCKLISTand redacted on a hit. That constant is now exported and reused rather than copied, so the instruction and the enforcement cannot drift apart.apps/loopover-miner-ui/vite-chat-api.ts(new) is transport only: route match, body validation, and re-emitting the engine's events astext/event-streamdata: <json>\n\nframes. Registered afterauthPlugin()invite.config.ts, so the existing session-cookie gate (Add auth to the local miner-ui API #4858) covers it with no new auth mechanism.Closes #6517
Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #123) — a linked open issue is required for every contributor PR.Validation
git diff --checknpm run typecheck— 0 errorsnpm run test:coveragelocally;codecov/patchrequires ≥99% coverage of the lines AND branches you changed —chat-grounding.tsis at 100%: 77/77 statements, 35/35 branches, no partialsnpm run build --workspace @loopover/enginenpm run test --workspace @loopover/engine— 578/578 pass (includes the newtest/chat-grounding.test.ts)npm run ui:lint— 0 errorsnpm run ui:typecheck— 0 errorsnpm run miner:env-reference:check/npm run selfhost:env-reference:check/npm run docs:drift-checkIf any required check was skipped, explain why:
npm audit --audit-level=moderatecould not complete locally — the registry audit endpoint was unreachable from this environment (npm error audit endpoint returned an error), not a reported advisory. No dependencies are added or changed by this PR.Safety
src/chat-api.test.tsasserts an unauthenticated/api/chatis rejected with the 401 body before the handler runs, and that an authenticated one falls through to it.Notes for review
Tool allowlist is asserted, not just reviewed. An invariant test pins the session's allowlist to exactly the 11 read-only names — a future accidental 12th tool (or a write-capable one) fails the suite.
Test seam coverage.
resolveChatQuery()is split out ofrunChatGrounding()deliberately: the generator invokes its query immediately, so an inlineoptions.query ?? defaultQuerywould only be branch-coverable by opening a live session. Resolving it in a separate exported function lets the default arm be exercised by binding it, never by calling it — the same "resolve, don't invoke" shapeagent-sdk-driver.ts's factory uses to reach 100% branch coverage.Dual test suites per the engine convention:
packages/loopover-engine/test/chat-grounding.test.ts(node:test, againstdist/) andtest/unit/chat-grounding-engine.test.ts(the vitest mirror againstsrc/, which is whatcodecov/patchactually measures forpackages/loopover-engine/src/**).