@kanadego/dsh-heartbeat 1.9.3 → 2.0.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.
Files changed (57) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +103 -110
  3. package/client.js +12 -0
  4. package/config/policy.json +74 -74
  5. package/config/profile-schema.json +306 -306
  6. package/dist/{bindings-225SI7KC.js → bindings-U2M3CO2D.js} +3 -3
  7. package/dist/{chunk-TY6UP74X.js → chunk-3DNGY4DS.js} +2 -2
  8. package/dist/{chunk-CUOSICYJ.js → chunk-544QOJN5.js} +397 -23
  9. package/dist/chunk-544QOJN5.js.map +1 -0
  10. package/dist/{chunk-TRO4E6WG.js → chunk-6MVKWICX.js} +587 -139
  11. package/dist/chunk-6MVKWICX.js.map +1 -0
  12. package/dist/{chunk-WLZSABBH.js → chunk-6XM6KY7M.js} +27 -10
  13. package/dist/chunk-6XM6KY7M.js.map +1 -0
  14. package/dist/chunk-AKIJ4LS2.js +680 -0
  15. package/dist/chunk-AKIJ4LS2.js.map +1 -0
  16. package/dist/{chunk-KB5SMG3F.js → chunk-DC5HMCJC.js} +3 -3
  17. package/dist/{chunk-3MDBU6WY.js → chunk-DZ7YCFGX.js} +37 -7
  18. package/dist/chunk-DZ7YCFGX.js.map +1 -0
  19. package/dist/{chunk-WRUTATW4.js → chunk-IBBAISSW.js} +13 -2
  20. package/dist/chunk-IBBAISSW.js.map +1 -0
  21. package/dist/{chunk-XYYEJK3W.js → chunk-IFRMKVIC.js} +82 -21
  22. package/dist/chunk-IFRMKVIC.js.map +1 -0
  23. package/dist/{chunk-XFNCTOW7.js → chunk-NCPKOWS7.js} +52 -4
  24. package/dist/chunk-NCPKOWS7.js.map +1 -0
  25. package/dist/{chunk-JDH2LDFS.js → chunk-TVX4HV4Y.js} +4 -6
  26. package/dist/{chunk-JDH2LDFS.js.map → chunk-TVX4HV4Y.js.map} +1 -1
  27. package/dist/cli/index.js +149 -26
  28. package/dist/cli/index.js.map +1 -1
  29. package/dist/{inbox-P26DDMWC.js → inbox-OWBK6PE2.js} +8 -7
  30. package/dist/index.d.ts +48 -315
  31. package/dist/index.js +259 -918
  32. package/dist/index.js.map +1 -1
  33. package/dist/migrate-TQ5PB2DI.js +27 -0
  34. package/dist/{orchestrator-ZPRGVDCB.js → orchestrator-P7AEWVYR.js} +23 -12
  35. package/dist/{pool-UTXQJJ5C.js → pool-2T6TVS2L.js} +4 -5
  36. package/dist/{snapshot-K5VHETTK.js → snapshot-F5B2DD56.js} +5 -5
  37. package/package.json +84 -83
  38. package/dist/chunk-3MDBU6WY.js.map +0 -1
  39. package/dist/chunk-CUOSICYJ.js.map +0 -1
  40. package/dist/chunk-IV2ZWQA3.js +0 -56
  41. package/dist/chunk-IV2ZWQA3.js.map +0 -1
  42. package/dist/chunk-K5Y6JP2B.js +0 -399
  43. package/dist/chunk-K5Y6JP2B.js.map +0 -1
  44. package/dist/chunk-TRO4E6WG.js.map +0 -1
  45. package/dist/chunk-WLZSABBH.js.map +0 -1
  46. package/dist/chunk-WRUTATW4.js.map +0 -1
  47. package/dist/chunk-XFNCTOW7.js.map +0 -1
  48. package/dist/chunk-XYYEJK3W.js.map +0 -1
  49. package/dist/migrate-D4VKAKYT.js +0 -21
  50. /package/dist/{bindings-225SI7KC.js.map → bindings-U2M3CO2D.js.map} +0 -0
  51. /package/dist/{chunk-TY6UP74X.js.map → chunk-3DNGY4DS.js.map} +0 -0
  52. /package/dist/{chunk-KB5SMG3F.js.map → chunk-DC5HMCJC.js.map} +0 -0
  53. /package/dist/{inbox-P26DDMWC.js.map → inbox-OWBK6PE2.js.map} +0 -0
  54. /package/dist/{migrate-D4VKAKYT.js.map → migrate-TQ5PB2DI.js.map} +0 -0
  55. /package/dist/{orchestrator-ZPRGVDCB.js.map → orchestrator-P7AEWVYR.js.map} +0 -0
  56. /package/dist/{pool-UTXQJJ5C.js.map → pool-2T6TVS2L.js.map} +0 -0
  57. /package/dist/{snapshot-K5VHETTK.js.map → snapshot-F5B2DD56.js.map} +0 -0
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Kanadego
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kanadego
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # dsh-heartbeat
2
2
 
3
- > 让 DeepSeek Harness 的 agent 拥有持续存在感的心跳插件:她按节律自己醒来,维护记忆与画像、感知你的忙闲、按兴趣闲逛看新东西,然后**按分寸决定开不开口**——开口要有真实来处,沉默要有具体理由。
3
+ > 让 DeepSeek Harness 的 agent 拥有持续存在感的心跳插件:她按节律自己醒来,维护记忆与画像、感知你的忙闲、按兴趣闲逛看新东西、**把没说完的话带过下一拍**,然后**按分寸决定开不开口**——开口要有真实来处,沉默要有具体理由。
4
4
 
5
- [![host](https://img.shields.io/badge/DSH-0.1.2--rc.1%20%7C%200.2.0--rc.2-blue)](https://github.com/deepseek-ai/deepseek-harness)
5
+ [![host](https://img.shields.io/badge/DSH-0.1.2--rc.1%20%7C%200.2.1--rc.2-blue)](https://github.com/deepseek-ai/deepseek-harness)
6
6
  [![npm](https://img.shields.io/npm/v/@kanadego/dsh-heartbeat)](https://www.npmjs.com/package/@kanadego/dsh-heartbeat)
7
7
  [![downloads](https://img.shields.io/npm/dm/@kanadego/dsh-heartbeat)](https://www.npmjs.com/package/@kanadego/dsh-heartbeat)
8
8
  [![platform](https://img.shields.io/badge/platform-Windows-lightgrey)]()
@@ -23,27 +23,27 @@
23
23
  | ![](docs/screenshots/card-profile.png) | ![](docs/screenshots/card-rhythm.png) |
24
24
 
25
25
  <details>
26
- <summary><b>展开看其余分区:会话绑定 / 兴趣范围 / 账本 · 周报 · 换机迁移</b></summary>
26
+ <summary><b>展开看其余分区:会话绑定 / 兴趣范围 / 账本 · 摘抄本 · 周报 · 换机迁移</b></summary>
27
27
 
28
28
  | 会话绑定 | 兴趣范围 |
29
29
  | --------------------------------------- | ---------------------------------------- |
30
30
  | ![](docs/screenshots/card-sessions.png) | ![](docs/screenshots/card-interests.png) |
31
31
 
32
- | 账本 · 周报 · 换机迁移 |
33
- | -------------------------------------------- |
34
- | ![](docs/screenshots/card-ledger-config.png) |
32
+ | 账本 · 摘抄本 · 周报 | 换机迁移 |
33
+ | -------------------------------------------- | --------------------------------------- |
34
+ | ![](docs/screenshots/card-ledger-excerpt-weekly.png) | ![](docs/screenshots/card-migrate.png) |
35
35
 
36
36
  </details>
37
37
 
38
- > 截图为 v1.9.3;画像与会话绑定两张里的条目文字已打码。
38
+ > 截图为 v2.0.0。画像与会话绑定两张里的条目文字已打码。
39
39
 
40
40
  **它想解决的三件事**:
41
41
 
42
- | 你想要的 | 插件怎么给 |
43
- | -------------------- | ---------------------------------------------------------------------------------------------------------- |
44
- | 零命令行 | 绑定会话、节律、素材池、画像、账本、迁移全在设置卡里;CLI 只留给脚本场景 |
45
- | 不打扰、不烧 token | 静默时段 / 忙时窗口 / 每日上限 / 开口冷却 / 在场联动全是代码层判定——判「别说」时,后面的模型调用根本不发生 |
46
- | 像活人,不像定时播报 | 素材分话题与聊天两仓,反刍成一句话再投递;说不说、说哪条,由你那个会话里的 agent 按当下处境拍板 |
42
+ | 你想要的 | 插件怎么给 |
43
+ | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44
+ | 零命令行 | 绑定会话、节律、素材池、画像、账本、迁移全在设置卡里;CLI 只留给脚本场景 |
45
+ | 不打扰、不烧 token | 静默时段 / 忙时窗口 / 每日上限 / 开口冷却 / 在场联动全是代码层判定——判「别说」时,之后的**决策与表达**两相根本不调用模型(闲逛相在闸门之前触发,另算) |
46
+ | 像活人,不像定时播报 | 素材分话题与聊天两仓,反刍成一句话再投递;说不说、说哪条,由你那个会话里的 agent 按当下处境拍板;没说完的话能留在**心事匣**里带到下一拍,看见想记的话就抄进**摘抄本** |
47
47
 
48
48
  ## 快速开始
49
49
 
@@ -74,7 +74,7 @@ dsh plugin --profile web add file:D:/path/to/dsh-heartbeat
74
74
  ```
75
75
  维护 → 采集 → 闲逛 → 闸门 → Digest → 反刍备料 → 投递 → 留痕
76
76
  │
77
- 判「别说」→ 本跳到此为止,一个 token 都不花
77
+ 判「别说」→ 本跳到此为止,决策与表达不再调模型
78
78
  ```
79
79
 
80
80
  <details>
@@ -86,7 +86,7 @@ dsh plugin --profile web add file:D:/path/to/dsh-heartbeat
86
86
  ╰─ 观察:把绑定会话里你新说的话收进画像收件箱
87
87
  ②′ 闲逛 ── 条件触发:agent 自己按兴趣焦点用 web_search 看新东西
88
88
  ③ 闸门 ── 纯代码判定:静默窗 / 忙时 / 在场 / 每日上限 / 冷却
89
- ╰─ 判「别说」→ 本跳到此为止,一个 token 都不花
89
+ ╰─ 判「别说」→ 本跳到此为止,决策与表达不再调模型
90
90
  ④ Digest ── 拼出「此刻状态」:画像 + 时间 + 素材池 + 待办
91
91
  ⑤ 反刍 ── 引擎室从素材池挑 ≤3 条,各压成一句话(备料)
92
92
  ⑥ 投递 ── 拼成素材包,送进你指定投递的那个会话
@@ -98,18 +98,18 @@ dsh plugin --profile web add file:D:/path/to/dsh-heartbeat
98
98
 
99
99
  文档里反复出现几个词,在这里一次说清:
100
100
 
101
- | 词 | 意思 |
102
- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
103
- | **心跳** | 本插件的核心动作——不用你开口,agent 按固定节律自己「醒一次」:做维护、感知环境、决定要不要说话。醒一次叫**一跳**,默认 60 分钟一跳。 |
104
- | **宿主** | 运行插件的那个程序,即 DSH 本体。心跳是挂在宿主里的插件——模型通道、会话、工具这些能力都由宿主提供。 |
105
- | **客户端** | 宿主里负责界面的那一侧(Web 界面或者 DSH desktop)。设置页的心跳卡片属于客户端;卡片显示不出来,通常是客户端插件没加载成功,与心跳本体无关。 |
106
- | **相** | 一跳内部的一个阶段。主循环共七相——维护 → 采集 → 闲逛 → 闸门 → Digest → 反刍备料 → 投递,最后统一由**留痕**收尾。每相结束都会在审计日志留一条 `phase_done`,所以「这一跳停在哪」可以直接查。 |
107
- | **引擎室** | 跑心跳内部轮次的那个 agent,和你在会话里对话的 agent 是分开的。它只负责采集、反刍、备料,**不直接跟你说话**;真正开口的是你读的那个会话里的 agent。 |
108
- | **心跳正身** | 心跳自己专用的一个会话,同时也是它的决策现场——每跳的推理轮次都在这里发生。它在会话列表里和普通会话长得一样,日常不用去动它(CLI 的 `sessions list` 会把它单独标出来)。 |
109
- | **闸门** | 决定「这一跳能不能开口」的纯代码判定层,依次看:静默时段 → 你是不是在忙 → 你在不在场 → 今天说够没有 → 距上次开口够久没。全不通过就安静,并在日志里写下具体原因。它也是省 token 的关键——**判「别说」时,后面的模型调用根本不发生**。 |
110
- | **观察** | 投递的反向动作——把绑定的会话里你新说的话收进**画像收件箱**,作为了解你的证据。它发生在采集相:只取当时有活 agent 的绑定会话、每跳最多 10 条、每条只留第一句(≤80 字),插件自己注入的消息会被跳过。 |
111
- | **反刍** | 投递之前的备料步骤:引擎室从素材池挑出最值得说的几条,各压缩成一句话(不带理由、不排序)。名字取自反刍动物把食物嚼细再咽。 |
112
- | **投递** | 把备好的素材包送进你某个会话的动作。把心跳绑定到常用会话,agent 就会「到你那个房间里说话」。 |
101
+ | 词 | 意思 |
102
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
103
+ | **心跳** | 本插件的核心动作——不用你开口,agent 按固定节律自己「醒一次」:做维护、感知环境、决定要不要说话。醒一次叫**一跳**,默认 60 分钟一跳。 |
104
+ | **宿主** | 运行插件的那个程序,即 DSH 本体。心跳是挂在宿主里的插件——模型通道、会话、工具这些能力都由宿主提供。 |
105
+ | **客户端** | 宿主里负责界面的那一侧(Web 界面或者 DSH desktop)。设置页的心跳卡片属于客户端;卡片显示不出来,通常是客户端插件没加载成功,与心跳本体无关。 |
106
+ | **相** | 一跳内部的一个阶段。主循环共七相——维护 → 采集 → 闲逛 → 闸门 → Digest → 反刍备料 → 投递,最后统一由**留痕**收尾。每相结束都会在审计日志留一条 `phase_done`,所以「这一跳停在哪」可以直接查。 |
107
+ | **引擎室** | 跑心跳内部轮次的那个 agent,和你在会话里对话的 agent 是分开的。它只负责采集、反刍、备料,**不直接跟你说话**;真正开口的是你读的那个会话里的 agent。 |
108
+ | **心跳正身** | 心跳自己专用的一个会话,同时也是它的决策现场——每跳的推理轮次都在这里发生。它在会话列表里和普通会话长得一样,日常不用去动它(CLI 的 `sessions list` 会把它单独标出来)。 |
109
+ | **闸门** | 决定「这一跳能不能开口」的纯代码判定层,依次看:静默时段 → 你是不是在忙 → 你在不在场 → 今天说够没有 → 距上次开口够久没。全不通过就安静,并在日志里写下具体原因。它也是省 token 的关键——**判「别说」时,之后的决策与表达两相根本不调用模型**(已经跑过的维护、采集、闲逛不再重来,也不因为沉默而多花)。 |
110
+ | **观察** | 投递的反向动作——把绑定的会话里你新说的话收进**画像收件箱**,作为了解你的证据。它发生在采集相:只取当时有活 agent 的绑定会话、每跳最多 10 条、每条只留第一句(≤80 字),插件自己注入的消息会被跳过。 |
111
+ | **反刍** | 投递之前的备料步骤:引擎室从素材池挑出最值得说的几条,各压缩成一句话(不带理由、不排序)。名字取自反刍动物把食物嚼细再咽。 |
112
+ | **投递** | 把备好的素材包送进你某个会话的动作。把心跳绑定到常用会话,agent 就会「到你那个房间里说话」。 |
113
113
 
114
114
  </details>
115
115
 
@@ -130,19 +130,21 @@ dsh plugin --profile web add file:D:/path/to/dsh-heartbeat
130
130
 
131
131
  - **素材池(话题 / 聊天双仓)**:话题库存 16 条 + 聊天素材 14 条,四条确定性淘汰(TTL / 消费退休 / 冷板凳 / 容量挤出);聊天种子来自画像合并时从对话里挑出的"值得聊的话题",有时效性——**被说出口后至少活到下一次回收**,有新证据还会继续留池(v1.9.1 之前是"投递过 1 次即退休",常常话刚出口上下文就断了)。归档区也有上限(默认 50 条),超出按**最老优先**裁掉;新增 / 合并 / 淘汰 / 裁剪 / 恢复 / 删除都会写进审计日志(只记编号与元数据,不记素材正文)。是缓存不是记忆,与画像单向隔离。
132
132
  - **反刍投递(目标会话拍板)**:代码先凑好候选(话题随机 4 + 聊天最新 2,互补至多 6 条),引擎室挑 ≤3 条各压缩成一句,拼成素材包注入你读的会话——附一句「他此刻大概在:…」(视觉识别成功时)+声明「这是心跳插件素材投递」+素材清单(必要时附「也可以说一句真心话」)。说不说、说哪条,由目标会话里的 agent 按当下处境自己判断——**决定不说就把正文留空**(v1.9.3 起,但是受模型指令遵循能力限制,如想要更好的效果,建议在系统提示词/长期记忆中补充一条相应规则);话落到你眼前的会话后,agent 会**按本次投递编号报账**——哪几条被用上、是引用素材还是顺着自己发挥,都如实记下。
133
+ - **心事匣(v2.0.0 新增)**:给心跳一格便签,用来把没说完的话留给下一跳的自己。投递的素材包里会带上「你上跳留下的心事:…」,以及它留于第几跳、已搁多久、这期间被叫醒过几次;想留住就**照原样再写一遍**(字样一致算保留,年龄不重算),想换就直接写新的,什么都不做它下一跳自动清掉。不强制每跳留话——没话说就空着。生命周期由两个序号判定,只认真正的拍(**启动那一拍不计入**),所以「搁了几跳」永远是真的。
134
+ - **摘抄本(v2.0.0 新增)**:心跳自己的本子,用来留句子和想记住的小事。注册成模型工具 `excerpt`(add / list / remove),日常会话里的 agent 想到什么就当场抄下,不用谁下命令;数据落 `data/excerpts.md`,**明文、可直接手编**——一本谁也翻不开的笔记本不叫笔记本,认不出的行在改写时会原样留着。它和另两本分工明确:账本记待办(会勾掉),心事匣是留给下一跳的一句引子(一格、会被盖掉),摘抄本只增不减,除非主动抹掉。
133
135
  - **会话绑定**:把心跳消息投递到你常用的会话(agent 会"到你房间里说话"),或观察会话内容喂养画像——投递/观察是两个独立开关,可双开。
134
136
 
135
137
  ### 记忆 · 账本 · 周报
136
138
 
137
139
  - **用户画像**:本地结构化档案(兴趣 / 项目 / 沟通偏好 / 心理基线),带来源与置信度,稳定特质不因时间遗忘;journal 单一权威,可校验、可重建、可解密导出。
138
- - **周报**:每 7 天一份,只报道这周干了些什么——表达与静默、新认识、账本、素材池、活跃高峰。由后台「心跳」署名写成平实中性的一句话档案,DPAPI 加密落盘,卡片可翻,默认只留最近 3 期(`policy.weekly.keepReports`)。
140
+ - **周报**:每 7 天一份,只报道这周干了些什么——表达与静默、新认识、账本、素材池、活跃高峰。由后台「心跳」署名写成平实中性的一句话档案,加密落盘,卡片可翻,默认只留最近 3 期(`policy.weekly.keepReports`)。
139
141
  - **账本工具**:账本注册成模型工具,日常会话里的 agent 听到你提到待办/承诺会自己记账、完成后自己勾掉,不用你下命令——和心跳、CLI 记的是同一本账。
140
142
  - **journal 快照基点**:画像 journal 超 5000 条自动折叠成快照 + 归档分片,重放成本有界;verify/rebuild 跨快照依然正确。
141
143
 
142
144
  ### 数据 · 迁移 · 焚毁
143
145
 
144
- - **换机迁移**:一键打包画像/账本/素材池/周报为口令加密容器(卡片或 CLI),新机器导入自动用本机 DPAPI 重加密。
145
- - **加密与焚毁**:敏感文件 DPAPI(CurrentUser)全文件加密;一键焚毁运行时数据;模型出库仅限合并裁决的最小必要摘要。
146
+ - **换机迁移**:一键打包画像/账本/素材池/周报为口令加密容器(卡片或 CLI),新机器导入自动用本机主密钥重加密。
147
+ - **加密与焚毁**:敏感文件 **AES-256-GCM** 整文件加密,数据密钥由 **Windows DPAPI(CurrentUser)** 保管在 `data/vault.key`,只在进程启动解一次;旧版(1.x)留下的 DPAPI 容器照样能读,且在写入与每跳维护时自动改写成新格式(`vault upgrade` 一次全换);一键焚毁运行时数据;模型出库仅限合并裁决的最小必要摘要。
146
148
 
147
149
  ## 常见故障与诊断
148
150
 
@@ -191,25 +193,27 @@ node <插件目录>/dist/cli/index.js <命令>
191
193
  <details>
192
194
  <summary><b>点此展开:CLI 命令速查</b></summary>
193
195
 
194
- | 分组 | 命令 | 说明 |
195
- | ------ | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
196
- | 总览 | `status` | 工作区路径 + 策略摘要(今日 cap / 冷却 / 静默窗) |
197
- | 素材池 | `seeds add <text> [--tag t] [--topic x] [--confidence n]` | 手写一条种子 |
198
- | | `seeds list [--archived]` / `seeds surface <id>` / `seeds archive <id>` | 列表 / 计一次曝光 / 归档 |
199
- | | `seeds gc` / `seeds stats` | 跑确定性淘汰 / 池子状态 JSON |
200
- | 账本 | `ledger add <text>` / `ledger list [--pending]` / `ledger done <id\|子串>` / `ledger open` | 待办登记 / 查询 / 勾掉 / 编辑器打开 |
201
- | 闲逛 | `browse status` / `browse dry` / `browse done <focus>` | 状态 / 强制裁决预演 / 手动登记一次闲逛 |
202
- | 绑定 | `sessions list` | 枚举会话 id |
203
- | | `bind list` / `bind add <id> [--observe]` / `bind remove <id>` | 绑定详情(正身也会列出)/ 绑定 / 解绑;`--observe-only` 仅观察、`--no-deliver` 关投递 |
204
- | 通知 | `notify check` / `notify register` / `notify send` | toast 通道体检 / 注册 / 测试发送(只提示新消息,不承载正文) |
205
- | 画像 | `profile list [--all]` / `profile export` | 当前有效(含过期)/ 解密导出 Markdown |
206
- | | `profile verify` / `profile rebuild [--check]` / `profile wipe` | journal 重放比对(只报不修)/ 重建(可先干跑)/ 清空(要 `--yes`) |
207
- | | `profile snapshot [--force]` | 折叠 journal 为快照 + 归档分片(维护相到阈值也会自动做) |
208
- | 周报 | `weekly list` / `weekly show [file]` | 列出已生成的周报 / 打印某期(默认最新一期) |
209
- | 迁移 | `migrate export [file]` / `migrate import <file>` | 打包记忆文件为口令加密容器(口令走 stdin 两行)/ 在新机器恢复(stdin 一行);卡片「换机迁移」分区同功能 |
210
- | 日志 | `logs cleanup [--dry-run]` | 立即执行日志保留策略 |
211
- | 预设 | `preset status` / `preset install [--force]` | 装没装、与模板是否一致 / 补装(`--force` 还原成模板) |
212
- | 焚毁 | `burn [--yes] [--all]` | 预演(默认)→ 执行;运行时数据覆写 x3 后删除,设置保留(`--all` 连设置归零) |
196
+ | 分组 | 命令 | 说明 |
197
+ | ------ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
198
+ | 总览 | `status` | 工作区路径 + 策略摘要(今日 cap / 冷却 / 静默窗) |
199
+ | 素材池 | `seeds add <text> [--tag t] [--topic x] [--confidence n]` | 手写一条种子 |
200
+ | | `seeds list [--archived]` / `seeds surface <id>` / `seeds archive <id>` | 列表 / 计一次曝光 / 归档 |
201
+ | | `seeds gc` / `seeds stats` | 跑确定性淘汰 / 池子状态 JSON |
202
+ | 账本 | `ledger add <text>` / `ledger list [--pending]` / `ledger done <id\|子串>` / `ledger open` | 待办登记 / 查询 / 勾掉 / 编辑器打开 |
203
+ | 摘抄本 | `excerpt add <text>` / `excerpt list [n]` / `excerpt remove <id\|子串>` / `excerpt open` | 抄一条 / 翻最近十条(可指定条数)/ 抹掉一条 / 编辑器打开 |
204
+ | 闲逛 | `browse status` / `browse dry` / `browse done <focus>` | 状态 / 强制裁决预演 / 手动登记一次闲逛 |
205
+ | 绑定 | `sessions list` | 枚举会话 id |
206
+ | | `bind list` / `bind add <id> [--observe]` / `bind remove <id>` | 绑定详情(正身也会列出)/ 绑定 / 解绑;`--observe-only` 仅观察、`--no-deliver` 关投递 |
207
+ | 通知 | `notify check` / `notify register` / `notify send` | toast 通道体检 / 注册 / 测试发送(只提示新消息,不承载正文) |
208
+ | 画像 | `profile list [--all]` / `profile export` | 当前有效(含过期)/ 解密导出 Markdown |
209
+ | | `profile verify` / `profile rebuild [--check]` / `profile wipe` | journal 重放比对(只报不修)/ 重建(可先干跑)/ 清空(要 `--yes`;连快照、归档分片、迁移备份一起删,设定不动) |
210
+ | | `profile snapshot [--force]` | 折叠 journal 为快照 + 归档分片(维护相到阈值也会自动做) |
211
+ | 周报 | `weekly list` / `weekly show [file]` | 列出已生成的周报 / 打印某期(默认最新一期) |
212
+ | 迁移 | `migrate export [file]` / `migrate import <file>` | 打包记忆文件为口令加密容器(口令走 stdin 两行)/ 在新机器恢复(stdin 一行);卡片「换机迁移」分区同功能 |
213
+ | 加密 | `vault status` / `vault upgrade` | 盘上各格式容器计数 + 主密钥位置 / 把 1.x 的 DPAPI 容器就地改写成 AES-GCM(平时写入 / 每跳维护自动换代,这条是一次全换) |
214
+ | 日志 | `logs cleanup [--dry-run]` | 立即执行日志保留策略 |
215
+ | 预设 | `preset status` / `preset install [--force]` | 装没装、与模板是否一致 / 补装(`--force` 还原成模板) |
216
+ | 焚毁 | `burn [--yes] [--all]` | 预演(默认)→ 执行;运行时数据覆写 x3 后删除,设置保留(`--all` 连设置归零) |
213
217
 
214
218
  </details>
215
219
 
@@ -219,36 +223,43 @@ node <插件目录>/dist/cli/index.js <命令>
219
223
 
220
224
  所有运行数据都在心跳数据目录(唯一可写位置,路径守卫强制),卸载或重装都不会丢:
221
225
 
222
- | 文件 | 内容 | 状态 |
223
- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ | ---------------------------------------- |
224
- | `data/settings/` | 绑定、策略覆盖、节律配置(`ui.json`)、画像白名单、心跳状态 | 明文(用户设定要可手编) |
225
- | `data/seeds.jsonl` | 素材池 | 🔒 DPAPI |
226
- | `data/profile.*` | 画像三件套(journal / 物化视图 / 收件箱) | 🔒 DPAPI |
227
- | `data/sent.json` / `data/browse.json` / `data/screen.*` | 表达记录 / 浏览流状态 / 屏幕快照 | 🔒 DPAPI |
228
- | `data/ledger.md` | 账本(agent 的待办与话题来源,可直接手编) | 明文 |
229
- | `data/profile_snapshot.json` / `data/profile_journal.archive-*.jsonl` | 画像快照基点 / journal 归档分片 | 🔒 快照 DPAPI / 分片明文 |
230
- | `data/weekly/` | 周报(每 7 天一份,含画像/账本摘录) | 🔒 DPAPI |
231
- | `data/envpulse.jsonl` / `data/logs/heartbeat.jsonl` | 作息聚合 / 审计日志(不含敏感原文) | 明文,按保留策略滚动清理 |
232
- | `data/cursors.json` / `data/preference.json` / `data/seed_report.jsonl` / `data/profile_rhythm.json` / `data/time-inject-state.json` | 观察游标 / 话题偏好权重 / 素材报账流水 / 作息直方图 / 时间注入节流 | 明文(只含编号、计数与聚合,无对话原文) |
233
- | `data/tmp/` / `data/exports/` | 临时文件 / 画像导出 | 临时区 |
234
-
235
- 加密用 **Windows DPAPI(CurrentUser 作用域)**整文件加密:本机本账户可自动解密使用,其他账户、其他机器、云同步副本都解不开。防的是他人 / 他机 / 误同步,不防同账户的恶意软件——那是操作系统边界的事。
226
+ | 文件 | 内容 | 状态 |
227
+ | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ---------------------------------------- |
228
+ | `data/settings/` | 绑定、策略覆盖、节律配置(`ui.json`)、画像白名单、心跳状态(场景 + ≤30 字发言摘要) | 明文(用户设定要可手编) |
229
+ | `data/vault.key` | 主密钥(AES 数据密钥,由 DPAPI 保护) | 🔒 本机本账户可解;不随迁移包移动 |
230
+ | `data/seeds.jsonl` | 素材池 | 🔒 加密 |
231
+ | `data/profile.*` | 画像三件套(journal / 物化视图 / 收件箱) | 🔒 加密 |
232
+ | `data/sent.json` / `data/browse.json` / `data/screen.*` | 表达记录 / 浏览流状态 / 屏幕快照 | 🔒 加密 |
233
+ | `data/notepad.json` | 心事匣:上一拍留给下一拍的一句引子 + 两个序号计数 | 🔒 加密 |
234
+ | `data/ledger.md` | 账本(agent 的待办与话题来源,可直接手编) | 明文 |
235
+ | `data/excerpts.md` | 摘抄本(留句子和小事,可直接手编) | 明文 |
236
+ | `data/profile_snapshot.json` / `data/profile_journal.archive-*.jsonl` | 画像快照基点 / journal 归档分片 | 🔒 快照加密 / 分片明文 |
237
+ | `data/weekly/` | 周报(每 7 天一份,含画像/账本摘录) | 🔒 加密 |
238
+ | `data/envpulse.jsonl` / `data/logs/heartbeat.jsonl` | 作息聚合 / 审计日志(**含她发言的前 80 字摘录**) | 明文,按保留策略滚动清理 |
239
+ | `data/cursors.json` / `data/preference.json` / `data/seed_report.jsonl` / `data/profile_rhythm.json` / `data/time-inject-state.json` | 观察游标 / 话题偏好权重 / 素材报账流水 / 作息直方图 / 时间注入节流 | 明文(只含编号、计数与聚合,无对话原文) |
240
+ | `data/tmp/` / `data/exports/` | 临时文件 / 画像导出 | 临时区 |
241
+
242
+ 加密分两层:数据用 **AES-256-GCM** 整文件加密(带认证,改一个字节就解不开也不会给出一半内容),数据密钥存在 `data/vault.key`,那一个文件由 **Windows DPAPI(CurrentUser 作用域)** 保护——所以本机本账户可自动解密使用,其他账户、其他机器、云同步副本都解不开。防的是他人 / 他机 / 误同步,不防同账户的恶意软件——那是操作系统边界的事。
243
+
244
+ 1.x 写的文件用的是旧的 DPAPI 容器(`KHBV1`),v2.0 读得懂但写的是新容器(`KHBV2`):升级插件不会让任何旧数据变成打不开的死文件。旧格式会自己换掉——任何一次写入都落成新容器,每一跳的维护相另外顺带把长期没动过的一个旧文件就地改写;想一次全换完,跑 `heartbeat vault upgrade`(`vault status` 看盘上各格式各有多少)。
245
+
246
+ 反过来要注意:`KHBV2` 只有 2.0 及以后的版本认识——**升级到 2.0 之后不要再回退到 1.x**,旧版本会把新容器当作打不开的文件。(要回退就趁早;跑过 `vault upgrade` 之后、或让数据写过一轮之后,就回不去了。)
236
247
 
237
248
  ### 安全边界
238
249
 
239
250
  - **路径守卫**:全部落盘经过守卫规范化比对,`..` 穿越、符号链接、8.3 短名、UNC 一律拒绝;`data/` 是唯一可写目录。
240
- - **模型工具硬限制**:心跳 agent 的工具面被钉死为**只有 `web_search`**——没有 shell、文件系统、子代理、技能目录。闲逛轮之外的模型调用(决策 / 合并)**零工具**。唯一的例外是上下文压缩类工具(v1.8.0 起):引擎室轮次会探测全局层,装了 [billion-context](https://github.com/ranxianglei/billion-context) 这类插件时放行 `compress` / `decompress` / `acp_status`,免得长会话把自己撑爆。账本与素材报账这两个工具注册在**全局层**(你的日常会话里可用),心跳自己的轮次看不到它们。
251
+ - **模型工具硬限制**:引擎室轮次的工具面由代码层白名单收窄——闲逛相唯一需要的 `web_search`,加上装了 [billion-context](https://github.com/ranxianglei/billion-context) 这类插件时的压缩三件套 `compress` / `decompress` / `acp_status`(v1.8.0 起,免得长会话把自己撑爆)。**账本、素材报账、心事匣、摘抄本这四个工具注册在全局层**(你的日常会话里可用),引擎室轮次看不到它们。白名单管的是**全局层**:宿主自己注册在 agent 作用域的行不受它约束——实测引擎室那一跳拿到的完整工具面是 9 个:`web_search`(预设挂的)、`compress` / `decompress` / `acp_status`(billion-context)、`ops_bash`(dsh-ops)、`schedule_create` / `schedule_list` / `schedule_update` / `schedule_delete`(dsh-schedule)——后五个是宿主插件注册在 agent **自身层**的行,白名单管不到它们(注意 `search_context` 与 `acp_cache` 是**故意**不放行的:前者与 dsh-acp 的同名工具撞名)。**复用的 agent 也会重新钉一遍**:插件重启后若引擎室会话正开着、agent 被宿主直接复用(日志里的 `agent_reuse_live`),复用分支会单独调一次 `pinEngineRoomTools` 把白名单重新钉上,结果记在同一行 `tool_policy` 里、带 `reuse` 前缀——2026-10-17 修的,修之前这条路径不套白名单(实测漏到 62 个工具)。
241
252
  - **建议 · 防注入**:网页内容应视为数据而非指令(闲逛提示词中明示);画像裁决 prompt 同理(观察内容是数据,不应改写人格设定)。这是推荐的做法,是否采纳由使用者自行决定,并非安装即强制生效。
242
253
  - **模型出库边界**:画像合并调用走宿主模型通道(插件不持有任何云凭证),送四个分区(**含 psy**)的条目字段与 ≤1 句观察引语——不送对话原文、不送证据全量。(2026-10-07 调整:先前排除 psy,结果模型能往自己看不见的分区写,而 UPDATE 必须给 id、id 又只出现在条目视图里,psy 条目写进去就改不动了。)
243
254
  - **一键焚毁**:`burn`(预演)→ `burn --yes`(运行时数据覆写 x3 后删除,用户设置保留)→ `burn --all`(连设置归零)。
244
- - **数据通道鉴权**:设置卡读写的 `/api/heartbeat` 通道**自身不持有凭证**,靠宿主对 `/api` 前缀路由的门禁(Host / Origin 校验 + 浏览器会话 secret)。v1.9.1 起插件在注册时自检这道门禁还在不在——不在就让删除 / 迁移 / 改配置这类端点一律拒绝并留痕(`rpc_host_gate_missing`),只读端点照常。
255
+ - **数据通道鉴权**:设置卡读写的 `/api/heartbeat` 通道**自身不持有凭证**,靠宿主对 `/api` 前缀路由的门禁(Host / Origin 校验 + 浏览器会话 secret)。v1.9.1 起插件在注册时自检这道门禁还在不在(当时只拒六个「破坏性」端点);**2.0 起不在就整个通道失败关闭**:只放行几个只读低敏端点(`status`、`config.get`、`sessions.list`、`bindings.get`、`weekly.list`),其余一律拒绝并留痕(`rpc_host_gate_missing`)——包括**所有涉及记忆内容的读取**(画像摘要 / 分区、素材池、兴趣词、周报正文、迁移导出)。(2026-10-17 收紧。)
245
256
 
246
257
  ### 版本兼容
247
258
 
248
259
  | 插件版本 | 适配的 DSH |
249
260
  | ------------------ | --------------------------- |
250
- | **v1.9.3**(当前) | `0.2.0-rc.1` – `0.2.0-rc.2` |
251
- | v1.7.4 – v1.9.2 | `0.2.0-rc.1` – `0.2.0-rc.2` |
261
+ | **v2.0.0**(当前) | `0.2.0-rc.1` – `0.2.1-rc.2` |
262
+ | v1.7.4 – v1.9.3 | `0.2.0-rc.1` – `0.2.0-rc.2` |
252
263
  | v1.7.2 – v1.7.3 | `0.1.7-rc.1` – `0.2.0-rc.1` |
253
264
  | v1.7.0 – v1.7.1 | `0.1.5-rc.2` – `0.1.7-rc.2` |
254
265
  | v1.2.x – v1.6.4 | `0.1.2-rc.1` – `0.1.5-rc.2` |
@@ -287,53 +298,35 @@ dsh plugin --profile web remove @kanadego/dsh-heartbeat
287
298
  ## 版本更新
288
299
 
289
300
  <details open>
290
- <summary><b>v1.9.x</b></summary>
291
-
292
- ### v1.9.3 · 2026-10-17(新功能:素材包可留白 + 摘要预算分区 + 画像与素材池 UI 重做)
293
-
294
- - **素材包可以留白了**:素材包以前默认"必须挑一条说",agent 想沉默也得硬挑一条凑数。现在改成判断**要不要开口**——该说才选一条,不该说就把正文留空,只如实报一次「这轮没开口」和原因。沉默不再需要客套话。
295
- - **摘要预算分区**:投递给引擎室的画像摘要总预算从 3200 放宽到 6000 字符,并且 tact / topic / wander 三类各有上限(2500 / ≥2400 / 850)——超预算按**整条**跳过并如实回报,不再从句子中间切断。
296
- - **出厂默认更省**:心跳间隔 60 分钟、每日表达上限 10、状态栏默认关闭(都能在设置卡里改)。
297
- - **节省 token 模式真能唤醒**:暂停期间每 3 分钟探一次(不取 agent、不调模型);绑定会话里出现你还没被消化的新消息就立刻恢复。暂停时状态栏写「你不在,心跳挂起」。
298
- - **周报保留期数**:新增 `policy.weekly.keepReports`(默认 3),保存新一期时删掉更老的并留痕 `weekly_pruned`;CLI 与设置卡只给最近 N 期。
299
- - **设置卡重做两块**:画像区改成四个分区按钮切换、每条一块、正文自动换行,并新增只读的「引擎室输入」视图;素材池改成四个按钮——**话题素材 / 聊天素材 / 话题·归档 / 聊天·归档**,归档不再单列一块,点进去行内按钮变成「恢复」。两处列表都加了滚动区,引擎室输入视图同样。
300
-
301
- ### v1.9.2 · 2026-10-10(修复:预设改为运行时注册 + 数据目录移出包外)
302
-
303
- - **预设不再写进安装清单**:修复了心跳模式预设在 profile 解析不到宿主包时,导致整个插件树加载失败、DSH 启动不起来的问题。现已改为插件在加载时自己向宿主注册,超时或失败会自动回退并留痕。
304
- - **补依赖不再把宿主装成两份**:修复了为解析预设包手工补齐依赖后,宿主装载机制出现两份实例、所有会话都无法恢复的问题。现在清单里不再出现任何宿主包,那条补法也用不上了。
305
- - **会话恢复失败不再无限重试**:修复了旧格式会话迁移被拒时,引擎室被反复重试拖住、永远空转的问题。现已把这类永久性失败单独识别,当场重建会话并把正身指过去;真正暂时性的错误仍照旧退避重试。
306
- - **数据目录默认移出包外**:修复了数据默认住在包目录里、`dsh plugin add` 与每次升级替换包目录时连带删掉画像、素材池与正身记录的问题(原本只有显式 `burn` 才会销毁)。默认位置现为 `<DSH_HOME>/heartbeat-data`;老版本装在包内的 `data/` 首次启动自动搬迁——先备份、再逐文件校验(清单、大小、sha256 全部一致),全部通过才删原件与备份,任何一步失败都原地不动。
307
-
308
- ### v1.9.1 · 2026-10-07(代码审查:86 项修复)
309
-
310
- 进行了一次全量代码审查,共 86 项。没有功能上的加减,优化了代码结构,清理并修复了一批 bug 与潜在问题:
311
-
312
- - **心跳不会再被卡死**:修复了心跳卡在模型调用上时会卡死的问题,现已增加看门狗兜底——此前某一跳若卡住,插件会一直以为自己在跳、之后再也不动,只有重启才好;现在超过 45 分钟即强制收尾并留痕 `beat_watchdog`,下一拍照常(不补跳)。
313
- - **说了话就不会丢账**:修复了投递成功后因超时或异常而漏账、进而导致冷却与每日上限计算错误的问题——现在投递成功无条件落账,超时的也记 `spoke_late`。
314
- - **观察游标改成宿主序号**:优化了游标记进度的逻辑,修复了可能存在的漏读、重读问题——此前按「第几条」记,会话一旦被压缩或事件被重写,游标就会指错地方;现在按宿主绝对序号记,升级后旧游标自动丢弃并重扫一遍(会留一条 `cursor_format_discarded`)。
315
- - **素材不再「说一次就消失」**:优化了聊天种子退场机制,agent 能够在长期话题上连续聊天了——chat 类素材以前被说出口就当场退场,连上下文都没接上;现在至少活到下一次回收,有新证据还会继续留池。
316
- - **画像合并能看到 psy 分区**:修订了用户画像 psy 分区的合并逻辑——**现 psy 分区的内容已可被 agent 看见**(此前模型看不见 psy 条目却能往那儿写,写进去因为没有 id 就再也改不动)。这有意放宽了原先「psy 不出库」的口径。
317
- - **表达被拦下时留下痕迹**:现表达被拦时会留下痕迹,方便查询原因——以前素材包发了出去、目标却没说话时,日志里一片沉默;现在记 `expression_dropped`,连续三次另记一条 `expression_dropped_streak`。
318
- - **RPC 通道多了道自检**:浏览器数据通道的鉴权依赖宿主提供的闸门;宿主没提供时,删除 / 迁移 / 改配置这类端点直接拒绝并留痕(`rpc_host_gate_missing`),而不是默默裸奔。
319
- - **配置里「写了但没生效」的开关全接上**:`config/interests.json` 的 `_schedule`(每日闲逛场次、每场焦点数、每焦点最多几条)和 `config/busy-rules.json` 的四个阈值,以前只有一部分真在读,现在全部读。
320
- - **作息峰值不再被噪声顶掉**:「近期活跃时段」要同时过两道门(强度阈值 + 相对最高值的占比),指数衰减残渣也会归零,不再把一次偶发熬夜报成你的作息。
321
- - 工程卫生:补了类型声明(`types` 字段 + 构建产出 d.ts)、清了重复的锁文件(只留 npm)、加了 CI(Windows runner:`npm ci` → 类型检查 → 测试),并新增 47 个回归测试,测试总数到 **282**。
322
-
323
- ### v1.9.0 · 2026-10-06(新功能:素材报账,归账不再靠猜)
324
-
325
- - **素材报账**:投递素材后由目标会话的 agent **亲口报账**——这轮用了哪几条、用的是「素材本身」还是「顺着素材自己说的话」,都如实记下。此前归账靠事后拿文本做兜底匹配,漏记错记都躲不掉;现在报账带**本次投递编号**核对,不会串到别轮。工具注册在全局层,日常会话里也能用。
326
- - **话题偏好加权**:每条话题按「被采纳率」算权重(30 天半衰期,冷启动中性),闲逛挑话题时自动偏向你真接得住的那些,接不住的慢慢退到后面;**14 天没被碰过的老话题有硬地板**优先轮换,不会被高权重话题永远压在下面。
327
- - **报账缺失可见**:真没等到报账的投递,周报里单独报一行,不再靠「静默次数」糊过去;这类投递不计入偏好统计,免得把「没记账」算成「发了没人要」。
328
- - **素材池审计**:新增 / 合并 / 淘汰 / 归档裁剪 / 恢复 / 删除,全部写进 `data/logs/heartbeat.jsonl`(`seed_added`、`seed_retired`、`seed_archive_trimmed`、`seed_restored`、`seed_deleted`)。只记 id 与元数据,**不记素材正文**——那本日志是明文,正文来自对话,不该落到明文里。
329
- - **归档区上限**:素材归档区超过 `seeds.archiveCap`(默认 50)时按**最老优先**裁掉,不再无限增长;CLI `seeds gc` 会打印裁剪条数。
330
- - **迁移容器 KDF 参数化**:加密容器现在把 KDF 强度记进容器、解密封装按其解密并做范围校验(此前写死,哪天调强就会把旧容器误报成「密码错」)。**旧容器不受影响,照常解**。
331
- - **周报锚定**:装好当周不再立刻出一份空报,而是记下起点、满 7 天出第一期——行为与说明文档一致。
332
- - 修复:报账里的素材编号带方括号 / 引号(如 `[s3]`)现在能正确识别;报账工具写盘超时从 5s 放宽到 15s(DPAPI 单次可能到 2.7s,原来偏紧);闲置时的兜底素材改用画像里的真实条目 id,不再是占位编号。
301
+ <summary><b>v2.0.x</b></summary>
302
+
303
+ ### v2.0.0 · 2026-10-17(心事匣 + 摘抄本 + 代码审查:22 项修复 + 结构整改)
304
+
305
+ 第二次全量代码审查(22 项主发现 + 一节低优先级观察)的修复与结构整改,外加两个新功能:**心事匣**——心跳第一次有了「跳与跳之间的缝」;**摘抄本**——心跳第一次有了自己的笔记本。
306
+
307
+ - **心事匣(新功能)**:一格加密便签(`data/notepad.json`)。投递素材包里带上「你上跳留下的心事」,并附它留于第几跳、已搁多久、这期间被叫醒过几次;**照原样再写一遍算保留(年龄不重算)**,写新的就换掉它,什么都不做下一跳自动清。新增模型工具 `notepad`(write / read / clear),和账本工具一样注册给日常会话,引擎室看不到。序号只认真正的拍——**启动那一拍不计**,状态栏的时间注入也不算。
308
+ - **摘抄本(新功能)**:留句子和想记住的小事的地方,落 `data/excerpts.md`(**明文**,和账本同一条理由:要能翻、能手编)。注册成模型工具 `excerpt`(add / list / remove),走和账本工具同一条路注册给日常会话;引擎室看不到。它只增不减——除非她主动抹掉。
309
+ - **加密换代**:数据改用 AES-256-GCM,数据密钥交给 Windows DPAPI(CurrentUser)保管在 `data/vault.key`,进程启动解一次;旧版 1.x 的 DPAPI 容器照样能读,且在写入或每跳维护时自动换代(`vault upgrade` 一次性全换)。**升级后不要再退回 1.x**——旧版本读不了新容器(要退回就趁早、且先别跑 upgrade)。副产物:每个文件一次 PowerShell 的加解密路径消失了,全量测试从 97 秒降到 12 秒。
310
+ - **遗忘边界补全**:`wipe` 以前只删 live 三个文件,快照、归档分片、迁移备份都还在,重放能把画像原样长回来。现在 `wipe` 是真遗忘(连快照、归档分片、迁移备份一起删),`burn` 也补上了迁移备份与导入暂存残留。
311
+ - **迁移全或无**:导入时只要有一个文件不合格(路径越界、名字不在白名单、超出 5000 文件 / 64MB 预算),整包不落地并如实报错;此前是跳过坏文件、把其余部分导进去。
312
+ - **记忆的正确性**:journal 从「记模型的原始操作」改成「记规范化之后的最终事实」,重放只是原样回放、老化也成为可重放的事件——实时算出来的画像与重启后重放出来的画像完全一致(此前两条路径的宽松度不同,老化还不入流水)。旧格式流水照旧能读,新旧混在同一个文件里也逐行判定。
313
+ - **证据由代码生成**:模型只能引用本批次给出的观察编号(`o1`、`o2`…),引用位置、时间、来源种类一律由代码填。伪造引用、或自称「手写」把新记忆钉到信心上限的路径,从构造上不再可达。
314
+ - **锁失败关闭**:跨进程锁等不到就跳过这次写入并留痕,不再「等 3 秒没等到就照写」;陈旧锁要同时满足年龄与「持有进程确实不在了」才被回收。
315
+ - **每一跳都有取消链**:超时、看门狗与插件卸载会真正取消这一跳的模型调用与子进程,不再只是「不再等待」而让旧任务继续往下写。
316
+ - **画面不认旧图**:本跳没截到图,就取消本跳的画面资格并烧掉盘上旧图;截图与本跳的时间戳印章对得上才解密使用。
317
+ - **观察不丢**:合并期间新到的观察不再被整体清空,只清掉这一轮真正读过的那几条。
318
+ - **投递记账**:按说话的那一刻记账(含写入时点的完整复查:静默时段、每日上限、冷却、忙窗、是否正在打字),闸门在复查时若已变化也不影响账——话已经在对方那里,拒绝只说明条件变了,绝不表示「她没说话」。
319
+ - **RPC 通道失败关闭**:宿主没提供闸门时,只有 5 个只读端点照常服务,其余全部拒绝并留痕(此前只拒 6 个破坏性端点,等于把画像与素材池向 loopback 敞开)。端点表改为无原型对象,`constructor`、`__proto__` 这类名字不再能穿透。
320
+ - **表述收紧**:判「别说」时,之后的**决策与表达**两相不再调用模型(闲逛相在闸门之前触发,另算)——此前文档写「一个 token 都不花」过宽。账本块与摘要也加了长度上限,超预算按整条跳过,不再从句子中间切断。
321
+ - **工程卫生**:所有外部输入(模型输出、流水读入、迁移包、RPC 载荷)统一校验形状;原子写先 flush 再改名;加密临时窗口的崩溃残渣在首次使用时清理;CI 增加构建检查、`dist` 一致性检查与客户端 JS 语法检查(跟踪的 `dist` 此前早已过期,npm 会发错包——已重建)。
322
+ - **复用的引擎室不会松掉工具面**:`ensureAgent` 取到已存在且活着的 agent 时(`agent_reuse_live`)会跳过 `setup`,而工具白名单正是在 `setup` 里钉的——插件重启后的那一跳因此带着全局层全量跑(2026-10-17 实测 62 个工具,shell、邮件、撤销都在里面)。现在复用分支会单独重新钉一遍(`pinEngineRoomTools`),结果写进同一行 `tool_policy`。
323
+ - **节律配置重启不再被默认值掀掉**:`timeInjectMin` / `statusbar` / `idleMode` / `tokenSaver` 四个字段原先带 schema 默认值,而启动时会用配置值覆盖 `ui.json`——默认值分不出「用户设了」和「没设」,于是每次重载都静悄悄把卡片的选择改回去(实测:状态栏开关重启后自己变回关)。现在这四个字段不写默认值,未设就走卡片、再落到出厂值。
324
+ - **投递时不再用裸 agent 发言**:目标会话没活着时,插件会 resume 一个临时 agent 来投递,而那次 resume 没挂预设——于是那一轮的工具面变成进程全局层(实测该会话从 1 个 `run_code` 变成 61 个、呈现模式退回 native,GUI 还弹出工具变更提示)。现在会读会话自己记着的预设 id(`agent-preset/selected`)并挂回去;读不到就照旧裸跑,并留痕 `deliver_target_preset`。
325
+ - 新增 38 条回归测试(审查修复)→ 测试 **344** 条;心事匣另加 17 条、摘抄本另加 14 条、复用分支的白名单另加 2 条、投递目标的预设另加 1 条,**总数 378**。
333
326
 
334
327
  </details>
335
328
 
336
- 更早的版本(v1.8.x → v1.0.0,以及 v0.x 脚本外挂时代)见 [CHANGELOG.md](CHANGELOG.md)。
329
+ 更早的版本(v1.9.x → v1.0.0,以及 v0.x 脚本外挂时代)见 [CHANGELOG.md](CHANGELOG.md)。
337
330
 
338
331
  ## 延伸阅读与致谢
339
332
 
package/client.js CHANGED
@@ -586,6 +586,11 @@ window.__ModuleLoader__.load({
586
586
  (v) => setLedgerMsg("已打开:" + v.path),
587
587
  (e) => setLedgerMsg("失败:" + String(e).slice(0, 80)),
588
588
  );
589
+ const [excerptMsg, setExcerptMsg] = React.useState("");
590
+ const openExcerpt = () => rpc("excerpt.open").then(
591
+ (v) => setExcerptMsg("已打开:" + v.path),
592
+ (e) => setExcerptMsg("失败:" + String(e).slice(0, 80)),
593
+ );
589
594
  return React.createElement(
590
595
  "div",
591
596
  { style: { padding: "2px 0" } },
@@ -601,6 +606,13 @@ window.__ModuleLoader__.load({
601
606
  React.createElement("button", { style: buttonStyle, onClick: () => { void openLedger(); } }, "一键打开账本"),
602
607
  React.createElement("span", { style: hintStyle }, ledgerMsg || "账本是心跳 agent 的待办与话题来源,可直接手编")),
603
608
  )),
609
+ React.createElement(Section, { title: "摘抄本" }, React.createElement(
610
+ "div",
611
+ null,
612
+ React.createElement("div", { style: rowStyle },
613
+ React.createElement("button", { style: buttonStyle, onClick: () => { void openExcerpt(); } }, "一键打开摘抄本"),
614
+ React.createElement("span", { style: hintStyle }, excerptMsg || "留句子和小事的地方:只增不减,直接手编也不会被覆盖")),
615
+ )),
604
616
  React.createElement(Section, { title: "周报" }, React.createElement(WeeklySection, null)),
605
617
  React.createElement(Section, { title: "换机迁移" }, React.createElement(MigrateSection, null)),
606
618
  React.createElement(Section, { title: "节律配置" }, React.createElement(ConfigSection, null)),
@@ -1,74 +1,74 @@
1
- {
2
- "heartbeat": {
3
- "intervalMin": 60,
4
- "idleMode": false,
5
- "archiveRotatedHome": true
6
- },
7
- "gate": {
8
- "maxDailySend": 10,
9
- "cooldownMinutes": 30,
10
- "quietHours": {
11
- "start": "01:00",
12
- "end": "08:00"
13
- }
14
- },
15
- "browse": {
16
- "windows": [
17
- {
18
- "start": "11:00",
19
- "end": "15:00"
20
- },
21
- {
22
- "start": "17:00",
23
- "end": "21:00"
24
- }
25
- ],
26
- "minIntervalHours": 4,
27
- "maxSeedsPerVisit": 2
28
- },
29
- "seeds": {
30
- "maxActive": 30,
31
- "ttlDays": {
32
- "news": 3,
33
- "fandom": 14,
34
- "scene": 60,
35
- "promise": 90
36
- },
37
- "coldBenchDays": 21,
38
- "archiveCap": 50,
39
- "retireAfterUsed": 2,
40
- "scoreWeights": {
41
- "freshness": 0.4,
42
- "unused": 0.3,
43
- "confidence": 0.3
44
- }
45
- },
46
- "profile": {
47
- "consolidation": {
48
- "minIntervalHours": 12,
49
- "inboxBacklog": 30
50
- },
51
- "partitionCap": 50,
52
- "maxOpsPerRun": 10,
53
- "confidenceCap": {
54
- "chat": 0.6,
55
- "screen": 0.4,
56
- "browse": 0.4
57
- },
58
- "volatileDays": 14,
59
- "stableLowActivityDays": 180,
60
- "psyEnabled": false
61
- },
62
- "observe": {
63
- "maxChars": 80,
64
- "perBeat": 10
65
- },
66
- "weekly": {
67
- "enabled": true,
68
- "keepReports": 3
69
- },
70
- "retention": {
71
- "envPulseHours": 48,
72
- "decisionLogDays": 30
73
- }
74
- }
1
+ {
2
+ "heartbeat": {
3
+ "intervalMin": 60,
4
+ "idleMode": false,
5
+ "archiveRotatedHome": true
6
+ },
7
+ "gate": {
8
+ "maxDailySend": 10,
9
+ "cooldownMinutes": 30,
10
+ "quietHours": {
11
+ "start": "01:00",
12
+ "end": "08:00"
13
+ }
14
+ },
15
+ "browse": {
16
+ "windows": [
17
+ {
18
+ "start": "11:00",
19
+ "end": "15:00"
20
+ },
21
+ {
22
+ "start": "17:00",
23
+ "end": "21:00"
24
+ }
25
+ ],
26
+ "minIntervalHours": 4,
27
+ "maxSeedsPerVisit": 2
28
+ },
29
+ "seeds": {
30
+ "maxActive": 30,
31
+ "ttlDays": {
32
+ "news": 3,
33
+ "fandom": 14,
34
+ "scene": 60,
35
+ "promise": 90
36
+ },
37
+ "coldBenchDays": 21,
38
+ "archiveCap": 50,
39
+ "retireAfterUsed": 2,
40
+ "scoreWeights": {
41
+ "freshness": 0.4,
42
+ "unused": 0.3,
43
+ "confidence": 0.3
44
+ }
45
+ },
46
+ "profile": {
47
+ "consolidation": {
48
+ "minIntervalHours": 12,
49
+ "inboxBacklog": 30
50
+ },
51
+ "partitionCap": 50,
52
+ "maxOpsPerRun": 10,
53
+ "confidenceCap": {
54
+ "chat": 0.6,
55
+ "screen": 0.4,
56
+ "browse": 0.4
57
+ },
58
+ "volatileDays": 14,
59
+ "stableLowActivityDays": 180,
60
+ "psyEnabled": false
61
+ },
62
+ "observe": {
63
+ "maxChars": 80,
64
+ "perBeat": 10
65
+ },
66
+ "weekly": {
67
+ "enabled": true,
68
+ "keepReports": 3
69
+ },
70
+ "retention": {
71
+ "envPulseHours": 48,
72
+ "decisionLogDays": 30
73
+ }
74
+ }