Skip to content

AI chat: stream responses and render tool results as grounded cards #381

Description

@sherrmann

Summary

The chat assistant currently renders answers as a single block of plain text after up to six tool rounds. This issue covers making it feel like a chat: streaming tokens, rendering tool results as grounded cards, and the drawer affordances a conversation needs.

This is the approved second half of the design spec. The first half — growing the tool layer from 6 to 18 model-facing tools — is PR #378. A full implementation plan already exists at docs/superpowers/plans/2026-07-27-chat-streaming-and-rendering.md (12 tasks, written in full with code).

Depends on #378.

Scope

Streaming. ai.py gains a streaming sibling to chat_completion_tools that yields content deltas and accumulates tool_calls deltas index-by-index into the same assistant-message shape the non-streaming path returns, so the agent loop is unchanged. A new delta SSE event carries tokens.

Result cards. tool SSE events grow a rows payload, and the client renders per-tool card types: spools (colour swatch, remaining weight, link to detail), filaments (low-stock and on-order tags), orders (shop, outstanding count), locations and vendors. Structure comes from the tool's own result payload, not from model-authored prose — a card cannot display a number the tools did not return.

Usage chart. get_usage_stats renders a compact variant of the existing dependency-free usageChart.tsx. No charting library added.

Drawer affordances. Conversation persisted in localStorage (the server loop stays stateless by design), a stop button via AbortController on the SSE reader, and starter prompt chips on the empty state.

Constraints worth knowing up front

  • No new client dependencies. Notably no markdown renderer — cards carry the structure instead.
  • The drawer stays 420 px. Cards are single-column, capped at five rows with a "+N more" deep-link. Nothing may scroll horizontally.
  • The message event must keep firing at end-of-turn with the complete text. delta is additive; the confirm/resume round-trip must be untouched.
  • Streaming needs a per-request fallback. Spoolman points at arbitrary OpenAI-compatible endpoints whose streaming fidelity varies. An endpoint that streams badly must produce a slow chat, never a broken one — and the fallback decision is made once per request, not per token.
  • Every new user-visible string needs all 30 locales. npm run check-i18n gates it. This is a meaningful slice of the work, not a footnote.
  • UI review gate: per the repo owner's standing rule, nothing visual commits until before/after screenshots of the running app have been reviewed and approved.

Non-goals

Photo attachment in chat, bulk actions and proactive nudges are the third sub-project and are tracked separately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions