DeepSeek Harness Hub
← 返回列表

epool86/dsh-monitor

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
未验证

对 DeepSeek Harness 写入 /.dsh 的会话日志进行只读实时查看,

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/17 · 已提供中文文档

DeepSeek Harness 的实时任务控制:实时观看你的智能体和子智能体工作。

综合分
41.7
GitHub 分
41.7
用户评分
★ Stars
11
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add epool86/dsh-monitor
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/20(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

DSH 任务控制台

对 DeepSeek Harness 写入 ~/.dsh 的会话日志进行只读实时查看,
回答智能体们现在在做什么?

DSH 任务控制台:主智能体位于中心,四个正在工作的子智能体环绕其周围,琥珀色状态环,信号点沿边缘流动,旁边是实时活动流和智能体名册

整个应用就是一个页面——index.php——外加 api.php。它读取 DSH
已经写入的会话日志,因此 DSH 无需处于运行状态,并且 ~/.dsh 下的任何内容都
绝不会被写入或移动。

此应用中没有任何身份验证。 它会渲染你的全部会话
历史:每一条提示、回复、命令、文件路径和工具结果。请将其保持在
127.0.0.1 上,或置于需要身份验证的代理之后——参见
在暴露它之前。

要求

| 组件 | 原因 |
|---|---|
| PHP 8.2+ | 整个应用 |
| PHP FFI 扩展 | 驱动系统 libzstd(参见下文) |
| libzstd ≥ 1.4 | 会话日志是一系列串联的 zstd 帧 |
| DSH 会话日志 | ~/.dsh/sessions/ 必须存在且可读 |

安装与设置

无需构建步骤,无需 Composer 安装,无需数据库,无需配置文件。克隆它,检查
要求,将 Web 服务器指向该目录。

git clone https://github.com//dsh-mission-control.git
cd dsh-mission-control

1. 检查要求

php -v                       # 8.2 或更新版本
php -m | grep -i '^ffi'      # 必须列出 FFI
ldconfig -p | grep libzstd   # 必须找到 libzstd.so.1
ls ~/.dsh/sessions           # DSH 必须已写入至少一个会话

如果有缺失:

| 发行版 | 安装 |
|---|---|
| Debian / Ubuntu | sudo apt install php8.2-cli php8.2-fpm php8.2-ffi libzstd1 |
| Fedora / RHEL | sudo dnf install php-cli php-fpm php-ffi libzstd |
| macOS (Homebrew) | brew install php zstd |

在 Debian 和 Ubuntu 上,FFI 通常包含在 php8.x-common 中;如果 php -m 没有
列出它,请安装 php8.2-ffi 并重启 PHP。

2. 运行它

最快的方式——使用 PHP 自带的服务器,在你自己的机器上查看它:

php -S 127.0.0.1:8080
open http://127.0.0.1:8080/

它只绑定到 localhost,不适合对外暴露。它在任何 SAPI 下都能工作:如果
FFI::cdef() 在进程内不可用,解码器会自动通过 shell 调用
bin/zstddump.php,这就是为什么同样的代码在 FPM 下也能原样运行。

或者置于 nginx + PHP-FPM 之后——任何普通的 PHP 虚拟主机都可以:

server {
listen 80;
server_name dsh.test;
root /path/to/dsh-mission-control;
index index.php;

location / { try_files $uri $uri/ /index.php?$query_string; }

location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
}

keeps .git and the var/ cache out of reach
location ~ /\.(?!well-known) { deny all; }
}

3. 一个坑:PHP 以哪个用户运行

应用通过读取 $HOME 来定位 DSH,并回退到 PHP 进程所运行用户的 home 目录。因此 PHP 用户必须是拥有 ~/.dsh 的那个用户 —— 通常就是你本人。

如果 PHP-FPM 以 www-data 运行,而你的日志在 /home/you/.dsh,仪表盘就会显示为空。改为以你自己的身份运行该 pool:

; /etc/php/8.2/fpm/pool.d/dsh.conf
[dsh]
user = yourname
group = yourname
listen = /run/php/php8.2-fpm-dsh.sock

……然后把 fastcgi_pass 指向那个 socket。

同一个用户还需要能够写入项目内的 var/cache/(那是解码日志缓存,也是这个应用唯一会写入的东西),所以请确保项目目录对该用户可写 —— 或者预先创建好由该用户拥有的 var/cache。

4. 打开它

打开站点根目录。不带 ?id= 时,它会 跟随最新的那个会话,所以只需启动一个 DSH 会话并委派给一个子代理 —— 机器人会在一两秒内出现。追加 ?id= 可以固定到某个会话。

监视器(实时)

每 1.5 秒轮询一次 api.php?action=live 并重绘。它的构建基于这样一个事实:DSH 将每个子代理存储为 它自己的会话日志,通过 parentSession 关联,并在父会话中用一个携带人类可读标签的 subagent/catalog 事件来宣告它。

