Knowledge/AgentStudio/12-ui-and-instantsearch.md
12 — UI Refactoring & Algolia InstantSearch Adoption
This section addresses a parallel workstream to agent + prompt refactoring: where Algolia's native frontend libraries (InstantSearch, the <Chat> widget, the <FilterSuggestions> widget, the AI SDK UI hooks) should replace our custom frontend code, and what stays custom.
Why this matters
We currently have:
- src/hooks/chat/useSpecialist.ts — 652 lines of state machine for specialist (Elena/Bruno) handoff and rendering
- src/hooks/chat/useChatStream.ts — 639 lines of state machine for Maverick streaming
- lib/agent-studio/stream-adapter.ts — 350 lines that convert Agent Studio's native JSON output to our custom XML format
- Frontend XML-tag parsing scattered across src/components/chat/
- Custom paragraph buffering, footnote stripping, tag injection post-processors
- ~1,600+ lines of frontend code doing what Algolia's native widgets do natively
That's the cost. The opportunity: replace large swaths of this with <Chat> (which is itself built on useChat from @ai-sdk/react) plus per-tool layoutComponent slots. Lower line count, lower bug surface, native streaming, native tool round-trip — and we get future widget improvements for free.
This is a major frontend refactor. It pairs with the agent refactor — when Elena/Bruno emit XML tags, we need our custom parser. When they emit tool calls, we need layoutComponents. Frontend refactor unlocks once tools replace XML.
What Algolia provides for frontend
Tier 1 — Agent-facing widgets (the focus of this refactor)
Per 04-frontend-widgets.md:
| Widget | Package | What it gives you |
|---|---|---|
<Chat> (React) |
react-instantsearch |
Full chat UI: streaming, tool round-trip, history, prompts, error states, suggestions, layouts |
chat() (Vanilla JS) |
instantsearch.js/es/widgets |
Same as above for non-React |
<FilterSuggestions> (React) |
react-instantsearch |
AI-suggested facet filters, debounced, integrated with InstantSearch state |
filterSuggestions() (Vanilla JS) |
instantsearch.js/es/widgets |
Vanilla equivalent |
Status: all four are BETA. Pin versions. Read changelogs.
Tier 2 — Search-facing components (potential future use)
Standard InstantSearch React components for non-AI search UIs:
<SearchBox>,<Hits>,<RefinementList>,<HierarchicalMenu>,<RangeInput>,<Pagination>— for traditional search interfaces- Connectors (
useSearchBox,useHits,useRefinementList) for custom UIs over the same data
These are stable, not beta. Use for any non-AI search surfaces (admin tooling, faceted search pages, etc.) Algolia Central might need.
Tier 3 — Hooks and primitives
useChatfrom@ai-sdk/react— what<Chat>is built on. Use directly for custom React UIs needing managed message state + streaming.liteClient as algoliasearchfromalgoliasearch/lite— lightweight client for browser use. Read-only, smaller bundle.- Insights API client — event tracking for personalization and analytics.
What we have today
Our frontend chat surface roughly maps as:
| File | Lines | What it does | Native equivalent |
|---|---|---|---|
src/hooks/chat/useChatStream.ts |
639 | Maverick streaming state machine | useChat hook OR <Chat> widget |
src/hooks/chat/useSpecialist.ts |
652 | Elena/Bruno handoff + XML rendering state | <Chat>'s tools prop with per-tool layoutComponent |
lib/agent-studio/stream-adapter.ts |
350 | JSON→XML conversion + paragraph buffering + footnote stripping | NOT NEEDED if agents emit tool calls instead of XML |
src/components/chat/ChatMessage.tsx |
unknown | XML tag parsing + render switching | Per-tool layoutComponent per <Chat>'s tools prop |
lib/search/stream_processor.ts |
unknown | Post-generation content mutation, tag injection | NOT NEEDED with structured tools |
| Discovery card UI (Maverick) | various | Render <discovery_pivot> tag |
<Chat>'s suggestionsComponent slot + config.suggestions.enabled |
| Handoff banner UI | various | Render <specialist_handoff> tag |
Custom layoutComponent for the handoff_to_specialist tool |
| Mermaid diagram render | unknown | Parse <strategic_architecture> tag, render via mermaid.js |
Custom layoutComponent for the render_architecture_diagram tool |
| Code block render | unknown | Parse <production_code> tag, render via syntax highlighter |
Custom layoutComponent for the render_code_block tool |
Total frontend code that becomes simpler or vanishes: roughly 1,600+ lines of state machines and XML parsers + N component-specific tag parsers.
Total NEW frontend code: per-tool layoutComponent (~30-80 lines each) × ~6-8 tools = ~300-500 lines.
Net reduction: ~1,000-1,300 lines.
Migration approach (NOT a big-bang rewrite)
The safest path: migrate one piece at a time, keeping the rest intact.
Phase 1 — Replace useChatStream with <Chat>
Smallest scope: just the Maverick conversation. <Chat agentId={maverickAgentId} /> inside an <InstantSearch> shell. Drop useChatStream.ts (639 lines). Maverick still emits XML tags during this phase — <Chat> can render them via assistantMessageLeadingComponent or a custom message renderer that does the parsing inline.
Output: useChatStream.ts deleted. Maverick UI is now the native widget. ~600 lines of custom code → 0.
Phase 2 — Add tools for Maverick's structured outputs
Define tools for discovery_pivot (or use platform suggestions.enabled), specialist_handoff, analogy_canvas (if kept). Update Maverick prompt to call tools instead of emitting XML. Pass tool definitions to <Chat tools={...} /> with layoutComponent for each.
Output: Maverick's XML tag parsing in frontend goes away. Discovery questions, handoff banner, analogy hook all rendered via layoutComponent.
Phase 3 — Replace useSpecialist with <Chat> + tool components
Same shape as Phase 1, but for Elena/Bruno. Uses the same <Chat> widget — just a different agentId (Elena's UUID or Bruno's UUID) and a different tool set.
This phase is BLOCKED on the agent refactor: tools have to exist on Elena/Bruno before frontend can render them. Order:
1. (Backend) Add tools to Elena/Bruno (per §10 migration order step 2).
2. (Frontend) Build layoutComponent for each tool.
3. (Frontend) Replace useSpecialist.ts with <Chat>.
Output: useSpecialist.ts deleted (652 lines). XML parser in stream-adapter.ts deleted (350 lines).
Phase 4 — Drop the JSON→XML stream adapter
Once Elena/Bruno emit tool calls instead of XML output, the stream adapter (lib/agent-studio/stream-adapter.ts) has nothing to do. Delete it. Frontend <Chat> consumes tool calls directly.
Output: 350 more lines deleted.
Phase 5 — Adopt platform features
- Enable
config.memory.enabledandconfig.suggestions.enabledon agents. - Replace custom Redis-based conversation history with platform memory (passing
id: "alg_cnv_..."and per-message IDs). - Replace Maverick's
<discovery_pivot>with platform suggestions, rendered via<Chat>'ssuggestionsComponentslot.
Output: Custom session continuity logic in the orchestrator becomes much smaller. Redis stays for 8-signal state only.
Phase 6 — Add right-panel artifact rendering (the "Claude Desktop" UX)
Per RC3-BRIEF and the agent UX direction: chat on left, opened artifacts on right. Each render tool's layoutComponent can:
- Render inline in the chat (default)
- Render a clickable card in the chat that, when clicked, opens the full artifact in a right panel
This is a frontend pattern we build on top of the native widget. The <Chat> layoutComponent prop gives us the slot; the right panel state and routing is custom.
Output: Two-panel UI shipped. Reference architecture for Claude Desktop-style content engagement.
Per-tool layoutComponent — concrete examples
// render_evidence_card layoutComponent
({ message }) => {
const { source, metric, driver_action_result } = message.input;
return (
<div className="evidence-card">
<span className="source-tag">{source}</span>
<h4>{metric}</h4>
<p>{driver_action_result}</p>
</div>
);
}
// render_architecture_diagram layoutComponent
({ message, addToolResult }) => {
const { mermaid_source } = message.input;
return (
<div className="architecture-diagram">
<Mermaid source={mermaid_source} />
<button onClick={() => openInRightPanel(mermaid_source)}>
Open full diagram
</button>
</div>
);
}
// suggest_searches layoutComponent (for Maverick)
({ message, indexUiState, setIndexUiState }) => {
const { suggestions } = message.input;
return (
<div className="suggestion-chips">
{suggestions.map(s => (
<button onClick={() => runSearch(s.query, s.facets)}>
{s.label}
</button>
))}
</div>
);
}
Each layoutComponent is small and focused. The widget handles the rest (streaming, error states, message threading).
What stays custom
Not everything maps to native. Some things stay code:
| What | Why it stays |
|---|---|
| Right panel ("Claude Desktop" two-panel) | Native widget ships with inline + overlay layouts only. Two-panel is custom. |
| Chat history navigation, conversation threading UI | Possibly custom, depending on how <Chat> handles multi-conversation switching |
| Custom branding (avatars, colors, themes) | Style overrides via classNames and translations props of <Chat> |
| Telemetry, audit logging | Platform sends usage data; we may want richer custom telemetry |
| 8-signal discovery state extraction (if stays code-based) | Algorithmic logic, not UI |
| Multi-agent routing (Maverick → specialist) | If kept, this is custom UX flow (handoff card, transition animation, persona switch) — but the native widget can host all these as layoutComponents |
| Right-panel artifact viewer (Mermaid full-screen, code editor with copy, comparison tables) | Custom components opened from layoutComponent cards |
InstantSearch adoption beyond the chat widget
While the focus is the AI chat surface, Algolia Central will likely need search surfaces beyond chat:
- Admin / merchandising UI — for managing the index content. InstantSearch React components are the standard.
- Faceted browse pages — if the user wants to browse content without chat.
<SearchBox>+<RefinementList>+<HierarchicalMenu>+<Hits>is the canonical recipe. - Public marketing / landing pages with embedded search —
<SearchBox>with<FilterSuggestions>for AI-suggested filters alongside.
For each surface, default to InstantSearch React components unless there's a specific reason to build custom. The library is mature, well-documented, and integrates natively with Algolia's analytics + personalization.
What this section does NOT cover
- Backend agent refactor — see 10-our-agents-vs-best-practice
- Tool design — see 02-tool-types, 05-output-shape-decision
- Native widget API surface details — see 08-frontend-integration
- The case for replacing custom code generally — see RC3-BRIEF Part 4 ("Native Algolia surface" research item R9)
Open questions for UI workstream
| ID | Question | Impact |
|---|---|---|
| Q-UI-1 | Which version of react-instantsearch exposes <Chat> stable enough for production? |
Determines pin version and migration timing |
| Q-UI-2 | Does the two-panel layout work as a layoutComponent wrapper, or do we need a separate frame above <Chat>? |
Drives Phase 6 architecture |
| Q-UI-3 | Can <Chat> host the multi-agent (Maverick → Elena/Bruno) handoff visually? |
Drives whether handoff is in-chat (one widget, persona switches) or out-of-chat (multiple widget instances) |
| Q-UI-4 | What's our policy for tracking widget breaking changes through beta? | Operational hygiene |