agent-dashboard: tool call durations, formatted inputs, subagent progress cards #3

Merged
muon merged 5 commits from muon/agent-subagent-progress into main 2026-10-15 14:01:16 +00:00 AGit
Owner

Summary

Tool calls now show how long they've been running, with a clock that ticks while they run and stays on the final duration afterwards. Subagents (opencode's task tool) get their own card showing the brief, live progress and the result. Tool and permission inputs are shown as labelled fields instead of a JSON dump. This follows what Claude Code, T3 Code and Zed do. I checked the opencode ACP source for v1.19.0 (installed) and for main (v2).

 <TranscriptEntry>
-  <ToolItem>  title · status · JSON input
+  <ToolItem>  title · ⏱ 12s · status · labelled input ($ cmd + description)
+  <SubagentItem> (task tool)
+    [explore] Find the bug · 3/5 tools · ⏱ 1m 05s · running
+    now: grep foo  4.2s
+    ▸ Prompt (Markdown) · Progress (child tools, each with a duration) · Latest message · Result (<task_result> as Markdown)

Server (sessions.ts):

tool_call / tool_call_update
  startedAt / endedAt stamped live (none in a session/load replay; reset() stashes them and the replay restores them)
  rawInput {description, prompt, subagent_type} -> item.subagent
  rawOutput.metadata.sessionId -> links the child session
initialize: _meta {"opencode/child-session-updates": true}
_opencode/session/child_update | session/update with _meta["opencode/child-session"]
  -> folded into the task card (child tools, message, status)
turn end / stop / exit -> clocks that are still open get stopped

Limitation: opencode 1.19 doesn't send child-session events over ACP. On 1.19 the subagent card only shows the brief, the clock and the result. Live child progress needs opencode ≥ 2, which sends it once the client advertises the capability above. Details are in docs/AGENT_DASHBOARD.md.

Evidence

  • Before: no timings and no subagent data on tool items; task calls showed as a plain tool row with a JSON input.
    After: bun test passes 29 tests, 0 failures; tsc --noEmit and bun build.ts succeed. New tests in test/stats.test.ts:
    tool sleeps 300ms -> endedAt - startedAt >= 250; same times after stop + resume (replay)
    fake task step with child_update notifications ->
      subagent {agent: explore, description, prompt, childSessionId, text: "found it", status: completed}
      tools [["grep foo","completed"],["read a.ts","completed"]], grep's duration >= 80ms
      and the child's calls are not shown as top-level tool rows
    

I didn't take screenshots: this environment has no browser.

Merge Danger

Door: two-way

The new fields are optional, so old transcripts load fine. They just show no durations.

Blast Radius: dashboard-transcript

Only the session transcript rendering and the ACP update handling change. The only change on the opencode side is one more _meta flag in initialize, which 1.19 ignores.

## Summary Tool calls now show how long they've been running, with a clock that ticks while they run and stays on the final duration afterwards. Subagents (opencode's `task` tool) get their own card showing the brief, live progress and the result. Tool and permission inputs are shown as labelled fields instead of a JSON dump. This follows what Claude Code, T3 Code and Zed do. I checked the opencode ACP source for v1.19.0 (installed) and for main (v2). ```diff <TranscriptEntry> - <ToolItem> title · status · JSON input + <ToolItem> title · ⏱ 12s · status · labelled input ($ cmd + description) + <SubagentItem> (task tool) + [explore] Find the bug · 3/5 tools · ⏱ 1m 05s · running + now: grep foo 4.2s + ▸ Prompt (Markdown) · Progress (child tools, each with a duration) · Latest message · Result (<task_result> as Markdown) ``` Server (`sessions.ts`): ```text tool_call / tool_call_update startedAt / endedAt stamped live (none in a session/load replay; reset() stashes them and the replay restores them) rawInput {description, prompt, subagent_type} -> item.subagent rawOutput.metadata.sessionId -> links the child session initialize: _meta {"opencode/child-session-updates": true} _opencode/session/child_update | session/update with _meta["opencode/child-session"] -> folded into the task card (child tools, message, status) turn end / stop / exit -> clocks that are still open get stopped ``` Limitation: opencode 1.19 doesn't send child-session events over ACP. On 1.19 the subagent card only shows the brief, the clock and the result. Live child progress needs opencode ≥ 2, which sends it once the client advertises the capability above. Details are in `docs/AGENT_DASHBOARD.md`. ## Evidence - **Before:** no timings and no subagent data on tool items; `task` calls showed as a plain tool row with a JSON input. **After:** `bun test` passes 29 tests, 0 failures; `tsc --noEmit` and `bun build.ts` succeed. New tests in `test/stats.test.ts`: ```text tool sleeps 300ms -> endedAt - startedAt >= 250; same times after stop + resume (replay) fake task step with child_update notifications -> subagent {agent: explore, description, prompt, childSessionId, text: "found it", status: completed} tools [["grep foo","completed"],["read a.ts","completed"]], grep's duration >= 80ms and the child's calls are not shown as top-level tool rows ``` I didn't take screenshots: this environment has no browser. ## Merge Danger **Door:** two-way The new fields are optional, so old transcripts load fine. They just show no durations. **Blast Radius:** dashboard-transcript Only the session transcript rendering and the ACP update handling change. The only change on the opencode side is one more `_meta` flag in `initialize`, which 1.19 ignores.
muon merged commit 05de543a52 into main 2026-10-15 14:01:16 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
muon/flake!3
No description provided.