An open API service indexing awesome lists of open source software.

https://github.com/hoangsonww/Claude-Code-Agent-Monitor

🚀 A real-time monitoring dashboard for Claude Code, built with SQLite3, Node.js, Express, React, Vite, TailwindCSS, and WebSockets. It tracks sessions, agent activity, tool usage, and subagent orchestration, providing live analytics, a Kanban status board, status notifications, a cute buddy, and an interactive web UI/MacOS/Windows native app.
https://github.com/hoangsonww/Claude-Code-Agent-Monitor

ai-agents claude-agents claude-code claude-skills express expressjs macos-app node nodejs python react rest-api rfc-6455 sqlite sqlite3 tailwind tailwindcss typescript vite websocket

Last synced: about 4 hours ago
JSON representation

🚀 A real-time monitoring dashboard for Claude Code, built with SQLite3, Node.js, Express, React, Vite, TailwindCSS, and WebSockets. It tracks sessions, agent activity, tool usage, and subagent orchestration, providing live analytics, a Kanban status board, status notifications, a cute buddy, and an interactive web UI/MacOS/Windows native app.

Awesome Lists containing this project

README

          

# Claude Code Agent Dashboard(Agent 监控面板)

### Claude Code Agent 活动实时监控平台 🚀

专业的 Dashboard,用于实时追踪和可视化你的 Claude Code Agent 会话、工具使用和子 Agent 编排。基于 Node.js、Express、React 和 SQLite 构建,通过 Claude Code 原生 Hook 系统直接集成,实现无缝的会话追踪和分析。

