dsh-timer:在 DSH 进程内按目录配置定时跑 Agent 会话

前言

在 DeepSeek Harness(DSH)里做长期运行的部署时,常见需求是「到点自动开一轮 Agent」:日报、周期巡检、一次性提醒、进程启动后的延迟任务等。若完全依赖外部 cron 或系统 systemd,往往要另起进程、另配环境,和 DSH 的 profile、会话、沙箱策略脱节。

dsh-timer 由社区维护者 xuan25 发布,分类为工作流插件;定位是「DSH 里的 systemd.timer 等价物」——任务定义落在 job 目录,触发后在 同一 dsh 进程内 为每次执行创建新的 prompted session。下面介绍它做什么、如何安装,以及 job 文件怎么写。

这是什么

dsh-timer(GitHub:xuan25/dsh-timer)是挂到某个 dsh profile 上的 进程内调度插件(package 版本 0.1.0,MIT 许可证)。它不替代系统 cron,而是在部署已运行的 dsh 里维护调度循环(README 与 package 描述均提到约 30 秒 tick),到期后为该 job 启动一次全新的 Agent 会话。

每次 run 可配置工作目录、preset、model、文件策略、reasoning effort 等;job 级状态文件记录下次触发时间与最近 50 条运行记录。行为上对齐 systemd.timer 的常见语义:停机后的 persistent 补跑、重叠策略(skip / stop / allow)、单次运行时长上限、时区与时钟回拨处理等。

核心功能

任务存放与热更新

  • Job 以 一 job 一文件 形式放在 job 目录:<id>.json;enable 为 false 时仍保留在同一文件,只是不参与调度,仍可手动触发或改回启用。
  • 运行时状态写在 state/<jobId>.json,仅插件写入。
  • 目录变更带 500 ms debounce 的 live rescan;也可通过 timer 工具的 reload 强制重扫(README)。
  • 除 job 文件与 state 外,其它文件名或子目录会被忽略。

三种触发方式

至少启用以下之一(字段可为 null 表示不用该路触发):

  1. onCalendar:5/6 段 cron,或完整 ISO 8601 一次性时间点。
  2. onActiveSec:自 最近一次 run 开始时刻 起算的间隔(如 30min)。
  3. onStartupSec:自 进程启动 起算、每个进程生命周期内触发一次的一次性延迟。

日历类条件可设 IANA timeZone;persistent、deferReactivation、runtimeMaxSec、overlap 等字段语义见官方 schema 与 README 表格。

每次 run 是什么

触发后不是内联函数,而是 新建一条 prompted session,run.prompt 为必填指令;框架会自动追加结果摘要类说明。run.cwd 决定会话工作目录,也是插件权限模型下的控制范围——未在 spec 中确定 run.cwd 时,管理会走审批通道,运行会 create_failed,需在 job 文件中显式设置。

会话归档

插件按分钟扫描:已结束且超过 job 的 run.sessionRetention 的 run session 会从框架 session store 归档到 $DSH_HOME/dsh-timer/ 下,避免会话堆积。

安装与启用

安装命令来自仓库 README(需替换 <name> 为实际 profile 名):

dsh plugin --profile <name> add dsh-timer

README 说明:无需额外插件配置 即可注册;job 默认从下文目录读取。可选配置项 jobsDir 用于指定 job 目录。

job 目录解析顺序

  1. 显式指定:jobsDir 插件配置,或环境变量 DSH_TIMER_JOBS_DIR(指定但不可用则插件启动失败)。
  2. 未显式指定时默认 <profileDir>/timers(profileDir 为安装该插件的 profile 目录)。
  3. 无法唯一确定 profile 时,可在 profile 的 cordis.patch.yml 用 loader 表达式固定路径,例如:
# <profileDir>/cordis.patch.yml
- id: dsh-timer
  config:
    jobsDir: !!js dshHomePath('profiles', '<name>', 'timers')

job id 须匹配 ^[a-z0-9][a-z0-9-]{0,63}$(小写字母或数字开头)。

典型用法

手写 job 文件

在 job 目录创建 daily-check.json(文件名即 job id)。下列示例来自仓库 schemas/job.schema.json 的 examples,仅作字段组合参考;实际路径与 prompt 请按你的部署修改,并 为 run 设置明确的 run.cwd(若创建时未继承会话工作区)。

工作日 9 点(上海时区)巡检:

{
  "onCalendar": ["0 9 * * 1-5"],
  "timeZone": "Asia/Shanghai",
  "deferReactivation": true,
  "overlap": "skip",
  "run": {
    "prompt": "Inspect new reports under /workspace/reports in the last 24h and summarize them",
    "cwd": "/path/to/your/workspace"
  }
}

每 30 分钟心跳(上次激活起算):

{
  "onActiveSec": "30min",
  "persistent": true,
  "run": {
    "prompt": "Output a one-sentence heartbeat with the current time",
    "cwd": "/path/to/your/workspace"
  }
}

一次性 ISO 时刻任务:

{
  "onCalendar": ["2026-10-01T00:00:00"],
  "timeZone": "Asia/Shanghai",
  "run": {
    "prompt": "National Day reminder task",
    "policy": "read-only",
    "cwd": "/path/to/your/workspace"
  }
}

保存后,插件会在数秒内 rescans;也可在会话里用 timer 工具(README 称九动词 CLI)增删改查 job,与手写文件二选一或混用。

完整字段说明与排错见仓库内 schemas/job.schema.json 与 skill/dsh-timer/SKILL.md。

适用场景与注意

适合谁

  • 已在 DSH 上跑长期 profile,希望定时任务与 同一套 preset、model、沙箱策略 一致。
  • 需要 systemd.timer 风格语义(persistent、overlap、runtime 上限),但不想把 Agent 逻辑拆到系统 cron 脚本里。
  • 接受「一次触发 = 一次完整 Agent 会话」,适合报告、巡检、提醒类工作流。

务必知晓

  • 插件以 当前 dsh 进程的身份与权限 运行;job 里的 prompt 与 run.policy 会在到期时自动执行,安装前请阅读源码与 MIT 许可证,仅添加你理解的 job。
  • 社区目录 SkillHub 插件页 与 GitHub 仓库独立于 DeepSeek / 幻方,并非官方应用商店;星标与版本以页面与仓库为准(截至资料抓取时 GitHub 星标为 0,package 版本 0.1.0)。
  • 依赖 Cordis 与多项 @deepseek-ai/dsh-* peer 包,需与宿主 DSH 版本兼容(见仓库 package.json)。

结尾

dsh-timer 把「何时跑」收敛到 profile 下的 job 目录,把「怎么跑」收敛到每次新建的 Agent 会话,适合在 DSH 生态里做进程内工作流调度。进一步细节与示例:

  • 目录页:https://www.skillhub.cn/plugins/xuan25/dsh-timer
  • 源码与 README:https://github.com/xuan25/dsh-timer
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