Skip to main content
F

dsh-task-time

flandern1211/dsh-task-time

Reminders and task timing for DeepSeek Harness: interval alerts while the model runs and overdue alerts past the planned time, plus a persistent red decision alert when the model needs your input (in-app reminder cards with sound while you are in DSH, system toasts when you are away, configurable or keep-until-dismissed card dwell time, and clicking a card jumps to its session); plan time per session, count model runtime only, and report actual vs. planned when the task ends, with a task board and persistent local records.

Install

dsh plugin --profile web add github:flandern1211/dsh-task-time

README

dsh-task-time

提醒 + 任务计时 — DeepSeek Harness 插件,主要就做两件事:

  • 🔔 提醒 — 按累计运行间隔提醒、超时提醒、需要你决策时的红色常驻提醒;人在 DSH 里走界面提醒卡 + 提示音,不在 DSH 里发系统桌面通知
  • ⏳ 任务计时 — 为每个会话设置计划用时,只计模型运行时间,任务结束自动统计实际用时与计划的差距

DSH Plugin License: MIT Version


目录


功能概览

两个主功能:🔔 提醒 · ⏱ 任务计时。 下面的清单按这两个主功能分组,第三节的配套能力是为了让这两件事更好用、数据不丢。

🔔 提醒(主功能)

能力描述
定时提醒按累计运行时间间隔提醒(间隔在设置弹窗里填,默认值可在设置页改)
超时提醒超过计划用时自动发出超时提醒
决策提醒模型需要你确定 / 选择方案时,红色「需要你决策」提醒(界面内常驻卡;不在 DSH 里时额外发系统桌面通知)
内部通知 / 外部通知分流人在 DSH 里只发内部通知(提醒卡 + 界面内提示音),人不在 DSH 里才发系统通知,两边不会同时响;「外部通知时机」可配置
提醒卡滞留时长可配置普通提醒卡显示多久后自动消散可设(2/3/4/5/8/10/15/30 秒、自定义 1–600 秒或「常驻」),默认 4 秒;决策红卡恒常驻
提醒卡可点击跳转点击界面提醒卡直接跳转到该提醒所属的会话
系统通知点击后自动定位点击 Windows 通知横幅会把 DSH 拉到前台,客户端检测到窗口重新聚焦后自动跳到「等你决策」的会话
标题闪烁 / 浏览器通知需要决策时浏览器标签页标题闪烁;不在当前标签页时通过浏览器 Notification API 推送
决策检测三通道 + DOM 兜底approval/requestask_user_question、确认类工具,外加 MutationObserver 监听审批弹窗,确保决策提醒不漏报

⏱ 任务计时(主功能)

能力描述
任务计划设置新会话自动弹出设置窗口,填写任务名、计划用时、提醒间隔(只有点「跳过」才会跳过,误触不会)
连续计时仅模型运行时累加计时,暂停(等待用户输入、审批)不计时,恢复后续计
结束统计任务结束自动统计累计实际用时与计划用时的差距(正数 = 超时)
计划可运行中调整模型可通过 task_plan_set 工具改计划用时,同时取消超时标记
重启续计重启后运行中任务接着累计,停机时间不计入
记录持久化任务记录写入 ~/.dsh/dsh-task-time-records.json(上限 500 条),重启后保留

🧩 配套能力(辅助)

能力描述
Composer Dock 状态条输入框下方实时显示任务名、运行状态、用时、计划时间、提醒次数
任务面板左下角 🗂 按钮打开,进行中 / 已完成分组,点击跳转会话
模块开关「任务用时」与「提醒」两大模块可独立开关,只开提醒也能正常工作
设置页面每个模块只放它最需要的配置:任务用时里配默认计划用时 / 默认提醒间隔,提醒里配提醒卡滞留时长 / 外部通知时机 / AUMID / 测试通知
孤儿任务清理会话已删除时自动收尾残留任务,任务面板不显示僵尸任务

安装

方式一:从 npm 安装(推荐)

dsh plugin add dsh-task-time

方式二:从 GitHub 仓库安装

dsh plugin add https://github.com/Flandern1211/dsh-task-time

方式三:本地安装

git clone https://github.com/Flandern1211/dsh-task-time.git
cd dsh-task-time
dsh plugin add .

安装后重启 DeepSeek Harness,插件自动加载。


快速开始

  1. 创建新会话 → 自动弹出「设置任务计划」窗口
  2. 填写任务信息
    • 任务名称(如"代码审查"、"文档编写")
    • 计划用时(分钟,预填 设置 → 任务用时 → 默认配置 里的「默认计划用时」,默认 60)
    • 提醒间隔(分钟,预填默认 10)
  3. 点击 开始 保存;只有点 跳过 才会跳过本次设置
    • 点击弹窗外区域不会关闭/跳过(只弹出提示),避免误触把设置吞掉
    • 跳过后想补设置:在输入框下方的计时条点 ⚙ 设置计划 即可重新打开弹窗
  4. 任务进行中,输入框下方显示状态条
  5. 超时、决策、提醒自动触发,无需手动操作

使用指南

任务面板