![Claude Code](https://img.shields.io/badge/Claude_Code-orange?style=flat-square&logo=claude&logoColor=white)
![Claude Code Plugins](https://img.shields.io/badge/Claude_Code-Plugins_&_Skills-orange?style=flat-square&logo=anthropic&logoColor=white)
![Model Context Protocol](https://img.shields.io/badge/Model_Context_Protocol-1.0-0f766e?style=flat-square&logo=modelcontextprotocol&logoColor=white)
![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933?style=flat-square&logo=node.js&logoColor=white)
![Python](https://img.shields.io/badge/Python-%3E%3D3.6-3776AB?style=flat-square&logo=python&logoColor=white)
![Express](https://img.shields.io/badge/Express-4.21-000000?style=flat-square&logo=express&logoColor=white)
![ws](https://img.shields.io/badge/ws-WebSocket_server-010101?style=flat-square&logo=socketdotio&logoColor=white)
![web-push](https://img.shields.io/badge/web--push-VAPID-3b82f6?style=flat-square&logo=javascript&logoColor=white)
![swagger-ui-express](https://img.shields.io/badge/swagger--ui--express-5.0-85EA2D?style=flat-square&logo=swagger&logoColor=white)
![multer](https://img.shields.io/badge/multer-multipart_upload-FF6B6B?style=flat-square&logo=express&logoColor=white)
![adm-zip](https://img.shields.io/badge/adm--zip-archive_extract-FBBF24?style=flat-square&logo=files&logoColor=white)
![tar](https://img.shields.io/badge/tar-tgz_extract-A78BFA?style=flat-square&logo=gnu&logoColor=white)
![React](https://img.shields.io/badge/React-18.3-61DAFB?style=flat-square&logo=react&logoColor=white)
![TypeScript](https://img.shields.io/badge/TypeScript-5.7-3178C6?style=flat-square&logo=typescript&logoColor=white)
![Javascript](https://img.shields.io/badge/JavaScript-ES6-F7DF1E?style=flat-square&logo=javascript&logoColor=white)
![Vite](https://img.shields.io/badge/Vite-6.1-646CFF?style=flat-square&logo=vite&logoColor=white)
![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-3.4-06B6D4?style=flat-square&logo=tailwindcss&logoColor=white)
![PostCSS](https://img.shields.io/badge/PostCSS-8.5-DD3A0A?style=flat-square&logo=postcss&logoColor=white)
![Autoprefixer](https://img.shields.io/badge/Autoprefixer-10.4-DD3735?style=flat-square&logo=autoprefixer&logoColor=white)
![React Router](https://img.shields.io/badge/React_Router-6.28-CA4245?style=flat-square&logo=reactrouter&logoColor=white)
![Lucide](https://img.shields.io/badge/Lucide_Icons-0.474-F56565?style=flat-square&logo=lucide&logoColor=white)
![D3.js](https://img.shields.io/badge/D3.js-7-F9A03C?style=flat-square&logo=d3&logoColor=white)
![Mermaid](https://img.shields.io/badge/Mermaid-10.2-ff3333?style=flat-square&logo=mermaid&logoColor=white)
![i18next](https://img.shields.io/badge/i18next-22.4-7A42FF?style=flat-square&logo=i18next&logoColor=white)
![i18next Language Detector](https://img.shields.io/badge/i18next_Language_Detector-6.1-7A42FF?style=flat-square&logo=i18next&logoColor=white)
![SQLite](https://img.shields.io/badge/SQLite-3-003B57?style=flat-square&logo=sqlite&logoColor=white)
![better--sqlite3](https://img.shields.io/badge/better--sqlite3-11.7-003B57?style=flat-square&logo=sqlite&logoColor=white)
![better-sqlite3 WAL](https://img.shields.io/badge/better--sqlite3-WAL_mode-003B57?style=flat-square&logo=sqlite&logoColor=white)
![WebSocket](https://img.shields.io/badge/WebSocket-RFC_6455-010101?style=flat-square&logo=socketdotio&logoColor=white)
![SSE](https://img.shields.io/badge/SSE-Server_Sent_Events-FF6600?style=flat-square&logo=googlechrome&logoColor=white)
![OpenAPI](https://img.shields.io/badge/OpenAPI-3.0-000000?style=flat-square&logo=openapiinitiative&logoColor=white)
![Swagger](https://img.shields.io/badge/Swagger-3.0-85EA2D?style=flat-square&logo=swagger&logoColor=white)
![VS Code](https://img.shields.io/badge/VS_Code-Extension-007ACC?style=flat-square&logo=vscodium&logoColor=white)
![Electron](https://img.shields.io/badge/Electron-35-47848F?style=flat-square&logo=electron&logoColor=white)
![electron-builder](https://img.shields.io/badge/electron--builder-25.1-2c2e3b?style=flat-square&logo=electron&logoColor=white)
![macOS](https://img.shields.io/badge/macOS-Desktop_App-000000?style=flat-square&logo=apple&logoColor=white)
![Windows](https://img.shields.io/badge/Windows-Desktop_App-0078D6?style=flat-square&logo=windows&logoColor=white)
![SMAppService](https://img.shields.io/badge/SMAppService-Login_Items-000000?style=flat-square&logo=apple&logoColor=white)
![Universal DMG](https://img.shields.io/badge/Universal_DMG-arm64_%2B_x64-7c3aed?style=flat-square&logo=apple&logoColor=white)
![NSIS Installer](https://img.shields.io/badge/Windows-NSIS_%2B_Portable-1f6feb?style=flat-square&logo=windows&logoColor=white)
![Vitest](https://img.shields.io/badge/Vitest-1.0-646CFF?style=flat-square&logo=vitest&logoColor=white)
![React Testing Library](https://img.shields.io/badge/React_Testing_Library-13.0-FF5733?style=flat-square&logo=testinglibrary&logoColor=white)
![ESLint](https://img.shields.io/badge/ESLint-8.44-4B32C3?style=flat-square&logo=eslint&logoColor=white)
![Prettier](https://img.shields.io/badge/Prettier-3.8-F7B93E?style=flat-square&logo=prettier&logoColor=white)
![Docker](https://img.shields.io/badge/Docker-20.10-2496ED?style=flat-square&logo=docker&logoColor=white)
![Podman](https://img.shields.io/badge/Podman-4.0-CC342D?style=flat-square&logo=podman&logoColor=white)
![Terraform](https://img.shields.io/badge/Terraform-%3E%3D1.5-844FBA?style=flat-square&logo=terraform&logoColor=white)
![Kubernetes](https://img.shields.io/badge/Kubernetes-%3E%3D1.24-326CE5?style=flat-square&logo=kubernetes&logoColor=white)
![Helm](https://img.shields.io/badge/Helm-3-0F1689?style=flat-square&logo=helm&logoColor=white)
![Kustomize](https://img.shields.io/badge/Kustomize-5.0-326CE5?style=flat-square&logo=kubernetes&logoColor=white)
![Nginx](https://img.shields.io/badge/Nginx-Ingress-009639?style=flat-square&logo=nginx&logoColor=white)
![Prometheus](https://img.shields.io/badge/Prometheus-2.x-E6522C?style=flat-square&logo=prometheus&logoColor=white)
![Grafana](https://img.shields.io/badge/Grafana-10.x-F46800?style=flat-square&logo=grafana&logoColor=white)
![Coralogix](https://img.shields.io/badge/Coralogix-Observability-1a1a2e?style=flat-square&logo=datadog&logoColor=white)
![OpenTelemetry](https://img.shields.io/badge/OpenTelemetry-Collector-4f46e5?style=flat-square&logo=opentelemetry&logoColor=white)
![AWS](https://img.shields.io/badge/AWS-ECS%20%7C%20RDS-232F3E?style=flat-square&logo=task&logoColor=white)
![Google Cloud](https://img.shields.io/badge/Google_Cloud-GKE%20%7C%20SQL-4285F4?style=flat-square&logo=googlecloud&logoColor=white)
![Azure](https://img.shields.io/badge/Azure-AKS%20%7C%20SQL-0078D4?style=flat-square&logo=cloudflare&logoColor=white)
![Oracle Cloud](https://img.shields.io/badge/Oracle_Cloud-OKE%20%7C%20DB-F80000?style=flat-square&logo=cloudways&logoColor=white)
![GitHub Actions](https://img.shields.io/badge/GitHub_Actions-pipelines-2088FF?style=flat-square&logo=githubactions&logoColor=white)
![GitLab CI](https://img.shields.io/badge/GitLab_CI-pipelines-FC6D26?style=flat-square&logo=gitlab&logoColor=white)
![Make](https://img.shields.io/badge/Make-4.3-000000?style=flat-square&logo=make&logoColor=white)
![Auto Release](https://img.shields.io/badge/CI-auto--release_to_GitHub-22c55e?style=flat-square&logo=githubactions&logoColor=white)
![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)

**语言支持 / Language Support**: English (`en`) · 中文 (`zh`) · 越南语 (`vi`)

切换文档:[`README.md`](./README.md) · [`README-CN.md`](./README-CN.md) · [`README-VN.md`](./README-VN.md)

---

## 目录

- [概述](#概述)
- [多语言支持(i18n)](#多语言支持i18n)
- [功能特性](#功能特性)
- [快速开始](#快速开始)
- [工作原理](#工作原理)
- [配置](#配置)
- [npm 脚本](#npm-脚本)
- [插件市场](#插件市场)
- [Agent 扩展](#agent-扩展)
- [Tabby](#tabby)
- [MCP 集成](#mcp-集成)
- [API 参考](#api-参考)
- [Hook 事件](#hook-事件)
- [浏览器通知](#浏览器通知)
- [更新提醒](#更新提醒)
- [连接状态弹窗](#连接状态弹窗)
- [VS Code 扩展](#vs-code-扩展)
- [桌面应用(macOS 与 Windows)](#桌面应用macos-与-windows)
- [数据存储](#数据存储)
- [状态栏](#状态栏)
- [服务端架构](#服务端架构)
- [客户端路由](#客户端路由)
- [Hook 处理流程](#hook-处理流程)
- [部署模式](#部署模式)
- [项目结构](#项目结构)
- [常见问题](#常见问题)
- [许可证](#许可证)

---

## 概述

通过专业的暗色主题 Web 界面追踪会话、实时监控 Agent、可视化工具使用、观察子 Agent 编排。通过 Claude Code 原生 Hook 系统直接集成。

```mermaid
graph LR
A["Claude Code
会话"] -->|Hook 触发
工具使用 / 停止| B["Hook Handler
(Node.js 脚本)"]
B -->|HTTP POST| C["Dashboard 服务器
(Express + SQLite)"]
C -->|WebSocket
广播| D["Dashboard UI
(React + Tailwind)"]
style A fill:#6366f1,stroke:#818cf8,color:#fff
style B fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style C fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style D fill:#10b981,stroke:#34d399,color:#fff
```





Star History Chart

### 多语言支持(i18n)

Dashboard 内置多语言界面,支持 `en`、`zh`、`vi` 三种语言,适用于跨语言协作和团队共享。

```mermaid
flowchart LR
A["用户选择语言
en / zh / vi / ko"] --> B["前端 i18n 路由"]
B --> C["翻译字典加载"]
C --> D["UI 文案与可访问性标签渲染"]
D --> E["实时仪表盘 / API 文档 / 设置页"]
style A fill:#6366f1,stroke:#818cf8,color:#fff
style B fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style C fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style D fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style E fill:#10b981,stroke:#34d399,color:#fff
```

完整实现细节与排障指南请见 [docs/I18N.md](./docs/I18N.md)。

### 用户界面

配有精美的暗色主题、响应式设计和直观的导航,让你轻松浏览 Agent 活动:


Dashboard 概览


📡 Dashboard · Monitor — 总览统计、活跃 Agent 卡片与最近活动流


Dashboard — 系统健康标签页


🩺 Dashboard · Health — 综合健康评分环、存储引擎甜甜圈图、缓存/错误/成功率仪表、工具调用条形图、子Agent效能、模型Token分布、压缩统计 — 每 5 秒自动刷新


Kanban 看板 — Agent 视图


📋 Kanban 看板(Agent 视图) — Agent 按状态分布于 4 个列:工作中 / 等待中 / 已完成 / 错误。黄色的“等待中”列突出显示被用户阻塞的会话(权限请求、回合结束,或停在新会话的提示符前)。每张卡片一目了然地显示模型、费用和当前工具。


Kanban 看板 — 会话视图


🗂️ Kanban 看板(会话视图) — 会话按状态分布于 5 个列:活跃 / 等待中 / 已完成 / 错误 / 已废弃,可在同一页面切换。将鼠标悬停在任意列标题上可查看生命周期转换的提示说明。


会话概览


📂 会话 — 包含费用、模型、Agent 数和时长的可搜索、可过滤、服务端分页的会话总表


会话详情 — Agent 标签页


🤖 会话详情 · Agent — 实时概览卡片(事件、工具调用、子 Agent、压缩、错误、时长)、Top 工具用量条形图、子 Agent 类型分布、Token 流和 Agent 层级树


会话详情 — Conversation 标签页


💬 会话详情 · Conversation — 实时对话查看器,支持 markdown 渲染、带行号和复制按钮的语法高亮代码块,按工具样式化的工具调用块、捕获其 TUI 输出的斜杠命令气泡,以及内联的会话重命名标记


会话详情 — Timeline 标签页


🔬 会话详情 · Timeline — 按时间排序的事件时间线,支持多维过滤、按 `tool_use_id` 进行 Pre/Post 分组,以及工具感知的载荷渲染


活动流概览


📰 活动流 — 实时事件日志,支持暂停 / 恢复、分组、多维过滤,每行带"会话 →"跳转按钮


分析概览


📊 分析 — 按模型的 Token 用量、工具使用频率、活动热力图与会话趋势,附在线 / 离线指示器


工作流概览


🔀 工作流 — Agent 编排 DAG、工具执行桑基图、协作网络,共 11 个交互式工作流智能模块


工作流页面上的动态工作流运行


🧬 工作流运行(工作流页面) — 由 Workflow 工具派生的「动态工作流」,依据磁盘上的运行日志重建:状态、Agent 数量、token 与工具调用,可展开为按 Agent 的明细(阶段、状态、token、工具、时长),并附经过人性化处理的结果预览


展开的动态工作流运行,含阶段筛选与按 Agent 的结果


🧬 工作流运行 · 展开 — 展开的一次运行:可点击的彩色阶段筛选、按 Agent 的指标表,以及完整的可点击结果项列表,点击即可展开每个 Agent 的完整提示词与结果


会话详情页面上的动态工作流运行


🧬 工作流运行(会话详情) — 同样的群组关联到其启动会话,因此会话的动态工作流子 Agent 及其已计入的 token 成本可在会话内直接查看


Claude 配置浏览器


🧰 Claude 配置浏览器 — 12 标签页检查器,涵盖 Claude Code 知道的一切:技能、子代理、斜杠命令、输出样式、插件(含每个插件的贡献计数)、市场、MCP 服务器、Hook、设置(密钥脱敏)、记忆(用户与项目 `CLAUDE.md` 文件,外加按项目分组、可搜索的文件型记忆存储 —— `~/.claude/projects//memory/` 下的每个 `*.md`)、快捷键与状态行。低风险文本文件表面支持创建/编辑/删除(含按项目的 auto-memory 文件),带强制时间戳备份


运行 Claude — 启动前配置


▶️ 运行 Claude — 直接在仪表盘内启动 claude 子进程。选择模式(对话 / 单次)、来源(新会话 vs 从完整历史中恢复)、工作目录(带最近 cwd 自动补全)、模型、权限模式与思考强度。路由上的同源守卫防止浏览器 drive-by spawn


运行 Claude — 实时流式输出


💬 运行 Claude · 实时流 — 聊天式流式输出,通过 --include-partial-messages 实现真正的逐字符渲染。工具调用、工具结果与思考块均可折叠。标题栏的进行中运行切换器允许你将运行留在后台并稍后重新附加。会话 ID 一旦获知,"查看会话 →"即可深链至常规 Sessions UI


设置概览


⚙️ 设置 — 模型定价规则、Hook 安装状态、数据管理、通知偏好与系统信息


设置 — 告警与 Webhook


🔔 设置 · 告警 — 基于规则的告警引擎与出站 Webhook 集于一处:告警规则(事件模式 / 不活动 / agent 卡住 / token 阈值)支持按规则冷却,实时的已触发告警流,以及 14 个一等公民 Webhook 提供方(Slack、Discord、Teams、Google Chat、Mattermost、Rocket.Chat、Telegram、PagerDuty、Opsgenie、Splunk On-Call、Zapier、Make、n8n、Pipedream)加一个支持可选 HMAC 签名的通用 JSON 端点

侧边栏提供快速访问 Dashboard、看板、会话列表、活动流、分析、工作流和设置。每个页面旨在通过实时更新和丰富的可视化,为你提供对 Claude Code Agent 活动的深度洞察。

---

## 功能特性

Dashboard 提供全面的功能来监控和分析你的 Claude Code 会话和 Agent:

| 功能 | 描述 |
|------|------|
| **Dashboard** | 两个标签页(存储于 `localStorage`):**Monitor** — 概览统计(6 张统计卡片)、可折叠子 Agent 层级的活跃 Agent 卡片、近期活动流,项目数量通过 `ResizeObserver` 动态填满视口高度。**Health** — 综合系统健康评分环(加权:0.4 × 成功率 + 0.25 × 缓存命中率 + 0.25 × (100 − 错误率) + 0.1 × (100 − 堆内存 %))、存储引擎甜甜圈图(记录分布)、缓存性能 / 错误率 / 成功率仪表、Top 8 工具调用水平条形图、子 Agent 效能条、模型 Token 分布、压缩影响统计。所有健康指标每 5 秒从 `/api/settings/info` 和 `/api/workflows` 自动刷新。所有图表均有跟随光标的工具提示并自动避免视口边缘溢出 |
| **看板** | 顶部带视图切换(在 `localStorage` 中持久化):**Agent 视图** — 4 列(工作中 / 等待中 / 已完成 / 错误),以及**会话视图** — 5 列(活跃 / 等待中 / 已完成 / 错误 / 已废弃)。**等待中**列直接映射 Agent 的持久化 `waiting` 状态 — 当 Claude Code 停在提示符前(新会话、回合之间或被权限 Notification 阻塞)时设置,在用户继续操作(UserPromptSubmit / PreToolUse)时转换为 `working`。每个列标题都有 `?` 图标的工具提示解释生命周期。每列按状态从服务端独立获取(每列实际无上限),随后客户端按每列 10 张卡片分页,附「显示更多」按钮。WebSocket 订阅范围跟随当前视图(`agent_*` 与 `session_*` 帧),切换视图后另一类的更新不会触发重新加载。 |
| **会话** | 可搜索、可筛选、**服务端分页**的全量会话表。每次翻页请求 `/api/sessions?status=&q=&limit=10&offset=…`,因此费用计算只针对当前可见页运行——与数据库中会话总量无关。搜索框(`q=`)在服务端对 `id` / `name` / `cwd` 做不区分大小写匹配,附 300 毫秒防抖;响应包含 `total` 计数供分页器使用。状态筛选、搜索与翻页可组合。每个会话的可读**名称**从 Transcript 实时读取并保持同步——显式标题(`/rename`、`claude -n`、选择器 Ctrl+R 写入的 JSONL `custom-title` 行)优先,否则回退到自动生成的 `ai-title`;若两者都没有,则用会话的**首条用户 prompt**(截断,并跳过 tool-result / 斜杠命令噪音)填充占位名称以及 main agent 的占位名称/任务——因此从未获得标题的会话(包括导入的会话)也能一目了然它在做什么;用户自定义的名称绝不会被自动标题覆盖。该名称(无名称时回退到短 ID)显示在 Agent 卡片、Dashboard、活动流以及 Run 恢复选择器上 |
| **会话详情** | 单会话实时概览面板,包含活跃 Agent 横幅(当前工具 + 任务)、六个统计卡片(事件数及事件/分钟速率、工具调用数、子 Agent 数、压缩次数、错误数、滚动计时的运行时长)、Top 工具使用条形图、子 Agent 类型分布、堆叠 Token 流图,以及事件类型胶囊云——所有内容均根据 Hook 事件实时刷新。下方:Agent 层级树(父/子)、完整事件时间线(多维筛选:状态、事件类型、工具、Agent、文本搜索、日期范围)、按 `tool_use_id` 进行 Pre/Post 分组、人类可读摘要块、工具感知的输入/响应渲染器(Bash 用终端、Edit 用统一 diff、Read/Write 用带行号代码、Grep 用匹配列表、MCP 工具用键值卡片),以及对话标签页:使用 markdown(标题、列表、引用块、表格、任务列表)、带行号和复制按钮的语法高亮代码块(js/ts、python、json、bash、html、css、sql、yaml、diff),以及按工具样式化的工具调用块(Bash → 终端、Edit → 旧/新并排、Write → 文件标签、Read → 路径胶囊、Grep → pattern 卡片)渲染对话记录 |
| **活动流** | 实时流式事件日志,支持暂停/恢复和分页;点击任意事件行可就地展开其完整 hook 载荷(内联 EventDetail 面板);每行右侧的专属「会话 →」按钮可直接跳转至会话详情页,不影响当前展开状态 |
| **分析** | Token 使用量、工具频率、活动热力图(居中显示、按周排列从周日开始、日期名称提示)、会话趋势、在线/离线连接指示器。加载时图表区域显示带**脉冲动画的骨架占位符**(不仅是顶部统计卡),数据到达后再渲染真实图表 |
| **实时更新** | WebSocket 推送 — 无轮询,即时 UI 更新 |
| **自动发现** | 会话和 Agent 从 Hook 事件中自动创建 |
| **历史导入** | 启动时从 `~/.claude/` 导入会话。增强的 JSONL 提取:API 错误(配额/速率/无效请求)、回合耗时、入口点(cli/sdk-ts)、权限模式、思考块计数、用量附加信息(service_tier、speed、inference_geo)、工具结果错误,以及子 Agent JSONL 文件(`subagents/agent-*.jsonl` 含 `.meta.json`)。重新导入时回填已有会话。近期 JSONL 文件(< 10 分钟)以"活跃"状态导入 |
| **子 Agent 层级** | Dashboard 和会话详情页可折叠的父子 Agent 树。有子 Agent 的 Agent 显示展开/折叠箭头;叶子 Agent 显示圆点指示器。子 Agent 活跃时自动展开 |
| **后台 Agent** | 正确追踪后台子 Agent,不会提前标记为完成 |
| **子 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 行时合并进去,因此不会创建并行的 `-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 也会重新拉取 |
| **成本追踪** | 按模型估算成本,支持可配置定价规则和按会话明细。压缩感知的 Token 核算在上下文压缩过程中保留总量。Transcript 读取通过增量字节偏移更新缓存,实现高效 Token 提取。介绍性价格可在 Settings 中完全编辑——Model Pricing 编辑器提供一个促销截止日期以及按类别的介绍性价格(input / output / cache-read / cache-write 5m & 1h),因此未来模型发布的促销无需改动代码,只需编辑即可。子代理卡片显示每个子代理各自的成本(依据该子代理 transcript 的 Token 用量推算,并按当前价格计价),而非整个会话的总额——主代理卡片代表整个会话并显示会话总成本,而子代理卡片仅显示该子代理花费的部分,因此子代理卡片不再误导性地显示为好像它花费了整个会话的成本 |
| **Transcript 缓存** | 从 JSONL Transcript 实时提取:Token、压缩、API 错误(`isApiErrorMessage` 条目存储为 `APIError` 事件)、回合耗时(存储为 `TurnDuration` 事件)、思考块计数和用量附加信息(service_tier、speed、inference_geo)。会话元数据实时丰富这些字段 |
| **通知** | 基于 Web Push (VAPID) 的持久化浏览器通知。即使 Dashboard 标签页未聚焦或浏览器已关闭也能送达。特别针对 macOS 音效支持进行了配置。支持按事件配置开关及订阅管理 |
| **更新提醒** | 服务端定期以非阻塞方式执行 `git fetch`,将本地检出与所选规范远程的默认分支对比。**支持分支与 fork:** 若同时存在 `upstream` 和 `origin`,优先使用 `upstream`(fork 的常规约定);命令也会根据用户处境调整——只有在本地分支真正跟踪规范引用时才建议 `git pull --ff-only`,否则给出 `git fetch`(fork 场景下加上 fast-forward 合并),让命令永不撒谎。侧边栏还有常驻的"检查更新"按钮及状态徽标。Dashboard **不会**自行拉取或重启——用户在终端中手动执行命令——因此该机制不会破坏开发会话、pm2/systemd/Docker 进程管理,也不会留下孤立进程 |
| **设置** | 系统信息、Hook 状态、模型定价管理、通知偏好、数据导出、会话清理。**模型定价**区块在标题旁提供一个信息浮层(`i` 图标),讲解规则匹配方式(首条匹配的模式生效)、SQL 风格 `%` 通配符的语法及具体示例(`claude-opus-4-7%`、`claude-%-haiku`、精确 id),并提醒:当 Anthropic 公布新价格时必须手动更新——已存储的会话仍保留入库时所用价格。每条规则的编辑器还带有一个可折叠的 Introductory rates 区块(一个 YYYY-MM-DD 促销截止日期 + 按类别的介绍性价格);将日期留空表示没有促销,而空日期会清除任何已存储的介绍性价格。CLAUDE_HOME 区块与 Import History 面板已完整覆盖 en/vi/zh 三语 i18n |
| **MCP 服务器(本地)** | 位于 `mcp/` 的企业级本地 MCP 服务器,支持三种传输模式(stdio、HTTP+SSE、交互式 REPL),6 个域共 25 个类型化工具,严格输入 Schema、重试/退避、仅限本地 API 强制执行,以及分层变更/破坏性安全门控。HTTP 模式在可配置端口上提供 Streamable HTTP(2025-11-25)和传统 SSE(2024-11-05)。REPL 模式提供带 Tab 补全和彩色输出的交互式工具调用 |
| **工作流** | 基于 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_.json`)重建:每次运行展示其阶段以及按 Agent 的 token / 工具调用 / 时长分解,并在日志写入前实时检测 `running` 状态,同时在每个会话详情页提供一个关联子区块 |
| **压缩追踪** | 从 JSONL Transcript 检测 `/compact` 事件,创建压缩 Agent 和事件。启动时回填历史压缩。周期性扫描器(频率从 `DASHBOARD_STALE_MINUTES` 派生)在无 Hook 触发时也能捕获压缩。共享 Transcript 缓存,避免重复文件读取 |
| **子会话/恢复会话** | 新事件到达时自动重新激活会话,正确处理 `/resume` 和孤立会话。周期性清理(每 ¼ 个 `DASHBOARD_STALE_MINUTES`,夹在 60 秒–5 分钟之间)标记遗漏事件检测的废弃会话 |
| **预存会话检测** | 服务器启动时已在运行的会话以"活跃"状态导入(基于近期 JSONL 文件修改时间)。Stop 事件也会重新激活已导入的完成/废弃会话,因此进行中的会话的第一个 Hook 始终会显示在 Dashboard 上 |
| **持续项目同步** | 启动时对 `~/.claude/projects` 的自动导入是一次性的(由标记位把关),因此在首次启动**之后**才创建的项目文件夹——其会话从不经过 Hook 流入(例如 host-only Hook 被禁用)——在手动重新扫描之前都将不可见。后台同步(`startSessionSync`)通过三个共享同一个 mtime 缓存 + 单次合并扫描的触发器弥补了这个空隙:启动时的**立即**扫描、一个去抖的 **`fs.watch`**(新会话文件 / 项目文件夹一出现就触发;在 macOS/Windows 上递归监听,在 Linux 上监听根目录 + 直接子文件夹,以规避用户态递归监听器的隐患),以及一个**周期性轮询**(`DASHBOARD_SESSION_SYNC_MS`,默认 30 秒)。每次扫描只重新解析 mtime 前进过的文件,并广播 `session_created`/`session_updated`(外加主 Agent),让 UI 实时刷新;DB 中已有且未变更的会话会被跳过、不再重新解析,因此重启成本保持为 O(新增/变更文件) |
| **响应式设计** | 适配移动端的布局,堆叠网格、可滚动表格和可折叠侧边栏 |
| **界面本地化** | 内置语言切换,UI 文案与无障碍标签已覆盖英文(`en`)、中文(`zh`)、越南语(`vi`)和韩语(`ko`)。覆盖范围现已贯穿 Workflows 页面的所有 tooltip:统计卡片的计算说明与按值分桶的解读、每个图表的「此图展示什么 / 如何阅读 / 为何重要」浮层、所有图形悬停 tooltip(编排 DAG、工具流、Pipeline、模型委派、并发时间线)、Workflow Patterns 详情面板的叙述与建议、设置页 → 模型定价的信息浮层、CLAUDE_HOME 面板,以及完整的 Import History 流程 |
| **种子数据** | 内置种子脚本,用于演示和开发 |
| **状态栏** | 彩色编码的 CLI 状态栏,显示模型、上下文使用率、Git 分支、Token 数 |
| **模型名称格式化** | 整个 UI 中使用人性化的模型名称:原始标识符如 `claude-opus-4-7-20260101` 或 `claude-opus-4-7[1m]` 显示为"Claude Opus 4.7"或"Claude Opus 4.7 (1M)"。支持 Claude、GPT 和 Gemini 家族的自动版本号点连接、日期/latest 后缀剥离、提供商前缀移除和上下文窗口标签格式化。设置页保留原始名称以配置定价规则 |
| **插件市场** | 官方 Claude Code 插件市场,包含 10 个插件(ccam-analytics、ccam-productivity、ccam-devtools、ccam-insights、ccam-dashboard、ccam-cost-guard、ccam-sessions、ccam-workflows、ccam-quality、ccam-config)。53 个技能、14 个 Agent、30 个斜杠命令、3 个 CLI 工具、3 个 Hook 配置。全部基于实际数据模型 — Token 基线、定价引擎、工作流智能(11 个数据集)、会话元数据。通过 `claude plugin marketplace add` 安装 |
| **运行 Claude** | 直接从仪表盘启动 `claude` 子进程,带聊天式流式 UI。两种模式:**对话**(多轮 — stdin 持续打开,后续轮次以 stream-json 信封通过 stdin 传送)与 **单次**(headless,一个 prompt → 一个响应)。对话模式还支持通过 `claude --resume ` **恢复任何已有会话** — 使用可搜索选择器从你的完整会话历史中挑选。标题栏的进行中运行切换器允许你将运行留在后台、启动另一个、稍后重新附加。重新附加是持久的:客户端会把派生进程的内存信封日志(`?envelopes=1`)与会话磁盘上的 JSONL 转录文件协调,优先选择 user/assistant 消息更多的那一份,因此从已恢复的运行离开再回来会保留全部历史(派生进程只看到 spawn 之后的轮次;转录文件包含先前 + 当前)。模型下拉(Opus 4.7 / 1M / Sonnet 4.6 / Haiku 4.5 / 自定义)、permission-mode 选择器(对 `bypassPermissions` 显式警告)、**思考强度**字段(low / medium / high — 映射到 `--effort`)、cwd 自动补全(预填用户的**主目录** — 一个中性的启动位置,不会继承仪表盘仓库自身的 `.claude` 项目上下文(agents、skills、rules、`CLAUDE.md`、`.mcp.json`);若没有 home 建议则回退到仪表盘 cwd,建议分组以 home 优先(home → dashboard → 最近))。通过 `--include-partial-messages` 实现真正的逐字符流式渲染,加上客户端 **打字机平滑层** 通过 `requestAnimationFrame` 让每个 `text_delta` / `thinking_delta` 逐字浮现 — 即便是短回复(claude 把整个回答打成一两块 chunk 的情况)也呈现为打字效果。合并代码在 claude 中途送达 canonical `assistant` 信封时保留 `_streaming` 标志和增量累积的 `content` 数组,所以 thinking 块不会在完成时丢失。WebSocket 分发为每个信封包裹 `flushSync`,避免 React 18 自动批处理把多个 deltas 合并成一次渲染。**TUI 对齐(Tier 1)**:**限制说明横幅** 可最小化为细条(永不消失)解释 stream-json 模式相对终端 TUI 能做和不能做什么;**带斜杠命令自动补全的提示编辑器** 使用分级评分(精确名称 → 前缀匹配 → 词边界 → 包含 → 子序列 → 描述匹配)列出用户 / 项目 / 插件命令(发送前在客户端按模板展开执行),并以"仅 CLI — 此处不会执行"标记呈现 `/clear`、`/model`、`/config` 等内置 CLI 命令;**`@` 文件引用** 通过对该 run 的 cwd 进行去抖模糊搜索(跳过 `node_modules`、`.git`、`dist`、`build` 等);**实时上下文窗口 / token 计** 显示输入 + 输出 + 缓存命中 token 与运行成本 — 实时流式时从 `stream_event` / `result.usage` 计算,从转录恢复 / 查看 / 重新附加时也从已完结的 assistant `usage` 块(input / output / cache-read / cache-creation)读取,因此不会卡在 0/200k;**状态头** 显示当前 model、effort、permission mode、cwd、session ID、信封计数与已运行时间。自动补全下拉框向上展开,避免与下方 cwd 选择器冲突。标题旁有 Live / Offline 指示器。路由上的同源守卫防止浏览器 drive-by spawn。并发实际上不设上限(默认安全上限 10000,与终端 TUI 一致 — 仅作为防止有缺陷客户端 fork-bomb 的兜底;通过 `RUN_MAX_CONCURRENT` 设置真正的上限)。统一的活动运行 / 历史模态框还提供两个一键跳转按钮:对话型历史行的 **Resume** 按钮立即派生 `claude --resume ` 并把过去的对话记录预填入聊天视图(无需重新输入 prompt — 派生的进程会在 stdin 上空转直到你发送跟进消息);单次型历史行的 **View** 按钮把已捕获的转录内联加载到 run 查看器中作只读展示(不派生进程 — 同一面板,无 Stop / 跟进控件)。生成的会话触发与任何 `claude` 进程相同的 hooks,因此自动出现在 Sessions / Analytics / Kanban / Workflows — 而 Sessions / SessionDetail 会为当前正由 Run 页驱动的会话显示绿色 **▶ Run** 徽标 / 横幅,可点击跳回 Run 页 |
| **Tabby** | 固定在每个页面右下角的可爱 SVG 小猫伴侣,会订阅实时会话 WebSocket 流并据此做出反应。**会做出反应的吉祥物**:基于实时会话流呈现 8 种情绪——空闲、观察、开心、担忧、卡住、思考、睡觉、断开连接;眼睛会追踪光标,每种情绪都有专属动画。**气泡台词**在值得关注的事件发生时弹出(会话开始/结束、出现错误、运行完成),带节流且可静音。点击小猫或按 **⌘B / Ctrl+B** 打开**面板**(Esc 关闭):实时状态行(N 个进行中 · M 个出错 · 连接状态)、快捷操作(跳转到 Run Claude / 活动 / 会话 / 出错的会话,静音,清除提醒)以及一个 **Ask** 提问框。Ask 提问框在本地回答简单的状态类问题;其他问题则交给现有的 **Run Claude** 页面(`/run?prompt=...`)以启动一个真正的 Claude Code 会话——**无需新增后端、无需 API 密钥**。完全构建在现有的 WebSocket 流之上,支持无障碍(键盘、`aria-live`、尊重 `prefers-reduced-motion`),可在「设置」中开关。代码位于 `client/src/components/Tabby/` |
| **告警与 Webhook** | 基于规则的告警引擎在服务端评估实时事件流,支持四种条件类型:**事件模式**(匹配事件类型 / 工具名 / 摘要子串,可选要求在时间窗口内出现 N 次匹配——例如「2 分钟内超过 5 个错误」)、**闲置**(活跃会话 N 分钟无事件)、**卡住的代理**(代理在 `working`/`waiting` 状态下 N 分钟无活动)和**令牌阈值**(会话总令牌超过上限)。每条规则都按 (规则、会话、代理) 维度做冷却去重。触发的告警显示在实时列表中(支持确认 / 全部确认),并扇出到 **14 个一等公民 Webhook 提供方**——**Slack**、**Discord**、**Microsoft Teams**(通过 Power Automate Workflows 的 Adaptive Card)、**Google Chat**、**Mattermost**、**Rocket.Chat**、**Telegram**(Bot API)、**PagerDuty**(Events API v2)、**Opsgenie**(Alert API)、**Splunk On-Call**(VictorOps)、**Zapier**、**Make**、**n8n**、**Pipedream**——以及任意通用 JSON 端点(可选 **HMAC-SHA256** 签名 + 自定义请求头)。每个提供方都有各自的原生负载格式,可按规则限定范围。投递与告警流程分离且完全失败安全:请求超时、有界重试/退避、响应体校验(Splunk On-Call 返回 200 但 `result:"failure"`)、同步的**「发送测试」**按钮以及每个目标的投递日志。URL、密钥和凭据均存储在服务端,**绝不**通过 API 返回(在所有响应中被掩码/脱敏)。规则与渠道在 **设置 → 告警** 中统一管理,每个字段都有解释性提示,并提供按提供方的设置指南(附说明:这些步骤可能已过时——请查阅官方文档) |
| **Claude 配置浏览器** | `/cc-config` 上的 12 标签页检查器,涵盖 Claude Code 知道的一切:技能、子代理、斜杠命令、输出样式、插件(每个插件包含贡献计数 + 来自 `plugin.json` 的作者/许可证/主页)、市场(包含从每个 `marketplace.json` 读取的插件计数)、MCP 服务器、Hook(含 `~/.claude/hooks/` 脚本列表)、设置(一目了然的**当前配置**摘要,跨 用户/项目/项目本地 作用域解析 `/config` 控制的选项——model、verbose、主题、输出样式、effort、自动压缩、通知 ……,未设置项显示为默认值,外加按文件的结构化键值视图 + 原始 JSON 切换、密钥脱敏)、记忆(用户与项目 `CLAUDE.md` 文件,外加按项目的文件型记忆存储 —— `~/.claude/projects//memory/` 下的每个 `*.md`,即一个 `MEMORY.md` 索引加上每条记忆事实一个文件,通常 100+ 个;Memory 标签页按项目分组(可折叠)、将索引文件与逐条事实文件分开,并带搜索框)、快捷键(按上下文分组,使用 `` 字符)、状态行(配置 + 脚本内容)。对低风险文本文件表面(技能 / 代理 / 命令 / 输出样式 / 记忆,含按项目的 auto-memory 文件),页面支持**带强制时间戳备份的创建/编辑/删除**(auto-memory 备份落在 `/.cc-config-backups/auto-memory/`),原子写入到 Claude Code 不扫描的目录之外,加上带自动构建 `mv` 恢复命令的备份模态框。插件、MCP、settings 中的 hooks 和 `settings.json` 文件保持只读,带说明横幅 + 可复制的 CLI 命令,以便用户知道要自行运行的确切命令。**实时更新**:服务端运行的 `cc-watcher` 通过 `fs.watch` 监听 `~/.claude/`(平台支持时递归)以及 `~/.claude.json`,以 500 ms 去抖,在 Claude Code 配置变化时(无论是仪表盘修改还是外部工具如 CLI 安装插件、手动编辑 `settings.json`、放入新技能)广播 `cc_config_changed` WebSocket 消息。页面订阅并自动重新拉取;标题旁的 Live / Offline 标识显示 WebSocket 连接状态 |
| **启动画面** | 应用加载时每个浏览器会话显示一次的品牌开场画面:根据时间的问候语(早上好 / 下午好 / 晚上好 / 夜深了)、一句醒目的本地化标语与两行副文案,以及深色背景(径向光晕、星座连线、颗粒质感)上带动画的节点图品牌标记。从首帧起即**不透明**(应用内容不会闪现),停留约 2.5 秒后淡出,点击任意处可跳过,尊重 `prefers-reduced-motion`,已本地化 en/zh/vi/ko |
| **渐进式 Web 应用 (PWA)** | 三个独立的 PWA — 仪表盘、着陆页和维基 — 每个都有自己的 Web App Manifest 和 Service Worker。将任意一个安装到主屏幕/Dock,获得无浏览器边框的独立应用体验。仪表盘 SW 对 Vite 哈希化的 `/assets/*` 资源采用 cache-first(URL 每次构建都不可变,缓存命中始终正确),其他所有内容(导航、SW 自身、`manifest.json`、图标、根 `/`)采用 network-first 并以缓存兜底。配合生产环境 Express 静态中间件上的显式 `Cache-Control` 头(`/assets/*` 用 `immutable, max-age=31536000`,`index.html`、`sw.js`、`manifest.json` 用 `no-cache, must-revalidate`),重新构建后浏览器中的代码始终自动刷新,无需硬刷新;`client/src/main.tsx` 中的 `controllerchange` 监听器会在新 SW 接管已被控制的页面时恰好重新加载一次(首次安装不会)。VAPID 推送通知管道完全保留。着陆页和维基 SW 预缓存各自的 shell 并在首次访问时延迟缓存图片,单次加载后即可离线访问。所有 manifest 使用 SVG 图标(`favicon.svg`,`sizes="any"`),包含 `apple-mobile-web-app-capable` + `apple-touch-icon` meta 标签以支持 iOS 独立模式 |
| **自托管资源(无 CDN)** | 所有字体与脚本均**本地托管,零第三方 CDN 请求**。React 应用通过 `@fontsource` 打包 Inter + JetBrains Mono(latin 子集;由 Vite 输出为带内容哈希的 WOFF2 至 `dist/assets/`)。着陆页与维基加载本地的 `fonts/fonts.css` `@font-face` 样式表(维基用 `../fonts/`)。维基的 Mermaid 改为本地内置(`wiki/mermaid.min.js`,`mermaid@10.9.6`)而非 jsDelivr。VS Code 扩展的错误页改用系统字体栈。移除了所有 `fonts.googleapis.com` / `gstatic` / CDN 调用,因此仪表盘与文档可**完全离线**渲染,不向第三方泄露任何信息 |
| **桌面应用(macOS 与 Windows)** | 用 Electron 35 构建的可选原生桌面应用,位于 `desktop/` 工作区,与 `client/`、`server/`、`mcp/`、`vscode-extension/` 平级。以 macOS `.app`(`.dmg`)**以及** Windows `.exe`(NSIS 安装包 + 免安装便携版)形式分发。它将现有的 Express 服务器**以进程内方式嵌入**(直接 `require()` `server/index.js` —— 没有子进程、没有 IPC),并在 `BrowserWindow` 中渲染已构建的 React 客户端。新增了原生标题栏、菜单栏 / 通知区域(托盘)图标(单击其下拉菜单会显示一份在点击时从 SQLite 实时拉取的**状态快照**:会话、Agent、今日事件)、原生应用菜单、开机自启(macOS 通过 `SMAppService` 登录项;Windows 通过按用户的 `HKCU\…\Run`)、一个 **⌘Q / Ctrl+Q 确认对话框**(再按一次即跳过)、关闭窗口只隐藏但服务器继续运行、单实例锁,以及 **在浏览器中打开**、**重启服务器**、**查看日志** 等托盘操作。优先使用端口 4820(回退到 4821–4829,再到随机高位端口),若 4820 上已有健康的 dashboard 在运行则直接采用而不重复绑定,并**与 Web dashboard 共存** —— `npm run dev` 与桌面应用可同时运行,Hook 会同时分发到两者。通知以原生操作系统弹窗(toast)形式触发(Web Push 在 Electron 中无法可靠工作)。首次由应用自有的服务器启动时,它会自动安装 Claude Code Hook 并启动后台服务,因此仅安装应用的用户无需任何手动设置即可让事件流转。详见 [`DESKTOP.md`](./DESKTOP.md) 与 [`desktop/README.md`](./desktop/README.md) |

---

## 快速开始

### 前置条件

- **Node.js** >= 18.0.0(推荐 22+)
- **npm** >= 9.0.0

### 1. 安装

```bash
git clone https://github.com/hoangsonww/Claude-Code-Agent-Monitor.git
cd Claude-Code-Agent-Monitor
npm run setup
```

### 2. 配置 Claude Code Hook

```bash
npm run install-hooks
```

此命令会在 `~/.claude/settings.json` 中添加 Hook 条目,将事件转发到 Dashboard。已有的 Hook 配置会被保留。

### 3. 启动

```bash
# 开发模式(服务端和客户端均支持热重载)
npm run dev

# 生产模式(单进程,构建后的客户端)
npm run build && npm start
```

> [!TIP]
> **Makefile 替代方案** — 如果你安装了 `make`,所有命令也可通过 `make` 执行。运行 `make help` 查看所有目标,或使用快捷命令如 `make dev`、`make build`、`make test` 等。

### 4. 访问

| 模式 | 地址 |
| ----------- | ----------------------- |
| 开发 | `http://localhost:5173` |
| 生产 | `http://localhost:4820` |

> [!NOTE]
> 服务器默认绑定 `127.0.0.1`(开箱即用不可从网络访问 —— GHSA-gr74-4xfh-6jw9);如需在局域网暴露,请设置 `DASHBOARD_HOST` 和 `DASHBOARD_TOKEN`(设置后 `/api/*` 与 WebSocket 都需要该 token),并在 `DASHBOARD_ALLOWED_HOSTS` 中列出局域网主机名。参见 `.env.example` / `.github/SECURITY.md`。

### 5. 可选:构建并运行本地 MCP 服务器

```bash
npm run mcp:install
npm run mcp:build
npm run mcp:start # stdio(默认 — 用于 MCP 宿主集成)
npm run mcp:start:http # HTTP + SSE 服务器,端口 8819
npm run mcp:start:repl # 带 Tab 补全的交互式 CLI
```

stdio 模式下,配置你的 MCP 宿主(Claude Code / Claude Desktop / 其他 MCP 客户端):

- command: `node`
- args: `["<绝对路径>/mcp/build/index.js"]`

HTTP 模式下,远程 MCP 客户端连接 `http://127.0.0.1:8819/mcp`(Streamable HTTP)或 `http://127.0.0.1:8819/sse`(传统 SSE)。

详见 [mcp/README.md](./mcp/README.md) 了解完整的宿主配置、传输详情、安全标志和工具目录。

### 可选:生成演示数据

```bash
npm run seed
```

创建 8 个示例会话、23 个 Agent 和 106 个事件,让你可以立即浏览 UI。

### 替代方案:桌面应用(macOS 与 Windows)

如果你不想一直开着终端窗口,可以安装可选的**原生桌面应用**。它将 Express 服务器以进程内方式嵌入运行,提供菜单栏 / 通知区域(托盘)图标,并支持开机自启(macOS 登录项 / Windows 启动项)。

最快的方式是从 [最新 GitHub Release](https://github.com/hoangsonww/Claude-Code-Agent-Monitor/releases/latest) **下载预构建的安装包**(每当 `master` 上的 `package.json` 版本号被提升时,CI 都会自动发布一个新的 `vX.Y.Z`):

- **macOS** —— 下载 `ClaudeCodeMonitor--arm64.dmg`(Apple Silicon)或 `-x64.dmg`(Intel),并将 **Claude Code Monitor.app** 拖入 `/Applications`。
- **Windows** —— 下载 `ClaudeCodeMonitor-Setup--x64.exe`(安装版)或 `ClaudeCodeMonitor--x64-portable.exe`(免安装版),然后运行它。

若希望自行构建:

```bash
npm run desktop:install # 在 desktop/ 中安装 Electron + electron-builder(预检原生依赖;失败时打印安装帮助)
npm run desktop:dmg:arm64 # macOS:Apple Silicon 专用 DMG(快速)
npm run desktop:win # Windows:NSIS 安装包 .exe(在 Windows 上运行)
```

> [!TIP]
> DMG 在 macOS 上构建,Windows `.exe` 在 Windows 上构建 —— electron-builder 针对宿主操作系统打包。为自己的 Mac 构建时,请使用架构专用命令(`desktop:dmg:arm64` 或 `desktop:dmg:x64`),它们速度快得多;通用版 `desktop:dmg` 会把应用构建两次再合并,仅在制作发布产物时才需要。

完整的生命周期语义、托盘/菜单功能、签名与公证钩子详见下文的 [桌面应用(macOS 与 Windows)](#桌面应用macos-与-windows) 章节,以及 [`DESKTOP.md`](./DESKTOP.md)。

### 替代方案:Docker / Podman

项目包含 `Dockerfile` 和 `docker-compose.yml`。同时支持 Docker 和 Podman。

**使用 Docker Compose:**

```bash
docker compose up -d --build
```

**使用 Podman Compose:**

```bash
CLAUDE_HOME="$HOME/.claude" podman compose up -d --build
```

**使用纯 Docker 或 Podman(无 Compose):**

```bash
# Docker
docker build -t agent-monitor .
docker run -d --name agent-monitor \
-p 4820:4820 \
-v "$HOME/.claude:/root/.claude:ro" \
-v agent-monitor-data:/app/data \
agent-monitor

# Podman
podman build -t agent-monitor .
podman run -d --name agent-monitor \
-p 4820:4820 \
-v "$HOME/.claude:/root/.claude:ro" \
-v agent-monitor-data:/app/data \
agent-monitor
```

Dashboard 可通过 `http://localhost:4820` 访问。

**卷挂载:**

| 挂载 | 用途 |
|---|---|
| `~/.claude:/root/.claude:ro` | 读取历史会话用于导入 |
| `agent-monitor-data:/app/data` | 跨重启持久化 SQLite 数据库 |

> [!IMPORTANT]
> **注意:** Claude Code Hook 仍需指向宿主机上运行的 hook-handler 进程。容器本身不接收 Hook — 在宿主机上运行 `npm run install-hooks` 以配置 Hook 将数据 POST 到 `http://localhost:4820`。

---

## 工作原理

Dashboard 通过 Claude Code 原生 Hook 系统集成,提供 Agent 活动的实时监控。以下是架构和数据流概览:

```mermaid
sequenceDiagram
participant CC as Claude Code
participant HH as Hook Handler
participant API as Express 服务器
participant DB as SQLite
participant WS as WebSocket
participant UI as React 客户端

CC->>HH: stdin(JSON 事件)
HH->>API: POST /api/hooks/event
API->>DB: 插入/更新记录
API->>WS: 广播更新
WS->>UI: 推送消息
UI->>UI: 重新渲染组件

Note over CC,HH: Hook 在 SessionStart、
PreToolUse、PostToolUse、
Stop、SubagentStop、
SessionEnd、Notification 时触发。
压缩从 JSONL 检测
Note over API,DB: 事务性写入
带自动会话/Agent 创建
Note over WS,UI: ~0ms 延迟,
无轮询
```

### Hook 生命周期

1. **Claude Code** 在会话开始、工具使用、回合结束、子 Agent 完成和会话退出时触发 Hook
2. **Hook Handler**(`scripts/hook-handler.js`)从 stdin 读取 JSON 事件并 POST 到 API。5 秒超时静默失败,永不阻塞 Claude Code
3. **服务器** 在 SQLite 事务内处理事件:
- 首次接触时自动创建会话和主 Agent
- 检测 `Agent` 工具调用以追踪子 Agent 创建
- `SessionStart` 时,在会话和主 Agent 上盖上 `awaiting_input_since` 时间戳,使停在提示符前的全新 CLI 立即落入**等待中**
- `UserPromptSubmit` 时(用户按下回车),清除等待标志并将主 Agent 提升为 `working` — 这是文本响应回合开始的唯一可靠信号,因为它们不发出 `PreToolUse`
- `PreToolUse` 时将 Agent 设为 `working`(同时清除等待标志),`PostToolUse` 后保持 working 状态(也清除等待标志 — 用于处理用户在工具运行期间批准权限提示的场景)
- 非错误 `Stop` 时,主 Agent 变为 `waiting` — Claude 完成本回合,主动权交给用户。错误 `Stop` 会将 Agent 和会话标记为 `error`。后台子 Agent 继续运行
- 在权限 `Notification` 时(按消息模式匹配:`permission`、`waiting for input`、`needs your approval` 等),将 Agent 设为 `waiting` 并盖上 `awaiting_input_since`
- `SubagentStop` 故意不清除等待标志 — 后台子 Agent 完成不能说明用户是否已响应
- 通过 `SubagentStop` 单独标记子 Agent 为完成。`res.json()` 返回后,触发 fire-and-forget 的 `scanAndImportSubagents`,遍历会话的 `subagents/agent-*.jsonl` 文件,根据 `tool_use_id` 配对 `tool_use` ↔ `tool_result` 块,并在每个子 Agent 自己的 `agent_id` 下发出 `PreToolUse` + `PostToolUse` 事件 — 弥补子 Agent 内部工具调用对 dashboard 不可见的空白
- `SessionEnd` 时(CLI 进程退出),清除等待标志。如果会话已处于 `error` 状态,则保留错误状态;否则将所有 Agent 和会话标记为 `completed`
- `SessionStart` 时,任何无活动超过 `DASHBOARD_STALE_MINUTES`(默认 180 = 3 小时,可通过环境变量覆盖)的其他活跃会话自动标记为"abandoned",其 Agent 标记为完成。处理会话内的 `/resume`、Ctrl+C 和其他会话无 `SessionEnd` 而被孤立的场景
- **错误恢复**:只有 `UserPromptSubmit` 和 `PreToolUse` 可以将会话从 `error` 恢复为 `active` — 表示用户主动进行了重试
- 新工作事件到达时重新激活 completed/error/abandoned 会话(会话恢复)。Stop 和 SubagentStop 事件也会重新激活 completed/abandoned 会话 — 处理服务器启动前已导入的预存会话,其中第一个 Hook 事件可能是 Stop
- 检测对话压缩(JSONL Transcript 中的 `isCompactSummary` 条目)并创建 `Compaction` Agent 和事件。Token 基线在压缩中保留,不丢失任何用量。Transcript 读取使用基于 stat 的缓存和增量字节偏移读取 — 仅解析自上次读取后追加的新字节,长会话约提速 50 倍
- 从 JSONL Transcript 提取 API 错误(`isApiErrorMessage` 条目:配额限制、速率限制、invalid_request)和原始 `type: "error"` 响应,存储为 `APIError` 事件。回合耗时(`system` 子类型 `turn_duration`)存储为 `TurnDuration` 事件。工具结果错误(`toolUseResult.is_error`)追踪为 `ToolError` 事件
- **错误检测看门狗** — 后台定时器每 15 秒运行一次,扫描没有近期 Hook 事件(>10 秒)的活跃会话。它重新读取 Transcript 文件查找 API 错误(认证失败、速率限制、配额耗尽),从会话 `cwd` 推导 Transcript 路径(用于没有 `transcript_path` 的导入会话),并在发现 API 错误时将会话/Agent 标记为 `error`。这可以捕获 Claude CLI 在 API 错误后不触发 Hook 的情况(例如 401 认证失败时 CLI 只显示错误并等待)
- **用户中断(Esc)恢复** — 用户按 `Esc` 取消回合时**不会触发任何 Hook**(Claude Code 已知的限制),因此若不加干预,主 Agent 会永远卡在 `working` 状态。同一个 15 秒看门狗以两种方式恢复这些会话:(1) 当取消在 Transcript 中留下 `[Request interrupted by user]` 标记时(Esc 发生在已有部分输出之后),Transcript 缓存通过 `pendingInterrupt` 标记它 —— 该标志纯粹由 Transcript 顺序推导得出(最新的中断与最新的真实回合活动相比,使用同一时钟,因此即便是亚秒级取消也有效)—— 会话在约 15 秒内转入**等待中**;(2) 当 Esc 在**任何输出之前**按下时,Claude Code 完全不写入标记,因此应用空闲超时回退 —— 当主 Agent 处于 `working`、**没有进行中的工具**(`current_tool` 为 null),且 `DASHBOARD_WORKING_IDLE_SECONDS`(默认 `120`)期间**既无 Hook 事件也无 Transcript 推进**时,该回合被视为已死,会话转入**等待中**。两条路径都记录一个 `Interrupted` 事件,并将会话置于与正常 `Stop` 相同的等待中状态。流式输出(Transcript 仍在增长)和进行中的工具调用(`current_tool` 已设置)不受影响;罕见的误判会在下一次真实 Hook 时自愈
- **死亡会话存活性回收(liveness reap)** — 退出 Claude Code(Ctrl+C、关闭终端)会触发 `SessionEnd` Hook,但如果此刻仪表盘没有运行,该事件将永远丢失,会话会一直停留在**等待中**,直到废弃清理(默认 3 小时)。同一个 15 秒看门狗通过**进程存活性探测**弥补这一缺口:列出正在运行的 `claude` CLI 进程(macOS 上 `ps` + `lsof`,Linux 上 `/proc`),并将 `cwd` 中不存在任何存活 claude 进程的 `active` 会话标记为完成——落入与真实 `SessionEnd` 相同的 `completed` 状态,并在时间线上留下一条合成的 `SessionEnd` 事件。保护条件:在看门狗节拍上,会话的 Transcript 必须至少有 `DASHBOARD_LIVENESS_IDLE_SECONDS`(默认 `60`)未被写入(磁盘上没有 Transcript 时以最后一次 Hook 写入为后备时钟)——启动时的回收**跳过该门槛**,因此即使在启动前一秒退出的会话也会立即清除;探测在 Windows 上、容器内(看不到宿主机进程)、`ps`/`lsof` 失败时,或通过 `DASHBOARD_LIVENESS_PROBE=0` 显式禁用时(Hook 来自另一台机器的场景的逃生阀)会报告"无法回答"(不做任何更改)。误判的完成会自愈:下一个 Hook 事件会重新激活会话。除 15 秒看门狗节奏外,回收还会在**启动时立即运行**(清除上次运行遗留在 DB 中的死亡会话,让它们根本来不及渲染),并在**约 5 秒后再运行一次**(覆盖启动同步刚导入的会话),因此仪表盘停机期间死亡的会话绝不会显示为等待中
- 周期性服务器清理捕获遗漏事件检测的废弃会话和新压缩(例如 `/compact` 不触发 Hook、会话创建后几秒内 `/resume`)。频率从 `DASHBOARD_STALE_MINUTES` 派生(¼ 阈值,夹在 60 秒–5 分钟之间)。清理共享 Hook Handler 的 Transcript 缓存,避免重复 I/O。废弃会话清理还会驱逐 Transcript 缓存条目以限制内存使用
- **持续项目同步**(`startSessionSync`)让 `~/.claude/projects` 在一次性、由标记位把关的启动回填之外仍可被发现:之后才加入、其会话从不经过 Hook 流入的项目,否则在手动重新扫描之前都将不可见。启动时的立即扫描、一个去抖的 `fs.watch`(在 macOS/Windows 上递归监听,在 Linux 上监听根目录 + 直接子文件夹),以及一个 `DASHBOARD_SESSION_SYNC_MS` 轮询(默认 30 秒;`0` 禁用轮询,但监听器保持运行),三者共享同一个 mtime 缓存与一次合并扫描,只重新解析 mtime 前进过的文件 —— 并跳过已导入且未变更的会话、不再重新解析,因此重启成本保持为 O(新增/变更文件)。每个新发现/增长的会话都会广播 `session_created`/`session_updated` 外加其主 Agent,与 Hook 发出的帧相同
4. **WebSocket** 将变更广播到所有已连接客户端
5. **UI** 接收更新并重新渲染受影响的组件

### Agent 状态机

持久化状态:`working | waiting | completed | error`。`awaiting_input_since`
字段是补充性的 — 它记录 Agent 开始等待的时间点,用于显示等待时长,但 `waiting`
现在是真正的持久化状态。

```mermaid
stateDiagram-v2
[*] --> waiting: ensureSession(首个 hook)
waiting --> working: PreToolUse / UserPromptSubmit
working --> working: PostToolUse(工具完成)
working --> waiting: Stop,非错误
working --> waiting: Notification(输入提示)
working --> waiting: Esc 取消(看门狗:标记或空闲超时)
waiting --> error: Stop 有错误
working --> error: Stop 有错误
waiting --> error: 检测到 API 错误(看门狗)
working --> error: 检测到 API 错误(看门狗)
error --> working: UserPromptSubmit / PreToolUse(恢复)
working --> completed: SessionEnd
waiting --> completed: SessionEnd

note right of waiting
Agent 在回合之间或
等待用户输入
end note
```

### 会话状态机

持久化状态:`active | completed | error | abandoned`。**等待中**会话状态
是 UI 覆盖层(status=`active` 加上 `awaiting_input_since` 被设置)。

```mermaid
stateDiagram-v2
[*] --> waiting: SessionStart(status=active + 标志)
waiting --> active: UserPromptSubmit / PreToolUse / PostToolUse
active --> waiting: Stop,非错误(标志重新盖上)
active --> waiting: 权限 Notification(Agent → waiting)
active --> waiting: Esc 取消(看门狗:标记或空闲超时)
active --> error: Stop, stop_reason=error
active --> error: 检测到 API 错误(看门狗)
waiting --> error: 检测到 API 错误(看门狗)
error --> active: UserPromptSubmit / PreToolUse(恢复)
waiting --> completed: SessionEnd(CLI 退出)
active --> completed: SessionEnd(CLI 退出)
error --> error: SessionEnd(保留错误状态)
waiting --> abandoned: 过期 > DASHBOARD_STALE_MINUTES(默认 180)
active --> abandoned: 过期 > DASHBOARD_STALE_MINUTES
completed --> active: 会话恢复(新工作事件)
error --> active: 会话恢复
abandoned --> active: 会话恢复
completed --> [*]
error --> [*]
abandoned --> [*]
```

### 成本计算流程

```mermaid
flowchart LR
TU["token_usage 行
(按会话 × 模型)"] --> GROUP["按模型分组"]
PR["model_pricing 规则
(基于模式匹配)"] --> SORT["按特异性排序
(最长模式优先)"]
GROUP --> MATCH{"匹配模型
到定价规则"}
SORT --> MATCH
MATCH --> CALC["成本 = Σ (tokens / 1M) × 费率
针对 input、output、cache_read、cache_write"]
CALC --> RESULT["{ total_cost, breakdown[] }"]
style TU fill:#003B57,stroke:#005f8a,color:#fff
style PR fill:#6366f1,stroke:#818cf8,color:#fff
style RESULT fill:#10b981,stroke:#34d399,color:#fff
```

> [!IMPORTANT]
> 成本计算流程基于 Token 使用量和模型定价规则。确保你的定价规则是最新的以反映准确成本。通过设置页面更新模型定价表以保持准确的成本追踪 — Dashboard 不会自动从外部来源获取定价更新。设置定价规则后,Dashboard 会追溯应用于所有会话以保持一致的成本报告。

---

## 配置

| 环境变量 | 默认值 | 描述 |
| ----------------------- | ------------- | --------------------------------------------- |
| `DASHBOARD_PORT` | `4820` | Express 服务器端口 |
| `CLAUDE_DASHBOARD_PORT` | `4820` | Hook Handler 连接服务器使用的端口 |
| `DASHBOARD_STALE_MINUTES` | `180`(3 小时) | 一个仍为 `active` 的会话(包括正在**等待中**用户输入的会话——"等待中"是 `active` 行上的 UI 覆盖层,而非存储状态)在被自动标记为 **abandoned** 并从活跃列表中移除之前的无活动分钟数。由 15 秒看门狗和周期性维护清理(每 ¼ 该值运行一次,夹在 60 秒–5 分钟之间)执行。调低(例如 `60`)可获得更短的空闲超时 |
| `DASHBOARD_WORKING_IDLE_SECONDS` | `120` | 用于恢复**在任何输出之前**以 `Esc` 取消(不会留下 Transcript 标记)回合的空闲工作超时。当主 Agent 处于 `working`、没有进行中的工具,且在此时长内既无 Hook 事件也无 Transcript 推进时,看门狗将会话转入**等待中**。调低可获得更迅捷的恢复,但代价是长时间静默思考的回合上偶尔出现误判(会自愈) |
| `DASHBOARD_LIVENESS_PROBE` | `1`(开启) | 设为 `0` 可禁用看门狗的**死亡会话存活性回收**(基于 `ps`/`lsof` 的探测,将 `claude` 进程已不存在的 `active` 会话标记为完成——恢复仪表盘停机期间丢失的 `SessionEnd`)。当 Hook 从**另一台机器**发送到本仪表盘时应禁用,因为本地进程无法证明任何事情。在 Windows 和容器内自动禁用 |
| `DASHBOARD_LIVENESS_IDLE_SECONDS` | `60` | **看门狗节拍**存活性回收的空闲门槛:只有当会话的 Transcript 至少有这么长时间未被写入时(磁盘上没有 Transcript 时以最后一次 Hook 写入为后备时钟),才会将其标记为完成,因此回合中或刚 resume 的会话绝不会因一次瞬时的探测偏差而消失。启动时的回收跳过该门槛——boot 时由探测单独决定,因此启动前一刻退出的会话会立即清除 |
| `DASHBOARD_SESSION_SYNC_MS` | `30000` | 持续 `~/.claude/projects` 后台同步的轮询间隔(毫秒),用于显示启动后才加入、其会话从不经过 Hook 流入的项目。无论如何 `fs.watch` 监听器都会近乎即时触发;该轮询是安全兜底(监听器可能错过事件 / 在网络文件系统上不触发)。设为 `0` 可禁用轮询,同时让监听器保持运行 |
| `NODE_ENV` | `development` | 设为 `production` 以提供构建后的客户端 |

---

## `ccam` CLI

仪表盘的完整功能面同样可以在终端中使用——零依赖的 **`ccam`** CLI(`bin/ccam.js`),由 `npm run setup` 自动链接(失败即降级的 `npm link`)。CLI 通过 `~/.claude/.agent-dashboard.json`(与 hook 处理器相同的注册表)自动发现正在运行的服务器,可用 `CLAUDE_DASHBOARD_PORT`/`DASHBOARD_PORT` 覆盖,默认 `http://127.0.0.1:4820`。

```bash
# 服务器
ccam status # ● 运行中 / ○ 未运行 指示器
ccam start [--port N] # 在后台启动服务器(分离进程)

# 监控
ccam health # 仪表盘是否在运行?
ccam stats # 总量、今日事件、状态分布
ccam kanban # 会话 + agent 按状态列分组
ccam tail [--session ] # 终端里的实时事件流(Ctrl+C 停止)

# 数据
ccam sessions [--status s] [--q text] [--limit n]
ccam session # 详情:agent 树、成本、最近事件
ccam agents [--status s] [--session id]
ccam events [--session id] [--limit n]

# 洞察
ccam analytics # token 总量、常用工具、agent 类型
ccam workflows [--session id] # 工作流智能统计与模式
ccam runs [--session id] # 动态 Workflow 工具运行
ccam cost # 按模型细分的总预估成本

# 告警 & webhook
ccam alerts [--unacked] # 触发告警流
ccam alerts ack | ack-all # 确认告警
ccam rules # 告警规则列表
ccam webhooks # webhook 目标列表
ccam webhooks test # 发送合成测试告警

# 价格
ccam pricing # 定价规则列表
ccam pricing set --input N --output N [--cache-read N --cache-write N]
ccam pricing delete
ccam pricing reset

# 导入
ccam import rescan # 重新扫描 ~/.claude/projects
ccam import path # 导入目录下所有 .jsonl

# 管理
ccam doctor # 连接、hook 与数据库诊断
ccam info # 原始系统信息 JSON
ccam export [file.json] # 导出全部数据为 JSON
ccam cleanup --hours N --days M # 放弃滞留会话 / 清理旧会话
ccam reinstall-hooks # 重新安装 Claude Code hook
ccam clear-data --yes # 删除全部数据(必须 --yes)
ccam open # 在浏览器中打开仪表盘
ccam version # 打印 CLI 版本(也可用 --version / -v)
```

基于 API 的命令需要服务器在运行——未运行时,**只读命令会自动回退为直接读取 `data/dashboard.db`**(显示明确的 `⚠ Offline mode` 横幅,且数据库中已死亡的 `active` 会话会用服务器看门狗所用的同一进程存活性探测在显示层校正),而无法在无服务器时正确运行的命令(实时 `tail`、分析/成本计算、写操作)会打印 `○ Dashboard server is NOT running` 指示、具体原因和启动命令;`ccam start` 可在后台拉起生产服务器。读取类命令始终安全;唯一的破坏性命令(`clear-data`)没有显式 `--yes` 时拒绝执行。输出是完整的终端 UI——带右对齐数字列的框线表格、状态图标(`● active`、`○ waiting`、`✔ completed`、`✖ error`)、stats/analytics/cost 的内联条形图,以及真正的 `├─`/`└─` 代理树——ANSI 颜色在 TTY 上自动启用、管道输出时自动关闭,并可通过 `--no-color` / `NO_COLOR` / `FORCE_COLOR` 控制。若 `ccam` 不在 PATH 上,在仓库根目录运行一次 `npm link`。完整参考——标志、服务器发现顺序、安全模型、退出码——见 [docs/CLI.md](./docs/CLI.md)。

## npm 脚本

| 命令 | 描述 |
| ----------------------- | ---------------------------------------------------------- |
| `npm run setup` | 安装服务端和客户端依赖 |
| `npm run dev` | 同时启动服务端(watch 模式)+ 客户端(Vite HMR) |
| `npm run dev:server` | 仅启动 Express 服务器(`--watch`) |
| `npm run dev:client` | 仅启动 Vite 开发服务器 |
| `npm run build` | 构建 React 客户端到 `client/dist/` |
| `npm start` | 启动生产服务器(提供构建后的客户端) |
| `npm run install-hooks` | 在 `~/.claude/settings.json` 中配置 Claude Code Hook |
| `npm run seed` | 用示例数据填充数据库 |
| `npm run import-history` | 从 `~/.claude/` 导入历史会话(启动时也会运行) |
| `npm run clear-data` | 删除所有会话、Agent、事件和 Token 用量 |
| `npm run mcp:install` | 安装本地 MCP 包(`mcp/`)的依赖 |
| `npm run mcp:build` | 构建 MCP 服务器 TypeScript 到 `mcp/build/` |
| `npm run mcp:start` | 启动 MCP 服务器(stdio 传输 — 用于 MCP 宿主) |
| `npm run mcp:start:http` | 启动 MCP 服务器(HTTP + SSE 传输,端口 8819) |
| `npm run mcp:start:repl` | 启动 MCP 服务器(带 Tab 补全的交互式 REPL) |
| `npm run mcp:dev` | 以开发模式运行 MCP 服务器(`tsx`,stdio) |
| `npm run mcp:dev:http` | 以开发模式运行 MCP 服务器(`tsx`,HTTP + SSE) |
| `npm run mcp:dev:repl` | 以开发模式运行 MCP 服务器(`tsx`,交互式 REPL) |
| `npm run mcp:typecheck` | 类型检查 MCP 源码,不生成构建输出 |
| `npm run mcp:docker:build` | 用 Docker 构建 MCP 容器镜像(`agent-dashboard-mcp:local`) |
| `npm run mcp:podman:build` | 用 Podman 构建 MCP 容器镜像(`localhost/agent-dashboard-mcp:local`) |
| `npm run desktop:install` | 在 `desktop/` 工作区安装 Electron + electron-builder(为 Electron 的 ABI 重新编译 `better-sqlite3`);预检原生 `better-sqlite3` 构建,失败时打印可操作的安装帮助(含无工具链的替代方案) |
| `npm run desktop:build` | 预构建校验 + `tsc`,编译 Electron 主进程到 `desktop/out/` |
| `npm run desktop:dev` | 构建后启动 Electron,加载本地桌面应用 |
| `npm run desktop:test` | 运行桌面应用冒烟测试(启动 Electron 并探测 `/api/health`) |
| `npm run desktop:dmg` | **macOS:** 构建**通用版** DMG(x64 + arm64)— 用于发布,**构建较慢** |
| `npm run desktop:dmg:arm64` | **macOS:** 构建 Apple Silicon 专用 DMG — **快速** |
| `npm run desktop:dmg:x64` | **macOS:** 构建 Intel 专用 DMG — **快速** |
| `npm run desktop:win` | **Windows:** 构建 NSIS **安装包** `.exe`(x64)— 在 Windows 上运行 |
| `npm run desktop:win:portable` | **Windows:** 构建**免安装便携版** `.exe`(x64)— 在 Windows 上运行 |

---

## Agent 扩展

本仓库包含 Claude Code 和 Codex 的完整扩展层:

- Claude Code:`CLAUDE.md`、`.claude/rules/`、`.claude/skills/`
- Claude 子 Agent:`.claude/agents/`
- Codex:`AGENTS.md`、`.codex/rules/`、`.codex/agents/`、`.codex/skills/`

### 扩展架构

```mermaid
graph TD
USER["开发者"]
CLAUDE["Claude Code"]
CODEX["Codex"]
MEMORY["CLAUDE.md + .claude/rules/*"]
C_SKILLS[".claude/skills/*"]
AGENTS_MD["AGENTS.md"]
X_RULES[".codex/rules/*.rules"]
X_AGENTS[".codex/agents/*.toml"]
X_SKILLS[".codex/skills/*"]

USER --> CLAUDE
USER --> CODEX
CLAUDE --> MEMORY
CLAUDE --> C_SKILLS
CODEX --> AGENTS_MD
CODEX --> X_RULES
CODEX --> X_AGENTS
CODEX --> X_SKILLS
```

### Claude Code 层

- 持久上下文:
- [`CLAUDE.md`](./CLAUDE.md)
- 路径作用域规则:
- [`.claude/rules/backend-node.md`](./.claude/rules/backend-node.md)
- [`.claude/rules/frontend-react.md`](./.claude/rules/frontend-react.md)
- [`.claude/rules/mcp-typescript.md`](./.claude/rules/mcp-typescript.md)
- [`.claude/rules/docs-markdown.md`](./.claude/rules/docs-markdown.md)
- 技能:
- `repo-onboarding`
- `ship-feature`
- `mcp-operations`
- `debug-live-issue`
- 子 Agent:
- `backend-reviewer`
- `frontend-reviewer`
- `mcp-reviewer`

### Codex 层

- 持久上下文:
- [`AGENTS.md`](./AGENTS.md)
- 执行策略:
- [`.codex/rules/default.rules`](./.codex/rules/default.rules)
- 自定义子 Agent 模板:
- [`.codex/agents/`](./.codex/agents)
- 技能:
- [`.codex/skills/`](./.codex/skills)
- 设置:
- [`.codex/README.md`](./.codex/README.md)

---

## Tabby

Tabby 是一只固定在**每个页面右下角**的可爱 SVG 小猫伴侣。它会订阅实时会话 WebSocket 流,并据此做出反应——既为仪表盘增添一丝活力,又提供随手可用的状态概览与快捷操作。它完全构建在现有的 WebSocket 流之上:**无需新增后端、无需 API 密钥。**

### 会做出反应的吉祥物

Tabby 基于实时会话流呈现 **8 种情绪**——空闲(idle)、观察(watching)、开心(happy)、担忧(worried)、卡住(stuck)、思考(thinking)、睡觉(sleeping)、断开连接(disconnected)。它的眼睛会追踪光标,每种情绪都有专属动画,让小猫始终反映当前的会话状态。

### 气泡台词

在值得关注的事件发生时(会话开始 / 结束、出现错误、运行完成),Tabby 会弹出**气泡台词**。台词带有节流,并且可以静音。

### 面板(⌘B / Ctrl+B)

点击小猫,或按 **⌘B / Ctrl+B** 打开面板(按 Esc 关闭)。面板包含:

- **实时状态行**:N 个进行中 · M 个出错 · 连接状态。
- **快捷操作**:跳转到 Run Claude / 活动 / 会话 / 出错的会话,静音,清除提醒。
- **Ask 提问框**:在本地回答简单的状态类问题;其他问题则交给现有的 **Run Claude** 页面(`/run?prompt=...`),以启动一个真正的 Claude Code 会话。**无需新增后端、无需 API 密钥。**

### 无障碍与设置

Tabby 完全构建在现有的 WebSocket 流之上,支持键盘操作、`aria-live`,并尊重 `prefers-reduced-motion` 设置。可在「设置」中随时启用或禁用 Tabby。相关代码位于 `client/src/components/Tabby/`。

---

## MCP 集成

本项目在 `mcp/` 目录下包含一个本地生产级 MCP 服务器,将 Dashboard 操作暴露为 AI Agent 的工具。支持三种传输模式以适应不同的集成场景。

### MCP 传输模式

```mermaid
flowchart LR
subgraph Transports["传输模式"]
STDIO["stdio\n(默认)"]
HTTP["HTTP + SSE\n(端口 8819)"]
REPL["交互式 REPL\n(终端 CLI)"]
end

subgraph Protocols["线路协议"]
P1["JSON-RPC\nstdin/stdout"]
P2["Streamable HTTP (2025-11-25)\n传统 SSE (2024-11-05)"]
P3["直接调用\nTab 补全 + 彩色输出"]
end

STDIO --> P1
HTTP --> P2
REPL --> P3

style STDIO fill:#6366f1,stroke:#818cf8,color:#fff
style HTTP fill:#f59e0b,stroke:#fbbf24,color:#000
style REPL fill:#a855f7,stroke:#c084fc,color:#fff
```

| 模式 | 命令 | 使用场景 |
| --- | --- | --- |
| **stdio** | `npm run mcp:start` | Claude Code、Claude Desktop、IDE MCP 宿主 |
| **HTTP** | `npm run mcp:start:http` | 远程 MCP 客户端、Web 集成、多会话 |
| **REPL** | `npm run mcp:start:repl` | 运维调试、手动工具调用、本地管理 |


MCP REPL

### MCP 架构

```mermaid
graph LR
HOST["MCP 宿主
(Claude Code / Claude Desktop)"]
HTTP_CLIENT["远程 MCP 客户端"]
OPERATOR["操作员 CLI"]

MCP_STDIO["MCP 服务器
stdio"]
MCP_HTTP["MCP 服务器
HTTP :8819"]
MCP_REPL["MCP 服务器
REPL"]

API["Dashboard API
Express /api/*"]
DB["SQLite
data/dashboard.db"]

HOST -->|"stdin/stdout"| MCP_STDIO
HTTP_CLIENT -->|"POST /mcp · GET /sse"| MCP_HTTP
OPERATOR -->|"交互式 CLI"| MCP_REPL

MCP_STDIO --> API
MCP_HTTP --> API
MCP_REPL --> API
API --> DB

style HOST fill:#6366f1,stroke:#818cf8,color:#fff
style HTTP_CLIENT fill:#f59e0b,stroke:#fbbf24,color:#000
style OPERATOR fill:#a855f7,stroke:#c084fc,color:#fff
style MCP_STDIO fill:#0f766e,stroke:#14b8a6,color:#fff
style MCP_HTTP fill:#0f766e,stroke:#14b8a6,color:#fff
style MCP_REPL fill:#0f766e,stroke:#14b8a6,color:#fff
style API fill:#339933,stroke:#5cb85c,color:#fff
style DB fill:#003B57,stroke:#005f8a,color:#fff
```

### MCP 工具全景

```mermaid
graph TD
ROOT["MCP 工具"]
OBS["可观测性
health, stats, analytics,
system info, export, snapshot"]
SES["会话
list/get/create/update"]
AGT["Agent
list/get/create/update"]
EVT["事件和 Hook
list events, ingest hook events"]
PRC["定价和成本
规则 CRUD, 总成本/会话成本, 重置默认"]
MNT["维护
cleanup, reimport, reinstall hooks, clear-all(受保护)"]

ROOT --> OBS
ROOT --> SES
ROOT --> AGT
ROOT --> EVT
ROOT --> PRC
ROOT --> MNT
```

### MCP 安全模型

```mermaid
flowchart TD
CALL["tools/call"] --> VALIDATE["zod 输入验证"]
VALIDATE --> TYPE{"工具类型?"}
TYPE -->|只读| EXEC["执行"]
TYPE -->|变更| M_FLAG{"ALLOW_MUTATIONS?"}
M_FLAG -->|否| DENY1["❌ 拒绝"]
M_FLAG -->|是| DEST{"破坏性?"}
DEST -->|否| EXEC
DEST -->|是| D_FLAG{"ALLOW_DESTRUCTIVE?"}
D_FLAG -->|否| DENY2["❌ 拒绝"]
D_FLAG -->|是| TOKEN{"confirmation_token?"}
TOKEN -->|无效| DENY3["❌ 拒绝"]
TOKEN -->|有效| EXEC
EXEC --> RESULT["返回工具结果"]

style EXEC fill:#339933,stroke:#5cb85c,color:#fff
style DENY1 fill:#dc2626,stroke:#f87171,color:#fff
style DENY2 fill:#dc2626,stroke:#f87171,color:#fff
style DENY3 fill:#dc2626,stroke:#f87171,color:#fff
```

### MCP 运行模式

- 只读模式(默认):`MCP_DASHBOARD_ALLOW_MUTATIONS=false`
- 管理模式:`MCP_DASHBOARD_ALLOW_MUTATIONS=true`
- 破坏性模式:需要同时满足:
- `MCP_DASHBOARD_ALLOW_MUTATIONS=true`
- `MCP_DASHBOARD_ALLOW_DESTRUCTIVE=true`
- 工具输入 `confirmation_token: "CLEAR_ALL_DATA"`

完整详情:[mcp/README.md](./mcp/README.md)

---

## API 参考

所有端点返回 JSON。错误响应遵循格式 `{ error: { code, message } }`。

### OpenAPI / Swagger

| 方法 | 路径 | 描述 |
| ------ | ------------------- | ----------------------------------- |
| `GET` | `/api/openapi.json` | 原始 OpenAPI 3.0 规范 |
| `GET` | `/api/docs` | 交互式 Swagger UI 文档 |
| `GET` | `/api/redoc` | ReDoc 参考文档(针对阅读优化的三栏式 API 文档)。**自托管**:捆绑包从本地 `/api/redoc/redoc.standalone.js` 提供,不依赖 CDN,可离线使用 |

OpenAPI 文档由 `server/openapi.js` 生成,Swagger UI 由后端直接提供。

仓库根目录还提交了一份 `openapi.yaml`,它镜像实时规范,并通过 `npm run openapi:yaml` 重新生成(唯一可信来源为 `server/openapi.js`,切勿手动编辑)。

API 文档现已**全面覆盖**:每个后端路由都有文档说明(共 75 个路径条目),包含参数、Schema、字段描述与示例;新增文档的路由组包括 `/api/push`、`/api/cc-config`、`/api/run`、`/api/workflows/runs`、`/api/sessions/facets` 与 `/api/settings/claude-home`。


Swagger UI


ReDoc UI

### 健康检查

| 方法 | 路径 | 描述 |
| ------ | ------------- | ------------------------------------- |
| `GET` | `/api/health` | 返回 `{ status: "ok", timestamp }` |

### 会话

| 方法 | 路径 | 查询参数 | 描述 |
| ------- | ------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `GET` | `/api/sessions` | `status`、`q`、`limit`、`offset` | 列出会话(含 Agent 计数与每会话费用)。`q` 对 `id` / `name` / `cwd` 做不区分大小写搜索;`limit` 默认 50,最大 10000;响应包含 `total` 字段供分页器使用 |
| `GET` | `/api/sessions/:id` | -- | 会话详情(含 Agent 和事件) |
| `GET` | `/api/sessions/:id/stats` | -- | 会话详情概览面板使用的聚合计数:事件总数、按类型分类的事件、Top 工具用量、错误数、按类型/状态的 Agent 数、子 Agent 类型分布、Token 总量、时间范围 |
| `GET` | `/api/sessions/:id/transcripts` | -- | 列出会话的可用 JSONL 转录文件(主 + 子 Agent + 压缩) |
| `GET` | `/api/sessions/:id/transcript` | `agent_id`、`limit`、`offset`、`after`、`before` | 从指定转录文件流式读取消息,使用游标分页 |
| `POST` | `/api/sessions` | -- | 创建会话(基于 `id` 幂等) |
| `PATCH` | `/api/sessions/:id` | -- | 更新会话状态/元数据 |

### Agent

| 方法 | 路径 | 查询参数 | 描述 |
| ------- | ----------------- | ----------------------------------------- | ----------------------------- |
| `GET` | `/api/agents` | `status`、`session_id`、`limit`、`offset` | 列出 Agent(支持筛选) |
| `GET` | `/api/agents/:id` | -- | 单个 Agent 详情 |
| `POST` | `/api/agents` | -- | 创建 Agent |
| `PATCH` | `/api/agents/:id` | -- | 更新 Agent 状态/任务/工具 |

### 事件

| 方法 | 路径 | 查询参数 | 描述 |
| ------ | ------------- | ------------------------------- | -------------------------- |
| `GET` | `/api/events` | `session_id`、`limit`、`offset` | 列出事件(最新优先) |

### 统计

| 方法 | 路径 | 描述 |
| ------ | ------------ | ------------------------------------------------------ |
| `GET` | `/api/stats` | 聚合计数、状态分布、WS 连接数 |

### 分析

| 方法 | 路径 | 描述 |
| ------ | ---------------- | ---------------------------------------------------------- |
| `GET` | `/api/analytics` | 用于图表和趋势视图的 Token / 工具 / 会话聚合数据 |

### Hook

| 方法 | 路径 | 描述 |
| ------ | ------------------ | -------------------------------------------- |
| `POST` | `/api/hooks/event` | 接收并处理 Claude Code Hook 事件 |

**Hook 事件载荷:**

```json
{
"hook_type": "PreToolUse",
"data": {
"session_id": "abc-123",
"tool_name": "Bash",
"tool_input": { "command": "ls -la" }
}
}
```

### 定价

| 方法 | 路径 | 描述 |
| -------- | ------------------------ | ---------------------------------------- |
| `GET` | `/api/pricing` | 列出所有定价规则 |
| `PUT` | `/api/pricing` | 创建或更新定价规则 |
| `DELETE` | `/api/pricing/:pattern` | 删除定价规则 |
| `GET` | `/api/pricing/cost` | 所有会话的总成本 |
| `GET` | `/api/pricing/cost/:id` | 指定会话的成本明细 |

### 工作流

| 方法 | 路径 | 描述 |
| ------ | ----------------------------- | ------------------------------------------------------- |
| `GET` | `/api/workflows` | 聚合工作流数据(编排、工具、模式)。可选 `?status=active|completed` 查询参数按会话状态筛选全部 11 个数据模块 |
| `GET` | `/api/workflows/session/:id` | 按会话下钻(Agent 树、工具时间线、事件) |

### 设置

| 方法 | 路径 | 描述 |
| ------ | ------------------------------ | ------------------------------------------------ |
| `GET` | `/api/settings/info` | 系统信息、数据库统计、Hook 状态 |
| `POST` | `/api/settings/clear-data` | 删除所有会话、Agent、事件、Token 用量 |
| `POST` | `/api/settings/reinstall-hooks` | 重新安装 Claude Code Hook |
| `POST` | `/api/settings/reset-pricing` | 重置定价为默认值 |
| `GET` | `/api/settings/export` | 以 JSON 下载方式导出所有数据 |
| `POST` | `/api/settings/cleanup` | 废弃过期会话、清除旧数据 |

### 导入历史(Import History)

将已有的 Claude Code 会话从三种不同来源导入到仪表盘。三条路径都汇入
服务器用于实时采集的同一套解析管线(`parseSessionFile` +
`importSession`),因此导入后的 Token 数量、按模型计费、compactions、
子 Agent、工具调用以及回合耗时与实时捕获的结果**完全一致**。重复导入
是幂等的:会话以 UUID 去重,compaction `baseline_*` 列保留压缩前的
Token 总量,所以多次运行导入也绝不会重复计算用量或成本。

```mermaid
flowchart LR
subgraph 来源
A1["默认文件夹
~/.claude/projects"]
A2["自定义文件夹
任意绝对路径"]
A3["上传文件
.jsonl / .meta.json /
.zip / .tar(.gz) / .gz"]
end

A1 -->|POST /api/import/rescan| R["server/routes/import.js"]
A2 -->|POST /api/import/scan-path| R
A3 -->|POST /api/import/upload
multipart| R

R -->|解压 + 路径穿越防护
+ zip-bomb 限额| X["server/lib/archive.js"]
R -->|递归遍历| I["importFromDirectory
(scripts/import-history.js)"]
X --> I
I -->|与实时 Hook 采集
相同的管线| P["parseSessionFile +
importSession"]
P -->|预处理语句,
单事务| D[("SQLite
sessions / agents / events /
token_usage")]
I -.->|import.progress
已节流| W["WebSocket /ws"]
W -.-> U["Settings → Import History
进度条 + 结果卡片"]

style A1 fill:#6366f1,stroke:#818cf8,color:#fff
style A2 fill:#6366f1,stroke:#818cf8,color:#fff
style A3 fill:#6366f1,stroke:#818cf8,color:#fff
style R fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style X fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style I fill:#1a1a28,stroke:#2a2a3d,color:#e4e4ed
style P fill:#f59e0b,stroke:#fbbf24,color:#000
style D fill:#10b981,stroke:#34d399,color:#fff
style U fill:#a855f7,stroke:#c084fc,color:#fff
```

**API 路由**

| 方法 | 路径 | 描述 |
| ------ | ----------------------- | ---------------------------------------------------------------- |
| `GET` | `/api/import/guide` | 按操作系统返回路径、打包命令、支持扩展名与分步说明 |
| `POST` | `/api/import/rescan` | 重新扫描默认目录 `~/.claude/projects` |
| `POST` | `/api/import/scan-path` | 扫描任意绝对路径的目录(body `{ path }`);递归遍历子目录 |
| `POST` | `/api/import/upload` | 多部分上传 `.jsonl`、`.meta.json`、`.zip`、`.tar(.gz)`、`.gz` |

**支持的输入。** 独立的 `.jsonl` 会话转录、配套的 `.meta.json`
元数据文件,以及包含任意嵌套目录结构的归档文件(`.zip`、`.tar`、
`.tar.gz`/`.tgz`、`.gz`)。Claude Code 的两种官方布局都会被自动识别:
`//subagents/agent-*.jsonl`(默认)和
`/subagents//agent-*.jsonl`(备选)。

**准确性保证。** 会话按 UUID 去重;重复导入始终安全。compaction 的
`baseline_input` / `baseline_output` / `baseline_cache_read` /
`baseline_cache_write` 列保留了压缩之前的 Token 总量,因此重新导入
压缩后的 JSONL 永远不会抹掉历史成本。

**安全性。** 归档解压会对每个条目进行路径穿越校验(绝对路径和 `..`
路径段会被拒绝)。可配置的解压尺寸上限
(`CCAM_IMPORT_MAX_EXTRACT_BYTES`,默认 4 GB)可阻止 zip/tar/gzip
炸弹。上传大小按单文件(`CCAM_IMPORT_MAX_BYTES`,默认 1 GB)与单次
请求(`CCAM_IMPORT_MAX_FILES`,默认 2000)分别限制。每个请求使用
**独立**的临时目录,`finally` 中会被回收——即使 multer 提前拒绝了所有
文件也会被清理。

**进度。** 导入活动通过现有的 WebSocket 以 `import.progress` 消息广播
(`phase`:`start` / `scan` / `extract` / `parse` / `complete` /
`error`),并进行节流以避免在大批量导入时刷屏。

**UI。** 在 **Settings → Import History** 面板中使用拖放式向导,查看
分步指引、实时进度,以及导入完成后的结果卡片(imported / enriched /
skipped / errors 计数)。


Import History UI

### WebSocket

连接 `ws://localhost:4820/ws` 接收实时推送消息:

```json
{
"type": "agent_updated",
"data": { "id": "...", "status": "working", "current_tool": "Edit" },
"timestamp": "2026-03-05T15:43:01.800Z"
}
```

**消息类型:** `session_created`、`session_updated`、`agent_created`、`agent_updated`、`new_event`

```mermaid
stateDiagram-v2
[*] --> Connecting: 组件挂载
Connecting --> Connected: onopen
Connected --> Closed: onclose / onerror
Closed --> Connecting: setTimeout(2000ms)
Connected --> [*]: 组件卸载
Closed --> [*]: 组件卸载
```

---

## Hook 事件

Dashboard 处理以下 Claude Code Hook 类型:

| Hook 类型 | 触发时机 | Dashboard 操作 |
| -------------- | ------------------------------ | -------------------------------------------------------------------------------------------- |
| `SessionStart` | Claude Code 会话开始 | 创建会话和主 Agent。盖上 `awaiting_input_since`,使新会话立即落入**等待中**。重新激活恢复的会话。废弃无活动超过 `DASHBOARD_STALE_MINUTES`(默认 180)的孤立会话 |
| `UserPromptSubmit` | 用户在提示符前按下回车 | 清除等待标志并将主 Agent 提升为 `working` — 文本响应回合开始的唯一可靠信号,因为它们不发出 `PreToolUse` |
| `PreToolUse` | Agent 开始使用工具 | 清除等待标志,设置 Agent 为 `working`,设置 `current_tool`。如果工具是 `Agent`,创建子 Agent 记录 |
| `PostToolUse` | 工具执行完成 | 清除等待标志(用于处理用户在工具运行期间批准权限提示的场景)。清除 `current_tool`。Agent 保持 `working` |
| `Stop` | Claude 完成响应 | 非错误:主 Agent → `waiting` — Claude 完成本回合,主动权交给用户。`stop_reason=error`:将 Agent 和会话标记为 `error`。后台子 Agent 继续运行 |
| `SubagentStop` | 后台 Agent 完成 | 通过描述、类型或任务匹配并完成子 Agent。故意不清除等待标志 — 子 Agent 完成不能说明用户是否已响应。**触发 fire-and-forget 的 JSONL 扫描**(`scanAndImportSubagents`),在子 Agent 自己的 `agent_id` 下为每个 tool 发出 `PreToolUse` + `PostToolUse` 事件,使 Timeline 显示子 Agent 运行的所有 tool,而不仅仅是 spawn 标记 |
| `Notification` | Agent 通知 | 记录事件。权限/输入提示消息将 Agent 设为 `waiting` 并盖上 `awaiting_input_since`(模式:`permission`、`waiting for input`、`needs your approval` 等)。压缩通知标记为 `Compaction` 事件。如果启用,触发浏览器通知 |
| `SessionEnd` | Claude Code CLI 进程退出 | 清除等待标志。如果会话已处于 `error` 状态,则保留错误状态;否则将所有 Agent 和会话标记为 `completed` |
| `Compaction` | JSONL 中检测到 `/compact` | 创建压缩子 Agent(类型 `compaction`)和 Compaction 事件。通过 Transcript JSONL 中的 `isCompactSummary` 条目检测。也可由周期性扫描器对活跃会话检测 |
| `APIError` | JSONL Transcript 中的 API 错误 | 从 `isApiErrorMessage` 条目(配额、速率限制、invalid_request)和原始 `type: "error"` 响应中提取。**立即将会话和 Agent 标记为 `error`** — 之前仅记录事件而不更改状态。存储为包含错误详情的事件 |
| `Interrupted` | 用户取消的回合(Esc) | 由看门狗合成 —— `Esc` 不触发任何 Hook,因此从 Transcript 的 `[Request interrupted by user]` 标记,或当 Esc 发生在任何输出之前时从空闲工作超时(`DASHBOARD_WORKING_IDLE_SECONDS`)检测出卡住的 `working` 会话。会话转入**等待中**(与正常 `Stop` 相同) |
| `TurnDuration` | JSONL Transcript 中的回合计时 | 从 `system` 子类型 `turn_duration` 消息中提取,含 `durationMs`。存储为回合级计时分析事件 |
| `ToolError` | JSONL 中的工具结果错误 | 从 `toolUseResult.is_error` 条目中提取。追踪工具级失败用于错误传播分析 |

---

## 浏览器通知

Dashboard 支持通过 Web Push (VAPID) 实现持久化浏览器通知。即使 Dashboard 标签页未聚焦或浏览器处于后台,也能提供实时警报。

### 工作原理

1. **启用** — 在设置页面通过主开关启用通知
2. **授权** — 在浏览器提示时授予权限 — 这将注册一个 Service Worker 并创建一个推送订阅
3. **配置** — 选择哪些事件触发通知:

| 事件 | 默认 | 描述 |
| ---------------------------- | ------- | --------------------------------------------------------------- |
| 新会话开始 | 开 | 新 Claude Code 会话创建时触发 |
| Claude 完成响应 | 关 | Claude 完成响应回合时 `Stop` 事件触发 |
| 会话关闭 | 关 | CLI 进程退出时 `SessionEnd` 触发 |
| 会话错误 | 开 | 会话以错误结束时触发 |
| 子 Agent 生成 | 关 | 后台子 Agent 创建时触发 |

此外,来自 Claude Code 的任何 `Notification` Hook 事件都会触发浏览器通知(只要主开关启用),不受按事件开关影响。

### 通知架构

- **VAPID 管道:** 服务端使用 `web-push` 进行安全消息传递。VAPID 密钥自动生成并存储在 `data/vapid-keys.json`。
- **Service Worker:** 专用 Worker (`client/public/sw.js`) 处理传入的 `push` 事件,并以 `silent: false` 显示通知,以确保在 macOS 上播放音效。
- **订阅:** 浏览器特定的端点存储在 SQLite 的 `push_subscriptions` 表中。
- **持久性:** 由于 Service Worker 在后台运行,即使浏览器已关闭,通知仍能送达。
- **测试通知:** 设置页面中的按钮可让你验证 VAPID 管道和音效播放。

---

## 更新提醒

Dashboard 会监视自身的 git 检出,当规范默认分支领先于 HEAD 时弹出模态框。**支持分支与 fork:** 若配置了 `upstream` 远程(fork 的常规约定),则优先于 `origin`;所选远程的 `master`/`main`/`HEAD` 即为对比引用。`manual_command` 会根据用户处境调整——只有在本地分支真正跟踪规范引用时才用 `git pull --ff-only`,否则用 `git fetch`(fork 场景再加上 fast-forward 合并),命令永不撒谎。用户得到的是要在终端里执行的确切命令——服务端**永远不会**自动拉取或重启自己,这样机制在