Qwen Code Web Shell 监控任务详情面板设计:零额外轮询的 Monitor 右侧面板实现
2026/9/13 18:31:52 网站建设 项目流程

Qwen Code Web Shell 监控任务详情面板设计:零额外轮询的 Monitor 右侧面板实现

【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code

本文基于 Qwen Code(qwen-code)仓库中的设计文档 web-shell-monitor-details.md,讲解 Web Shell 如何让用户从右侧背景任务列表中直接打开 monitor(监控)任务详情:为什么 monitor 工具调用被视为"背景任务活动"、session_monitor_tool_correlation能力标签如何门控"从转录点击工具跳到面板"的行为、以及右侧面板如何完全复用既有的背景任务轮询来保持快照新鲜而不再增加一条独立的轮询链路。读完本文,你可以理解该功能的完整设计约定、生命周期规则,以及 App.tsx 中对应的关键实现(面板 tab 的键控与快照合并、工具调用到 monitor 任务的解析、轮询唤醒与 tab 同步)。

目标:不新增轮询循环也能实时查看 monitor 详情

设计文档的 Goal 部分给出了一个很明确的产品诉求:

Let users open a monitor task from the background-tasks list in the Web Shell right panel and keep its details current without adding another polling loop.

即:让用户能够从 Web Shell 右侧面板的背景任务列表中打开一个 monitor 任务,并让其详情保持最新——同时不引入任何新的轮询循环

Web Shell 中已经存在一套背景任务(background tasks)状态条与轮询机制:状态栏(status bar)上的任务入口是 monitor 的发现面,当会话中出现 monitor 工具调用时,既有的任务轮询会被启动。这份设计的核心思路就是"寄生"在这条已有的轮询链路上:右侧面板不自己发请求、不自己定时器,而是订阅/复用既有轮询拿到的任务快照来更新已打开的 monitor tab。

设计约定逐条解析

下面按设计文档 Design 章节的条目展开,每一条都尽量对应到仓库中的实际实现。

1. 状态栏任务入口是 monitor 的发现面

文档约定"Keep the status-bar task entry as the monitor discovery surface"。也就是说,用户看到"这里有一个 monitor 在跑"的入口依然是状态栏上的任务徽标,右侧详情面板是它的延伸视图,而不是又一个并列的发现渠道。

2. monitor 工具调用被当作背景任务活动

文档约定"Treat a monitor tool call as background-task activity so the existing task polling starts when a monitor is created"。这意味着 monitor 的创建动作会触发既有的任务轮询启动逻辑——一旦 monitor 出现,轮询就开始,后续 monitor 快照就天然有来源,右侧面板才有数据可用。

3. 从背景任务列表打开 monitor:关对话框、开专属右侧 tab

文档约定:在嵌入式背景任务列表中打开某个 monitor 时,会关闭该对话框,并打开一个专属的右侧面板 tab。该行为对应的实现是 openMonitorPanel:它构造一个kind: 'monitor'ArtifactPanelTab,把 tab 写入右侧 Artifact Panel 的 tab 列表、设为活动 tab,并在面板首次打开时给一个默认宽度。

4. 从转录点击 monitor 工具:按 tool-call ID 解析任务

文档约定:在转录(transcript)中点击一个 monitor 工具时,客户端通过发起该 monitor 的 tool-call ID去解析出对应的任务,并打开同一个右侧 tab;对于较旧的历史快照,则回退到工具结果中内嵌的 monitor ID。

这一链路的客户端入口是 openMonitorPanelFromTool,流程如下(可直接从源码读出):

  1. 捕获会话所有权(sessionOwnerGuard.capture()),防止会话切换导致把结果写错会话;
  2. 调用sessionActions.getTasks()拉取当前任务快照,并校验会话未变(snapshot.sessionId !== sessionId则放弃);
  3. findMonitorTaskForTool(snapshot.tasks, tool)在任务列表中解析出对应的 monitor 任务,解析不到就返回false
  4. 解析成功后递增backgroundTasksRefreshTrigger(即唤醒轮询,见下文第 7 条),再调用openMonitorPanel(task)打开面板。

