dsh-heartbeat
Verifieddsh-heartbeat · v0.1.0 · MIT · Web UI
DSH 心跳:按时间规则向指定会话的主 Agent 主动投递一条你自己写的提示;模型输出期间不计时
Install
dsh plugin add dsh-heartbeat Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-heartbeat · DSH 心跳
让 DeepSeek Harness 按你定的时间,主动来跟你说话。
dsh-heartbeat 是给 DeepSeek Harness 的定时任务组件:按时间规则,向你指定的那个会话投递一条你自己写的提示。最终说什么,由那个会话的主 Agent 结合上下文和人设自己发挥。
你写: 早上好呀,看看 {weekday} 的日程,用你平时的语气跟我说句话
DSH 收到:「早上好呀,看看周一的日程,用你平时的语气跟我说句话」
目标会话的 Agent 结合上下文生成真正的回复 → 你收到一条消息
它和别的定时插件有什么不一样
| 模型说话期间不计时 | 下一次触发从「模型这次输出结束」那一刻重新起算。输出中不会被心跳打断,也不会因为回复耗时长而让计时漂移。 |
| 投给谁由你定 | 指定任意根会话(含 IM 渠道会话)。子 Agent 会话会被自动过滤掉 —— 心跳只找「主 Agent」。 |
| 文案权留给你和那个人设 | 插件只投递你写的提示,不预设任何固定回复。目标会话的 Agent 用自己的性格说话。 |
| 可视化设置界面 | 设置 → 心跳:任务列表、下次触发时间、已触发次数、无回应次数、立即触发一次、新建/编辑/删除。中英双语。 |
| 零运行时依赖 | 只依赖 DSH 官方包(peerDependencies)与 Node 内置模块。没有第三方运行时依赖。 |
安装
dsh plugin add dsh-heartbeat --profile web
因为包内声明了 dsh.bundle,dsh 会把它追加进 profile 的 dsh.profile.bundles 并自动应用配置层。装完需要重启一次 DSH web(新增插件行不会热加载)。
其他安装方式
# 从 GitHub 源码安装(需要额外给 pnpm 授权构建脚本)
dsh plugin add github:<你的账号>/dsh-heartbeat --profile web
# 从本地 tarball 安装
pnpm pack
dsh plugin add ./dsh-heartbeat-0.1.0.tgz --profile web
要求:DSH 0.1.5-rc.2 及以上、Node ≥ 20、Web 系 profile(设置界面依赖 Web 的客户端插槽)。
快速开始
- 打开 设置 → 心跳,勾选「启用组件」;
- 点「新建任务」;
- 填 名称、选 目标会话(下拉列出了你的所有根会话和它们的标题)、写 提示文案;
- 选时间规则 → 保存。
任务立刻生效。列表里能看到状态、下次触发时间、已触发次数、连续无回应次数,也可以「立即触发」一次来验证链路。
时间规则
| 类型 | type |
说明 | 主要字段 |
|---|---|---|---|
| 一次性 | once |
某个日期时刻触发一次 | at |
| 每天 | daily |
每天固定时刻 | at |
| 每周 | weekly |
每周指定日 + 时刻 | at、days |
| 固定间隔 | interval |
每 N 分钟/小时触发 | every、anchor |
| 窗口内固定间隔 | windowed-interval |
只在每天的时间窗口内按间隔触发 | every、window、align |
- 生效日(
weekly):每天 / 工作日 / 周末 / 自定义。 - 计时起点(
interval):enable-time从启用时刻起算,或interval-end每次触发后重新计时。 - 窗口内对齐(
windowed-interval):以窗口起点对齐,或从启用时刻对齐。 - 时长写法:
30s、15m、2h、1d,也可以组合。
配置最终落在 settings.yaml:
heartbeat:
tasks:
- id: task-1
name: 早安
session: <sessionId>
schedule:
type: interval
every: 1m
anchor: interval-end
payload:
kind: prompt
text: 早上好呀,用你平时的语气跟我说句话
noReply:
max: 3
提示文案里的占位符
在文案里写 {名字} 即可,触发时求值。带参数的写 {time:HH:mm},方括号可转义字面量([、])。
| 占位符 | 默认参数 | 含义 |
|---|---|---|
{time} |
HH:mm |
当前时刻 |
{date} |
YYYY-MM-DD |
当前日期 |
{datetime} |
YYYY-MM-DD HH:mm |
当前日期与时间 |
{weekday} |
— | 星期几(周一…周日) |
{taskId} |
— | 任务 ID |
{taskName} |
— | 任务展示名 |
{fireCount} |
— | 含本次在内的累计触发次数 |
{noReplyStreak} |
— | 当前连续无回应次数 |
{lastFiredAt} |
YYYY-MM-DD HH:mm |
上次触发时间 |
{nextFireAt} |
YYYY-MM-DD HH:mm |
下次计划触发时间 |
{sinceLastUserMsg} |
— | 距用户上次发言的时长 |
{lastUserMsgAt} |
YYYY-MM-DD HH:mm |
用户上次发言时刻 |
{random} |
1-100 |
随机整数(可指定范围,如 {random:1-6}) |
例:{weekday} {time:HH:mm} 了,{sinceLastUserMsg} 没找我说话了,{random:1-3} 句话哄哄我
投递策略
| 字段 | 取值 | 说明 |
|---|---|---|
onBusy |
queue(默认)/ skip / inject |
目标会话正忙(模型在输出)时:排队等这轮结束 / 跳过本次 / 立刻插进去 |
missed |
skip(默认)/ fire-once |
进程重启或机器休眠期间错过的触发:跳过 / 补发一次 |
noReply.max |
数字,0 = 关闭 |
连续 N 次没有回应就暂停这个任务(静默),不再打扰你 |
noReply.window |
时长,留空 = 到下次触发前 | 判定「有没有回应」的时间窗 |
被静默的任务会显示「暂停」状态,界面上可以直接重新启用。
状态与接口
组件把运行期状态(下次触发、触发次数、无回应次数、静默原因)通过只读 HTTP 暴露,不写进 settings.yaml,免得把配置文档弄脏:
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/heartbeat/state |
全部任务的运行期状态 |
GET |
/api/heartbeat/sessions |
可投递的会话候选(已过滤子会话) |
POST |
/api/heartbeat/fire |
立即触发一次(仅限本机请求),body {"id":"<taskId>"} |
状态目录:$DSH_HOME/storages/heartbeat/(每个任务一个 JSON,原子写)。
试出来的坑(给插件开发者的三条经验)
这个组件在真实 DSH 里调试时踩到几个只在运行时才暴露的坑,挑三条最值钱的:
link:安装会分裂模块身份 —— 插件里的@deepseek-ai/*会解析到源码目录自己的副本,于是inject的服务永远解析不到,fiber 静静停在PENDING。用真实副本 / npm 安装。- 直读一个没有
inject的服务会抛异常(不是返回undefined)——(ctx as {x?: T}).x这种"可选直读"会让apply直接抛,插件整个不加载。正确写法是ctx.get(name)+ctx.inject([name], cb)。 settings.section的 owner 只传close一个 prop —— 分区组件的scope/t/数据都要靠注册时闭包带进去,直接注册裸组件会拿到一堆undefined。
另外:客户端插槽所在的 Loader 行,行名必须与 package.json 的 name 完全一致,否则客户端半边不会被扫描到(宿主正常、界面永远不出现)。
开发
pnpm install
pnpm test # 24 个测试文件 / 521 个用例
pnpm typecheck
pnpm build # ESM → lib/,客户端 bundle → lib/client.js
src/schedule/时间规则(时长、时区、日历、下次触发推导)src/templating/占位符解析与求值src/runtime/状态机、时钟 seam、单 timer 调度器、编排器、持久化src/delivery/投递端口(只给主 Agent 这条约束在这里)src/host/唯一直接接触@deepseek-ai/*的胶水层src/client/设置界面(纯逻辑与 React 组件分离)
English
dsh-heartbeat is a scheduling component for DeepSeek Harness: on your schedule, it delivers a prompt you wrote to a session you pick, and that session's main agent answers in its own voice, with full context.
Highlights:
- Output-aware timing — the next fire is counted from the moment the model finishes its output, not from the trigger. Heartbeats never interrupt an in-flight reply, and long answers don't drift the schedule.
- You choose the target — any root session, including IM channels. Subagent sessions are filtered out; heartbeats only talk to a main agent.
- The text is yours — the plugin delivers your prompt (13
{placeholders}supported) and leaves the wording to the target agent's persona. - Visual settings UI — task list, next fire time, fire count, no-reply streak, fire-now button, create/edit/delete. Bilingual (中文 / English).
- Zero runtime dependencies — DSH official packages as peer dependencies plus Node built-ins.
dsh plugin add dsh-heartbeat --profile web
Requires DSH 0.1.5-rc.2+, Node ≥ 20, and a Web-family profile.
License
MIT