diff --git a/rfcs/001-multi-agent-visual-indicator.md b/rfcs/001-multi-agent-visual-indicator.md new file mode 100644 index 0000000..fe7ad90 --- /dev/null +++ b/rfcs/001-multi-agent-visual-indicator.md @@ -0,0 +1,336 @@ +# RFC-001: Multi-Agent Visual Indicator for Active Sessions + +**Status**: Draft +**Author**: Feature Request (via Community) +**Created**: 2026-09-08 +**Related Issue**: #817 + +## Summary + +Add a persistent visual indicator in the Command Code CLI that shows users when multiple agents are running concurrently within a session. This provides real-time awareness of active subagent work — similar to implementations in T3 Code (by Theo) and Codex. + +## Motivation + +When Command Code delegates work to multiple subagents via the `agent` tool, users currently have no visibility into: + +- Which agents are running +- How many are active +- What each agent is working on +- Their current status (running, completed, failed) + +This makes the main session appear idle while invisible work happens in the background, and makes debugging failed agents difficult. + +## Design + +### Component Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Session UI (Ink/React) │ +├─────────────────────────────────────────────────────────────┤ +│ ┌───────────────────────────────────────────────────────┐ │ +│ │ AgentStatusBar (new) │ │ +│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ +│ │ │AgentBadge│ │AgentBadge│ │AgentBadge│ +2 more... │ │ +│ │ └─────────┘ └─────────┘ └─────────┘ │ │ +│ └───────────────────────────────────────────────────────┘ │ +│ │ +│ ┌───────────────────────────────────────────────────────┐ │ +│ │ Main Conversation Area │ │ +│ │ │ │ +│ └───────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### TypeScript Interfaces + +```typescript +// ─── Agent Status Types ─────────────────────────────────────────────── + +type AgentStatus = 'queued' | 'running' | 'completed' | 'failed' | 'killed'; + +interface AgentIdentity { + /** Unique agent identifier (from agent_id returned by agent tool) */ + id: string; + /** Agent name/type: 'general' | 'explore' | 'plan' | custom name */ + name: string; + /** Optional custom color for the badge (hex or ansi color) */ + color?: string; +} + +interface AgentTask { + /** Short description of what the agent is doing */ + description: string; + /** Timestamp when the agent started */ + startedAt: number; + /** Optional: estimated completion (for progress indication) */ + progress?: number; // 0-100 +} + +interface ActiveAgent extends AgentIdentity, AgentTask { + status: AgentStatus; + /** Error message if status is 'failed' */ + error?: string; + /** Token usage stats (populated on completion) */ + usage?: { + inputTokens: number; + outputTokens: number; + cost: number; + }; +} + +// ─── AgentStatusBar Configuration ───────────────────────────────────── + +interface AgentIndicatorConfig { + /** Whether the indicator is visible */ + enabled: boolean; + /** Display style */ + style: 'compact' | 'detailed' | 'minimal'; + /** Maximum number of badges to show before collapsing */ + maxVisible: number; + /** Show token usage on completion */ + showUsage: boolean; + /** Animation enabled for running state */ + animate: boolean; +} + +// ─── Default Configuration ──────────────────────────────────────────── + +const DEFAULT_CONFIG: AgentIndicatorConfig = { + enabled: true, + style: 'compact', + maxVisible: 5, + showUsage: true, + animate: true, +}; +``` + +### Status Badge Component + +```typescript +// ─── AgentBadge Component ────────────────────────────────────────────── + +import React from 'react'; +import { Box, Text } from 'ink'; + +interface AgentBadgeProps { + agent: ActiveAgent; + compact?: boolean; +} + +const STATUS_ICONS: Record = { + queued: '◌', + running: '●', // Spinner in practice + completed: '✓', + failed: '✗', + killed: '⊘', +}; + +const STATUS_COLORS: Record = { + queued: 'gray', + running: 'cyan', + completed: 'green', + failed: 'red', + killed: 'yellow', +}; + +export function AgentBadge({ agent, compact }: AgentBadgeProps) { + const icon = STATUS_ICONS[agent.status]; + const color = STATUS_COLORS[agent.status]; + + if (compact) { + return ( + + {icon} + {agent.name} + + ); + } + + return ( + + {icon} + {agent.name} + — {truncate(agent.description, 40)} + + ); +} +``` + +### Status Bar Component + +```typescript +// ─── AgentStatusBar Component ────────────────────────────────────────── + +interface AgentStatusBarProps { + agents: ActiveAgent[]; + config: AgentIndicatorConfig; +} + +export function AgentStatusBar({ agents, config }: AgentStatusBarProps) { + if (!config.enabled || agents.length === 0) return null; + + const running = agents.filter(a => a.status === 'running'); + const completed = agents.filter(a => a.status === 'completed'); + const failed = agents.filter(a => a.status === 'failed'); + const queued = agents.filter(a => a.status === 'queued'); + + const visibleAgents = agents.slice(0, config.maxVisible); + const remaining = agents.length - config.maxVisible; + + return ( + + {/* Summary line */} + + 🤖 + {agents.length} agent{agents.length !== 1 ? 's' : ''} + {running.length > 0 && ● {running.length} running } + {completed.length > 0 && ✓ {completed.length} done } + {failed.length > 0 && ✗ {failed.length} failed } + {queued.length > 0 && ◌ {queued.length} queued} + + + {/* Detailed badges (if style is 'detailed') */} + {config.style === 'detailed' && ( + + {visibleAgents.map(agent => ( + + ))} + {remaining > 0 && +{remaining} more} + + )} + + ); +} +``` + +### Session Integration + +```typescript +// ─── Integration with Session State ──────────────────────────────────── + +// In the main session state store: +interface SessionState { + // ... existing state + activeAgents: Map; +} + +// When agent tool is called: +function handleAgentStart(params: AgentStartParams): string { + const agentId = generateId(); + const activeAgent: ActiveAgent = { + id: agentId, + name: params.agentName, + description: params.taskDescription, + status: 'running', + startedAt: Date.now(), + }; + sessionState.activeAgents.set(agentId, activeAgent); + renderAgentStatusBar(); // Trigger re-render + return agentId; +} + +// When agent completes (via agent_output): +function handleAgentComplete(agentId: string, result: AgentResult): void { + const agent = sessionState.activeAgents.get(agentId); + if (agent) { + agent.status = result.success ? 'completed' : 'failed'; + agent.error = result.error; + agent.usage = result.usage; + renderAgentStatusBar(); + } +} +``` + +### Settings Schema + +```json +{ + "agentIndicator": { + "enabled": true, + "style": "compact", + "maxVisible": 5, + "showUsage": true, + "animate": true + } +} +``` + +## Visual Mockups + +### Compact Style (Default) +``` + 🤖 3 agents ● 2 running ✓ 1 done + + ┌────────────────────────────────────────────────────────┐ + │ User: Can you refactor the auth module? │ + │ Assistant: I'll delegate this to multiple agents... │ + └────────────────────────────────────────────────────────┘ +``` + +### Detailed Style +``` + ┌────────────────────────────────────────────────────────┐ + │ 🤖 4 agents ● 2 running ✓ 1 done ✗ 1 failed │ + │ │ + │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ + │ │● Explore │ │● General │ │✓ Plan │ │✗ Tester │ │ + │ │ searching│ │ refactor │ │ design │ │ timeout │ │ + │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ + └────────────────────────────────────────────────────────┘ +``` + +### Minimal Style +``` + 🤖 ●●●○ (running dots + queued) +``` + +## Behavior + +### Lifecycle +1. **Agent Started** → Badge appears with spinner +2. **Agent Running** → Spinner animates, optional progress % +3. **Agent Completed** → Badge turns green with ✓, shows token usage +4. **Agent Failed** → Badge turns red with ✗, shows error on hover/expand +5. **Session Ends** → All agents cleared from display + +### Auto-Cleanup +- Completed/failed badges fade after 30s (configurable) +- Or persist until next user message +- User can manually dismiss with keypress + +### Interaction +- Press `Tab` to cycle through active agents +- Press `A` to toggle agent panel (detailed view) +- Click/tap badge to see full output (in supported terminals) + +## Migration Path + +### Phase 1: Passive Indicator +- Show agent status without interaction +- Read-only display, no controls +- Behind feature flag `agentIndicator.enabled` + +### Phase 2: Interactive Controls +- Expand/collapse agent details +- Kill running agents from UI +- View agent output inline + +### Phase 3: Advanced Features +- Agent timeline visualization +- Performance metrics per agent +- Agent output streaming in real-time + +## Open Questions + +1. **Position**: Status bar at top vs bottom of terminal? +2. **Persistence**: Should completed agents persist or auto-dismiss? +3. **Streaming**: Should we stream agent output live or only on completion? +4. **Theming**: Should badge colors follow terminal theme or be fixed? +5. **Mobile**: How does this render in mobile/remote terminal setups? + +## References + +- T3 Code agent indicator (Theo) +- Codex multi-agent UI +- GitHub issue #817