feat(coc): drill into sub-agents from the Agents canvas (cascade dropdown + in-place read-only detail)#329
Merged
Merged
Conversation
Sub-agents are captured flat in the main conversation, linked by `parentToolCallId`. buildAgentRunTreeFromTurns used to attach every Task call directly to the orchestrator (a 2-level tree); it now nests each Task under the sub-agent that spawned it, giving the tree real depth (L0 → L1 → L2 → …). A Task whose parent isn't another captured Task — or whose parent chain is cyclic — falls back to the root, so a malformed chain can't recurse infinitely. Siblings at every level stay ordered by start time, and the root status now reflects any running/queued descendant at any depth. This also improves the existing canvas + inspector, which already render nested children. The generic tool-call readers (collectToolCalls, rawToolName, rawArgs, nonEmptyArgs, asRecord, asString, parseTime, firstLine) move to a shared agentToolCalls.ts so the upcoming per-sub-agent reconstructor reuses them; the dedup merge now also preserves parentToolCallId across snapshots. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eep-link Three pure helpers powering the upcoming cascading dropdown + in-place sub-agent detail view: - agentLevels.ts: flattenAgentLevels(root) projects the tree into contiguous depth levels (L0…Ln) for the dropdown; findAgentNode / pathToAgent resolve a node and its breadcrumb chain by id. - buildSubAgentTurns.ts: reconstructs one sub-agent's conversation as synthetic [userTurn(prompt), assistantTurn(steps + result)] by collecting its full descendant subtree via parentToolCallId. It keeps each step's parentToolCallId so the existing thread renderer re-roots the subtree (direct steps flat, nested sub-agents as Task cards) — identical tool-call rendering, no extra fetch. - chatAgentHash.ts: read/applyAgentToHash for an `agent=<id>` deep-link param that coexists with `view=agents` (mirrors chatViewHash.ts). All exported from the agent-canvas barrel. Covered by 29 new unit tests (levels/find/path, subtree filtering incl. nested-task + cycle + background + empty cases, and hash round-trips incl. coexistence with the view param). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two presentational components for the sub-agent drill-in: - AgentCascadeMenu: a cascading dropdown for the chat top bar. Left pane lists the tree's depth levels (L0…Ln), right pane lists that level's agents; picking an agent opens it, picking the orchestrator (L0) returns to the thread. Outside- click + Escape close. Styled with the app's utility palette (it renders in the header, outside the .agent-canvas scope). Renders nothing when only L0 exists. - SubAgentDetailView: a breadcrumb (orchestrator → … → agent, ancestors clickable) above the SAME ConversationArea the main thread uses, fed the synthetic turns from buildSubAgentTurns — identical tool-call rendering, no follow-up input. The task status is shaped to the sub-agent so a completed sub-agent doesn't show a live tail while the orchestrator still runs. Both exported from the barrel. 15 new component tests (cascade open/hover/select/ escape/aria, breadcrumb navigation, turn/status prop shaping); ConversationArea is stubbed in the detail-view test to keep it isolated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the cascading "Agents" dropdown and the in-place read-only sub-agent detail view: - selectedAgentId state, seeded from `?agent=<id>` and mirrored back into the hash alongside `?view=agents` (single replaceState composing both helpers). - A three-way render precedence in the conversation column: sub-agent detail → agents canvas → thread. The detail branch renders SubAgentDetailView with the synthetic turns from buildSubAgentTurns and the breadcrumb path. - The cascade menu mounts beside the Thread/Agents toggle (same hasSubAgents + loading/pending/variant gate). Selecting an agent forces the agents context so closing returns to the canvas; selecting the orchestrator (null) returns to it. - Read-only: every FollowUpInputArea/no-session-notice guard now also requires !showSubAgentDetail, and the thread-only Ralph/Implement cards are guarded too. - A stale/invalid `?agent=<id>` clears itself (parity with effectiveView's "can't strand on a deep-link" guarantee); selection resets on chat switch. Covered by a static-analysis wiring test (mirrors the sibling ChatDetail tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tail view Updates the Agents-view section of dashboard-spa.md: the run tree now nests via parentToolCallId (real L0→L1→L2 depth, replacing the "arbitrary depth later" note); the shared tool-call readers live in agentToolCalls.ts; and a new paragraph covers the cascading dropdown (AgentCascadeMenu / flattenAgentLevels), the in-place read-only sub-agent detail (SubAgentDetailView / buildSubAgentTurns re-rooting the subtree through the same ConversationArea renderer), the ?agent=<id> deep-link, and the content-prose attribution limitation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two node-card glitches on the Agents canvas (seen on a stuck general-purpose sub-agent): - A long role like "general-purpose" wrapped to a second line and overflowed the fixed-height card, spilling below the status bar. The role · time row now stays on one line: the role ellipsis-truncates while the separator and elapsed time keep their size. - A long-running/stuck agent rendered an unbounded minute count (e.g. 7353:17). Durations now collapse to compact units — `m:ss` under an hour, `Hh Mm` past an hour, `Dd Hh` past a day (so 7353:17 → "5d 2h"). The formatter is shared by the canvas node and the inspector (extracted to format.ts; sub-hour output is unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…y detail The pre-existing static-analysis test pins the exact FollowUpInputArea / no-session-notice guard expressions. Wiring the sub-agent drill-in added `&& !showSubAgentDetail` to those guards (the composer is suppressed in the read-only detail view), so the pinned substrings shifted. Update the two assertions to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Drill into an individual sub-agent from the chat's Agents canvas and view what it actually did — rendered identically to the main thread, read-only.
What's new
ConversationArea/ConversationTurnBubbleas the main thread (identical tool cards; nested sub-agents appear as drill-in Task cards), with a breadcrumb (Orchestrator → … → agent) and no follow-up input.?agent=<id>alongside?view=agents; a stale/invalid id clears itself and resets on chat switch.Why it works with no backend change
Every tool call — not just
Task— is captured flat in the main conversation and linked byparentToolCallId. So:parentToolCallId(real depth; also improves the existing canvas + inspector), andparentToolCallIdand only nests when the parent is in the same turn, so feeding it a synthetic per-sub-agent turn (with the steps'parentToolCallIdkept) re-roots the subtree and renders it identically.Verified against real data: on a live L0→L3 chat the dropdown shows the right levels/agents and a sub-agent reconstructs its full 67-step subtree.
Also included
general-purpose) no longer wraps/overflows the card (it ellipsis-truncates while the elapsed time stays), and a long-running/stuck agent's timer now formats compactly (m:ss→Hh Mm→Dd Hh, so7353:17→5d 2h). Shared by the canvas node and inspector.Commits
parentToolCallId(+ extract sharedagentToolCalls.ts).flattenAgentLevels,buildSubAgentTurns,chatAgentHash.AgentCascadeMenu+ read-onlySubAgentDetailView.ChatDetail(state, hash sync, 3-way render precedence, composer suppression).dashboard-spa.md).Testing
~50 new tests across the layers (tree nesting incl. cycle/orphan guards, level flattening, sub-agent turn reconstruction, hash round-trips, the cascade menu, the read-only detail view, duration formatting) plus a static-analysis wiring test for
ChatDetail(too heavyweight to render). Build clean, 0 lint errors, full affected suite green (146 tests).Reviewer notes
ChatDetailis verified via source-analysis tests (the repo's existing convention for it);ConversationAreais stubbed in the detail-view test to keep it isolated.content-type timeline items carry noparentToolCallId, so a sub-agent's prose isn't attributed — its Taskresultshows as the closing message instead.agentToolCalls.tshere was extracted from a local mixed commit (it had been swept into an unrelatedprompt-builderrefactor); that refactor is intentionally not part of this PR.🤖 Generated with Claude Code