点击左下角 🗂 按钮打开任务面板,查看全部任务:

  • 进行中 — 当前运行的任务,显示状态(运行中 / 已暂停 / 需决策)、已用时间、计划时间
  • 已完成 — 已结束的任务,显示实际用时、与计划的差异
  • 点击任意任务 → 跳转到对应会话
  • 进行中任务可「结束」归档,已完成记录可「删除」

计时规则

  • 只计模型运行时间:模型在思考、调用工具时累加计时
  • 暂停不计时:等待用户输入、审批时暂停计时
  • 恢复后续计:模型再次运行后续计

决策提醒

当以下情况触发时,插件自动发出「需要你决策」提醒:

  • approval/request 事件 — 沙箱升级、权限审批等
  • ask_user_question 调用 — 模型向用户提问
  • 工具执行ask_user_question / confirm / approve
  • DOM 审批弹窗[data-approval-key])—— MutationObserver 兜底检测

决策提醒特征:

  • 🔴 红色提醒卡(常驻不自动消散,点 × 关闭)
  • 🔴 Windows 系统通知(循环报警音)
  • 🔴 浏览器标签页标题闪烁
  • 🔴 浏览器通知(不在当前标签页时)

功能详解

提醒卡系统

  • 提醒卡滞留时长可配置:设置 → 提醒 → 内部通知配置 →「提醒卡滞留时长」,预设 2/3/4/5/8/10/15/30 秒或自定义(1–600 秒),也可选 「常驻(不自动消散)」= 0,只能点 × 或点击卡片关闭);默认 4 秒,对齐 DSH 内置 Toast 的保持时长
  • 普通提醒卡:右下角堆叠显示,按上面配置的滞留时长自动淡出消散(淡出 1 秒后移除)
  • 提醒卡:一个会话最多一张(同一会话反复决策会替换旧卡,不会叠卡);定时/超时/结束/设置完成类卡片都按配置的滞留时长消散;client 本地生成的提示卡(删除/结束/移除失败、测试通知)同样跟随该配置
  • 决策提醒卡:红色常驻,不自动消散(固定 autoMs=0,不受滞留时长配置影响),只能点 × 关闭;决策处理完(host 推「决策已处理」)后自动收掉,标题停止闪烁
  • 点击跳转:点击提醒卡 → 跳转到对应会话 → 卡片消失(跳转同时清掉该会话的决策卡)
  • 点击 ×:仅关闭卡片,不跳转
  • 提醒卡由「提醒 → 内部通知」开关统一控制;关掉后右下角不再出现任何卡片(也不会再走直写 DOM 的渲染路径)

通知:内部 / 外部两条通道

通知按「人是不是正在 DSH 里」自动分流,不会在你看得见界面的时候还弹系统通知:

人在哪通道表现
在 DSH 里(页面可见,默认还要求窗口在前台)内部通知右下角提醒卡(决策时红色常驻)+ 界面内提示音(WebAudio 合成,不依赖外部文件)+ 需要决策时标题闪烁
不在 DSH 里(切到别的窗口 / 最小化 / 关掉页面)外部通知Windows 系统 Toast(含系统提示音)+ 浏览器通知,点击通知后回到 DSH 会自动定位到待决策会话
  • client 每 2 秒(以及焦点/可见性变化时)向 host 上报一次 ui-presence(focused / visible);host 超过 10 秒收不到上报就按「人不在 DSH」处理,保证页面关掉后仍然收得到系统通知
  • 判定结果由 host 统一计算并通过 ui-presence 的返回值同步回 client,两边结论一致:响了内部提示音就不会再发系统通知,反之亦然
  • 「外部通知时机」可配置:DSH 不在前台时(默认,切走就发)或 只有 DSH 页面不可见时(只要 DSH 还看得见就不发);设置页「当前状态」实时显示此刻算不算「在 DSH 里」
  • 「发送测试通知」按钮不受时机限制,任何时候点都会真发一条,方便验证链路

外部通知(Windows 系统 Toast)

  • 默认借用 DSH Desktop 的 AUMID(ai.deepseek.dsh.desktop)发出通知
  • 支持自定义 AUMID
  • 可一键发送测试通知验证链路
  • Windows 11 默认静默丢弃未注册应用身份的通知,插件已做适配
  • 只在人不在 DSH 里时才发(见上一节的通道表)
  • 点击通知后回到 DSH:通知横幅带 dshjump:<sessionId>,点击会把 DSH 窗口置前;客户端在检测到「窗口重新获得焦点」时,先消费 get-pending-jump 的跳转请求,没有请求就直接跳到「正在等你决策」的会话。之所以不做事件回调:Windows PowerShell 5.1 无法订阅 WinRT 通知事件(见「已知问题」)

外部通知声音

场景声音
任务结束Notification.IM
定时提醒Notification.Reminder
决策提醒Notification.Looping.Alarm
超时提醒Notification.Looping.Alarm2

标记状态颜色

状态颜色
运行中🟡 #eab308
已暂停🔵 #60a5fa
需要决策🔴 #f87171
已完成🟢 #4ade80
超时🟠 #fb923c

持久化与重启恢复

  • 记录文件:~/.dsh/dsh-task-time-records.json
  • 重启后恢复全部状态:
    • 已完成记录
    • 会话配置(已配置 / 已跳过)
    • 运行中任务(从当前时间续计,停机时间不计入)
    • 会话历史
  • 防抖写入(800ms),避免频繁磁盘 I/O
  • 退出时同步落盘,确保数据不丢失

