sage-reminder
Verifiedsage-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.slotsAPI、以及「到点唤醒会话」走的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