feat: add dedicated subagent view to TUI
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 22/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- javascript, nodejs
- Domain
- cli
Research direction
Start in callProvider in index.js (around line 228), where _namespace from agent.stream() is currently discarded, and confirm how the root graph and subagent namespaces differ. Then read createStreamingHandler in src/tui/conversationArea.js and src/tui/panels.js to see how events and panels are routed. Done means subagent events reach a separate store and a SUBAGENTS panel renders them, with tests in tests/unit/tui/subagentsPanel.test.js. The issue says the design must be approved first, so confirm with maintainers before writing code.
Written by the indexing model from the issue text.
Description
Summary
Add a dedicated SUBAGENTS panel to the TUI that captures and renders each subagent's reasoning and message segments separately from the orchestrator's conversation, including subagent tool calls.
Motivation
When the orchestrator delegates work via the task tool, the subagent's reasoning and message segments are currently flattened into the same assistant bubble as the orchestrator's own output. The stream namespace that identifies which subgraph produced each event is discarded in callProvider, so there is no way to distinguish what the orchestrator said from what each subagent did. This makes multi-agent runs hard to follow and audit.
Proposed Solution
Capture the stream namespace in callProvider (index.js) to distinguish subagent subgraphs from the root graph. With subgraphs: true, the stream yields [namespace, mode, payload] where the root graph has an empty namespace and each subagent subgraph has a populated namespace (e.g. ["task:<uuid>"]). Emit new event types for subagent reasoning, message, and tool activity, and route them to a dedicated SUBAGENTS panel instead of the main assistant bubble.
Alternatives Considered
- Inline collapsible sections — subagent activity renders in the conversation as collapsible/indented blocks. Keeps everything in context, but the conversation gets busier and harder to scan.
- Second message list in the conversation area — a dedicated subagent stream always visible alongside the main one. More complex layout, but always visible.
- Dedicated SUBAGENTS panel (chosen) — consistent with the existing panel system (SKILLS/MEMORIES/SETTINGS/SESSIONS/PROJECTS). Clean separation; tab to it to see what each subagent did.
Dependencies
- None — this uses the existing LangGraph stream namespace (
subgraphs: true) and the existing TUI panel system. No new npm packages required.
Testing Strategy
- Unit tests: Verify namespace discrimination (root graph empty namespace vs subagent populated namespace), event routing to the subagent store, and that subagent tool calls are redirected.
- Integration test: Run a multi-agent turn and confirm subagent reasoning/message segments land in the SUBAGENTS panel, not the main conversation.
- Edge cases: Multiple subagents in one turn, nested subagent delegation, subagent with reasoning-only output, subagent tool calls with errors.
Security Considerations
- Credential storage: No new credentials — this is a TUI rendering change.
- Input validation: No new user input surface.
- OWASP: No new attack surface; existing URL allowlist and tool validation rules still apply.
Environment
- OS: Linux 7.0.14-17-pve
- Node.js: v25.8.1
- madz version: 1.107.0
- LLM provider: Unknown — user to confirm
OpenSpec Note
This project uses OpenSpec for feature development. If this request is approved, I will:
- Run
/opsx:proposeto generate a full proposal with specs and tasks - Iterate on the design before any code is written
- Follow the task-driven implementation workflow
Additional Context
The key interception point is callProvider in index.js (around line 208), where the _namespace from agent.stream() is currently discarded. The TUI streaming handler in src/tui/conversationArea.js (createStreamingHandler) folds all events into a single assistant bubble via streamingMsgIdRef. The capture must happen in callProvider because the namespace is lost by the time events reach the TUI.
Audit Findings (for Issue #1360)
- index.js:228 —
callProvideriteratesagent.stream(input, { streamMode: ["messages", "tools"], subgraphs: true })and destructures[_namespace, mode, payload]. The_namespaceis discarded — this is the single interception point. Withsubgraphs: true, the root graph yields an empty namespace[]and each subagent subgraph yields a populated namespace (e.g.["task:<uuid>"]).namespace.length > 0discriminates subagent activity. - index.js:228-300 — The
messagesmode handler emits{ type: "message" }and{ type: "reasoning" }; thetoolsmode handler emitson_tool_start/on_tool_end. All of these are emitted regardless of namespace, so subagent events are indistinguishable from orchestrator events. - src/tui/conversationArea.js:370 —
streamingMsgIdRef.current = messageListRef.current.addMessage("assistant", "", ...)creates the single assistant bubble that all stream events fold into viacreateStreamingHandler. - src/tui/conversationArea.js:createStreamingHandler — Handles
message,reasoning,on_chat_model_stream,on_tool_start,on_tool_end,tool,on_tool_error. All updatestreamingMsgIdRef.current. Subagent events must be routed to a separate store instead. - src/tui/panels.js:4 —
PANELSenum. Add aSUBAGENTSpanel here and register it ingetPanelOrder()for tab navigation. - src/tui/app.js — Panel routing in the render switch (
currentView === PANELS.SKILLSetc.). Add aSUBAGENTSbranch rendering the new panel. - src/tui/messageBubble.js — Reusable
MessageBubblecomponent; the subagent panel can reuse it (or a variant) to render each subagent's reasoning/message segments.
Fix Steps
- Capture the namespace — In
callProvider(index.js:228), stop discarding_namespace. Whennamespace.length > 0, emit new event types (subagent_message,subagent_reasoning,subagent_tool_start,subagent_tool_end) carrying the namespace, instead of the flatmessage/reasoning/on_tool_*events. Keep the root graph (empty namespace) emitting the existing event types. - Route subagent events in the TUI — In
createStreamingHandler(src/tui/conversationArea.js), handle the newsubagent_*event types by routing them to a dedicated subagent store (e.g. a ref keyed by namespace), not tostreamingMsgIdRef.current. - Add the SUBAGENTS panel — Add
SUBAGENTStoPANELS(src/tui/panels.js) and register it ingetPanelOrder(). Createsrc/tui/subagentsPanel.jsrendering each subagent's reasoning/message segments and tool calls, reusingMessageBubbleor a variant. - Wire the panel into App — Add a
currentView === PANELS.SUBAGENTSbranch in the App render switch (src/tui/app.js) rendering the new panel, passing the subagent store. - Write unit tests — Add
tests/unit/tui/subagentsPanel.test.jscovering namespace discrimination, event routing, and tool-call redirection. - Verify — Run
npm run test,npm run lint, andnpm run coverageto confirm everything passes.
- Dominant language
- JavaScript
- Stars
- 2
- Forks
- 0
- Avg merge
- 1h 15m
- Merged PRs (30d)
- 237
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from avoidwork/madz
-
feature
Difficulty 4/5 3-5 days Newbie friendliness 22/100
avoidwork/madz#1358 · 1 comment ·
Maintainers usually reply within 1 day
-
feature
Difficulty 5/5 Over a week Newbie friendliness 15/100
avoidwork/madz#1355 · 1 comment ·
Maintainers usually reply within 1 day
-
feature
Difficulty 5/5 Over a week Newbie friendliness 35/100
Maintainers usually reply within 1 day
Similar issues
-
factory-active factory-automatic task-bug-reproduction-success task-identify-harness-labels-done task-identify-issue-type-done
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
vercel/ai#22796 · 2 comments ·
Maintainers usually reply within 1 day
-
documentation
Difficulty 2/5 Half a day Newbie friendliness 78/100
facioquo/stock-indicators-dotnet#2300 ·
Maintainers usually reply within 1 day
-
good first issue needs-triage priority: low
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
melodic-software/claude-code-plugins#6982 · 1 comment ·
Maintainers usually reply within 1 day
-
val() setter uses the first control's type for a mixed selectionPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 Half a day Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 1-3 hours Newbie friendliness 75/100
douglascrockford/Misty#35 ·
Maintainers usually reply within 1 day