孤儿任务清理

插件每 3 秒检查一次存活会话,自动清理:

  • 运行中但会话已删除 → 静默收尾计入已完成记录
  • 从未启动且会话已删除 → 清除残留条目
  • 确保任务面板不显示僵尸任务

命令

模型工具:task_plan_set

模型可在运行中通过此工具调整当前任务的计划用时。

{
  "plannedMinutes": 30,
  "note": "任务说明(可选)"
}
  • 修改后自动取消超时标记(overdueFired = false
  • 若任务尚未启动,自动设置启动时间
  • 返回确认消息含新计划和提醒间隔

此工具已注册到模型 system prompt,会在合适的时机自动调用。


模块开关(按需启用)

插件分为 任务用时提醒 两大模块,可在设置中自由开关,两者完全独立。

在 DSH 设置 → 任务用时模块开关与默认配置 中配置:

任务用时
☑ 启用任务用时
  ☑ 计时引擎(运行/暂停连续计时)
  ☑ 计划配置(设置弹窗 / 跳过恢复)
  ☑ 定时提醒(间隔提醒 + 超时提醒)
  ☑ 任务面板(进行中/已完成 + 操作)
  ☑ Dock 状态条(输入框下方实时显示)
  ☑ 孤儿清理(会话已删除的残留清理)
  ─ 默认配置(新会话设置弹窗的预填值)
    默认计划用时   [60 分钟 ▾](15/30/45/60/90/120/180 或「自定义…」)
    默认提醒间隔(分钟) [10]

提醒
☑ 启用提醒
  ☑ 决策检测(三通道 + 客户端独立检测)
  ☑ 内部通知(在 DSH 里:提醒卡 + 提示音)
  ☑ 外部通知(不在 DSH 里:系统 Toast + AUMID)   ← 外部通知只有这一个开关
  ☑ 标题闪烁(需要决策时)
  Toast 点击跳转(点通知回到 DSH 后自动定位到待决策会话)
  ─ 内部通知配置(在 DSH 里:提醒卡 + 提示音)
    提醒卡滞留时长   [4 秒 ▾](2/3/4/5/8/10/15/30 秒 / 常驻(不自动消散)/ 自定义…)
  ─ 外部通知配置
    外部通知时机   [DSH 不在前台时 ▾](或「只有 DSH 页面不可见时」)
    当前状态       [在 DSH 里 → 只发内部通知 / 不在 DSH → 会发系统通知]
    通知应用身份(AUMID) [ai.deepseek.dsh.desktop]
    外部通知链路   [发送测试通知]

外部通知只有一个开关:提醒模块下的「外部通知」。它的时机、应用身份与测试入口在同一个模块的「外部通知配置」里,不再有第二个会互相覆盖的开关。 通知按「人在不在 DSH 里」走两条通道:在 DSH 里只发内部通知(提醒卡 + 界面内提示音),不在 DSH 里才发系统通知 —— 见上方「通知:内部 / 外部两条通道」。

行为说明

配置结果
只开 提醒、关掉 任务用时只保留决策提醒(红卡 + 系统通知),不显示设置弹窗、任务面板、Dock 状态条
关掉主开关该模块下所有子开关被屏蔽;子开关保留各自状态,主开关重新打开后恢复原状
关掉 内部通知右下角不再显示任何提醒卡(决策红卡、定时/超时/结束提醒卡都归它管)、不再响界面内提示音(系统通知仍可用)
关掉 外部通知人不在 DSH 时不再发系统 Toast / 浏览器通知(「发送测试通知」按钮仍可用)
关掉 任务用时不再计时、不产生"任务完成"提醒(没有任务就没有完成事件)

配置文件~/.dsh/dsh-task-time-modules.json

⚠️ 改完开关需要重启 DSH Desktop(或刷新页面)才会生效 —— 客户端 bundle 在应用启动时载入,热更新仅在 pnpm run dev:web 运行时可用。


配置

在 DSH 设置 → 任务用时 中可调整(配置就放在对应模块里):

配置项位置描述默认值
默认计划用时任务用时 → 默认配置新会话「设置任务计划」弹窗的预填值(预设下拉或自定义)60 分钟
默认提醒间隔任务用时 → 默认配置累计运行多长时间提醒一次(分钟),也是设置弹窗的预填值10
提醒卡滞留时长提醒 → 内部通知配置普通提醒卡显示多久后自动消散(秒,1–600;0 = 常驻不自动消散);决策红卡恒为常驻,不受影响4
内部通知提醒 → 模块开关在 DSH 里时的提醒卡 + 界面内提示音开启
外部通知提醒 → 模块开关不在 DSH 里时的系统通知(唯一开关,开/关只看它)开启
外部通知时机提醒 → 外部通知配置DSH 不在前台时(默认)/ 只有 DSH 页面不可见时unfocused
当前状态提醒 → 外部通知配置实时显示此刻算不算「在 DSH 里」(只读,5 秒刷新)
通知应用身份(AUMID)提醒 → 外部通知配置系统通知的应用身份ai.deepseek.dsh.desktop
外部通知链路提醒 → 外部通知配置「发送测试通知」按钮,手动验证 Toast + 提示音(不受时机限制)

配置说明

AUMID(AppUserModelId) 是 Windows 通知系统的关键标识:

  • 默认借用 DSH Desktop 的已注册身份,横幅可正常弹出
  • 留空使用默认值
  • 修改后可通过 发送测试通知 验证
  • 最近一次通知诊断信息会显示在设置页面下方

记录文件

  • 位置~/.dsh/dsh-task-time-records.json
  • 容量:上限 500 条,超限自动淘汰旧记录

记录字段

字段类型说明
sessionIdstring会话 ID
taskNamestring任务名称
startedAtstring (ISO)启动时间
finishedAtstring (ISO)完成时间
plannedMsnumber计划用时(毫秒)
actualMsnumber实际用时(毫秒)
diffMsnumber差距(毫秒,正数 = 超时)

持久化数据结构

{
  "records": [ ... ],       // 已完成记录
  "configs": { ... },       // 会话配置(含 configured 标记)
  "dismissed": [ ... ],     // 跳过设置的会话列表
  "tasks": { ... },         // 进行中任务
  "history": { ... },       // 会话历史
  "rootSessions": [ ... ],  // 根会话 id(用于状态裁剪)
  "defaults": {             // 全局默认配置
    "reminderIntervalMinutes": 10,      // 默认提醒间隔(分钟)
    "plannedMinutes": 60,               // 默认计划用时(分钟)
    "reminderAutoDismissSeconds": 4,    // 提醒卡滞留时长(秒,0 = 常驻不自动消散)
    "toastAppId": "ai.deepseek.dsh.desktop",
    "externalWhen": "unfocused"         // unfocused | hidden
  }
}

configs / dismissed / rootSessions 按会话累积,加载时会裁剪到 200 条(活跃会话优先保留);只回写上面这些已知字段,历史遗留键不再被写回磁盘。


项目结构

dsh-task-time/
├── lib/
│   ├── index.js             # Host 端:计时、提醒、持久化、RPC、工具注册、prompt section
│   ├── index.d.ts           # Host 端类型声明
│   ├── client.js            # Client 端:浏览器 UI(React)、提醒卡、任务面板、设置页
│   ├── client.d.ts          # Client 端类型声明
│   └── toast.ps1            # Windows WinRT Toast 通知脚本(PowerShell 5.1+)
├── scripts/
│   ├── verify.mjs           # Host 全链路:legacy 迁移 / 状态恢复 / 决策提醒 / 写入 / 孤儿清理
│   ├── verify-modules.mjs   # 模块开关 + 默认配置 + 决策检测三通道 + 通知跳转链路
│   ├── verify-channels.mjs  # 内部/外部通知分流(无头实例 + Toast 打桩)
│   ├── verify-timing.mjs    # 计时 / 间隔与超时提醒 / 结束统计 / 配置边界 / 提醒卡滞留时长
│   ├── verify-dom.mjs       # 客户端 UI(mock React + DOM):提醒卡生命周期、设置弹窗、Dock 状态条
│   └── test-changes.mjs     # 变更合规性检查(源码级断言)
├── cordis.patch.yml         # 插件 bundle patch(插入 host composition)
├── package.json             # npm 包配置
├── README.md                # 本文件
├── LICENSE                  # MIT License
└── .gitignore

架构说明

┌──────────────────────────────────────────────────┐
│                   DSH Desktop                     │
│  ┌─────────────────────────────────────────────┐  │
│  │              Host 进程 (Electron)             │  │
│  │  ┌─────────────────────────────────────────┐ │  │
│  │  │         dsh-task-time (Host)             │ │  │
│  │  │  • 计时 & 提醒 (3s tick)                 │ │  │
│  │  │  • 持久化 (防抖 800ms)                    │ │  │
│  │  │  • 外部通知 (spawn toast.ps1)            │ │  │
│  │  │  • HTTP RPC 服务 (/api/dsh-task-time/rpc)│ │  │
│  │  │  • tool: task_plan_set                    │ │  │
│  │  │  • prompt section 注册                    │ │  │
│  │  │  • 事件监听 (agent/created/status/disposed│ │  │
│  │  │  • 决策提醒 (三通道)                       │ │  │
│  │  │  • 孤儿任务清理                            │ │  │
│  │  └─────────────────────────────────────────┘ │  │
│  │                     │ HTTP RPC                │  │
│  │                     ▼                         │  │
│  │  ┌─────────────────────────────────────────┐ │  │
│  │  │        Renderer 进程 (Browser)           │ │  │
│  │  │  ┌─────────────────────────────────────┐ │ │  │
│  │  │  │    dsh-task-time (Client)            │ │ │  │
│  │  │  │  • 设置弹窗 (shell.overlay)          │ │ │  │
│  │  │  │  • 提醒卡堆叠 (shell.overlay)        │ │ │  │
│  │  │  │  • 任务面板 (shell.overlay)          │ │ │  │
│  │  │  │  • 状态条 (composer.dock)            │ │ │  │
│  │  │  │  • 设置页 (settings.section)         │ │ │  │
│  │  │  │  • 标题闪烁 / 浏览器通知              │ │ │  │
│  │  │  │  • DOM 审批弹窗兜底                   │ │ │  │
│  │  │  │  • 每 2s 轮询 + 上报 ui-presence      │ │ │  │
│  │  │  └─────────────────────────────────────┘ │ │  │
│  │  └─────────────────────────────────────────┘ │  │
│  └─────────────────────────────────────────────┘  │
│                     │ OS                          │
│                     ▼                             │
│  ┌─────────────────────────────────────────────┐  │
│  │  Windows Toast (toast.ps1 via powershell)    │  │
│  │  • WinRT ToastNotificationManager            │  │
│  │  • 点击 → DSH 窗口置前 → client 焦点检测 → 跳到待决策会话 │  │
│  └─────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────┘

开发

技术栈

技术
HostNode.js (ESM), Electron (可选)
ClientReact (via ModuleLoader), 原生 JS
通信HTTP POST RPC (/api/dsh-task-time/rpc)
通知Windows WinRT Toast (PowerShell toast.ps1)
持久化JSON 文件 (~/.dsh/dsh-task-time-records.json)
打包cordis patch (cordis.patch.yml)

构建

# 构建(如果使用 TypeScript 源码)
npm run build

# 或直接修改 lib/ 下文件(JS 源码无构建步骤)

验证

# 运行全部套件(mock 运行时,无需 DSH Desktop;不会真的弹系统通知)
npm test

# 单独跑某个套件
node scripts/verify.mjs            # Host 全链路
node scripts/verify-modules.mjs    # 模块开关 + 默认配置 + 决策三通道
node scripts/verify-channels.mjs   # 内部/外部通知分流
node scripts/verify-timing.mjs     # 计时 / 提醒 / 配置边界 / 滞留时长
node scripts/verify-dom.mjs        # 客户端 UI(提醒卡生命周期、设置弹窗、Dock 状态条)
node scripts/test-changes.mjs      # 变更合规性检查

npm test 串跑 6 个套件(verifyverify-modulesverify-channelstest-changesverify-timingverify-dom),全部通过才返回 0:

  1. Legacy 迁移 — 旧格式记录文件迁移
  2. 状态恢复 — configs/dismissed/tasks/history/defaults 重启恢复
  3. 决策提醒 — 三通道(approval/userQuestions/tools-pre-execute)+ 20s 节流
  4. 持久化写入 — 配置写入 + 重加载验证
  5. 孤儿任务清理 — 已删除会话的残留任务清理
  6. 计时与提醒 — 运行/暂停累加、间隔与超时提醒去重、结束统计、配置边界(含提醒卡滞留时长 0/负数/过大值)
  7. 客户端 UI — 提醒卡渲染与生命周期(按配置滞留时长消散、常驻卡不消散)、决策卡去重、设置弹窗校验、Dock 状态条各状态

部署

插件作为 profile bundle 安装,通过 cordis.patch.yml 插入 host composition:

# cordis.patch.yml
- insert:
    - id: dsh-task-time
      name: 'dsh-task-time'

事件钩子

事件用途
agent/created新会话创建时检查是否需要弹出设置窗
agent/status监听运行/暂停状态切换,累计计时
agent/disposed真实根会话关闭时自动结束任务并归档(子会话/subagent 收工不算任务结束:按会话头血统识别后静默丢弃,不写记录也不发提醒)
approval/request需要审批时触发决策提醒
internal/service监听 userQuestions 服务注册,hook ask 方法
tools/pre-execute确认类工具执行前兜底触发决策提醒

RPC 接口

所有通信通过 HTTP POST POST /api/dsh-task-time/rpc,请求体格式:

{ "method": "methodName", "args": { ... } }
方法用途
get-pending-setup查询会话是否待设置
get-pending-sessions查询所有待设置会话
get-status获取某会话计时状态
get-reminders获取增量提醒
get-pending-jump获取待处理的 toast 点击跳转(兼容/扩展通道,见「已知问题」)
get-latest-decision-session当前「等你决策」的会话(点击通知回到 DSH 后用它定位)
get-config获取全局配置
set-config设置全局配置(非法值直接忽略:提醒间隔/计划用时必须是 >0 的分钟数;reminderAutoDismissSeconds 必须是 >=0 的秒数,0 = 常驻,过大夹到 600)
get-session-config获取会话配置
set-session-config设置会话配置(计划用时必填,缺失/<=0 返回 ok:false + 可读错误)
dismiss-session-setup跳过会话设置(仅由「跳过」按钮调用)
reopen-session-setup撤销「已跳过」,重新打开设置弹窗(计时条「⚙ 设置计划」)
get-history获取会话历史(保留接口,客户端暂未调用)
get-pending-sessions查询所有待设置会话(保留接口,客户端暂未调用)
get-task-board获取任务面板数据
end-task结束任务
drop-task移除残留任务
delete-task-record删除已完成记录(同时清掉 history 并落盘)
clear-task-records清空全部已完成记录
test-toast测试通知
trigger-decision-alert触发决策提醒

已知问题 / 边界情况

  • Windows 11 通知静默丢弃:未注册 AUMID 的通知可调用成功但横幅不显示,默认身份为 DSH Desktop 的已注册身份
  • PowerShell 5.1 编码toast.ps1 保持纯 ASCII,非 ASCII 注释会导致 ANSI/GBK 解码错误
  • PowerShell 无法订阅 WinRT 通知事件(2026-09 实测):Register-ObjectEvent 直接报 "Windows PowerShell cannot subscribe to Windows RT events";用 TypedEventHandler 委托转换虽然能 add_Activated 成功,但回调永不执行(WinRT 在无 PowerShell runspace 的线程上触发)。因此 toast.ps1 不做点击回调,点击定位改由「窗口重新聚焦 + get-latest-decision-session」实现;get-pending-jump 保留为兼容通道(谁写文件谁生效,TTL 5 分钟)
  • 提醒间隔/计划用时的最小值set-config 会忽略 <=0 的值,过大值夹到 24 小时;set-session-config 缺计划用时直接拒绝(弹窗会在本地先拦一次并给出提示)
  • 提醒卡滞留时长set-config 接受 0(= 常驻)与 1–600 秒,负数/非数忽略;「需要你决策」红卡恒为 autoMs=0,不受该配置影响。上限 600 秒是因为再长实际等同于常驻,直接选「常驻」更清楚
  • agents.roots() 不是「用户会话」判据(2026-09 踩坑,v0.2.10 修复):dsh-agentAgentsRegistry.roots() 只表示「运行时没有 owner 的顶层 agent」,其源码注释明确写了它不反映持久化会话血统,subagent 会话同样在其中。判断"这个会话算不算用户任务"必须用会话头字段 origin === 'subagent' / delegationDepth > 0;也不能改用 parentSession —— 用户 fork 出来的会话同样带 parentSession(另有 seedLength),属真实会话。sessions 服务(dsh-sessionSessionStore)的 get(id) 返回带 .header 的 Session,可用来读这些字段;已关闭的会话不在 store 里,读不到时按"不排除"处理

更新日志

v0.2.10

  • 修复 子会话(subagent)被当成用户任务,表现为「任务还没结束却一直报已经结束」:判定"这是不是一个用户任务"用的是 isRoot()agentsSvc.roots() 成员资格),但 dsh-agentAgentsRegistry.roots() 只按「运行时没有 owner」过滤(源码注释明确写了它不反映持久化会话血统),而 subagent 会话在运行时同样是无 owner 的顶层 agent —— roots() 里照样有它。后果:每个 subagent 都被建了任务、agent/statusrunning 就开始计时,subagent 一收工 agent/disposed 就报一次「✅ 任务结束」(界面卡 + 系统通知)并写一条"已完成"记录(无名、计划 0)。实测一次 fan-out 4 个 subagent 会在约 10 分钟内陆续报 4 次"结束",而父会话自己的任务一直在跑
  • 修复 判据改为会话头的持久化血统字段:origin === 'subagent'dsh-session 里该字段的唯一合法值)或 delegationDepth > 0(顶层 0、子会话 1、嵌套子会话 2…)。不能改用 parentSession 判定:用户 fork 出来的会话同样带 parentSession(并额外有 seedLength),但那是真实会话,必须照常计时
  • 修复 覆盖全部入口:agent/created / agent/status / agent/disposed(优先读 live agent 自带的 agent.session.header,不依赖服务)以及 tick 孤儿清理 / get-task-board / get-status / get-pending-setup / get-pending-sessions(走 sessions 服务 SessionStore.get(id).header,读到的头部会缓存)。读不到任何会话头时保持原行为(不排除),绝不误杀真实任务
  • 修复 修复前遗留的幽灵条目、以及 rootSessions 里混入的 subagent id,由 tick 静默清理:不写"已完成"记录、不发提醒(resolveRootSession 也不再因为 rootSessions 里有 subagent id 就把子会话当根会话返回)
  • 新增 get-status 返回 lineage 诊断字段(headerRead / headerOrigin / delegationDepth / parentSession / isSubagentSession):headerRead:false 说明会话头没读到、判定退回了保守行为,可据此确认修复是否真的生效
  • 测试 scripts/verify-timing.mjs 新增场景 P(12 项):子会话不进设置弹窗候选、不开始计时、收工不写记录/不发「任务结束」卡/不发系统通知、遗留幽灵条目被静默清理;并以 fork 会话与真实根会话作对照,防止"一刀切"误杀。脚手架新增可注入的假 sessions 服务。回归强度:把 lib/index.js 回退到修复前,场景 P 精确失败 7 项(含 ✅ 未命名任务 · 任务结束 那条系统通知断言)

