Skip to content

Split agent cascade streaming into separate streamer module#267

Merged
alexkroman merged 2 commits into
mainfrom
claude/gifted-fermat-3xzjv8
Jun 23, 2026
Merged

Split agent cascade streaming into separate streamer module#267
alexkroman merged 2 commits into
mainfrom
claude/gifted-fermat-3xzjv8

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

Summary

Refactors the agent cascade's per-turn streaming reply logic into a dedicated streamer module, splitting it from brain along the natural build-vs-drive seam. This keeps both files within the 500-line gate while maintaining a clean one-directional dependency (streamer imports brain, never the reverse).

Key Changes

  • New module aai_cli/agent_cascade/streamer.py: Contains all streaming/driving logic previously in brain.py:

    • build_streamer() — the streaming reply leg factory
    • _stream_graph() — per-turn graph streaming with error handling
    • _stream_gated() — write-approval pause/resume loop for --files mode
    • _pending_writes() / _decide() — approval decision handling
    • _events_from_chunk() — chunk-to-event translation
    • Flow logging utilities (_FLOW_LOG, _RESULT_LOG_CAP, _clip(), _content_text())
  • New module aai_cli/agent_cascade/plan.py: Extracts plan/todo subsystem:

    • TodoItem / TodoUpdate dataclasses for the agent's task list
    • TodoCollector — accumulates streamed write_todos args across chunks
    • _parse_todos() / _todos_from_list() — JSON parsing for plan events
    • WRITE_TODOS_TOOL_NAME constant
  • Updated aai_cli/agent_cascade/brain.py:

    • Removed build_streamer() and all streaming functions (moved to streamer)
    • Removed flow logging and plan collection (moved to plan and streamer)
    • Kept graph assembly, tool definitions, and shared event types (SpeechDelta, ToolNotice, ApprovalPause)
    • Updated module docstring to clarify ownership: brain owns assembly, streamer owns driving
  • Updated aai_cli/agent_cascade/messages.py:

    • Added TodoList widget for rendering the plan checklist
    • Added _todos_markup() helper to style plan items (completed/in-progress/pending)
    • Integrated plan rendering into the TUI
  • Updated aai_cli/agent_cascade/tui.py:

    • Added todos_updated() renderer method
    • Integrated TodoList widget mounting and refresh logic
    • Added _todo_widget tracking for in-place plan revisions
  • Updated aai_cli/agent_cascade/engine.py:

    • Refactored _consume() to handle plan.TodoUpdate events
    • Added _terminal_result() helper for cleaner event dispatch
  • Updated aai_cli/agent/events.py:

    • Added TodoItem and PlanUpdate event types for JSON/text rendering
  • Updated aai_cli/agent/render.py:

    • Added plan rendering for both JSON and text modes
    • Added _mark() helper for status-based checklist markers
  • Test updates:

    • New tests/test_live_tui_plan.py — TUI plan panel tests (500-line gate compliance)
    • New tests/test_tui_snapshots/test_live_plan.raw — snapshot golden for plan rendering
    • Updated tests/test_agent_cascade_streamer.py — tests for write_todosTodoUpdate flow
    • Updated tests/test_agent_cascade_brain.py — adjusted imports, moved utility tests to streamer
    • Updated tests/test_agent_render.py — added plan event rendering tests
    • Updated tests/test_agent_cascade_engine.py — added TodoUpdate event handling tests
    • Updated tests/test_agent_cascade_files.py, test_agent_cascade_subagents.py, test_agent_cascade_command.py — adjusted imports

Notable Implementation Details

https://claude.ai/code/session_0153dUtXjPNhfMvFDyjiLgdh

`assembly live` answers each spoken turn with a deepagents graph, which
auto-installs langchain's TodoListMiddleware (the `write_todos` tool) — but
the plan it writes was invisible. Surface it so a multi-step spoken request
("book a flight, then check the weather there") shows a visible checklist
alongside the spoken "first I'll…, then I'll…", a recognizable
Gemini-Live-style affordance.

- plan.py: the write_todos plan subsystem — TodoItem/TodoUpdate, the
  JSON-arg parsing, and TodoCollector, which reassembles a write_todos call
  from streamed chunk args (streaming) or a non-streaming model's complete
  .tool_calls dict.
- streamer detects the write_todos tool result and emits a TodoUpdate (never
  a generic "Using write_todos" affordance); engine forwards it to
  renderer.todos_updated without gating the spoken reply.
- Voice TUI renders an in-place TodoList checklist (✓ done, ▸ in progress,
  ○ pending), one panel per turn, revised in place. AgentRenderer emits a
  `plan` NDJSON event / a stderr checklist on the non-TUI paths.

Also splits brain.py along its build-vs-drive seam: graph assembly stays in
brain.py, the per-turn streaming leg (build_streamer + helpers) moves to the
new streamer.py (one-directional: streamer imports brain). This keeps both
files under the 500-line gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0153dUtXjPNhfMvFDyjiLgdh
@alexkroman alexkroman enabled auto-merge June 23, 2026 16:49
Reconcile the write_todos plan UI + brain/streamer split with main's
agent-cascade changes (path-scoped --auto-write write_gate, --files on by
default, SummarizationMiddleware context management, auto-added subagent,
project_context grounding). Both feature sets are kept.

Conflicts resolved:
- brain.py: kept main's write_gate-based _graph_kwargs and
  register_gp_subagent_profile; kept the streaming-removed structure
  (streaming lives in streamer.py); dropped the brain-side flow-log
  constants (now in streamer.py).
- aai_cli/AGENTS.md: merged both narratives; fixed brain._stream_* ->
  streamer._stream_* references.
- tests/test_agent_cascade_brain.py, test_agent_cascade_subagents.py:
  unioned imports (plan, streamer, write_gate, subagents).

Also: trimmed two engine.py docstrings to stay under the 500-line gate
after the merge, and added two TodoCollector tests killing the and->or
mutants the widened diff base surfaced (plan.py:100, :109).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0153dUtXjPNhfMvFDyjiLgdh
@alexkroman alexkroman added this pull request to the merge queue Jun 23, 2026
Merged via the queue into main with commit cc0e29f Jun 23, 2026
20 checks passed
@alexkroman alexkroman deleted the claude/gifted-fermat-3xzjv8 branch June 23, 2026 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants