Skip to content

feat: render stream events in TUI message bubbles #604

Description

@avoidwork

Summary

Render the captured LangChain streamEvents in the TUI message bubbles so users can see what's actually happening during agent execution — tool calls, agent actions, chain events, reasoning, errors.

Motivation

The streaming pipeline now captures all event types and stores them on the message model's events array (issue 602). However, the TUI doesn't render this data. Users see only the final text response with no insight into the agent's internal work — what tools ran, what the orchestrator decided, what subagents produced, what errors occurred, or what reasoning was generated.

This creates a black box experience. The IRC-style chat shows messages but not the rich event data that's already flowing through the pipeline. Rendering events makes the TUI transparent and informative.

Proposed Solution

The events array is already available on each message in the TUI. The rendering approach is intentionally open — this is a creative task where the implementer can explore what works best in the IRC-style render.

Possible approaches:

  • Inline event badges — Small colored indicators next to each message showing event types (tool call, reasoning, error)
  • Collapsible event sections — Expandable sections within each bubble showing detailed event data
  • Event timeline — A visual timeline within the bubble showing the sequence of events
  • Color-coded event lines — Each event type gets a distinct color/style in the message flow
  • Event panel — A separate panel/section within the bubble for event details

The message model already has events as an array of objects with type, name, data, tags, and metadata fields. The implementer should explore what renders well in the Ink-based TUI.

Alternatives Considered

  • Hardcoding specific event types — too rigid, doesn't scale to new event types
  • Only rendering a subset of events — loses information, user can't see what matters
  • External event viewer — adds complexity, defeats the purpose of inline visibility

OpenSpec Note

This project uses OpenSpec for feature development. If this request is approved, I will:

  1. Run /opsx:propose to generate a full proposal with specs and tasks
  2. Iterate on the design before any code is written
  3. Follow the task-driven implementation workflow

Additional Context

  • The events array is already on the message model (issue 602)
  • MessageBubble receives events as a prop but doesn't render them
  • The TUI uses Ink for rendering — consider terminal constraints (no colors beyond ANSI, limited width)
  • IRC-style render means events should fit naturally in the message flow
  • Event data structure: { type, name, data, tags, metadata }

Environment

  • OS: Linux 7.0.6-2-pve
  • Node.js: v25.8.1
  • madz version: 1.35.2
  • LLM provider: Unknown — user to confirm

Audit Findings (for Issue #604)

  • src/tui/messageBubble.js:119-128 — MessageBubble destructures props but does NOT include events. The component receives events from messageList.js:368 but ignores it.
  • src/tui/messageBubble.js:157-246 — Existing rendering pattern: reasoningEl, toolCallEl, toolDisplayEl are conditionally rendered as Box elements below the content. Events rendering would follow this same pattern.
  • src/tui/messageBubble.js:243-245reasoningEl, toolCallEl, toolDisplayEl are rendered in sequence inside the bubble-inner Box. An eventsEl would fit naturally here.
  • src/tui/messageList.js:368events: data.events is passed to MessageBubble but the component doesn't accept it yet.
  • src/tui/messages.js:8 — Message typedef includes events field — already defined.
  • src/tui/app.js:709-714 — Events are captured and appended to the message's events array during streaming.

Key insight: The plumbing is complete. MessageBubble just needs to:

  1. Destructure events from props
  2. Build an eventsEl following the existing pattern (conditional rendering, Box wrapper, Text elements)
  3. Render eventsEl alongside reasoningEl, toolCallEl, toolDisplayEl

The implementer has creative freedom on the visual design — the structure is already there.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions