feat(client): built-in chat UI — streaming chat panel + inline write confirmations #347
No reviewers
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol!347
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "346-chat-ui"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Replaces the
/chatplaceholder with a real streaming chat panel for the built-in agent — the last piece of epic #47's in-app surface. Hits the exit criterion: the user asks, Carol answers token-by-token, and write proposals are confirmed inline before anything changes. Consumes the merged backend (loop #338, streaming #340, proposal read #343) + api-client hooks/SSE transport (#344).What's in it
lib/agent/chatReducer.ts— a pure, React-free reducer folding theConversationStreamEventwire into a render-ready turn snapshot (live text bubble, persisted rows, tool activity,awaiting_confirmation+ proposalId, terminal done/error). The tested logic core;chat.tsxonly dispatches into it.lib/agent/streamUrl.ts+apiClient.ts—resolveAgentUrlreuses the typed client's off-origin rule via a new exportedresolveOffOriginBase()(additive — the existing per-request rewriter is untouched), so the SSE stream and the openapi-fetch client never drift on base URL.lib/agent/agentFetch.ts—expo/fetchon native (RN's default fetch buffers and can't stream SSE),globalThis.fetchon web.lib/agent/useAgentTurn.ts— send/resume overstreamConversationTurn, with a JSON fallback when the stream errors before emitting any event, then invalidates the conversation so the persisted thread is authoritative.chat.tsx— no-provider empty state linking to Settings, most-recent conversation + new/switcher, streaming bubbles, and an inline proposal confirmation card (summary + action + diff → commit/reject → resume). Mirrorsnotes.tsx(DS tokens, RN primitives, voice).chatnamespace strings in Carol's voice;es.jsonuntouched (per-key English fallback).Verification (run locally on this branch)
typecheck✓ ·lint✓ (no disables/non-null assertions) ·test✓ — 23 files / 149 passed, incl. 8 reducer tests + URL/fetch-picker branch testsen.jsonvalid JSON; no hardcoded user-facing strings, no raw colors inchat.tsx(all viatheme.tokens)⚠️ Needs maintainer verification (cannot be done headlessly)
Client vitest is
node-env — no browser/device. Not automatically verified, please check manually:expo/fetchSSE streaming on Android, and the JSON fallback path (e.g. behind a buffering proxy).Deferred (follow-ups)
Part of epic #47. Closes the in-app chat surface (MCP external surface already shipped).
Closes #346
🤖 Generated with Claude Code
📊 Test coverage
Patch coverage: no testable lines changed.
Overall (
app/,lib/,db/, excluding UI per ADR-0019):Soft thresholds per ADR-0019. Coverage is informational and does not block merge.