点击入口本身通过 MonitorDetailsContext 注入:MonitorDetailsProvider向子树暴露一个onOpen(tool: ACPToolCall) => Promise<boolean>,ChatPane.tsx 将其传给 Provider,ToolGroup.tsx 中的openMonitorDetailsOnce则负责工具行的点击处理。

5. 能力门控:session_monitor_tool_correlation

文档约定"Gate transcript-to-panel navigation on thesession_monitor_tool_correlationcapability. Older daemons retain the original inline tool expansion.",即"从转录跳到面板"这一新行为必须受 daemon 能力标签门控;不支持该标签的旧 daemon 保持原来的行内工具展开(inline tool expansion)行为不变。

该标签在两侧都有对应实现:

  • daemon 侧:capabilities.ts 的SERVE_CAPABILITY_REGISTRY中注册了session_monitor_tool_correlation: { since: 'v1' }。它是一个基线标签(不在CONDITIONAL_SERVE_FEATURES的谓词表中),因此一旦 daemon 实现即无条件广告。
  • 客户端侧:constants/sessions.ts 定义了SESSION_MONITOR_TOOL_CORRELATION_FEATURE = 'session_monitor_tool_correlation',供 Web Shell 各表面统一做能力预检,避免在旧 daemon 上触发会失败的跳转。

测试用例也在能力开关上做了覆盖:App.test.tsx 中多处通过mockConnection.capabilities.features = ['session_monitor_tool_correlation']构造"新 daemon"场景,并验证了MonitorDetailsProvideronOpen(测试中取testState.latestMonitorDetailsOnOpen)能正确解析并打开面板。

6. 解析失败时回退到行内展开,而不是报错

文档约定:如果一个支持该能力的 daemon 也无法把被点击的工具解析到某个 monitor 快照(例如任务已被清理),客户端应回退到行内展开行为,而不是把错误抛给用户。从 openMonitorPanelFromTool 的实现看,这个约定体现得很直接:会话校验失败、findMonitorTaskForTool返回undefined、或getTasks()抛错,三条路径全部return false——调用方拿到false后就走既有的行内展开逻辑,全程无用户可见错误。

7. 打开 monitor 时唤醒既有轮询,保证恢复会话"保持活跃"

文档约定"Wake the existing background-task poller when a monitor opens so resumed sessions stay live even when the originating tool call is outside the loaded transcript window"。场景是:用户恢复(resume)一个旧会话,发起 monitor 的那次工具调用可能已经不在当前加载的转录窗口内,因此"工具调用出现时启动轮询"这条路径可能没有机会触发;此时用户手动打开 monitor 面板就必须主动把既有轮询唤醒一次。实现上,openMonitorPanelFromTool在打开面板前执行setBackgroundTasksRefreshTrigger((value) => value + 1),正是这个"轻推"动作。

8. 右侧面板不独立拉取:从既有轮询同步 tab

文档约定:"Store the latest monitor snapshot in the tab. While the existing background-task hook is polling, synchronize matching open tabs from those snapshots. The right panel does not fetch or poll independently."

即:最新 monitor 快照保存在 tab 自身上;当既有背景任务 hook 在轮询时,把匹配的已打开 tab 从轮询拿到的快照中同步过去;右侧面板自己不发请求、不跑定时器。