| 区域 | 显示内容 |
|---|---|
| 指标卡片 | 子代理工作中/已完成/停滞、轮次、步骤、工具调用、错误、token、消耗速率(平滑处理,带迷你走势图)、已用时间、距上次事件的时间 |
| 代理星座图 | 机器人:main 在中心,每个子代理按委派深度环绕,按状态着色 |
| 连线 | 代理工作时,发光的信号点向外流动;当它完成时,信号点会回流到 main 几秒钟(交接) |
| NOW 栏 | 根代理这一秒正在执行的工具,或在等待模型时显示“思考中” |
| 实时活动 | 来自每个代理的合并信息流,最新的在前,短名称,进行中的工具显示旋转指示器 |
| 代理卡片 | 每个代理:状态、深度、工具数量、错误、持续时间、token、当前活动、工具构成条 |

页眉中的控件:

| 按钮 | 效果 |
|---|---|
| follow: on/off | on(不带 ?id 打开时的默认值)跟踪最新的那个会话,因此之后启动的会话会被自动接管。off 将页面固定到你正在观看的会话。 |
| finished: auto/shown/hidden | 循环切换三种模式。auto(默认)在代理最后一次事件 6 秒后将其移除;shown 保留每个代理;hidden 从不绘制已完成的代理。同时作用于星座图和名册。 |
| pause | 停止轮询而不丢失当前视图。 |

代理永远不会自行消失

这个屏幕上的一个代理是 磁盘上的一个文件,而不是一个正在运行的进程:监视器每次轮询都会重新 glob ~/.dsh/sessions,并为它找到的每个日志绘制一个机器人。DSH 没有
删除或保留 API,所以已完成的 agent 日志会永远保留,agent 也会永远留在屏幕上——因此才有了自动隐藏,它是一种视图过滤器,而不是删除。

子 agent 以 mode: "continuable" 生成:harness 会保留一个持久会话,如果向该 agent 再发送一条消息,就会启动一个全新的 turn。这会写入一个新的 turn/start,状态会翻回 working,并且该 agent 会自行重新出现——所以隐藏是安全的。它们不会自行唤醒;只有显式消息才能将其唤醒。

自动隐藏有意以已完成为依据,而不是以原始的静默时间为依据:一个正在等待模型的 agent 在真正工作时会有 20–60 秒不写入任何日志事件,所以仅凭静默时间隐藏会让机器人在思考中途闪烁消失又出现。

停滞的 agent

状态来自日志:当 turn/start 的数量多于 turn/end 时,就有一个 turn 处于打开状态。一个被中断的可继续子 agent 可能会留下一个没有任何后续内容的打开 turn——harness 会启动一个全新的 turn 来传递“已停止”通知,该 turn 被放弃,之后再也不会有任何事件来平衡这个计数。这样的 agent 会被永久读取为 working,这就是一次现场演示最终出现幽灵机器人的原因,而且没有任何办法清除它们。

因此,一个打开的 turn 如果既已静默超过 5 分钟,又没有任何正在执行的工具,就会被报告为 stalled(红色),自动隐藏会像对待已完成的 agent 一样将其丢弃。要求同时满足这两个条件才使其安全:一个缓慢的命令会在其整个运行期间持有一个待处理的工具调用,所以真正长时间的工具调用绝不会被误标,而一个仅仅在思考的 agent 离 5 分钟还差得远。

一个繁忙的环会随 agent 数量增大其半径,标签会从每个节点向外辐射,因此十个并发委派仍然可读,而不会相互重叠。

轨道与碰撞避免

每个子 agent 沿着其深度对应的环以自己随机的速度和方向漂移(0.075–0.24 rad/s,所以一圈大约需要 26–84 秒)。因此,这个星座是一个活的东西,而不是每 1.5 秒轮询就跳一下的快照。有两条规则让机器人彼此分开:

| 规则 | 效果 |
|---|---|
| 预测性反弹 | 一个 agent 在 88 px 内正在接近邻居时,会反转两个 agent 的方向,所以它会在接触前转向离开 |
| 硬分离 | 任何仍在 52 px 内的东西——机器人宽约 49 px——会沿每个 agent 的环切线被推开,上限为 0.14 rad/帧 |

反转冷却时间是按对计算的,而不是按 agent 计算的:一次反弹不能让一个 agent 对下一个即将遇到的邻居视而不见。已在无头 Chrome 中针对 41 个 agent 的回放进行验证——最小成对距离 48–51 px,即没有机器人相互接触。

角度和速度存在于 JS 状态中,绝不放在 DOM 上:renderGraph() 会在每次轮询时从字符串重建 SVG,所以任何挂在元素上的东西都会在 1.5 秒后消失。在 prefers-reduced-motion: reduce 下会完全跳过运动。

信号点是唯一不得不改变形状的东西。它们过去是 SMIL
animateMotion 沿着一条静态路径运动,这本来会让它们沿着机器人早已离开的路线航行。现在它们只是普通的圆点,由同一个动画循环在父→子线段上做线性插值,因此它们始终紧贴两个移动的端点。

布局

