dsh-later 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,59 @@
4
4
 
5
5
  ## 未发布
6
6
 
7
+ ## 1.3.0 — 2026-10-10
8
+
9
+ - **feat** `/later` 附件支持:带图提交不再被拦截,附件写进 sidecar,到点按记录顺序拼在文本块之后代发。
10
+ - **feat** 多次写法(Beta,默认开启):`+1h*3`(等间隔×N)、`+1h+1h+0.5h`(相对链式)、`15:32+16:32`(绝对锚点)展开成 N 条独立 one-shot 任务。上限 10 条、最小间隔 60 秒。
11
+ - **feat** 残片守卫:`+1h*3 x` 不再静默误解析成单条,一律显式报错。
12
+ - **fix** 时区墙钟修复:UTC 负偏移时区(如 `America/Los_Angeles`)整体错一天的问题。
13
+ - **refactor** UI 图标全部改用官方图标集 `@deepseek-ai/dsh-client-ui-primitives`(issue #19):删掉 8 处手写内联 SVG 与 3 处文本符号,统一换 `<IconXxxRegular />`。dock 操作按钮改为 portal 化的 `DockTip` tooltip。
14
+ - **fix** 定时配置面板抬升面对齐官方风格(issue #24):背景/描边/阴影/圆角/层级对齐官方 PickerPopover,面板 portal 到 document.body,定位改用官方 `useAnchoredPosition`。
15
+ - **docs** README 与微文案补上多次写法、附件与设置项说明。
16
+
17
+ ## 1.2.0 — 2026-10-09
18
+
19
+ - **feat** 暴露跨插件「定时任务摘要」公共 API(issue #17)。
20
+ - 新增 `src/schedule-summary.ts`(纯函数,宿主/浏览器/`node --test` 共用):
21
+ `buildScheduleSummary` 把 `sessions.list` 折叠成
22
+ `Map<SessionId, { count, pausedCount, nextAt, state }>`;`summarizeScheduleGroup`
23
+ 按分组聚合(会话数/任务数/最早触发/最高优先级,`overdue > urgent > scheduled`)。
24
+ - 新增 `src/schedule-summary-store.ts`:可订阅 store(`getSnapshot` / `subscribe` /
25
+ `reload` / `dispose`)+ 全局登记表,**唯一数据权威**——内部负责 `sessions.list`
26
+ 订阅、30s 状态 tick 与 60s `session.list` 读 RPC(仅页面可见时)。
27
+ - 新增 `src/client/useScheduleSummary.ts`:React 订阅 hook(`useSyncExternalStore`,
28
+ store 迟到时自动重渲染)。
29
+ - `lib/client.js` 导出 `getScheduleSummaryStore` / `subscribeScheduleSummaryStore` /
30
+ `useScheduleSummary` / `buildScheduleSummary` / `schedulesOfProjection` /
31
+ `summarizeScheduleGroup` / `createScheduleSummaryStore` 及类型;消费方(如
32
+ dsh-group-status)声明 `dsh.client.external: ["dsh-later"]` 后
33
+ `require('dsh-later/client')` 即可使用。契约见
34
+ [API 参考 §7](./docs/technical/03-api-reference.md)。
35
+ - **refactor** 侧栏 badge 不再自己读 `sessions.list` / 周期 `refresh()`,改为消费同一个
36
+ store:外部插件读到的摘要与本插件行内 badge 必然同源,且 `session.list` 读 RPC
37
+ 不再被拉两次。
38
+ - 新增 15 个单测:纯逻辑(数量/状态分档/暂停语义/分组聚合/判等)、store
39
+ (列表变化、状态 tick、refresh 闸门、dispose、登记表)与构建产物契约
40
+ (`lib/client.js` 导出面 + apply 登记/注销 store)。
41
+
42
+ - **feat** 侧栏 badge 进度环(方案 A:track + prog 双环)。
43
+ - 新版时钟 glyph 从单环改为双环:`ss-clock-track` 恒定底环(总窗口参照),
44
+ `ss-clock-prog` 进度弧从 12 点顺时针填充(`stroke-dashoffset` 驱动)。
45
+ - 新增 `progressFrom` 字段(`PresenceSummary`):窗口起点时刻(epoch ms),
46
+ 只跟随 `nextAt` 那条任务。
47
+ - 新增 `windowStartOf` 推导逻辑,按优先级取窗口跨度:
48
+ 1. `window_seconds`(宿主/投影显式给)→ 最权威
49
+ 2. `after_seconds` / `every_seconds`(按 `kind` 推)
50
+ 3. `created_at`(`at` 类型回退)
51
+ 推不出则 `progressFrom` 缺省,视觉退回恒定整环(旧形状即退化态)。
52
+ - `progressOf(entry, now)` 返回 0–1 比例;超时停满环(不绕出去)。
53
+ - urgent 只脉冲底环(进度弧跟着闪会让比例不可读)。
54
+ - 减少动效模式:进度弧位移过渡一并停。
55
+ - 新增 11 个单测:after/every/at 窗口推导、window_seconds 优先、退化/非法值、
56
+ 多任务窗口跟随 nextAt、暂停项排除、camelCase 兼容、无窗口时形状不变。
57
+ - `sameScheduleSummary` 补 `progressFrom` 比对(暂停→恢复窗口换了但 nextAt 未变时
58
+ 仍需通知订阅者刷新进度环)。
59
+
7
60
  ## 1.1.0 — 2026-10-09
8
61
 
9
62
  - **feat** 设置页 UI 对齐官方 `SettingsForm`:从折叠卡片改为裸表单,字段直接平铺。
package/README.en.md CHANGED
@@ -36,7 +36,7 @@ Click the **"Schedule send"** button to the right of the input box. The panel us
36
36
 
37
37
  ### Time formats
38
38
 
39
- All of these are accepted (`/schedule`, `/later`, and the panel's custom time share the same parser):
39
+ All of these are accepted (`/schedule`, `/later`, and the panel's custom time share the same parser; the rows marked *commands only* are rejected by the panel):
40
40
 
41
41
  | Kind | Examples |
42
42
  |---|---|
@@ -44,23 +44,55 @@ All of these are accepted (`/schedule`, `/later`, and the panel's custom time sh
44
44
  | Time today | `15:32` · `9点` · `9点半` · `1532` |
45
45
  | Relative day + time | `明天9点` · `后天 10:00` |
46
46
  | Date + time | `8月21日 15:32` · `2026-08-21 15:32` · `0821-1532` |
47
+ | Repeat, fixed interval (*commands only*) | `+1h*3 drink water` · `+30m×2 check build` |
48
+ | Repeat, chained sequence (*commands only*) | `+1h+1h+0.5h watch the rollout` · `+1h +30m submit report` |
49
+ | Anchor sequence (*commands only*) | `15:32+16:32+17:32 three meetings` · `9:00+1d+1d standup` · `明天9点+1h` |
50
+
51
+ The three bolded rows above are **[Advanced syntax (Beta)](#configuration)** and can be switched off together in settings; every other form is unaffected.
47
52
 
48
53
  A bare time (like `1532`) rolls over to tomorrow if it has already passed today (up to 7 days). Writing `明天`/`后天` is taken literally — if that time has passed you are told to use a concrete date instead. A month/day only (like `8月21日`) rolls over to next year when this year's has passed.
49
54
 
55
+ Multi-shot rules — **the first occurrence lands after the first interval**:
56
+
57
+ | Input | Expands to |
58
+ |---|---|
59
+ | `+1h*3` | t+1h, t+2h, t+3h |
60
+ | `+1h+1h+0.5h` | t+1h, t+2h, t+2.5h (chained — good for "2 h → 1 h → 30 min before the deadline" nagging) |
61
+ | `15:32+16:32+17:32` | those three clock times today |
62
+ | `23:00+01:00` | 23:00 today, **tomorrow** 01:00 (an earlier time in the chain rolls over, same rule as a single shot) |
63
+ | `9:00+1d+1d` | 09:00 on three consecutive days |
64
+
65
+ At most **10 tasks** per command, and multi-shot intervals must be **≥ 60 seconds** (single shots are unrestricted — `/later +5s` still works). `*`, `×` and `x` all work, an optional Chinese counter suffix is accepted (`*3次`), and the count must be followed by a space. Every occurrence is an **independent task**: cancel it, edit it or send it early on its own, and `/later` attachments are carried by each one. Absolute times (`15:32`, `明天9点`) do **not** accept `*count` — you get an explicit error instead of a silently wrong single task.
66
+
67
+ Chains allow exactly **one direction: an absolute anchor, then forward accumulation.** `+1h+15:32` (a clock time after a duration) is rejected rather than guessed.
68
+
69
+ One difference between anchor and relative chains (deliberate, not a bug):
70
+
71
+ | Form | Stored as | Pausable | Across DST |
72
+ |---|---|---|---|
73
+ | `+1h*3` / `+1h+30m` (relative) | `after` | ✅ | —— |
74
+ | `15:32+16:32` / `9:00+1d` (anchor) | `at` | ❌ (button disabled with a reason) | ✅ wall clock holds: on a fall-back day `9:00+1d` is **25 h** later and still 09:00, whereas adding 86400 s would drift to 08:00 |
75
+
76
+ `+1d` advances by **calendar day**, not 86400 seconds — which is why `+1d*3` and `9:00+1d+1d` differ in zones with DST (the former counts real seconds, the latter counts wall clock). A wall clock that does not exist (the hour skipped by the spring-forward) is an error, never a silently shifted time.
77
+
50
78
  ### Commands
51
79
 
52
80
  | Command | Effect |
53
81
  |---|---|
54
82
  | `/schedule <time> <content>` | Create a reminder (shows up in the conversation when due) |
55
83
  | `/later <time> <content>` | Delayed send: deliver the content later as yourself |
56
- | `/schedule every <interval> <content>` | Recurring reminder (interval ≥ 5 min) |
84
+ | `/later <time>*<count> <content>` | Same, expanded into N delayed sends (`+1h*3`) |
85
+ | `/schedule <time>+<time> <content>` | Same, a chained sequence expanded into N reminders (`+1h+30m`) |
86
+ | `/later <clock>+<clock> <content>` | Anchor sequence: expand by wall-clock instants (`15:32+16:32`, `9:00+1d+1d`) |
57
87
 
58
88
  ```
59
89
  /schedule 明天9点 提交周报
60
90
  /later 1532 提醒团队同步进度
91
+ /later +1h*3 起来喝水
92
+ /later +2h+1h+30m 催我交周报
61
93
  ```
62
94
 
63
- The model can also call user tools on your behalf to create/inspect/delete/edit reminders (`user_schedule_create` / `user_schedule_list` / `user_schedule_delete` / `user_schedule_edit`).
95
+ The model can also call user tools on your behalf to create/inspect/delete/edit reminders (`user_schedule_create` / `user_schedule_list` / `user_schedule_delete` / `user_schedule_edit`). **Endlessly recurring** reminders (every hour, forever) are only reachable this way: ask the model "每小时提醒我喝水" and it creates an `every_seconds` schedule — such a task has no end condition and must be deleted by hand.
64
96
 
65
97
  ### Pending dock
66
98
 
@@ -186,11 +218,64 @@ Works out of the box. Adjust in **DSH Settings → Plugins → Session Scheduler
186
218
  | Allow long prompts | Lift the 1000-character default cap | Off |
187
219
  | Custom cap | Character cap when `allowLongPrompts` is on | 1000 |
188
220
  | Work hours / lunch / night quiet | Five HH:mm fields driving smart slots and auto-scheduling | See settings page |
221
+ | Interjection delivery | Which queue "Send interjection" uses: own turn (default, safe) / current turn (faster, can drop in an edge case) | Own turn |
222
+ | Delivery confirmation / on unconfirmed | Whether to verify a reminder really landed, and what to do when it isn't confirmed | On / keep |
223
+ | Clock icon style | `auto` (by kernel version) / `modern` / `classic` | auto |
224
+ | **Advanced syntax** (Beta) | Master switch for multi-shot forms: `+1h*3`, `+1h+1h+0.5h`, `15:32+16:32`, `9:00+1d`. When off, those forms are rejected with a pointer back to settings | **On** |
225
+
226
+ > Advanced syntax is marked Beta not because it is untested (43 new unit tests cover it) but because **the expanded result still needs real-world observation**: whether 10 tasks flood the dock, and whether the 60-second floor and 10-task cap are the right sizes.
227
+ > Turning it off does **not** restore the old silent mis-parse (`+1h*3 x` creating one task whose content is `*3 x`) — that was a bug, not a fallback. The residual-token guard is independent of this switch and always on.
189
228
 
190
229
  > Advanced: defaults can also be overridden at startup via `cordis.patch.yml` (e.g. `maxSchedules`) — see `docs/` and the schema.
191
230
 
192
231
  ---
193
232
 
233
+ ## For plugin developers: the schedule summary API (cross-plugin)
234
+
235
+ dsh-later exposes a workspace-wide schedule summary as a public data face
236
+ ([issue #17](http://192.168.4.77:3000/dsh-plugins/dsh-later/issues/17)), so other client
237
+ plugins (e.g. dsh-group-status) can aggregate schedule state on folders/sidebar rows
238
+ **without** parsing the `userSchedules` projection themselves — and without drifting
239
+ from the inline row badge.
240
+
241
+ Declare the external module in `package.json` first:
242
+
243
+ ```json
244
+ { "dsh": { "client": { "platform": "web", "external": ["dsh-later"] } } }
245
+ ```
246
+
247
+ Then `require('dsh-later/client')` inside the client factory:
248
+
249
+ ```js
250
+ const { getScheduleSummaryStore, summarizeScheduleGroup, useScheduleSummary } = require('dsh-later/client')
251
+
252
+ // 1) Read the store directly: session id → { count, pausedCount, nextAt, state }
253
+ const store = getScheduleSummaryStore() // undefined when dsh-later is not loaded
254
+ const summary = store?.getSnapshot() ?? new Map()
255
+
256
+ // 2) Aggregate per group (used by the folded-folder badge)
257
+ const group = summarizeScheduleGroup(summary, groupSessionIds)
258
+ // → { sessions, count, pausedCount, nextAt?, state? }
259
+
260
+ // 3) Subscribe inside a React component (re-renders if the store arrives late)
261
+ const live = useScheduleSummary()
262
+ ```
263
+
264
+ | Field | Meaning |
265
+ |---|---|
266
+ | `count` | Number of schedules (**including** paused ones) |
267
+ | `pausedCount` | Paused schedules (they count toward `count` but **not** toward `nextAt`) |
268
+ | `nextAt` | Earliest fire time (epoch ms); `undefined` when everything is paused |
269
+ | `state` | `scheduled` / `urgent` (≤5 min) / `overdue` (due but not dispatched), judged at snapshot time |
270
+
271
+ Contract notes:
272
+
273
+ - The summary contains **only sessions with schedules**; consumers look up their own `sessionIds`.
274
+ - The store is the **single source of truth**: dsh-later's own sidebar badge reads it too, so the
275
+ two can never disagree. It owns the `sessions.list` subscription, the 30s state tick and the
276
+ 60s `session.list` read RPC (visible-page only).
277
+ - Consumers must tolerate `undefined` (dsh-later not installed/enabled) and degrade silently.
278
+
194
279
  ## Technical documentation
195
280
 
196
281
  Developer- and maintainer-oriented content has moved out of this README:
package/README.md CHANGED
@@ -36,7 +36,7 @@
36
36
 
37
37
  ### 时间格式
38
38
 
39
- 以下写法都认(`/schedule`、`/later` 与面板自定义时间共用):
39
+ 以下写法都认(`/schedule`、`/later` 与面板自定义时间共用;标了「仅 `/later` `/schedule`」的写法面板不收):
40
40
 
41
41
  | 类型 | 示例 |
42
42
  |---|---|
@@ -44,23 +44,55 @@
44
44
  | 当天时刻 | `15:32` · `9点` · `9点半` · `1532` |
45
45
  | 相对日 + 时刻 | `明天9点` · `后天 10:00` |
46
46
  | 日期 + 时刻 | `8月21日 15:32` · `2026-08-21 15:32` · `0821-1532` |
47
+ | **重复 · 等间隔**(仅 `/later` `/schedule`) | `+1h*3 喝水` · `+30m×2 看构建` · `两小时后×3次` |
48
+ | **重复 · 链式序列**(仅 `/later` `/schedule`) | `+1h+1h+0.5h 盯发布` · `+1h +30m 交周报` |
49
+ | **锚点序列**(仅 `/later` `/schedule`) | `15:32+16:32+17:32 三会` · `9:00+1d+1d 打卡` · `明天9点+1h` |
50
+
51
+ 上表三行加粗的都属于 **[高级语法(Beta)](#配置)**,可在设置里一键关闭;其余写法不受开关影响。
47
52
 
48
53
  只写时刻(如 `1532`)时,若今天已过会自动顺延到明天(最多 7 天);写了 `明天`/`后天` 则按字面理解,该时刻已过会提示改用具体日期;只写月日(如 `8月21日`)时,今年已过则顺延到明年。
49
54
 
55
+ 多次写法的规则(**首次落在第一个间隔之后**):
56
+
57
+ | 写法 | 展开成 |
58
+ |---|---|
59
+ | `+1h*3` | t+1h、t+2h、t+3h |
60
+ | `+1h+1h+0.5h` | t+1h、t+2h、t+2.5h(链式累加,用来写「提前 2h→1h→30m」这类梯度催办) |
61
+ | `15:32+16:32+17:32` | 今天 15:32、16:32、17:32(三个挂钟时刻) |
62
+ | `23:00+01:00` | 今天 23:00、**次日** 01:00(后一个时刻更早则自动顺延,规则同单条) |
63
+ | `9:00+1d+1d` | 连续三天都是 9:00 |
64
+
65
+ 一次最多 **10 条**,多次写法的间隔不低于 **60 秒**(单条不受此限,`/later +5s` 照旧)。`*` `×` `x` 都认,可跟中文量词(`*3次`),次数后面要留空格。每条都是**独立任务**:可单独取消、编辑、插话发送(暂停见下表)。
66
+
67
+ 序列里只允许**「绝对时刻开头,往后累加」**这一个方向:`+1h+15:32` 会明确报错,不会猜。
68
+
69
+ **锚点序列与相对序列的一处差别**(有意为之,不是 bug):
70
+
71
+ | 写法 | 落库类型 | 能暂停 | 跨夏令时 |
72
+ |---|---|---|---|
73
+ | `+1h*3` / `+1h+30m`(相对) | `after` | ✅ | —— |
74
+ | `15:32+16:32` / `9:00+1d`(锚点) | `at` | ❌(按钮置灰并说明原因) | ✅ 墙钟不变:`9:00+1d` 在回拨日相隔 **25 小时**仍是 9:00,按 86400 秒累加会漂成 8:00 |
75
+
76
+ `9:00+1d` 里的「一天」按**日历日**推进(不是 86400 秒),所以 `+1d*3` 与 `9:00+1d+1d` 在有夏令时的时区结果不同——前者按真实秒数、后者按墙钟。落到不存在的墙钟(前拨跳过的那一小时)会报错而不是悄悄挪时刻。绝对时刻(`15:32`、`明天9点`)不支持 `*次数`,会明确报错而不是悄悄变成一条。
77
+
50
78
  ### 命令
51
79
 
52
80
  | 命令 | 作用 |
53
81
  |---|---|
54
82
  | `/schedule <时间> <内容>` | 创建一条定时提醒(到点出现在对话里) |
55
83
  | `/later <时间> <内容>` | 延迟发送:到点以你的身份发出该内容 |
56
- | `/schedule every <周期> <内容>` | 周期性提醒(周期 ≥ 5 分钟) |
84
+ | `/later <时间>*<次数> <内容>` | 同上,展开成 N 条代发(`+1h*3`) |
85
+ | `/schedule <时间>+<时间> <内容>` | 同上,链式序列展开成 N 条提醒(`+1h+30m`) |
86
+ | `/later <时刻>+<时刻> <内容>` | 锚点序列:按挂钟时刻展开(`15:32+16:32`、`9:00+1d+1d`) |
57
87
 
58
88
  ```
59
89
  /schedule 明天9点 提交周报
60
90
  /later 1532 提醒团队同步进度
91
+ /later +1h*3 起来喝水
92
+ /later +2h+1h+30m 催我交周报
61
93
  ```
62
94
 
63
- 模型也可以直接调用用户工具为你创建/查询/修改提醒(`user_schedule_create` / `user_schedule_list` / `user_schedule_delete` / `user_schedule_edit`)。
95
+ 模型也可以直接调用用户工具为你创建/查询/修改提醒(`user_schedule_create` / `user_schedule_list` / `user_schedule_delete` / `user_schedule_edit`)。**无限周期**提醒(每 1 小时一直发下去)只有这条路:让模型说「每小时提醒我喝水」,它会用 `every_seconds` 建一条周期任务;周期任务没有结束条件,需要你手动删。
64
96
 
65
97
  ### 待发送 dock
66
98
 
@@ -186,11 +218,62 @@ dsh plugin --profile web add "file:/path/to/dsh-later"
186
218
  | 允许长文本 | 解除 1000 字符默认下限 | 关闭 |
187
219
  | 自定义上限 | `allowLongPrompts` 开启时的字符上限 | 1000 |
188
220
  | 工作时间 / 午休 / 晚间静默 | 智能时段 5 个 HH:mm 字段,决定快捷芯片与智能排期 | 见设置页 |
221
+ | 插话投递方式 | 「插话发送」进哪个队列:独立成一轮(默认,安全)/ 当前轮(更快,极端情况下会丢) | 独立成一轮 |
222
+ | 投递确认 / 未确认处理 | 是否核对提醒真的送达,以及未确认时保留还是丢弃 | 开 / 保留 |
223
+ | 时钟图标风格 | `auto`(按内核版本)/ `modern` / `classic` | auto |
224
+ | **高级语法** <span>Beta</span> | 多次写法总开关:`+1h*3`、`+1h+1h+0.5h`、`15:32+16:32`、`9:00+1d`。关闭后这些写法被明确拒绝并提示回设置开启 | **开启** |
225
+
226
+ > 「高级语法」标 Beta 的原因不是没测(43 个新单测覆盖),而是**展开后的观感仍需观察**:一次命令写 10 条会不会刷屏、60 秒地板与 10 条上限是否合适。
227
+ > 关闭它**不会**退回旧版的静默误解析(`+1h*3 x` 建成一条内容为 `*3 x` 的任务)——那是 bug,不在可回退的功能面里;残片守卫与本开关无关,始终生效。
189
228
 
190
229
  > 进阶:也可以用 `cordis.patch.yml` 在启动期覆盖默认值(如 `maxSchedules`),见 `docs/` 与 schema。
191
230
 
192
231
  ---
193
232
 
233
+ ## 给插件开发者:定时摘要 API(跨插件)
234
+
235
+ dsh-later 把「全会话定时任务摘要」作为公共数据面暴露出来
236
+ ([issue #17](http://192.168.4.77:3000/dsh-plugins/dsh-later/issues/17)),其它客户端插件
237
+ (如 dsh-group-status)据此在分组/侧栏聚合显示定时状态,**无需**自己解析
238
+ `userSchedules` 投影,也不会与行内 badge 漂移。
239
+
240
+ 消费方先在 `package.json` 声明外部模块:
241
+
242
+ ```json
243
+ { "dsh": { "client": { "platform": "web", "external": ["dsh-later"] } } }
244
+ ```
245
+
246
+ 然后在客户端 factory 里 `require('dsh-later/client')`:
247
+
248
+ ```js
249
+ const { getScheduleSummaryStore, summarizeScheduleGroup, useScheduleSummary } = require('dsh-later/client')
250
+
251
+ // 1) 直接读 store:会话 id → { count, pausedCount, nextAt, state }
252
+ const store = getScheduleSummaryStore() // dsh-later 未加载时为 undefined
253
+ const summary = store?.getSnapshot() ?? new Map()
254
+
255
+ // 2) 按分组聚合(折叠分组徽标直接用它)
256
+ const group = summarizeScheduleGroup(summary, groupSessionIds)
257
+ // → { sessions, count, pausedCount, nextAt?, state? }
258
+
259
+ // 3) React 组件内订阅(store 迟到也会自动重渲染)
260
+ const live = useScheduleSummary()
261
+ ```
262
+
263
+ | 字段 | 含义 |
264
+ |---|---|
265
+ | `count` | 任务数(**含**暂停项) |
266
+ | `pausedCount` | 暂停项数量(暂停项计入 count,但**不参与** `nextAt`) |
267
+ | `nextAt` | 最早触发时刻(epoch ms);全部暂停时为 `undefined` |
268
+ | `state` | `scheduled` / `urgent`(≤5 分钟)/ `overdue`(已到期未派发),按快照时刻判定 |
269
+
270
+ 契约要点:
271
+
272
+ - 摘要**只包含有任务的会话**,无任务的会话不出现;消费方按自己的 `sessionIds` 查表即可。
273
+ - store 是**唯一数据权威**:dsh-later 侧栏 badge 也读它,因此两边必然同源;它内部负责
274
+ `sessions.list` 订阅、30s 状态 tick 与 60s `session.list` 读 RPC(仅页面可见时)。
275
+ - 消费方必须容忍 `undefined`(dsh-later 未安装/未启用时静默降级)。
276
+
194
277
  ## 技术文档
195
278
 
196
279
  以下内容面向开发者与维护者,已移出本 README: