|
| 1 | +--- |
| 2 | +phase: design |
| 3 | +title: Agent Detail Command - System Design |
| 4 | +description: Technical design for the agent detail CLI command |
| 5 | +--- |
| 6 | + |
| 7 | +# System Design & Architecture |
| 8 | + |
| 9 | +## Architecture Overview |
| 10 | + |
| 11 | +The `agent detail` command follows the existing adapter pattern. It reuses `AgentManager` for agent discovery and resolution, then adds a conversation-reading layer. |
| 12 | + |
| 13 | +```mermaid |
| 14 | +graph TD |
| 15 | + CLI["CLI: agent detail --id abc"] |
| 16 | + AM["AgentManager"] |
| 17 | + CCA["ClaudeCodeAdapter"] |
| 18 | + CA["CodexAdapter"] |
| 19 | + SR["Session Reader"] |
| 20 | + SF["Session JSONL Files"] |
| 21 | +
|
| 22 | + CLI -->|resolve agent| AM |
| 23 | + AM --> CCA |
| 24 | + AM --> CA |
| 25 | + CLI -->|read conversation| SR |
| 26 | + SR -->|parse JSONL| SF |
| 27 | + CCA -->|AgentInfo + sessionFilePath| CLI |
| 28 | + CA -->|AgentInfo + sessionFilePath| CLI |
| 29 | +``` |
| 30 | + |
| 31 | +**Key insight:** The existing adapters already read session files to extract status/summary. For `detail`, we need the full conversation, which requires a second pass that reads and parses all JSONL entries (not just the last few lines). |
| 32 | + |
| 33 | +## Data Models |
| 34 | + |
| 35 | +### AgentDetail (extends AgentInfo conceptually) |
| 36 | + |
| 37 | +```typescript |
| 38 | +interface AgentDetail { |
| 39 | + sessionId: string; |
| 40 | + cwd: string; |
| 41 | + startTime: Date; |
| 42 | + status: AgentStatus; |
| 43 | + type: AgentType; |
| 44 | + name: string; |
| 45 | + slug?: string; |
| 46 | + conversation: ConversationMessage[]; |
| 47 | +} |
| 48 | + |
| 49 | +interface ConversationMessage { |
| 50 | + role: 'user' | 'assistant' | 'system'; |
| 51 | + content: string; |
| 52 | + timestamp?: string; |
| 53 | +} |
| 54 | +``` |
| 55 | + |
| 56 | +### Conversation parsing rules |
| 57 | + |
| 58 | +**Claude Code JSONL:** |
| 59 | +- Each line is `{ type, timestamp, message, ... }` |
| 60 | +- `type: "user"` → role=user, extract text from `message.content` |
| 61 | +- `type: "assistant"` → role=assistant, extract text from `message.content` |
| 62 | +- `type: "system"` → role=system |
| 63 | +- Skip metadata types: `file-history-snapshot`, `last-prompt`, `progress`, `thinking` |
| 64 | +- **Default mode:** Extract only text blocks from `message.content` arrays |
| 65 | +- **Verbose mode:** Also include tool_use blocks (name + input summary) and tool_result blocks |
| 66 | + |
| 67 | +**Codex JSONL:** |
| 68 | +- First line: `session_meta` → skip (metadata only) |
| 69 | +- Subsequent lines have `type`, `timestamp`, and `payload` with `payload.message` (plain string) and `payload.type` |
| 70 | +- Map `payload.type` to roles: `user_message` → user, `agent_message` → assistant, others → system |
| 71 | +- Default mode: extract `payload.message` strings |
| 72 | +- Verbose mode: include additional payload details if present |
| 73 | + |
| 74 | +## API Design |
| 75 | + |
| 76 | +### CLI Interface |
| 77 | + |
| 78 | +``` |
| 79 | +ai-devkit agent detail --id <name> [--json] [--full] [--tail <n>] [--verbose] |
| 80 | +``` |
| 81 | + |
| 82 | +**Options:** |
| 83 | +- `--id <name>` (required): Agent name (as shown in `agent list` output) |
| 84 | +- `--json` (optional): Output as JSON |
| 85 | +- `--full` (optional): Show entire conversation history (default: last 20 messages) |
| 86 | +- `--tail <n>` (optional): Show last N messages (default: 20) |
| 87 | +- `--verbose` (optional): Include tool call/result details in messages |
| 88 | + |
| 89 | +**Default output format (human-readable, text only, last 20 messages):** |
| 90 | +``` |
| 91 | +Agent Detail |
| 92 | +──────────────────────────────── |
| 93 | + Session ID: a6ce7023-6ac4-40b7-a8a5-dde50645bed5 |
| 94 | + CWD: ~/Code/ai-devkit |
| 95 | + Start Time: 2026-03-27 10:30:00 |
| 96 | + Status: 🟢 run |
| 97 | + Type: Claude Code |
| 98 | +
|
| 99 | +Conversation (last 20 messages) |
| 100 | +──────────────────────────────── |
| 101 | +[10:30:05] user: |
| 102 | + Fix the login bug in auth.ts |
| 103 | +
|
| 104 | +[10:30:12] assistant: |
| 105 | + I'll look at the auth.ts file... |
| 106 | + ... |
| 107 | +``` |
| 108 | + |
| 109 | +**Verbose mode adds tool details:** |
| 110 | +``` |
| 111 | +[10:30:12] assistant: |
| 112 | + I'll look at the auth.ts file... |
| 113 | + [Tool: Read] auth.ts |
| 114 | + [Tool: Edit] auth.ts (lines 15-20) |
| 115 | +``` |
| 116 | + |
| 117 | +### Internal API |
| 118 | + |
| 119 | +Add `getConversation()` as a **required** method on the `AgentAdapter` interface: |
| 120 | + |
| 121 | +```typescript |
| 122 | +interface AgentAdapter { |
| 123 | + // existing... |
| 124 | + getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[]; |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +Each adapter implements its own parsing logic (Claude: structured content blocks, Codex: `payload.message` strings) but all return the same `ConversationMessage[]` output. |
| 129 | + |
| 130 | +## Component Breakdown |
| 131 | + |
| 132 | +1. **CLI command handler** (`packages/cli/src/commands/agent.ts`): |
| 133 | + - New `detail` subcommand under the existing `agent` command |
| 134 | + - Uses `AgentManager` to list + resolve agent |
| 135 | + - Calls conversation reader |
| 136 | + - Formats and displays output |
| 137 | + |
| 138 | +2. **Conversation reader** (`packages/agent-manager/src/`): |
| 139 | + - New method or utility to parse full conversation from JSONL |
| 140 | + - Claude-specific parsing in `ClaudeCodeAdapter` |
| 141 | + - Codex-specific parsing in `CodexAdapter` |
| 142 | + - Returns `ConversationMessage[]` |
| 143 | + |
| 144 | +3. **AgentInfo extension**: |
| 145 | + - Add `sessionFilePath?: string` to `AgentInfo` — already known at detection time, avoids re-discovery |
| 146 | + |
| 147 | +## Design Decisions |
| 148 | + |
| 149 | +| Decision | Choice | Rationale | |
| 150 | +|----------|--------|-----------| |
| 151 | +| Where to add conversation parsing | Required method on `AgentAdapter` interface | Each adapter has its own JSONL format but unified output | |
| 152 | +| How to pass session file path | Add to `AgentInfo` | Already known at detection, avoids re-discovery | |
| 153 | +| Output format | Table header + message list | Consistent with `agent list` style | |
| 154 | +| Message filtering | Skip progress/thinking/metadata | Show only meaningful conversation turns | |
| 155 | +| Default message count | Last 20 | Practical default; `--full`/`--tail` for overrides | |
| 156 | +| Tool display | Text-only default, `--verbose` for tools | Keeps output clean; tools are verbose | |
| 157 | +| Identifier type | Name only | Consistent, simple; users copy from `agent list` | |
| 158 | +| Non-running agents | Not supported | Scoped to running processes only | |
| 159 | + |
| 160 | +## Non-Functional Requirements |
| 161 | + |
| 162 | +- **Performance:** Session files can be large. Read the file once, parse line-by-line. No concern for very large files since we're already reading them in `readSession()`. |
| 163 | +- **Reliability:** Graceful handling of corrupted JSONL lines (skip and continue). |
| 164 | +- **Compatibility:** Works on macOS and Linux (same as existing commands). |
0 commit comments