Skip to content

sage-reminder

Verified

sage-reminder · v0.1.2 · MIT · Web UI

时间提醒(reminder)— DSH 会话头部的时间模块:现在时间 / 距上次发言 / 提醒 / 倒计时。定位是会话内的短期提醒(纯 client 半,数据存浏览器本地)

Install

dsh plugin add sage-reminder

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Readme

sage-reminder(时间提醒)

DSH 会话头部的一个时间模块:现在时间 / 距你上次发消息 / 提醒 / 倒计时。

定位是会话内的短期时间提醒 —— 管"此刻这台机器上的计时",不承担"关了也不会忘"的责任。 收起时只占一小条,点开向下拉出面板;到点时模块边框呼吸发亮 + 界面浮条。

DSH 版本兼容性(0.1.1 核对于 0.1.7-rc.1) conversation.session.header.actions 槽位、ctx.slots API、以及「到点唤醒会话」走的 sessions.binding(id).session.prompt() 链,在 0.1.6 → 0.1.7 之间均未改变(@deepseek-ai/dsh-client-ui-slots 两版 SHA256 完全相同)。 0.1.1 另加了槽位挂载的失败留痕:ctx.slots 缺失或注入抛异常会在控制台报警 —— 以前这两种情况下模块是彻底无声的,只表现为「这插件好像没装」。

纯前端插件(dsh.client.platform = web):

  • 时间是浏览器本地时间
  • 提醒与倒计时存在浏览器 localStorage
  • 到点提示 = 模块边框呼吸灯 + 界面浮条
  • host 半不参与任何事 —— 不发系统通知、不读文件、不建网络连接

两个模块,分工是「留不留」

提醒 倒计时
是什么 一天内的一个钟点(如 21:40) 一段时长(天 / 时 / 分 / 秒)
寿命 长期保留在列表里;响过之后回到初始状态,继续等下一个钟点 一次性:跑完自动从列表清理
开关 ▶ 启动、⏸ 关闭(两个动作分开,当前生效的那个亮着) ⏸ 暂停 / 继续、× 删除,带进度条
设定 时 / 分 两个滚轮 天 / 时 / 分 / 秒 四个滚轮

界面

收起态:

┌──────────────────────────────┐
│ 2026年9月19日          ┌─────┐│
│ 周六 15:47            │01:27││   ← 右侧是最近一个到期的计时
└───────────────────────└─────┘┘

展开态分三段:现在时间(含秒)、提醒、倒计时。

  • 「还剩多久」只在 10 分钟内转成金色,其余时候是灰的
  • 条目的操作按钮 hover 才浮出来
  • 模块右边的小胶囊 = 最近一个要响的东西,不用展开就能看见
  • 到点浮条只显示你设的内容(名字),不加「到点了」这类旁白

用法

  • 点模块展开面板;Esc、点面板外面、或再点一次收起
  • 提醒 → + 新建:名称 + 时间(时 / 分滚轮)→ 保存即启动
    • 到点响一次,之后照常留在列表里等下一个钟点(长期保留、不消费)
    • 列表里 ▶ 启动、⏸ 关闭、⋯ 编辑(编辑页左下角是 删除)
  • 倒计时 → + 新建:名称 + 时长(天 / 时 / 分 / 秒滚轮)→ 开始
    • 它是一次性的:跑完自动从列表清理
    • 列表里 ⏸ 暂停 / 继续、× 删除
  • 到点唤醒会话(面板最底部,默认关):提醒到点时往当前会话发一条署名消息,把空闲的会话叫醒
    • 提醒专属;倒计时不唤醒
    • 会话正在跑的时候不发 —— 不打断正在进行的回合
    • 发了没有、为什么没发,面板上看得到(上次唤醒:已投递 / 跳过:会话正在跑 / …)
    • ⚠️ 每次唤醒 = 一轮完整的模型调用(真花钱),所以默认关、一条条单独开

时间怎么选

  • 一律滚轮,不接受文字输入 —— 避免有人敲进 ahfkhas 这类非时间字符
  • 三种改法:点上下行、鼠标滚轮、↑ / ↓ / PageUp / PageDown
  • 24 / 60 进制:时 0-23、分 0-59、秒 0-59、天 0-99。列之间不做进位 —— 要 90 分钟就写「1 时 30 分」,不写「90 分」
  • 不做重复规则(只一次 / 每天 / 每周):提醒天生就是"这个钟点",没什么可选的