v0.2.9

  • 文档 简介改为以 提醒任务计时 两大主功能为主线:顶部简介从「任务用时管理与提醒插件」改为两条要点(🔔 提醒 / ⏳ 任务计时),「功能概览」原来一张 17 行的平铺表拆成三组——🔔 提醒(主功能)⏱ 任务计时(主功能)🧩 配套能力(辅助),内容未删减,只是把主功能提到前面
  • 文档 package.jsondescription 同步改写:先说提醒(定时/超时/需要你决策时的红色常驻提醒 + 内外通知分流 + 卡片可点击跳转),再说任务计时(会话级计划用时、仅模型运行时连续计时、结束统计与计划的差距、本地持久化),与市场条目 data/plugins/Flandern1211__dsh-task-time.ymldescription.en / description.zh 保持一致
  • 无代码改动:lib/scripts/ 未动,功能与 v0.2.8 完全相同

v0.2.8

  • 新增 提醒卡滞留时长可配置:设置 → 提醒 → 内部通知配置 →「提醒卡滞留时长」,预设 2/3/4/5/8/10/15/30 秒、自定义(1–600 秒)或「常驻(不自动消散)」,默认仍是 4 秒(对齐 DSH 内置 Toast 的 HOLD_MS+FADE_MS)。配置存在 defaults.reminderAutoDismissSeconds,host 每条普通提醒卡按它下发 autoMs,重启后从状态文件恢复
  • 修复 滞留时长此前是硬编码:host 的 pushReminder 写死 4000,client 本地提示卡(删除/结束/移除失败、测试通知)写死 6000/8000 —— 同一个插件里三种时长且都改不了。现在 host 下发的 autoMs 优先,client 本地卡回落到同一个配置值(启动时 get-config 同步一次,设置页改动即时同步)
  • 不变 「需要你决策」红卡恒为常驻(autoMs=0),不受该配置影响——几秒后消散的红卡对离开电脑的用户等于没提醒
  • 测试 新增 host 端 12 项(默认值/自定义/负数拒绝/过大夹取/0=常驻/决策卡不受影响/重启恢复)+ 客户端 12 项(10 秒不快消、常驻不消散、本地卡跟随配置、设置页下拉预设与自定义)

