跳到主要内容

dsh-schedule-view

已验证

@lijian-ui/dsh-schedule-view · v0.2.0 · MIT · Web 界面

Cron-based scheduled task plugin for DeepSeek Harness (dsh): UI panel plus a cron_manage agent tool (list/create/update/delete), cross-session timer, multi-level notifications.

安装

dsh plugin add @lijian-ui/dsh-schedule-view

用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。

源码

标签

说明文档

dsh-schedule-view · 定时任务插件

English | 中文

为 DeepSeek Harness (dsh) 提供基于 cron 的定时任务功能:既可在设置面板中创建 / 编辑 / 删除 / 立即执行任务,也可让模型在对话里直接查 / 建 / 改 / 删任务(cron_manage 工具);支持跨 session 的 agent follow-up 和多级通知。

功能概览

功能 说明
7 种调度类型 间隔 / 每天 / 每周 / 每月 / 每年 / 一次性 / cron
Cron 表达式 完整 5 字段 cron,实时校验 + 人类可读预览("每个工作日 09:00")
跨 session 触发 Timer 在 host 进程级存活,目标 session 关闭也能触发
Agent follow-up 到期以 user role 注入提示词(请根据系统指令开始执行任务。),AI 立即执行
执行生命周期 追踪 delivered → running → completed/failed,记录 AI 回复摘要
多级通知 页面 toast(8s 自动消失)+ WebAudio 提示音(零文件)+ Electron 桌面通知 + 未读徽标
模型选择 每个任务可指定 provider/model,未指定则用部署默认
工作目录 每个任务可绑定 cwd,上下文感知执行
错过处理 跳过错过窗口,或下次启动补跑一次(应用重启 / 休眠场景)
LLM Tool 一个 cron_manage 工具(list / create / update / delete),模型可在对话里查询、新建、修改、删除任务,与面板共享同一份配置

背景

dsh 官方 @deepseek-ai/dsh-schedule 插件存在以下局限:

局限 官方 本插件
调度粒度 after_seconds / at / every_seconds 完整 cron + 7 种类型
Session 范围 仅 session 本地 跨 session(host 级 timer)
通知 仅对话内 toast + 提示音 + 桌面通知 + 徽标
UI 管理 无(纯 LLM tool) 完整设置面板
LLM schema 开销 3 个 tool(schedule_create/list/delete) 1 个 tool(cron_manage)覆盖查 / 建 / 改 / 删

安装

前置条件

  • DeepSeek Harness (dsh)(Web / CLI,或桌面端)
  • Node.js >= 18

安装

Web / CLI 环境装进 web profile:

dsh plugin --profile web add @lijian-ui/dsh-schedule-view

桌面端请注意:desktop profile 由 Electron 应用独占管理,CLI 会拒绝写入,需要在应用内的「插件」页填写包名 @lijian-ui/dsh-schedule-view 完成安装。

本地开发

# 进入插件目录
cd dsh-schedule-view

# 安装依赖
npm install

# 构建
npm run build

# 监听模式
npm run watch

# 类型检查
npm run typecheck

构建产物在 lib/ 目录下,通过 junction 自动同步到 node_modules/@lijian-ui/dsh-schedule-view。每次构建后需重启桌面端加载新 bundle。

使用方式

  1. 打开 dsh 桌面端
  2. 进入 设置 → 定时任务
  3. 在任务列表中:
    • 点击 新建任务 创建定时任务
    • 点击滑块按钮启用/停用
    • 点击 编辑 修改调度规则、提示词或模型
    • 点击 立即执行 手动触发一次
    • 点击 删除 永久移除
    • 点击 执行历史 查看运行记录

用对话管理任务(cron_manage 工具)

不必打开设置面板——说「每天 9 点检查一次构建」,模型会调用 cron_manage 把任务建好;说「把每日构建检查改到 10 点」「暂停周报提醒」「删掉那个任务」,它用同一个工具处理。

参数 必填 说明
action 是 list 列出全部任务 / create 新建 / update 修改 / delete 删除
task update、delete 必填 目标任务,可填 id 或任务名(先用 list 拿 id 最稳)
title create 必填 任务名称;update 传入表示重命名
prompt create 必填 每次触发时发给新会话的指令(需自包含,不依赖当前对话上下文)
schedule_type create 必填 interval / daily / weekly / monthly / yearly / once / cron;update 不传则原排期不动
time 按类型 daily / weekly / monthly / yearly 的触发时刻,HH:mm
every_minutes 按类型 interval 的间隔分钟数(≥1)
at 按类型 once 的执行时刻,带时区偏移的 ISO 8601
cron 按类型 cron 的 5 段表达式
weekdays 按类型 weekly 的星期列表,0=周日 … 6=周六
month_day 否 monthly / yearly 的日期,1-31,或 -1 表示当月最后一天
month 否 yearly 的月份,1-12
catch_up 否 skip(默认)或 once(补跑一次)
cwd 否 工作目录;不填则继承发起对话的那个会话的工作目录
enabled 否 create 默认 true;update 传 false 暂停任务、true 恢复

