feat(web-shell): display nested sub-agents as a tree in the tasks panel - #6239
Conversation
Carry nested-agent lineage (parentAgentId, parentName, depth) through the daemon tasks snapshot as optional fields and render the web-shell tasks panel as a tree: children group under their parent with a ↳ marker and clamped indentation, agents whose parent left the roster are promoted to root with a "from <parent>" annotation, and the detail view gains a nesting line. The [blocking] tag and the two-step stop confirmation now apply only to provably user-blocking chains, mirroring the TUI's agent-forest semantics from #6191.
E2E test reportVerified against the local bundle on macOS with a real model (DashScope), driving a live two-level chain (parent agent foreground → child agent running
Screenshots of the tree and the detail view are embedded in the PR description (Evidence section). |
|
Thanks for the PR! Template looks good ✓ — all required sections present with a detailed test plan and before/after evidence. On direction: clearly aligned. Nested sub-agents (#6189) made multi-level trees a normal case, and the web-shell's flat list is a real UX gap vs the TUI (#6191). Bringing parity between the two surfaces is a straightforward win — no question this belongs in the project. On approach: scope feels proportional. Twelve files, each with a clear role — daemon-side lineage capture, SDK/bridge types, tree helpers for the web-shell, component rendering, i18n. The Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ — 所有必填章节齐全,测试计划详尽,附有前后对比证据。 方向:明确对齐。嵌套子代理 (#6189) 使多层树成为常态,web-shell 的平铺列表相对 TUI (#6191) 存在真实的 UX 差距。两个界面之间的对齐是明确的胜利,毫无疑问属于项目范畴。 方案:范围合理。12 个文件各有明确职责——daemon 端谱系捕获、SDK/bridge 类型、web-shell 树形辅助函数、组件渲染、国际化。 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewReviewed the full diff (12 files, 914+/11-) against the TUI counterpart at No critical issues found. The implementation is a clean, focused port:
Minor observation (not a blocker): the TUI and web-shell Test ResultsTotal: 29/29 tests passed. Build green. Typecheck green. Tmux E2ENot applicable for this PR — the feature renders in the web-shell's browser UI, not the terminal. The tree display, ↳ markers, indent, and blocking tags are all DOM-level changes visible only in the browser. The author's E2E report confirms browser-based verification with Playwright on macOS. 中文说明代码审查审阅了完整 diff(12 个文件,914+/11-),并与 TUI 对应模块 未发现关键问题。 实现干净、聚焦:
轻微观察(非阻塞):TUI 和 web-shell 的 测试结果全部 29 个测试通过。构建成功。类型检查通过。详见上方英文部分。 Tmux E2E此 PR 不适用——该功能在 web-shell 浏览器 UI 中渲染,而非终端。树形显示、↳ 标记、缩进和 blocking 标签均为仅在浏览器中可见的 DOM 级变更。作者的 E2E 报告确认了在 macOS 上使用 Playwright 进行浏览器验证。 — Qwen Code · qwen3.7-max |
|
Stepping back: this is a textbook TUI-to-web parity PR. The motivation is real (nested sub-agents are common since #6189, the flat list is genuinely confusing), the approach is the minimal set of changes needed, and the implementation is a faithful port of the TUI's battle-tested tree helpers. What I like:
The only mild reservation is the ~200 lines of duplicated logic between TUI and web-shell LGTM. Ships the feature cleanly, the tests cover it, and the code reads like the TUI version that's been working in production. ✅ 中文说明退后一步:这是一个教科书式的 TUI 到 web 对齐 PR。动机真实(自 #6189 以来嵌套子代理很常见,平铺列表确实令人困惑),方案是实现目标所需的最小变更集,实现是对 TUI 经过实战验证的树形辅助函数的忠实移植。 亮点:
唯一轻微保留是 TUI 和 web-shell 的 可以合并。功能实现干净,测试覆盖到位,代码读起来像已在生产环境运行的 TUI 版本。✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
wenshao
left a comment
There was a problem hiding this comment.
Well-structured TUI-to-web parity PR. The tree helpers are correctly bounded (cycle detection, orphan promotion, self-parent rejection), the daemon lineage fields are properly optional and additive, and the chain-aware blocking logic matches the TUI semantics. Build passes, all 724 web-shell tests pass. Minor nit: the branch bundles an unrelated stream-timeout fix — consider splitting if a tree-feature revert becomes necessary.
— qwen3.7-max via Qwen Code /review
What this PR does
With nested sub-agents, an agent can itself spawn agents, but web-shell's background-tasks panel still rendered every agent as an undifferentiated flat row. This PR carries each agent's lineage — who spawned it, that parent's display name, and its launch depth — through the daemon's task snapshot as optional fields, and teaches the panel to render the roster as a tree: children group directly beneath their parent with an
↳marker and indentation (clamped for deep trees), and a nested agent whose parent has already left the roster is promoted back to root level with a "from <parent>" annotation instead of dangling. The detail view gains a nesting line showing the agent's absolute launch level and parent. The[blocking]tag and the two-step stop confirmation also become lineage-aware: they now apply only when cancelling the agent would actually end the user's turn (the whole ancestor chain is foreground), instead of firing for every foreground agent — a nested worker awaited by a background parent now stops on the first press. Public daemon protocol docs are updated with the new payload shape.This mirrors the tree UX the TUI gained in #6191, sharing its semantics: the ordering pass preserves the panel's existing sort for roots while keeping trees contiguous, missing parents are treated as a normal case rather than an error, and unprovable chains (departed ancestor, cycles) fail toward the milder rendering.
Why it's needed
The nested sub-agents feature (#6189) makes multi-level agent trees a normal sight in the tasks roster. On the web surface, a user watching such a run today sees several identical-looking rows with no way to tell which agent spawned which, and the blocking tag over-warns — it marks agents whose cancellation would not end the user's turn. The TUI already solved both in #6191; this brings the web surface to parity so the same session reads the same way in both clients.
Reviewer Test Plan
How to verify
Start the daemon with the web UI enabled and open web-shell in a browser. In a session, ask the model to spawn a sub-agent whose task is to spawn another sub-agent that runs a long shell command (e.g.
sleep 45), so a two-level chain is alive for a while. Open the background-tasks panel with/taskswhile both agents run and confirm: the child row renders directly beneath its parent with an↳marker and indentation, while the parent row has neither; opening the child's detail shows a nesting line reading "Level 2 · from <parent type>". For the blocking semantics, note both rows carry[blocking]in this foreground chain (correct — cancelling either ends the turn), and pressingxon either asks for confirmation; if you instead launch the parent as a background agent, its foreground child shows no[blocking]tag and stops on a single press. Older payloads without the new fields (or a daemon predating this change) degrade to the previous flat list. The daemon side can be checked directly: the session tasks endpoint now returns the three optional lineage fields on nested agent entries and omits them on top-level ones.Evidence (Before & After)
Before: both agent rows render as an identical flat list (no marker, no indent, no lineage in detail).
After:
Tested on
Environment (optional)
Local bundle (
npm run build && npm run bundle,node dist/cli.js serve --web), real model via DashScope; browser E2E driven with Playwright.Risk & Scope
Linked Issues
Builds on the nested sub-agents feature #6189 (merged); web counterpart of the TUI tree in #6191.
中文说明
本 PR 做了什么
有了嵌套子代理后,代理自身也可以派生代理,但 web-shell 的后台任务面板仍将每个代理渲染为无差别的平铺行。本 PR 将每个代理的谱系信息——由谁派生、父代理的显示名称、启动深度——作为可选字段通过 daemon 的任务快照传递,并让面板以树形渲染任务列表:子代理直接分组在父代理下方,带
↳标记和缩进(深层树会收敛缩进),父代理已离开列表的嵌套代理会被提升回根层级并附加"来自 <父代理>"注释,而不是悬空缩进。详情视图新增嵌套信息行,显示代理的绝对启动层级和父代理。[blocking]标签和两步停止确认也变为谱系感知:仅当取消该代理确实会结束用户当前轮次(整条祖先链均为前台)时才生效,而不是对每个前台代理都触发——由后台父代理等待的嵌套工作代理现在一次按键即可停止。同时更新了公开的 daemon 协议文档。这与 TUI 在 #6191 中获得的树形 UX 保持一致,共享相同语义:排序后处理在保持面板既有根节点排序的同时让树保持连续,父代理缺失被视为正常情况而非错误,无法证明的链(祖先已离开、循环引用)以更温和的方式渲染。
为什么需要
嵌套子代理特性(#6189)使多层代理树成为任务列表中的常见景象。在 web 界面上,用户观察这样的运行时只能看到几行看起来完全相同的条目,无法分辨谁派生了谁,且 blocking 标签过度警告——它标记了那些取消后并不会结束用户轮次的代理。TUI 已在 #6191 中解决了这两个问题;本 PR 让 web 界面达到同等水平,使同一会话在两个客户端中呈现一致。
审阅者测试计划
如何验证
启动带 web UI 的 daemon 并在浏览器中打开 web-shell。在会话中让模型派生一个子代理,其任务是再派生一个运行长时间 shell 命令(如
sleep 45)的子代理,使两层代理链保持存活。在两个代理运行时用/tasks打开后台任务面板并确认:子代理行直接渲染在父代理下方,带↳标记和缩进,父代理行则没有;打开子代理详情可见嵌套信息行"第 2 层 · 来自 <父代理类型>"。关于 blocking 语义:在这条前台链中两行都带[blocking](正确——取消任一都会结束轮次),按x会要求确认;若改为以后台方式启动父代理,其前台子代理不显示[blocking]标签,且一次按键即可停止。不含新字段的旧载荷(或早于此变更的 daemon)会退化为之前的平铺列表。daemon 侧可直接检查:会话任务端点现在在嵌套代理条目上返回三个可选谱系字段,顶层代理则省略。证据(前后对比)
之前:两个代理行渲染为完全相同的平铺列表(无标记、无缩进、详情无谱系)。之后:见上方英文部分附图——面板中带
↳标记和缩进的树,以及子代理详情中的"嵌套:第 2 层 · 来自 general-purpose"信息行。风险与范围
关联 Issue
基于已合并的嵌套子代理特性 #6189;是 TUI 树形显示 #6191 的 web 对应实现。