v0.2.7

  • 修复 决策提醒卡重复渲染:一条「需要你决策」提醒同时走 React 提醒栈与 pushReminder() 里直写 DOM 的旧路径,界面上会叠出两张红卡(一张 bottom:76px、一张 bottom:16px,后者还盖住左下角 🗂 任务面板按钮),而且点一张的 × 另一张不消失。现在只保留 React 一条渲染路径
  • 修复 决策处理完不消卡:用户在审批弹窗里做完决策后,host 只推了一条普通的「决策已处理」提醒,没人移除常驻红卡,标题也继续闪 ❓ 需要你决策。现在 client 收到 decision-cleared 会收掉该会话的红卡,host 也会把该会话的 needDecision 提醒从队列里摘掉(否则刷新页面重新拉取时红卡会"复活",get-latest-decision-session 也会指向过期会话)
  • 修复 同一会话两张红卡:客户端本地 approval/request 检测(负 id)与 host 轮询提醒(正 id)没有合并。现在 pushReminder 对同一会话只留最新一条决策卡
  • 修复 「内部通知」开关关不住提醒卡:ReminderGate 原来用的是「reminderUI intervalReminder」,定时提醒开着就照样显示卡片;直写 DOM 的决策卡更是完全不看开关。现在提醒卡统一归「内部通知」管,与文档一致
  • 修复 end-taskdiffMs 符号反了(写成 plannedMs - actualMs,与 finishTask 和文档的「正数=超时」相反)
  • 修复 delete-task-record 在清理 history 之前落盘,且之后不再落盘 → 磁盘上残留已删除的记录,重启后"复活"
  • 修复 计划用时留空时会把 plannedMinutes: null 存下来并标记 configured=true,结果是任务永远不启动(不计时、不提醒、也不再弹设置窗),确认文案还写成"计划用时 null 分钟"。现在 host 拒绝非法计划用时(ok:false + 可读错误),client 在弹窗里先本地拦截并给出提示,弹窗不关闭
  • 修复 set-config 不校验提醒间隔:0 / 负数会被存下来,随后 intervalMs <= 0 让定时提醒静默失效(开关显示开着却再也不提醒)。现在非法值直接忽略、过大值夹到 24 小时;状态文件里遗留的非法值在加载时修正为默认值
  • 修复 决策外发通知的 20 秒节流把「人在 DSH 里、根本没发出去」的那次也记进节流时间戳 → 用户 20 秒内切出 DSH 会收不到这条决策通知。现在只有真的发出去了才记
  • 修复 状态文件无限增长:dismissed / configs / rootSessions 按会话累积、从不清理(实测几十个会话后 dismissed 已有 70 条)。现在加载时裁剪到 200 条(保留活跃会话),并且只回写已知字段,历史遗留键(已移除的 externalAlert、旧版默认值快照)不再被一次次写回磁盘
  • 修复 「外部通知时机」默认值与文档/设置页不一致:代码里是 hidden(只有页面不可见才发),注释、README 三处与设置页都写默认 unfocused。按文档与既有落盘配置统一为 unfocused
  • 修复 点击系统通知后的会话定位链路实际是断的:get-pending-jump 读的跳转文件已经没有任何写入方(toast.ps1 回退为 fire-and-forget 后没人写),客户端也从不调用 host 的 get-latest-decision-session。现在客户端在「窗口重新获得焦点」时先消费跳转请求,没有再跳到「正在等你决策」的会话;toast.ps1 与 README 里的相关说明同步改写(并记录 PowerShell 订阅不到 WinRT 事件这一实测结论)
  • 改进 提醒卡上限溢出时连淡出定时器一起清理;弹窗遮罩提示与「开始」校验共用同一套 hint 逻辑
  • 测试 npm test 从 4 个套件扩到 6 个:新增 host 全链路套件(101 项:计时/提醒/记录/工具/决策/边界/瘦身)与客户端 UI 套件(39 项:真实 DOM + mock React 渲染决策卡与设置弹窗);旧套件补上系统通知打桩——在此之前跑测试会真的在桌面上弹 Windows 通知