安装

// profile 的 package.json
"dependencies": {
  "sage-reminder": "link:<你的路径>/sage-reminder"   // 本地目录用 link:,发布版写版本号
},
"dsh": { "profile": { "bundles": [ ..., "sage-reminder" ] } }

然后在该 profile 目录跑 pnpm install,重启(或刷新页面)。

host 侧注册了什么

什么都没注册。 host 半是故意空的(lib/index.js 只有一个 apply() 打一行启动标记):

  • 不注册工具、不注册服务、不读文件、不起进程、不建网络连接
  • 数据全在浏览器 localStorage,定时也全在浏览器
  • host 半被加载之后不做任何事 —— profile 与它的关系只是"这个包在 bundles 里"

原则是把复杂度压到最小:会话头部的一个时间模块,不该为了"更稳"而拖出一条宿主侧的数据通道。

可选升级路线(当前故意不做)

下面几条都是讨论过、明确决定先不做的方向。记在这里是因为它们各有真实代价 —— 将来真要做时照这张表决策,别凭感觉加。

想解决 要动什么 代价
提醒活过 DSH 重启 数据从 localStorage 搬到宿主侧文件 需要一条 host ↔ client 的数据通道。注意 localStorage 的隔离键是 scheme + host + 端口,而桌面壳每次启动都是 --port 0(OS 随机分配)→ 重启必丢,刷新页面不受影响
让智能体自己设提醒 宿主侧注册一个工具(agent 只能调宿主工具) 和上一条是同一个改动 —— 一加工具,宿主就必须自己存数据,"重启不丢"会被一起带进来
页面关着也能到点提醒 定时也搬到宿主侧常驻 还要重新设计"提醒长什么样"(系统通知 / 托盘 / 等页面回来再补),并重新确认"不要系统通知"这条取舍

别做半迁移。 只把某一类提醒搬到宿主侧、另一类留在 localStorage,结果是两个真相源 + "为什么这条留下了那条没了"的困惑。要做就整库一起搬。

设计上值得一提的三处

一、面板挂在模块自己的容器上,且右对齐。 不是开一个全屏 overlay,而是 「模块 + 下拉面板」包在同一个 position: relative 的容器里,面板绝对定位到它 —— 跟着模块走,不需要算坐标。用 right: 0 而不是 left: 0:模块在会话头部右侧, 往右展开会顶出窗口(实测溢出 127px)。

二、滚轮的对齐靠几何保证,不靠 margin 凑。 列高 = 可见行数 × 行高(3 × 24px), 中间那一行天然落在列的正中,而高亮条也画在列的正中 —— 两者必然重合。 (早先用 margin-top 把首行往下推,本来就偏 10px;再叠上一条被上一行孤儿声明吃掉的 .ck-picker 规则,整块滚轮就退化成了上下两坨。)

三、会话事件窗口要「订阅」而不是「读一次」。 「距你上次发消息」取自 sessions.binding(sessionId).eventSource。 实测它第一次 getSnapshot() 拿到的 entries 是空数组,要 subscribe() 等事件填进来。 用户消息的事件名是 user/message。 拿不到就整行隐藏 —— 不编假数据。

已知限制

  • 数据活不过一次 DSH 重启。 提醒与倒计时存在浏览器 localStorage, 而 localStorage 的隔离键是 scheme + host + 端口;桌面壳每次启动都用 --port 0 (OS 随机分配端口)→ 重启之后 origin 变了,这些数据就读不到。 这是 2026-09-19 当面确认的定位:它就是"会话内的短期提醒",不承担跨重启持久化 —— 真要做,就不止是个 DSH 插件了(要 host 半存盘 + 定时 + 系统级提醒)。 刷新页面不影响。
  • 「距你上次发消息」依赖 DSH 客户端内部接口(sessions 服务 + 会话事件流)。 接口若变,这一行会安静地消失,不影响其余功能。
  • 不含系统通知。到点提醒只有界面内的呼吸灯与浮条 —— 页面不在前台时会错过。

许可

MIT