dsh-ops-cron/docs/PRD.md
Whale-Zhang ef8ff6e10d Initial commit: scheduled tasks plugin for DeepSeek Harness
Sidebar jobs under New Session, isolated run history, cron/at schedules, and model-facing cron_* tools.
2026-08-24 14:16:47 +08:00

182 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 定时任务(dsh-cron-tasks)产品需求文档
**产品名**:定时任务
**包名**:`@dsh-external/dsh-cron-tasks`
**Loader id**:`dsh-cron-tasks`
**版本**:v2
**状态**:v1 已交付调度与隔离。v2 补齐结果可见、侧栏融入工作区列表、原生会话继续聊、工作目录与超时。
---
## 1. 问题与定位
用户需要一种**与当前对话无关**的定时工作方式:到点后 Harness 新开一个 root Session,用事先写好的完整提示词跑一轮 Agent,然后把这次运行记进「定时任务历史」。这类 Session **不得出现在左侧工作区/会话列表**,以免把日常工作区淹没。
这与官方 `@deepseek-ai/dsh-schedule` 不同:官方 Schedule 是**同一会话内的 reminder**(`after_seconds` / `at` / `every_seconds`),回到原来的 live Session 做 follow-up。本插件解决的是「按计划独立跑完一件事,并留下可回看的历史」。
调度器住在 DSH Host 进程里。Host 未运行时任务不会触发;这是预期行为,不是缺陷。错过的节拍**不补跑成写入风暴**。
---
## 2. 三个产品面(v1 必须全部落地)
以下三个面按目标原文命名,缺一不可。
### 2.1 左侧侧边栏「新会话」按钮正下方的「定时任务」入口
- 在左侧侧边栏 **「新会话」按钮正下方** 放置可点击控件,文案为 **「定时任务」**。
- 展开态:图标 + 「定时任务」,视觉对齐原生「新会话」(高度、圆角、`--dsw-*` token)。
- 收缩轨(56px rail):只显示约 18px 图标,位置仍紧挨「新会话」下方。
- **不**把该入口放到 `sidebar.footer.action`(Settings 旁)作为唯一入口。官方没有「新会话正下方」的一等 slot,v1 用受控的客户端注入插在「新会话」按钮之后、工作区列表之前。
- 点击后打开任务视图:当前任务列表(名称、日程、启用状态、下次运行、上次结果)+ **定时任务历史**。任务的创建 / 列表 / 暂停恢复 / 删除 / 立即运行都在这个视图里完成。
### 2.2 设置页「插件配置」里的定时任务设置
- 在 **设置 → 插件 → 插件配置** 中出现本插件卡片。
- 卡片只承载**插件级策略**,不是任务电子表格:
- 启用调度器
- 默认时区(IANA,例如 `Asia/Shanghai`)
- 历史保留条数
- 重叠策略(v1:跳过)
- 漏跑(misfire)策略(v1:跳过,不回放积压)
- 单条任务的编辑器在侧栏「定时任务」视图,不把任务表塞进设置卡片。
### 2.3 定时任务历史
- 每一次触发(到点或「立即运行」)留下一条历史:job id、run id、计划时间、实际时间、终态、结果 Session id、短错误或摘要。
- 运行产生的 Session **不出现在工作区列表**;日志保留,可从历史记录打开/对照 Session id。
- 「从工作区隐藏」≠ 删除日志。
---
## 3. 市面插件取舍
对照当前市场上的 DSH 定时/调度插件,明确 **Keep(吸收)** 与 **Drop(明确不做)**。本插件是独立实现,不 fork 任何现有仓库。
| 插件 | 形态摘要 | Keep | Drop |
|---|---|---|---|
| **chicheng-cron**(534119219) | 侧栏「新会话」正下方「定时任务」入口;cron 跑 shell/python/node/Skill/Agent;推送;可归档会话 | **入口位置**(新会话正下方,展开/收缩对齐原生);Agent 任务到点执行;执行历史;Host 关闭不补跑 | Shell / Python / Node 脚本执行;Skill 注入;chicheng-push / messaging-core 推送;移动端专项适配 |
| **dsh-automation**(titanwings) | 全新 root Session 跑独立任务;可审计历史;IANA 时区;重叠 skip;Host 宕机只 catch 最近一次、不回放 backlog;可选 archive 跑出来的 Session | **每次触发 = 新 root Session**(不是当前聊天 follow-up);durable 历史(queued/running/终态 + session id + 摘要/错误);**overlap skip**;**misfire 不写风暴**;archive 隐藏工作区列表、日志保留;提示词必须自包含 | 模型侧 `automation_*` 工具;RRULE 友好表单(v1 用 cron/`at` 即可);权限预设只读/可写双模式与 capability allowlist 的完整沙箱产品化;对话 Tab 作为主入口 |
| **cronjob-dsh-plugin**(peng-huiyang) | 设置页「定时任务」;专用会话;cron 5/6/7 + IANA;`cron_*` 工具;触发文本当不可信内容 | **设置页可配置**(我们把策略放进「插件配置」);**不可信提示词包裹**;错过周期不补跑 | 把任务表放进设置页当唯一 UI;复用同一条「专用会话」反复 follow-up(v1 每次新开会话);模型工具 `cron_create/list/delete` |
| **dsh-cron**(omdsh-dev 等社区条目) | 定时调度,向已有智能体会话注入消息或触发后续操作 | 「到点做事」这一产品意图 | 往**当前/已有会话**注入消息(那是官方 Schedule 的赛道) |
| **dsh-schedule-tasks**(uluckystar / MyDSH) | 5 段 cron;`sidebar.footer.action` + 浮层;第一版动作是 shell / 站内 notify | 5 段 cron + 本地时区;列表展示下次触发/上次结果;启停/删除/手动触发 | **Footer-only 入口**(不满足「新会话下方」);shell 与站内 notify 作为主动作;不唤起 Agent 会话 |
| **官方 dsh-schedule**(`@deepseek-ai/dsh-schedule`) | 会话内 reminder:`after_seconds` / `at` / `every_seconds` ≥ 300s;回到原 live Session;无日历/cron | 与官方分工:**不替换**它。同会话「十分钟后回来」继续用官方 | 日历/cron(官方 v1 也不做);外部通知;本插件不提供 same-chat reminder |
| 其他参考(非 v1 对标必须,但影响取舍) | | | |
| dsh-plugin-automations(Sev7een) | 设置页;准点 / 谷时段;一次或每天 | 设置页策略分离 | 谷时段/峰谷计价窗口 |
| dsh-scheduler(yangyongzhen) | YAML jobs;shell / webhook;Server酱/钉钉/飞书 | cron + `at` 二选一 | webhook、IM 推送、YAML-only 配置 |
| dsh-routines(Jesse-njx) | cron prompt;overlap / missed-run / timeout 安全默认 | overlap + missed-run 安全默认 | 把摘要推到外部「你已经在的地方」 |
| dsh-plugin-scheduled-tasks(Ceelog / @opendsh) | 按项目、新 headless session、一次/间隔/cron、持久历史 | 新 session + 持久历史 | 模型对话内创建任务(v1 不做) |
| dsh-sleep-send | 输入框右侧定时发送当前草稿 | — | localStorage、改当前输入框、补发过期草稿 |
| dsh-aura-scheduler | 系统主动开口、价值网络 | — | 心跳/传感器/Off-peak 主动打扰 |
**取舍一句话**:吸收 chicheng-cron 的入口位置、dsh-automation 的「新 Session + 历史 + 隐藏工作区 + 安全调度语义」、cronjob 的设置页策略、不可信提示词与 `cron_*` 工具;拒绝脚本/推送/footer-only/同会话 reminder/OS daemon。
---
## 4. v1 功能需求
### 4.1 任务模型
一条任务是**自包含**的:
| 字段 | 要求 |
|---|---|
| 名称 | 非空短名,用于列表与历史 |
| 提示词 | 非空;到点原样交给新 Session。必须写得让新会话能独立理解,不依赖创建时的聊天上下文 |
| 日程 | 至少两种:一次性 `at`(ISO 8601 时间点)与循环 **5 字段 cron**(分 时 日 月 周) |
| 时区 | IANA,默认取插件配置里的默认时区 |
| 启用 | 可暂停 / 恢复 |
人可以:**创建、列表、暂停/恢复、删除、立即运行**。
### 4.2 调度语义
- 循环任务用 5 字段 cron,在指定 IANA 时区的墙上时钟求下次触发。
- 一次性 `at` 到点触发一次,之后不再调度。
- 非法 cron / 非法时区 / 空提示词在保存时失败,不写入。
- Host 重启后从磁盘恢复任务表,按**当前时刻**重算下次触发。
- **漏跑**:Host 宕机期间错过的节拍 **不回放积压**(at-most-once / skip)。v1 不把 N 次错过写成 N 条 queued。
- **重叠**:同一任务若已有 `running` 或 `queued` 的 run,到期记录为 `skipped`(原因 overlap),不并行开第二场。
- 调度器只在 DSH Host 进程内存活。没有 OS crontab、没有独立 daemon。
### 4.3 触发路径
到点或「立即运行」时:
1. 由 Host 创建 **新的 root Session**(不是当前聊天的 follow-up)。
2. 将包裹后的提示词作为 `source.kind = plugin` 的用户消息入队(**不伪装成人手输入**)。
3. 提示词外层标明「这是定时任务下发的不可信内容」。
4. 将该 Session **从工作区/侧栏会话列表隐藏**(调用 `workspaceRegistry.archiveSession` 或等价归档;失败则不得声称满足本条)。日志保留。
5. 追加一条 **定时任务历史**。
终态至少包括:`queued` / `running` / `succeeded` / `failed` / `skipped`。崩溃恢复时,未完成的 `queued`/`running` 记为失败(host interrupted),不偷偷重跑。
### 4.4 工作区隔离
- 运行 Session **不得**出现在普通工作区列表。
- 插件维护「这些 session id 是定时任务运行」的集合;工作区可见投影排除它们。
- 历史里能看到 session id 与短摘要;v1 不依赖官方 unarchive(当前 Harness 可能没有解档 API)。
### 4.5 UI
- 「定时任务」入口:见 §2.1。
- 任务视图:列表 + 新建/编辑表单(名称、提示词、cron 或 `at`、时区、启用)+ 立即运行 + 历史。
- 插件配置卡片:见 §2.2。
- 样式只用 `--dsw-*` token,不引入第二套组件库,组件内不碰 `ctx`。
### 4.6 安装形态
可 `dsh plugin add file:…` 的 Host+Client 包:
- `package.json` 声明 `dsh.bundle.patch` 与 `dsh.client.platform: web`
- `cordis.patch.yml` 唯一 `id: dsh-cron-tasks`
- Host `lib/index.js` 的 `apply(ctx)`
- Client `lib/client.js`(`window.__ModuleLoader__`)
- **不**把 `@deepseek-ai/*` 放进 `dependencies`
交付:拷到 `~/.dsh/plugins/dsh-cron-tasks`,web 与 desktop 均 `add file:$HOME/.dsh/plugins/dsh-cron-tasks`,改 bundle 后重启 profile。
---
## 5. 非目标(v1 明确不做)
- Shell / Python / Node 脚本任务、webhook、Server酱 / 钉钉 / 飞书 / 其它 push(chicheng-cron / dsh-scheduler / dsh-schedule-tasks 第一版那条路)。
- 替换官方 `@deepseek-ai/dsh-schedule` 的同会话 `at` / `after_seconds` reminder。
- OS daemon,或 Host 没在跑时仍触发;错过节拍的 catch-up backlog。
- 发布到 npm / GitHub;`link:` 安装;使用本机 GitHub PAT。
- 自然语言解析 cron(仍由模型自己把口语转成 5 段 cron / ISO `at`,插件只收结构化参数)。
- 谷时段 / 峰谷计价窗口、多 Host 抢锁、无人值守 `danger-full-access`。
- 把任何现有插件 copy/fork 当交付物。
- 6 字段秒级 cron、RRULE、自然语言建任务、邮件 SMS。
---
## 6. 后续阶段(不阻塞 v1)
- 打开历史 Session 的一等 UI(取决于官方 unarchive / 按 id 打开能力)。
- 超时取消、并发上限、工作区绑定与权限预设。
- 可选通知(且默认关闭)。
---
## 7. v2 产品补充
- 历史行展示**最后一条助手回复**(如「测试成功」),不只是 `Turn finished`。
- 点击「定时任务」后,**工作区列表区域**换成任务列表(分组=任务,行=运行记录),增删改查/立即运行都在这里,视觉对齐原生 session/project 行。
- 点任务:右侧主栏是编辑页(提示词、cron、工作目录、超时)。
- 点运行记录:右侧打开**原生 DSH 会话**(`sessions.open`),可以继续聊。因此 v2 **不再归档**这些会话(归档会把当前选中会话清掉);普通工作区里用标题前缀「定时任务 ·」把它们藏起来。
- 任务可配 **工作目录** 与 **超时**,便于拉网页写文件、出图等长任务。
## 8. 模型工具
- 与侧栏手动新建共用同一份任务表。
- 工具:`cron_create`、`cron_list`、`cron_pause`、`cron_resume`、`cron_delete`。
- 口语日程由模型转成 5 段 cron 或 ISO `at`;插件不做自然语言解析。
- 这不是官方同会话 reminder(`schedule_create`)。
## 9. 风险与约束
- **隐藏 Session** 依赖当前 Harness 的 `workspaceRegistry.archiveSession`。若归档调用失败,不得把 Session 留在普通列表却宣称验收通过。
- **入口位置**:无官方「新会话下方」slot;注入必须紧挨该按钮。仅 footer 不满足需求。
- **Host 必须在跑**。写进本 PRD 与 README,避免被当成 bug。
- 提示词按不可信内容包裹,降低提示注入把定时通道变成越权入口的风险。