单屏,无页面滚动:bar / metric tiles / stage / agent roster,构建为一个无缝马赛克。面板之间用 1px 分隔线而非间隙隔开,没有圆角,并按色相着色——星座为青色,活动为琥珀色,名册为绿色——而每个指标磁贴都有自己的强调色。只有信息流可以垂直滚动,名册可以横向滚动。在宽度低于 1000px 时,舞台堆叠,页面重新允许滚动。

curl 'http://127.0.0.1:8080/api.php?action=live'          # newest root session
curl 'http://127.0.0.1:8080/api.php?action=live&id='  # a specific root

为什么这里有一个解码器

DSH 将会话存储为 session.vN.jsonl.zstd——而且它不是一个 zstd 流。它是一系列串联的 zstd 帧,每次追加一帧:

[frame: session header][frame: event][frame: event][frame: …]   ← one frame per append

这打破了那些显而易见的工具:

| 方法 | 结果 |
|---|---|
| unzstd / zstd CLI | 未安装 |
| PHP zstd 扩展 | 不存在 |
| zlib.zstdDecompressSync(node 24) | 只解码第一帧(215 字节,1 个事件) |
| zlib.createZstdDecompress() | 同样在第一帧停止,除非你在帧之间重置流 |

Node 的解码器是那种诱人的差一点成功:它看起来像是能工作,却悄悄地从约 1,900 个事件中只返回 1 个。

这台机器确实有 libzstd1(1.5.5),所以 lib/Zstd.php 用 ZSTD_findFrameCompressedSize() / ZSTD_decompress() 遍历各帧并将它们拼接起来。实测帧大小:压缩后 86–34,538 B,解压后 79–150,463 B,而且每一帧都报告未知的内容大小——因此目标缓冲区无法根据头部预先确定大小,必须在 ZSTD_error_dstSize_tooSmall(代码 70)时增长,而该错误被刻意不视为输入结束。

一个小麻烦:PHP-FPM 自带 ffi.enable=preload,它禁止在 Web 请求中使用 FFI::cdef()。与其削弱共享的 PHP 配置(并冒着影响同一 8.2 池上另外 50 个站点的风险),解码器会检测到这一点并回退到 bin/zstddump.php,后者在允许 FFI 的 CLI SAPI 下运行。同一个类,同一个库,字节通过 stdin 管道传输。

browser ──► index.php / api.php ──► LiveMonitor ──► Zstd::decompressedCached()
│                  │
│      FFI allowed? ├─ yes ─► libzstd in-process (CLI)
│                  └─ no  ─► bin/zstddump.php ─► libzstd
│                  │
│                  └─► var/cache/.jsonl + .offset
└─► 每个 agent 的计数器、树、feed ─► JSON ─► SVG/DOM

解码是增量式的:缓存会记录已经消费了多少压缩字节,
因此一次轮询只会解码新的尾部——对最大的日志(约 2 MB 压缩后)进行完整解码
在进程内约需 33 ms,而一次已追上的轮询则完全不做任何解码。
偏移量总是落在帧边界上,因此一个仍在写入的日志只需在下次重试其不完整的最后一帧。

文件

README.md           本文件——安装、设置和设计说明
LICENSE             MIT
docs/screenshot.*   上面的图片
index.php           实时监控外壳(嵌入第一个快照)——站点根目录
api.php             JSON:仅实时
lib/Zstd.php        拼接帧 zstd 读取器、增量缓存、CLI 回退
lib/LiveMonitor.php 实时快照:agent 树、计数器、状态、最近 feed
bin/zstddump.php    FPM 回退使用的 CLI 解码器(仅读取 stdin)
assets/monitor.*    实时监控样式 + 渲染
var/cache/          已解码日志——每个会话三个文件(.jsonl、.offset、.lock)

安全性

- 对 ~/.dsh 只读。其下不会写入或移动任何内容。
- bin/zstddump.php 仅读取 stdin,因此它自身没有文件系统暴露面。
- ?id= 只会被用作 session-header 映射中的键,绝不会用作路径,因此它
不提供遍历攻击面;未知 id 会返回 404。
- 容错解码:一个仍在追加写入的会话可能以不完整的帧结尾,因此
尾部解码失败时会返回目前已解码的事件,而不是报错。
- Token 总数来自 harness 的投影缓存;每条消息的用量来自
assistant/message 事件。

在你将其暴露之前

这是一个本地开发者工具:没有身份验证、没有会话、没有速率限制、没有
只读查看模式。 任何能访问该 URL 的人都可以读取你运行过的每一个会话——
提示词、回复、命令、文件路径和工具输出。

- 将其绑定到 127.0.0.1(如 php -S 示例所做的那样)或仅绑定到私有接口。
- 若要从其他地方访问它,请将其置于会进行身份验证的反向代理之后,并让
DSH 主机本身远离公共互联网。
- 不要从 nginx 片段中删除 location ~ /\. 规则——正是它阻止了
.git/(以及因此你的完整仓库历史)被提供出去。

许可证

MIT——使用它、fork 它、修改它、发布它、出售它。无担保。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群