Add the assistant: a chat that drives the MCP tools in-process
Docs / docs (push) Successful in 42s
Playwright Tests / test-playwright (1, 2) (push) Failing after 2m42s
Playwright Tests / test-playwright (2, 2) (push) Failing after 10s
pre-commit / pre-commit (push) Failing after 2m33s
Test Backend / test-backend (push) Failing after 2m48s
Compose Smoke Test / test-compose (push) Failing after 9s
Playwright Tests / merge-reports (push) Failing after 2m17s

A provider set under Settings (OpenAI-compatible; key encrypted in
flow-data/ai.json) turns on an Assistant panel in the sidebar. Each turn runs
the FastMCP tools in-process as the person chatting, streams the rounds over
SSE and keeps one transcript per user. The panel floats on any screen and
shares the editor's lane: a settings panel keeps the top half, the chat the
bottom. The MCP client is now built once, for the endpoint and the chat.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 23:28:23 +02:00
co-authored by Claude Fable 5.1
parent d9630f66f5
commit cc4091166e
25 changed files with 1957 additions and 43 deletions
+17 -2
View File
@@ -1,7 +1,9 @@
import { useQuery } from "@tanstack/react-query"
import {
ArrowLeft,
Bell,
BookOpen,
Bot,
FlaskConical,
Home,
KeyRound,
@@ -16,6 +18,8 @@ import {
} from "lucide-react"
import { useState } from "react"
import { aiStatusQueryOptions, ChatPanel } from "@/components/Chat/ChatPanel"
import { chatStore } from "@/components/Chat/chatStore"
import { GlobalSearch } from "@/components/Common/GlobalSearch"
import { Logo } from "@/components/Common/Logo"
import {
@@ -97,10 +101,17 @@ export function AppSidebar() {
onClick: () => setSearchOpen(true),
}
// Only once an operator has set a provider up: with none there is nothing
// to open, and nothing on screen says the feature exists.
const assistant = useQuery(aiStatusQueryOptions())
const tools: Item[] = assistant.data?.available
? [{ icon: Bot, title: "Assistant", onClick: chatStore.toggle }, search]
: [search]
const footerItems: Item[] = portal
? [search, docs, { icon: Settings, title: "Settings", path: "/settings" }]
? [...tools, docs, { icon: Settings, title: "Settings", path: "/settings" }]
: [
search,
...tools,
docs,
{ icon: Settings, title: "Settings", path: "/settings" },
{ icon: LogOut, title: "Log Out", onClick: logout },
@@ -140,6 +151,10 @@ export function AppSidebar() {
{/* Outside the sidebar: on a phone that is a sheet, and a dialog is not
one of its children. */}
<GlobalSearch open={searchOpen} onOpenChange={setSearchOpen} />
{/* Mounted once, here, for the same reason: both shells carry the
sidebar, and the chat follows whoever opened it from screen to
screen. */}
<ChatPanel />
</>
)
}