dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Host-measured task timing for DSH agents: a task_clock tool that does not touch presets
A task-timing plugin for DSH local AI agents.
为 DSH 本地 AI Agent 提供可核实的任务计时能力,把预计耗时、实际耗时和任务状态分开管理。
项目状态:0.1.8 已发布到 GitHub(仓库 · Releases)。
已验证环境:DSH 0.2.1-rc.2(仅此版本实测;其他版本未验证)。
AI Agent 能理解“5 分钟”“20 分钟”“超时”这些词,但这不代表它知道现实中已经过去了多久。
典型问题:Agent 预计任务要 20 分钟,实际不到 2 分钟就做完,之后却把“预计 20 分钟”说成“已经过了 20 分钟”。
这暴露的是系统设计问题,不是模型态度问题:
DSH 自带的时间上下文能提供当前时间,但任务级计时需要另一套机制。这个插件的做法是:宿主程序负责计时,Agent 负责在合适的时候调用工具并解释结果。
task_clock 工具提供五个动作:
操作 · 用途 start · 创建任务计时,记录预计耗时与单调时钟起点 check · 查询当前实际耗时、预计耗时与剩余时间 finish · 结束计时,记录实际耗时、偏差与状态 list · 查询已保存的任务记录 now · 查询当前本地时间与时区(不创建任务;需要报时刻时用它,不要推算)
参数(完整定义以包内 index.js 为准):
参数 · 必填 · 说明 action · 是 · start / check / finish / list / now taskId · 否 · 指定任务;check / finish 默认当前 session 的 running 任务 plannedSeconds · 否 · 预计耗时,单位秒(start) label · 否 · 任务标签(start) outcome · 否 · completed / aborted,默认 completed(finish) limit · 否 · list 条数,默认 10,最大 50 allSessions · 否 · list 是否包含其他 session 的记录
返回值为一段人类可读文本(text),不是结构化 JSON 字段。
同一进程内,实际耗时用 performance.now() 的差值计算。墙钟(Date.now())只用于显示开始/结束时间,以及跨进程续算时的空档计算。
这样,普通的系统日期时间调整不会直接改变进程内连续运行的耗时结果。
任务记录分开保存:
plannedMs:预计耗时actualMs:实际耗时status:running / completed / aborted / uncertain偏差:
Variance = actualMs - plannedMs负数表示比预计早完成,正数表示超时。
显示时长按整秒向下取整;内部仍保留毫秒。精确核对请看 tasks.json 的原始值,不要拿格式化后的文本反推。
记录写入 ~/.dsh/clock-context/tasks.json(JSON 数组,原子替换)。
list 能查到。list 只看当前 session;allSessions: true 可看同一文件里的全部记录。验证口径(DSH 0.2.1-rc.2):
start / check / finish / list):Standard / Cordis / Minimal / PTC 四种 preset 均完成真实会话验证。now 动作。preset · 四动作(0.1.0) · now(0.1.1) Standard · ✅ · ✅ Cordis · ✅ · 未单独验证 Minimal · ✅ · 未单独验证 PTC · ✅(run_code / ptc-dispatch) · 未单独验证
插件在 Profile 层注册,不依赖 preset。但这不代表所有 DSH 版本、所有 Profile 或所有平台都已验证。
插件默认通过 ctx.systemPrompt.context() 把三条固定规则放进每次模型请求的运行时上下文,不产生 user 消息,不作为对话消息出现在历史里:
时间规则(dsh-clock-tools):
- 报时间、写时间戳前先查 task_clock,禁止推算;查不到的不写。
- 查到原样用:HH:MM 截断,不四舍五入,问秒才给秒。
- 跨度不等于工时,有断档分段说。文本是包内常量,只读本地配置决定开/关,不发网络请求。
关闭方式按优先级从高到低:
- id: clock-tools
name: 'dsh-clock-tools'
config:
injectTimeRules: false~/.dsh/clock-context/config.json:{ "injectTimeRules": false }两者都没有设置时默认注入。文件不存在、不是合法 JSON、字段不是布尔值,一律当作“未配置”,仍然默认开启。配置文件与任务记录放在同一目录(如果改过 storeDir,config.json 也跟着走)。
开关在 DSH 启动、插件加载时读取;改完需要完全退出并重启 DSH 才生效。
task_clock.start,记录任务标识、预计耗时、墙钟与单调时钟起点。task_clock.check 查询实际经过时间。task_clock.finish。task_clock.list 查询。插件不会因为存在计时能力就自动给所有任务计时;是否在合适的时机调用,是 Agent 的行为问题,不是插件的保证。
情况 · 行为 连续运行(同一进程) · 单调时钟差值,continuity: continuous 墙钟发生跳变 · continuity: clock-changed,仍用单调时钟,标注墙钟变化 仅重启 DSH(系统未重启) · continuity: resumed:前后两段用单调时钟,空档用系统运行时间桥接,标注 (resumed, uptime-bridged) 系统也重启 · continuity: resumed-gap:重启前、重启后两段精确,关机空档用墙钟估算,标注 (resumed, gap estimated) 墙钟回退、无法估算 · actualMs: null,状态 uncertain,不编造耗时
跨重启的空档只能是估算,不能等同于连续运行时的单调时钟结果。插件也不声称能抵御所有系统级计时篡改。
DSH 重启会结束进程,单调时钟起点随之丢失。0.1.4 起支持续算,0.1.5 修正了分段公式并加入累计量:
accumulatedMs(截至检查点已确认的累计耗时)和产生它的计时基准(baseBootId / baseMono / baseUptime / baseWall)。accumulatedMs 并更新基准。纯本地文件读写,不调模型。情况 · 公式 同一进程 · accumulatedMs + (nowMono - baseMono) 跨 DSH 重启、系统未重启 · accumulatedMs + (processStartUptime - baseUptime)×1000 + (nowMono - processStartMono) 跨系统重启 · accumulatedMs + (processStartWall - baseWall) + (nowMono - processStartMono),标 resumed-gap 无法桥接 · 退回整段墙钟估算(restarted),或 unknown
Actual 的含义始终是 `start` 到 `finish` 的总经过时间,包含 DSH 关闭期间和系统睡眠时间;它不是 Agent 实际执行工作的时间。
平台 · 启动周期识别 · 验证状态 Windows · 墙钟 − os.uptime() 推断(差 ≤ 2 秒视为同一次启动) · 实测 Linux · 读 /proc/sys/kernel/random/boot_id;读不到回退墙钟推断 · 未测(代码已支持) macOS · 回退墙钟推断 · 未测
读取失败(文件不存在、权限不足)不会报错,自动回退墙钟推断。未验证平台上如果识别错误,结果会降级为 restarted / unknown,不会伪造精确值。
需要自定义时,编辑 index.js 顶部的 readPlatformBootId(),或在插件配置里设置 platformBootToken(字符串)直接覆盖。
dsh-clock-tools 目录的绝对路径(选择目录,不是 ZIP 文件本身)装完后插件页面会显示标题与描述,新会话工具列表出现 task_clock。
装好只是第一步,还得把规则写进 AGENTS.md,见下方「让 Agent 真的用起来」。
dsh plugin --profile <你的 Profile> add "<本地目录绝对路径>"当前发行包未提供升级命令;升级时建议卸载后重新安装(未实测)。
GUI:插件页面 → 对应插件 → Remove(入口存在,卸载流程本身未实测)。
CLI(未实测):
dsh plugin --profile <你的 Profile> remove dsh-clock-tools任务记录不会被删除;需要清理时手动删除 ~/.dsh/clock-context/。
task_clock start plannedSeconds=1200 label="写报告"
task_clock check
task_clock finish outcome=completed
task_clock list
task_clock now