update 是打补丁语义:只覆盖你传了的字段,没传的保持原值(不传 schedule_type 就只改名称 / 指令 / 状态,排期原样保留)。

几个要点:

  • 与面板共用同一份配置:模型建的任务就是普通任务,在设置面板里可以照常编辑、启停、删除,重启后依然在。
  • 每个任务分配独立 agent 会话:定时执行不会打扰创建它的那次对话。
  • 写入前先校验:复用插件的调度核心算一次「下次触发时间」,cron 不可解析、时间格式不合法、排期算不出触发时刻都会被直接拒绝,不会留下永不触发的死任务。
  • 改 / 删前先定位:update / delete 用 id 或任务名匹配(精确名 → 唯一子串);命中多个会拒绝并列出候选,避免改错任务。
  • 只注册这一个工具:查 / 建 / 改 / 删共用一个 cron_manage,不必再往模型提示词里塞第二个 schema。

任务字段

字段 必填 说明
任务名称 是 任务名
调度规则 是 7 种类型:间隔 / 每天 / 每周 / 每月 / 每年 / 一次性 / cron
提示词指令 是 到期注入目标 agent 的指令
目标 Session 是 接收提示词的 session
工作目录 否 任务 agent 绑定的 cwd(绝对路径)
执行模型 否 provider/model 覆盖,未指定则用部署默认
时区 否 cron 解析用的本地时区(默认系统时区)

调度类型

类型 示例 说明
间隔 每 60 分钟 固定分钟间隔
每天 每天 09:00 壁钟时间
每周 周一、三、五 09:00 选择星期
每月 每月 1 号 09:00 每月第几天(1-31 或最后一天)
每年 每年 1 月 1 日 09:00 月份 + 日期
一次性 2026-09-01T09:00:00 单次触发,触发后自动停用
Cron 0 9 * * 1-5 完整 5 字段 cron 表达式

错过处理

应用关闭或休眠时错过了触发窗口:

策略 行为
跳过 丢弃错过窗口,等待下次调度时间
补跑一次 下次 tick 时补跑一次(一次性任务默认)

执行生命周期

任务触发时:

delivered → running → completed/failed
阶段 触发条件 处理
已投递 agent.followup 成功 创建历史记录,发送桌面通知
执行中 user/message 事件匹配注入的 messageId 捕获 AI 回复摘要,记录耗时
已完成 turn/end 事件 最终状态、endReason、toast + 提示音通知
已失败 turn/end 含错误 标记失败,持久 toast,错误提示音
已跳过 触发时 agent 不 live 不 followup,跳过通知,历史标记 skipped

技术架构

目录结构

extensions/dsh-schedule-view/
├── src/
│   ├── index.ts                    # Host 入口(安装设置面板 + 启动 timer)
│   ├── remote.ts                   # Host RPC 方法(list/create/update/delete/fireNow)
│   ├── tools.ts                    # agent 工具 cron_manage(模型在对话里查/建/改/删任务)
│   ├── timer-runtime.ts            # 核心 timer 引擎(cron 解析 + tick 轮询 + 触发)
│   ├── schedule-core.ts            # 调度计算(下次触发时间)
│   ├── lifecycle-tracker.ts        # Session/event 监听器,追踪执行生命周期
│   ├── notify.ts                   # 多级通知(toast + 提示音 + 桌面通知)
│   ├── guarded.ts                  # 故障隔离包装(所有回调 try-catch)
│   ├── types.ts                    # TimerTask、RunRecord、TaskSchedule 类型
│   ├── schema.ts                   # 配置 schema(schemastery)
│   └── client/
│       ├── index.ts                # Client 入口(设置 section 注册)
│       ├── TimerSettingsSection.tsx # 主设置 UI(列表 + 表单 + 历史)
│       ├── client-i18n.ts          # 国际化(中/英)
│       ├── config-api.ts           # Client 端 RPC 包装
│       ├── model-catalog.ts        # 模型选择下拉 UI
│       └── chime.ts                # WebAudio 双音提示音合成
├── lib/                            # 构建产物
├── cordis.patch.yml                # Bundle patch 声明
├── package.json
└── tsdown.config.ts

