Skip to content

Commit 52c9a5e

Browse files
authored
Merge pull request #200 from hoangsonww/fix/nested-subagent-hierarchy
fix: nested subagent hierarchy + graceful shutdown + no card-click toggle
2 parents d16f2a7 + 81fb93c commit 52c9a5e

52 files changed

Lines changed: 2423 additions & 289 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ARCHITECTURE.md

Lines changed: 29 additions & 15 deletions
Large diffs are not rendered by default.

README-CN.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,12 @@ Dashboard 提供全面的功能来监控和分析你的 Claude Code 会话和 Ag
288288
| **历史导入** | 启动时从 `~/.claude/` 导入会话。增强的 JSONL 提取:API 错误(配额/速率/无效请求)、回合耗时、入口点(cli/sdk-ts)、权限模式、思考块计数、用量附加信息(service_tier、speed、inference_geo)、工具结果错误,以及子 Agent JSONL 文件(`subagents/agent-*.jsonl``.meta.json`)。重新导入时回填已有会话。近期 JSONL 文件(< 10 分钟)以"活跃"状态导入 |
289289
| **子 Agent 层级** | Dashboard 和会话详情页可折叠的父子 Agent 树。有子 Agent 的 Agent 显示展开/折叠箭头;叶子 Agent 显示圆点指示器。子 Agent 活跃时自动展开 |
290290
| **后台 Agent** | 正确追踪后台子 Agent,不会提前标记为完成 |
291-
| **子 Agent 工具归属** | 子 Agent 内部的工具调用(Read、Bash、Edit、Grep 等)只存在于每个子 Agent 自己的 JSONL 文件中 — Claude Code 不会为其触发任何 Hook。每次 `SubagentStop` 后,dashboard 触发 fire-and-forget 的 `scanAndImportSubagents`:解析每个 `subagents/agent-*.jsonl`,根据 `tool_use_id` 配对 `tool_use``tool_result` 块,并在子 Agent 自己的 `agent_id` 下发出 `PreToolUse` + `PostToolUse` 事件。具备幂等性(通过 `data LIKE '%"tool_use_id":"X"%'` 去重),并在按类型 + 启动时间在 30 秒内匹配到 hook 创建的 live 行时合并进去,因此不会创建并行的 `<sid>-jsonl-*` 行。同一路径在 `npm run setup` 启动导入时也会运行,实现完整的历史回填 — 早于 dashboard 安装的会话也能获得完整的每子 Agent 工具时间线。Activity Feed 和会话详情页将父链以 `main › coder › explorer` 形式渲染嵌套子 Agent |
292-
| **成本追踪** | 按模型估算成本,支持可配置定价规则和按会话明细。压缩感知的 Token 核算在上下文压缩过程中保留总量。Transcript 读取通过增量字节偏移更新缓存,实现高效 Token 提取 |
291+
| **子 Agent 工具归属** | 子 Agent 内部的工具调用(Read、Bash、Edit、Grep 等)只存在于每个子 Agent 自己的 JSONL 文件中 — Claude Code 不会为其触发任何 Hook。每次 `SubagentStop` 后,dashboard 触发 fire-and-forget 的 `scanAndImportSubagents`:解析每个 `subagents/agent-*.jsonl`,根据 `tool_use_id` 配对 `tool_use` 与 `tool_result` 块,并在子 Agent 自己的 `agent_id` 下发出 `PreToolUse` + `PostToolUse` 事件。具备幂等性(通过 `data LIKE '%"tool_use_id":"X"%'` 去重),并在按类型 + 启动时间在 30 秒内匹配到 hook 创建的 live 行时合并进去,因此不会创建并行的 `<sid>-jsonl-*` 行。同一路径在 `npm run setup` 启动导入时也会运行,实现完整的历史回填 — 早于 dashboard 安装的会话也能获得完整的每子 Agent 工具时间线。Activity Feed 和会话详情页将父链以 `main › coder › explorer` 形式渲染嵌套子 Agent。该父链由 `reconcileSubagentParents` 权威重建:子 Agent 行最初被平铺插入到 main agent 之下(单个 hook 事件或 JSONL 文件不携带 spawn 方身份),随后从每个子 Agent transcript 的 Task 工具结果(`toolUseResult.agentId`,以 `spawnedChildren` 形式采集)恢复其 spawn 方,因此自己再 spawn 子 Agent 的子 Agent 会嵌套到其**真正的** spawn 方之下,而不会塌陷为 main 之下的单一层级。该过程幂等且仅追加 — 只重新指向 `parent_agent_id`,不插入或删除行 — 并在同一次 `SubagentStop` 扫描中运行,该扫描返回 `reparented` 计数,因此即使仅是 reparent 改变了树形结构,dashboard 也会重新拉取 |
292+
| **成本追踪** | 按模型估算成本,支持可配置定价规则和按会话明细。压缩感知的 Token 核算在上下文压缩过程中保留总量。Transcript 读取通过增量字节偏移更新缓存,实现高效 Token 提取。介绍性价格可在 Settings 中完全编辑——Model Pricing 编辑器提供一个促销截止日期以及按类别的介绍性价格(input / output / cache-read / cache-write 5m & 1h),因此未来模型发布的促销无需改动代码,只需编辑即可。子代理卡片显示每个子代理各自的成本(依据该子代理 transcript 的 Token 用量推算,并按当前价格计价),而非整个会话的总额——主代理卡片代表整个会话并显示会话总成本,而子代理卡片仅显示该子代理花费的部分,因此子代理卡片不再误导性地显示为好像它花费了整个会话的成本 |
293293
| **Transcript 缓存** | 从 JSONL Transcript 实时提取:Token、压缩、API 错误(`isApiErrorMessage` 条目存储为 `APIError` 事件)、回合耗时(存储为 `TurnDuration` 事件)、思考块计数和用量附加信息(service_tier、speed、inference_geo)。会话元数据实时丰富这些字段 |
294294
| **通知** | 基于 Web Push (VAPID) 的持久化浏览器通知。即使 Dashboard 标签页未聚焦或浏览器已关闭也能送达。特别针对 macOS 音效支持进行了配置。支持按事件配置开关及订阅管理 |
295295
| **更新提醒** | 服务端定期以非阻塞方式执行 `git fetch`,将本地检出与所选规范远程的默认分支对比。**支持分支与 fork:** 若同时存在 `upstream``origin`,优先使用 `upstream`(fork 的常规约定);命令也会根据用户处境调整——只有在本地分支真正跟踪规范引用时才建议 `git pull --ff-only`,否则给出 `git fetch`(fork 场景下加上 fast-forward 合并),让命令永不撒谎。侧边栏还有常驻的"检查更新"按钮及状态徽标。Dashboard **不会**自行拉取或重启——用户在终端中手动执行命令——因此该机制不会破坏开发会话、pm2/systemd/Docker 进程管理,也不会留下孤立进程 |
296-
| **设置** | 系统信息、Hook 状态、模型定价管理、通知偏好、数据导出、会话清理。**模型定价**区块在标题旁提供一个信息浮层(`i` 图标),讲解规则匹配方式(首条匹配的模式生效)、SQL 风格 `%` 通配符的语法及具体示例(`claude-opus-4-7%``claude-%-haiku`、精确 id),并提醒:当 Anthropic 公布新价格时必须手动更新——已存储的会话仍保留入库时所用价格。CLAUDE_HOME 区块与 Import History 面板已完整覆盖 en/vi/zh 三语 i18n |
296+
| **设置** | 系统信息、Hook 状态、模型定价管理、通知偏好、数据导出、会话清理。**模型定价**区块在标题旁提供一个信息浮层(`i` 图标),讲解规则匹配方式(首条匹配的模式生效)、SQL 风格 `%` 通配符的语法及具体示例(`claude-opus-4-7%``claude-%-haiku`、精确 id),并提醒:当 Anthropic 公布新价格时必须手动更新——已存储的会话仍保留入库时所用价格。每条规则的编辑器还带有一个可折叠的 Introductory rates 区块(一个 YYYY-MM-DD 促销截止日期 + 按类别的介绍性价格);将日期留空表示没有促销,而空日期会清除任何已存储的介绍性价格。CLAUDE_HOME 区块与 Import History 面板已完整覆盖 en/vi/zh 三语 i18n |
297297
| **MCP 服务器(本地)** | 位于 `mcp/` 的企业级本地 MCP 服务器,支持三种传输模式(stdio、HTTP+SSE、交互式 REPL),6 个域共 25 个类型化工具,严格输入 Schema、重试/退避、仅限本地 API 强制执行,以及分层变更/破坏性安全门控。HTTP 模式在可配置端口上提供 Streamable HTTP(2025-11-25)和传统 SSE(2024-11-05)。REPL 模式提供带 Tab 补全和彩色输出的交互式工具调用 |
298298
| **工作流** | 基于 D3.js 的可视化页面,包含 11 个交互式模块:Agent 编排 DAG、工具执行 Sankey 图、协作网络、子 Agent 有效性(按周 sparkline 通过 portal 渲染——可越过卡片的 `overflow:hidden`,并自动夹在视口内不再被裁切)、检测到的流程模式、模型委派流、错误传播图(带比率徽章的水平条形图、Agent 类型分解、API/会话错误卡片)、并发时间线、会话复杂度散点图、压缩影响分析和按会话下钻。**全方位、多语言的丰富 tooltip:** 每个图表标题旁都有一个 `i` 图标,可弹出结构化的「此图展示了什么 / 如何阅读 / 为何重要」浮层;悬停节点、边、条、气泡都会显示带有确定性、值相关解读的多段 tooltip(例如占源/占目标比例、成功率健康分级、Opus / Sonnet / Haiku 模型系列说明,以及前段/中段/后段等时间模式)。六张总览统计卡片各自在右下角带一个信息浮层,用自然语言解释指标的计算方式与当前数值含义。Tooltip 通过每张图唯一的 DOM ref 直接更新,并附带容器级 `mouseleave` 兜底,绝不会落后于光标或在重新渲染后残留。点击 **检测到的工作流模式** 中的任意一行会就地展开详情面板,包含完整步骤序列、统计网格、确定性叙述(循环检测、频率分级)和一条务实的建议。状态筛选标签(仅活跃 / 已完成 / 全部)可筛选全部 11 个模块。支持交叉筛选、JSON 导出和 3 秒防抖的实时 WebSocket 自动刷新。**工作流运行**面板呈现「动态工作流」——由 `Workflow` 工具(及自定节奏的 `/loop`)派生的 sub-agent 群组——它们不触发任何 hook,因此改为依据磁盘上的运行日志(`workflows/wf_<runId>.json`)重建:每次运行展示其阶段以及按 Agent 的 token / 工具调用 / 时长分解,并在日志写入前实时检测 `running` 状态,同时在每个会话详情页提供一个关联子区块 |
299299
| **压缩追踪** | 从 JSONL Transcript 检测 `/compact` 事件,创建压缩 Agent 和事件。启动时回填历史压缩。周期性扫描器(频率从 `DASHBOARD_STALE_MINUTES` 派生)在无 Hook 触发时也能捕获压缩。共享 Transcript 缓存,避免重复文件读取 |
@@ -597,6 +597,7 @@ flowchart LR
597597
| ----------------------- | ------------- | --------------------------------------------- |
598598
| `DASHBOARD_PORT` | `4820` | Express 服务器端口 |
599599
| `CLAUDE_DASHBOARD_PORT` | `4820` | Hook Handler 连接服务器使用的端口 |
600+
| `DASHBOARD_STALE_MINUTES` | `180`(3 小时) | 一个仍为 `active` 的会话(包括正在**等待中**用户输入的会话——"等待中"是 `active` 行上的 UI 覆盖层,而非存储状态)在被自动标记为 **abandoned** 并从活跃列表中移除之前的无活动分钟数。由 15 秒看门狗和周期性维护清理(每 ¼ 该值运行一次,夹在 60 秒–5 分钟之间)执行。调低(例如 `60`)可获得更短的空闲超时 |
600601
| `DASHBOARD_WORKING_IDLE_SECONDS` | `120` | 用于恢复**在任何输出之前**`Esc` 取消(不会留下 Transcript 标记)回合的空闲工作超时。当主 Agent 处于 `working`、没有进行中的工具,且在此时长内既无 Hook 事件也无 Transcript 推进时,看门狗将会话转入**等待中**。调低可获得更迅捷的恢复,但代价是长时间静默思考的回合上偶尔出现误判(会自愈) |
601602
| `DASHBOARD_SESSION_SYNC_MS` | `30000` | 持续 `~/.claude/projects` 后台同步的轮询间隔(毫秒),用于显示启动后才加入、其会话从不经过 Hook 流入的项目。无论如何 `fs.watch` 监听器都会近乎即时触发;该轮询是安全兜底(监听器可能错过事件 / 在网络文件系统上不触发)。设为 `0` 可禁用轮询,同时让监听器保持运行 |
602603
| `NODE_ENV` | `development` | 设为 `production` 以提供构建后的客户端 |

0 commit comments

Comments
 (0)