对应实现在 App.tsx 的同步 effect:它监听backgroundTasks(由既有轮询产出,且只保留 live 状态、不掺入项目保留的历史 workflow),把其中kind === 'monitor'的任务按task.id建索引,然后遍历 Artifact Panel 中kind === 'monitor'的 tab(并跳过属于其他会话的 tab),用mergeMonitorTaskSnapshot(tab.task, task)合并快照。由于openMonitorPanel同样调用这个合并函数更新已有 tab(App.tsx#L5290-L5301),整条数据链路收敛为一条:既有轮询 → 背景任务列表 → tab 合并更新,没有任何第二条轮询源,这与 Goal 的"不新增轮询循环"完全一致。

9. tab 按 monitor 任务 ID 键控,重复打开即选中并刷新

文档约定"Key the tab by monitor task ID so reopening the same monitor selects and refreshes the existing tab"。实现上 tab id 为monitor:<task.id>(跨会话场景下为monitor:<sourceSessionId>:<task.id>,见 openMonitorPanel)。setArtifactPanelTabs的更新逻辑是:id 已存在则就地替换/合并该 tab,否则追加新 tab,然后setActiveArtifactPanelTabId(tab.id)选中它。因此无论是从背景任务列表还是从转录工具行再次点击同一个 monitor,行为都是"选中已有 tab 并用新快照刷新",而不是堆叠出重复 tab。

快照合并规则本身值得单独看:mergeMonitorTaskSnapshot 的实现只有一条规则——若当前 tab 中的快照已处于终态(非running)而新快照又是running,则保留当前的终态快照,否则采用新快照。这防止了晚到的过期running快照把面板上已经显示的"已完成/已停止"状态冲回去。测试 App.test.tsx 中expect(mergeMonitorTaskSnapshot(cancelled, running)).toBe(cancelled)expect(mergeMonitorTaskSnapshot(running, cancelled)).toBe(cancelled)双向验证了这一点:终态优先,且终态对终态直接引用相等(避免无谓的重渲染)。

10. 详情渲染沿用背景任务对话框的口径

文档约定右侧面板渲染既有的任务详情呈现,使运行时长、命令、事件计数、丢弃行数、退出码、失败/停止原因与背景任务对话框保持一致("remain consistent with the background-tasks dialog"),并沿用 Subagent 详情的排版层级:描述(description)在最前,状态以 Badge 标签形式放在 Stop 操作旁边,紧凑指标(compact metrics)在下,命令用等宽(monospace)代码块展示。

同时约定 Stop 操作在右侧面板详情中继续可用,且取消后立即刷新其快照("refresh its snapshot immediately after cancellation")——这对应"取消后马上拿到终态快照"的需求,确保面板不必等到下一轮轮询周期就显示停止结果。

而 shell 与 agent 两类任务行保持其原有的行内详情行为("Keep shell and agent rows on their existing inline-detail behavior"),只有 monitor 升级为右侧专属 tab,改动范围被刻意收窄。

生命周期:跟随既有轮询的起止规则

设计文档的 Lifecycle 章节对 monitor 的生命周期约定如下:

  • 运行中:active monitor 持续通过既有背景任务轮询更新(即上述第 8 条的 tab 同步链路);
  • 进入终态:最终一次快照更新 tab,轮询按既有规则停止;
  • 对话框开关不破坏 tab:关闭或重新打开背景任务对话框,不会丢弃已经打开的 monitor tab——tab 的生命周期属于右侧 Artifact Panel,与发现它的对话框解耦。

结合第 6、9 条的回退与合并规则,可以推断出完整的状态保障:终态不会被旧快照回退、解析失败不会报错、轮询停止后面板上保留的就是最后一次终态快照。

小结

这份设计的取舍可以用三句话概括:发现面留在状态栏,数据面寄生在既有背景任务轮询上,展示面复用右侧 Artifact Panel 的 tab 机制。它通过session_monitor_tool_correlation能力标签与旧 daemon 干净地划界,用findMonitorTaskForTool+ 工具结果内嵌 monitor ID 的双路解析兼容新旧转录快照,并用mergeMonitorTaskSnapshot的"终态优先"规则保证面板状态的单调性。对于需要理解 Qwen Code Web Shell 如何"零额外轮询"接入 daemon 快照的场景,建议直接对照 设计文档、App.tsx 的 openMonitorPanel / openMonitorPanelFromTool / 同步 effect 与 能力注册表 阅读,相关行为在 App.test.tsx 与 ChatPane.test.tsx 中均有能力开关维度的测试覆盖。

【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询