From fd7712f5c12ed30d70a93a99c8911bddfcf662df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?m=C3=BCde?= Date: Mon, 18 May 2026 11:25:54 +0200 Subject: [PATCH] agent terminal: pretty-render task_started / task_notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit claude's Task tool spawns subagents whose progress lands as stream-json events with subtype=task_started or task_notification — previously fell through to the .sys catch-all and rendered as a raw json dump that wrapped per char in the live pane. now matched by subtype before the catch-all: - task_started → cyan tool-use row, ⌁ glyph, first 8 chars of task_id, description, and optional [task_type] - task_notification → row styled by status: completed → turn-end-ok (green ✓), failed → turn-end-fail (red ✗), other → tool-result (muted ◌). output_file rendered inline if present so the operator can trace where the body landed. matching on `v.subtype` rather than a particular `v.type` so the renderer survives claude wrapping these under different top-level type fields across versions. --- hive-ag3nt/assets/app.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/hive-ag3nt/assets/app.js b/hive-ag3nt/assets/app.js index 34b4ca5..f5a2afd 100644 --- a/hive-ag3nt/assets/app.js +++ b/hive-ag3nt/assets/app.js @@ -742,12 +742,45 @@ api.details('tool-result-block', summary, txt); } } + // Pretty-render claude's background-task subagent events + // (`task_started`, `task_notification`). They share the same + // task_id so the operator can correlate start ↔ result; render + // each as a peer of tool_use / tool_result with a `⌁` glyph to + // mark "this happened in a subagent" rather than the main + // session. + function renderTaskEvent(v, api) { + const id = (v.task_id || '').slice(0, 8); + const kind = v.task_type ? ` [${v.task_type}]` : ''; + const desc = v.description || v.summary || '(no description)'; + if (v.subtype === 'task_started') { + api.row('tool-use', `⌁ task ${id} started · ${desc}${kind}`); + return true; + } + if (v.subtype === 'task_notification') { + const status = v.status || 'unknown'; + const glyph = status === 'completed' ? '✓' : status === 'failed' ? '✗' : '◌'; + const cls = status === 'completed' ? 'turn-end-ok' + : status === 'failed' ? 'turn-end-fail' + : 'tool-result'; + const out = v.output_file ? ` · → ${v.output_file}` : ''; + api.row(cls, `⌁ task ${id} ${glyph} ${status} · ${desc}${out}`); + return true; + } + return false; + } function renderStream(v, api) { // Drop session init, claude's result line, rate-limit — noise. // TurnEnd communicates pass/fail; session init isn't actionable. if (v.type === 'system' && v.subtype === 'init') return; if (v.type === 'rate_limit_event') return; if (v.type === 'result') return; + // Background-task subagent events (claude's `Task` tool spawns + // a separate session whose progress lands here as `task_*` + // subtypes). Match by subtype so we don't have to track which + // top-level `type` claude wraps them under across versions. + if (v.subtype === 'task_started' || v.subtype === 'task_notification') { + if (renderTaskEvent(v, api)) return; + } if (v.type === 'assistant' && v.message && v.message.content) { for (const c of v.message.content) { if (c.type === 'text' && c.text && c.text.trim()) api.row('text', c.text);