Host 端(src/)

模块 职责
index.ts 插件启动:安装设置面板、启动 timer、配置变更时同步
remote.ts RPC API:list / create / update / delete / fireNow / runs
tools.ts Agent 工具:cron_manage(list / create / update / delete)。复用 runtime 的增删改查与同一份 persist(),与面板改出来的任务无差别
timer-runtime.ts cron 解析、tick 轮询、agent followup 注入、生命周期追踪
lifecycle-tracker.ts 监听 session/event,匹配注入的 messageId,更新 run 状态
notify.ts toast(React portal)+ 提示音(WebAudio)+ 桌面通知(Electron IPC)
guarded.ts 所有回调包在 try-catch 中;插件故障不拖垮宿主
schema.ts schemastery 配置校验

Client 端(src/client/)

模块 职责
index.ts 通过 ctx.slots.inject 注册设置 section
TimerSettingsSection.tsx React 组件:任务列表、创建/编辑表单、执行历史面板
client-i18n.ts 中英文翻译
config-api.ts RPC 客户端包装
model-catalog.ts 模型选择下拉 UI
chime.ts WebAudio 双音提示音合成(零音频文件)

持久化

数据 存储方式 说明
任务定义 dsh-settings UI 可编辑,revision 防冲突
执行历史 dsh-storage-domain 结构化 KV,封顶 500 条

Tick 轮询策略

使用 setInterval tick 轮询(默认 15s)而非每个任务一个 setTimeout:

  • 避开 setTimeout 的 2^31 - 1 ms(~24.8 天)上限
  • 重启恢复:从持久化任务重新计算 nextFireMap
  • 错过窗口:重启后首次 tick 立即触发,由 catch-up 策略处理
  • 触发精度:受 tickSeconds 限制(定时任务场景完全可接受)

已知问题与解决方案

重启后 Session ID 冲突

问题:桌面重启后,任务配置中持久化的 sessionId 可能与已存在的 agent session 冲突,导致 agents.create 报 "session already exists"。

根因:sessionId 被持久化到 settings 中;恢复时旧 ID 与 agent 在磁盘上的会话日志冲突。

我们的方案:sessionId 不持久化。每次触发时通过 agents.create 用新 UUID 创建专属会话。配置中的 sessionId 是临时的——只在 session 生命周期内追踪 agent handle。

Agent 被 dispose 后 handle 过期

问题:Agent 被外部 dispose(如用户关闭 session),但 runtime 仍持有过期 handle。

我们的方案:ensureAgent 检测 handle 是否过期(已 dispose 或不在 agents.list() 中)。若过期则 dispose handle 并轮换 sessionId 以创建新会话。

会话日志文件被删除后 ENOENT

问题:用户删除了会话日志文件,但 agent 仍在运行,无法写日志。

我们的方案:agent/error 监听器检测 ENOENT → dispose agent → 轮换 sessionId → 创建新会话。

归档会话

问题:dsh 通过标记 archivedSessionIds 归档会话,但不 dispose agent。agents.get() 仍返回 agent,followup 照常执行但用户 UI 看不见。

我们的方案:ensureAgent 中检查 workspaceRegistry.archivedSessionIds,若已归档则 dispose agent 并轮换 sessionId 创建新会话。

未挂载 preset 导致模型选择失效

问题:agents.create 不带 setup 回调不会挂载 standard preset,agent 无工具且 prompt assembly 无法解析 {{provider}} / {{model}} 变量。

我们的方案:所有 agents.create / agents.resume 调用均带 setup 回调,在 agent 发布前挂载 agentPresets('standard')并安装模型选择。

国际化

支持中文和英文。翻译文件在 src/client/client-i18n.ts。语言切换跟随 dsh 桌面端设置。

技术栈

  • 语言:TypeScript
  • 构建:tsdown (rolldown)
  • 前端:React 18
  • Cron 解析:cron-parser(~30KB)
  • 人类可读 Cron:cronstrue
  • 配置 Schema:@deepseek-ai/schemastery
  • Agent Tool:@deepseek-ai/dsh-tools(defineTool,惰性 ctx.inject(['tools']) 注册)
  • 设置持久化:@deepseek-ai/dsh-settings
  • 存储:@deepseek-ai/dsh-storage-domain

许可证

MIT

相关链接