v0.2.5

  • 改进 系统提示词同步双通道行为:原来写的是「含系统提示音与桌面通知」,v0.2.4 改成按人在不在 DSH 分流后文案没跟上——现在改为「人在 DSH 里时发界面内提醒卡 + 提示音,不在 DSH 里时发系统桌面通知;需要决策时界面内常驻红卡,不在 DSH 里时额外发桌面通知」,避免 agent 向用户转述错误的通知方式
  • 清理 移除调试期留下的 console.log:host 端 6 处(get-pending-setup 每次弹窗轮询都打一行、两条 stale waitingDecision 分支、三处 cleared waitingDecision)、client 端 3 处(SetupManager poll 每 2 秒打一行并 JSON.stringify 全部会话 id、get-pending-setup 结果、approval/request 到达)——这些在 DSH 控制台里会持续刷屏,且 SetupManager poll 的字符串拼接本身有开销
  • 清理 保留 4 处真正有诊断价值的日志(模块配置加载/保存失败的 console.warnsession/eventapproval/request 处理异常的 console.error
  • 移除仓库里的临时文件:.npm-cache/(暂存的 npm 缓存,已加入 .gitignore)与 _dbg_channels.mjs(调试探针,功能已被 scripts/verify-channels.mjs 覆盖)
  • 顺带修正 clearWaitingDecision 三处调用点残留的缩进

v0.2.4

  • 修复 设置弹窗误判为「跳过」:遮罩(弹窗外区域)点击不再等于跳过——以前点一下弹窗外区域就会永久跳过本次设置,鼠标滑出窗口后点回窗口、或在输入框里拖拽选字时松手到卡片外,都会命中这条路径
  • 新增 误跳过可恢复:计时条新增「⚙ 设置计划」,对应的 reopen-session-setup RPC 会撤销「已跳过」标记并重新弹出设置窗
  • 改进 get-status 增加 configured 字段;设置弹窗点击遮罩时给出提示文案
  • 修复 外部通知有两个开关互相打架:设置页原来的「外部通知(提示音 + 系统弹窗)」勾选框与提醒模块里的「外部通知」开关会各自覆盖对方(开关显示「开」、实际被旧值静默关掉)。现在只保留模块开关里的那一个,旧配置项 defaults.externalAlert / session.externalAlert 已移除
  • 新增 任务用时的可修改默认配置:设置页「任务用时 → 默认配置」可改默认计划用时(15/30/45/60/90/120/180 预设或自定义)与默认提醒间隔,新会话设置弹窗按此预填(defaults.plannedMinutesget-pending-setup 下发 defaultPlannedMinutes
  • 改进 设置页按模块分组:开关与这个模块最需要的配置放在同一组里(任务用时 → 默认配置;提醒 → 外部通知配置 + 发送测试通知按钮)
  • 新增 内部通知 / 外部通知双通道:client 每 2 秒上报 ui-presence(焦点 + 可见性,超时 10 秒按离开处理),host 用 shouldSendExternal() 统一判定——人在 DSH 里只发内部通知(提醒卡 + WebAudio 合成提示音),不在 DSH 里才发系统通知;判定结果回传 client,两个通道不会同时响
  • 新增 「外部通知时机」配置(defaults.externalWhen):DSH 不在前台时(默认)或 只有 DSH 页面不可见时;设置页「当前状态」实时显示此刻算不算「在 DSH 里」
  • 改进 通知开关改为对称的一对:内部通知(在 DSH 里:提醒卡 + 提示音) / 外部通知(不在 DSH 里:系统 Toast + AUMID);浏览器通知也归外部通道(人在 DSH 里不再弹);「发送测试通知」不受时机限制
  • 修复 插件刚加载就被停用时,停用回调会拿内置默认值覆盖磁盘上已存的配置(默认计划用时 / 外部通知时机等)—— 现在只有状态真的从磁盘恢复过(stateLoaded)才落盘,且停用落盘改用 RECORDS_FILE 而不是另写一份硬编码路径
  • 修复 get-config / get-external-status 现在会 await ensureLoaded(),设置页不会先显示内置默认值再跳变

v0.2.1 (2026-09-10)

  • 修复 end-task 缺少 history 截断,已完成记录可无限增长
  • 修复 决策节流记录(decisionAlerts)只增不删导致内存泄漏 + 新决策被旧节流压制
  • 修复 任务结束时未清理决策节流记录,影响同一会话的新任务
  • 修复 跳过会话设置时丢弃已累积的任务计时,改为归档到已完成记录
  • 修复 set-session-config/dismiss-session-setuppersistRecords() 无 await,即时落盘保证不可靠
  • 修复 3s tick 定时器可能重叠执行导致竞态,改为递归 setTimeout
  • 修复 tools/pre-execute 正则未锚定,子串匹配可误触发决策提醒

v0.2.0

  • 首个正式发布版本,完整功能集见上方功能概览

License

MIT License — 详见 LICENSE

Copyright (c) 2026 dsh-task-time contributors

Related plugins