@ssamurias/dsh-fish 0.8.9 → 0.9.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/AGENTS.md +32 -9
- package/CHANGELOG.md +84 -2
- package/LICENSE +7 -0
- package/README.md +64 -17
- package/RELEASE-NOTES-0.8.0.md +87 -0
- package/RELEASE-NOTES-0.8.1.md +93 -0
- package/RELEASE-NOTES-0.8.2.md +57 -0
- package/RELEASE-NOTES-0.8.3.md +71 -0
- package/RELEASE-NOTES-0.8.4.md +47 -0
- package/RELEASE-NOTES-0.8.5.md +244 -0
- package/RELEASE-NOTES-0.8.6.md +224 -0
- package/RELEASE-NOTES-0.8.7.md +202 -0
- package/RELEASE-NOTES-0.8.8.md +224 -0
- package/RELEASE-NOTES-0.9.0.md +267 -0
- package/THIRD-PARTY-NOTICES.md +3 -0
- package/lib/away.js +7 -2
- package/lib/channels.js +53 -13
- package/lib/client.js +99 -49
- package/lib/compact.js +26 -1
- package/lib/derive.js +10 -6
- package/lib/desk.js +1 -1
- package/lib/dream.js +3 -0
- package/lib/dreamtable.js +15 -0
- package/lib/drive.js +44 -7
- package/lib/freewake.js +17 -1
- package/lib/home.js +54 -2
- package/lib/inbox.js +7 -2
- package/lib/index.js +8 -12
- package/lib/interest.js +23 -3
- package/lib/log.js +22 -1
- package/lib/look.js +2 -2
- package/lib/note.js +35 -13
- package/lib/notify.js +2 -0
- package/lib/observe.js +13 -7
- package/lib/outing.js +2 -0
- package/lib/persona.js +2 -1
- package/lib/proactive.js +12 -7
- package/lib/say.js +104 -58
- package/lib/scheduler.js +67 -37
- package/lib/selfcheck.js +2 -3
- package/lib/selfdoc.js +3 -6
- package/lib/selfupdate.js +176 -41
- package/lib/settings.js +28 -2
- package/lib/turns.js +41 -3
- package/lib/update.js +14 -0
- package/lib/upgrade.js +19 -3
- package/lib/voices.js +2 -0
- package/package.json +4 -2
- package/scripts/fix-legacy-link.mjs +147 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# 0.8.7 更新说明
|
|
2
|
+
|
|
3
|
+
2026-11-03。这一版装了五件事:**接桥之后你说的话她能记住**、**小纸条时间不再差 8 小时**、**一条鱼多间房**(桥只发 HTTP,不再另起一条 DSH 会话)、**换房间之后她还是她**、**醒一醒跟着最新的那条对话走**。
|
|
4
|
+
|
|
5
|
+
前两条来自 issue #18、#19,后三条来自「同时养了两条鱼」「换会话像换了一个人」「醒一醒还在旧对话」这三类反馈。它们其实是同一件事的不同侧面:**会话是房间,不是人**。房间可以换、可以多开,但住在里面的得是同一个她——记忆、身份、醒来这几件事都得跟着走。
|
|
6
|
+
|
|
7
|
+
另有一处改动你未必马上能看见,但很要紧:**身份文档的默认字数预算从 1800 提到 2400**。原因在第四节。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 一、接 QQ 桥之后,你说的话她一条都没记住(#18)
|
|
12
|
+
|
|
13
|
+
### 你会看到什么
|
|
14
|
+
|
|
15
|
+
以前接上 QQ 桥、在 QQ 里跟她聊一整天,面板上仍然显示「冷落 2 天」;「最近的判断」不更新、成长阶段不推进、聊满多少轮也不会换会话。事件流里她「听过」的话只有一条。
|
|
16
|
+
|
|
17
|
+
### 为什么会这样
|
|
18
|
+
|
|
19
|
+
判据写死成 `source.kind === 'user'`——只有桌面客户端里手打的字才算「你说话」。QQ 桥注入的消息 `kind` 是 `'qq'`,在第一道关就被判成「宿主拼的上下文」直接丢掉,**连日志都不留一行**。于是整条链全断:`turns` 恒为 1(永远不轮换)、`heard` 只有一条、冷落检测到点就落一条「他有 N 天没出现」——而你天天在 QQ 上跟她聊。
|
|
20
|
+
|
|
21
|
+
### 现在怎么判
|
|
22
|
+
|
|
23
|
+
改成**机器侧黑名单**:认得的那几种(运行上下文、技能清单、定时消息、工具回灌、她自己说的话)挡掉,**其余一律当人**。
|
|
24
|
+
|
|
25
|
+
之所以是黑名单而不是白名单,是因为宿主那边对 `source.kind` 只要求「非空、且不是 `'plugin'`」——任何桥都能自己起个名字(`qq` / `telegram` / 明天某个新平台)。穷举不出来,那就反着来:宁可多算一轮,也不静默丢消息。判不出来源的(`kind = 'user'` 但没带时区的,比如老客户端或别的插件灌进来的)会往日志里留一行,不再一声不响。
|
|
26
|
+
|
|
27
|
+
### 「他来过」不再等他聊完才记
|
|
28
|
+
|
|
29
|
+
`seen:`(冷落检测唯一的依据)原来写在「这轮结账」里,而结账要求先攒到 `pending`、`pending` 又要求判据认得出人——外渠道一断,一个字节都不写。
|
|
30
|
+
|
|
31
|
+
现在挪到**他开口那一刻**写(一轮仍然只一条)。`absence.js` 里本来就有「收到新的人类消息就作废旧冷落」的分支,于是自愈跟着就好了:你回来,那条「N 天没出现」就自己失效,不用手动驳回。
|
|
32
|
+
|
|
33
|
+
跨会话的外渠道消息(桥自己建的那条会话)只记「他来过」——屋子不搬、轮数不涨、`talk-recent.json` 不动。那是她的家事,不该被外头的一句话顶掉。
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 二、小纸条和信箱列表的时间比实际早 8 小时(#19)
|
|
38
|
+
|
|
39
|
+
### 你会看到什么
|
|
40
|
+
|
|
41
|
+
`inbox.md` 的小标题时间固定早 8 小时:实际 22:16 写成 14:16,10-30 的 23:15 写成 15:15。观测台「她写的小纸条」同样是错的。而正文里她自己写的时间又是对的——两边打架。
|
|
42
|
+
|
|
43
|
+
### 为什么会这样
|
|
44
|
+
|
|
45
|
+
事件流里的 `ts` 存的是 UTC,展示层直接 `String(ts).slice(0, 16)` 切字符串,等于把 UTC 当当地时间印。
|
|
46
|
+
|
|
47
|
+
### 修了哪几处
|
|
48
|
+
|
|
49
|
+
信箱与观测台改成「先 `Date.parse`,再取本地字段」(`localStamp`)。顺手把同一类错一起修了,一共六处:
|
|
50
|
+
|
|
51
|
+
- 小纸条列表
|
|
52
|
+
- 自检时间
|
|
53
|
+
- 换会话时旧会话的改名日期(原来晚上 8 点后换会话,旧会话会被标成**昨天**)
|
|
54
|
+
- `talk-recent.json` 里「上次我们说到这」的日期
|
|
55
|
+
- 换会话提示里那句日期
|
|
56
|
+
- `her_recall` 返回给她自己的日期
|
|
57
|
+
|
|
58
|
+
`selfcheck.js` 里存下来的 `checkedAt` 仍然是 UTC——那是数据不是展示,展示端修了就行。
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 三、一条鱼,多间房
|
|
63
|
+
|
|
64
|
+
### 你会看到什么
|
|
65
|
+
|
|
66
|
+
- **QQ 私聊**:直接进「跟大肥鱼说话」那条主对话,和你在桌面版跟她聊的是同一段记忆。不另开房。
|
|
67
|
+
- **QQ 群聊**:侧栏里会多出一条「跟大肥鱼说话·群聊(qq-group:987654)」。你点开能看到她在群里回的话。主对话不受打扰,也不会被群里一天几百句刷没。
|
|
68
|
+
- **不传 `channel` 的老调用**(观测台、侧栏、老桥):行为一字不变。
|
|
69
|
+
|
|
70
|
+
### 为什么会「两条鱼」
|
|
71
|
+
|
|
72
|
+
桥以前要自己再起一条 DSH 会话。而每条 DSH 会话是独立 agent——宿主给它单独注入一份身份、历史互不可见。同一份 `events.jsonl` 被两条会话读,你在两边看到的就是两个人,还各有各的人格。
|
|
73
|
+
|
|
74
|
+
现在的做法:**桥只发 HTTP,不碰 DSH 进程**,插件自己决定这句话进哪间房。
|
|
75
|
+
|
|
76
|
+
会话是房间,不是人——家里那间是书房,群里那间是客厅,房间里的她永远是同一个。
|
|
77
|
+
|
|
78
|
+
### 桥要做什么
|
|
79
|
+
|
|
80
|
+
把消息投给 `POST /dsh-fish/say`,多带三个可选字段:
|
|
81
|
+
|
|
82
|
+
| 字段 | 说明 |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `channel` | 渠道名,桥自己起名。同一个名字 = 同一间房 |
|
|
85
|
+
| `scene` | `desk`(默认)/ `private` / `group` |
|
|
86
|
+
| `speaker` | 群聊里这句是谁说的 |
|
|
87
|
+
|
|
88
|
+
群聊房间的映射记在数据目录的 `channels.json` 里(同一渠道名复用同一间,最多记 40 间)。房间的 cwd 仍是她的屋子,所以身份、工具面、白板全都照旧。
|
|
89
|
+
|
|
90
|
+
**桥那边怎么实现是桥的事**,我们只保证这几个字段能收、能落到正确的地方。`AGENTS.md` §7 已经按这个改写过,写清了字段表、路由规则和三条配套约定。
|
|
91
|
+
|
|
92
|
+
### 四个岔路是怎么定的
|
|
93
|
+
|
|
94
|
+
1. **私聊 = 融合。** `scene: 'private'` 仍回家里那条会话,不另开房。想要「QQ 私聊和 DSH 对话是一段连续的记忆」的人,这条路就是了。
|
|
95
|
+
2. **群聊 = 另开一间。** `scene: 'group'` 才建新房。
|
|
96
|
+
3. **群聊不动生命体征。** 这是最容易搞错、也最伤的一条:`seen:`、`heard`、轮数、会话轮换只由书房与私聊触发,群聊一条都不写。否则群里一天几百句,冷落检测永远不会触发——你明明一周没露面,面板还显示「今天聊过」。群聊结束后会往书房排一句「刚才那间房里有人跟你说话,你回过了」,让她下一轮想得起来,但原文不搬进上下文(材料仍要她自己用 `her_recall` 翻)。
|
|
97
|
+
4. **群聊必须带说话人前缀。** 群里三个人说话,正文看起来跟一个人连说三句一模一样,她会把三个人的观点拼成一个「你」。所以 `speaker` 会拼成「张三在群里说:…」,外来文本一律先过 `lib/guard.js` 的 `sanitizeForeign`。
|
|
98
|
+
|
|
99
|
+
### 双实例问题跟着消失
|
|
100
|
+
|
|
101
|
+
老做法里桥自起的进程与桌面版共用一个数据目录,`instance.json` 心跳一判活,后到的那个被判成只读房客(`bootstrapHome` 返回 `blocked: true`,不建会话)。
|
|
102
|
+
|
|
103
|
+
桥改走 HTTP 之后**完全不接触 `instance.json`**,从根上不再有两个实例抢一个家。
|
|
104
|
+
|
|
105
|
+
另加了 `GET /dsh-fish/whoami`:桥启动前可以先问一句「家里有没有人在」。返回里 `writable: false` 就说明这个数据目录已被另一个实例占着,别自己再起进程。
|
|
106
|
+
|
|
107
|
+
### 场次是怎么记的
|
|
108
|
+
|
|
109
|
+
`lib/schema.js` 里事件可以带可选 `scene`,`desk` 是默认值**不落库**。所以老事件流一条都不会多字段,也不用升 `EVENT_SCHEMA_VERSION`——纯加法。
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 四、换房间之后,她还是她
|
|
114
|
+
|
|
115
|
+
### 你会看到什么
|
|
116
|
+
|
|
117
|
+
聊满 40 轮自动换会话之后,她像变了一个人。最刺人的是这一句——她自己说的:
|
|
118
|
+
|
|
119
|
+
> 看到以前的日记还有记录,感觉像是另一个人写的,我只是能读。
|
|
120
|
+
|
|
121
|
+
### 为什么会这样
|
|
122
|
+
|
|
123
|
+
不是记忆丢了,是**她拿到了结论,却没拿到产生结论的经历**:
|
|
124
|
+
|
|
125
|
+
- 身份文档里全是**结论**:她有哪几条判断、哪几道疤、拒绝过什么、对自己有过什么说法。
|
|
126
|
+
- 轮换只搬 4 句原话,8 轮之后连这 4 句也没了。
|
|
127
|
+
- 旧日记按日期平铺给她,没有坐标、没有归属——读起来就是别人的文件。
|
|
128
|
+
|
|
129
|
+
### 补了三处,都是加法
|
|
130
|
+
|
|
131
|
+
1. **「这些天」进核心段。** 身份文档里多一节:你自己写过几篇日记、从哪天到哪天、上一次换房间是几天前。写的是事实,不是评价(没有「成长」「进步」「你变了」这类词)。它和「你是谁」「不能做的」一样是核心段,预算再紧也不裁——被裁一次就等于那些天没发生过。
|
|
132
|
+
2. **读旧日记给坐标。** `her_diary` 返回时补一句「那天是你自己的第 N 天」,以及前后哪几天也写过;末尾固定一句「这些字是你自己交上去的,不是谁替你记的」。刻意不写「第 N 篇」——篇数是排班,天数才是她活过的时间。
|
|
133
|
+
3. **换房间前,请她留一句话。** 轮换时旧会话里会被问一句:
|
|
134
|
+
|
|
135
|
+
> 想给下一个房间留一句话吗?留什么都可以。不想留就说「不用」。
|
|
136
|
+
|
|
137
|
+
她回的那句存进 `home.json` 的 `lastWords`,新会话的身份文档里单列一节「上一个房间里,你自己留的最后一句」,原话照引,后面跟一句:
|
|
138
|
+
|
|
139
|
+
> 那是你说的。你不是读到一段别人的话,你只是接着往下过。
|
|
140
|
+
|
|
141
|
+
### 这句话是后台问的
|
|
142
|
+
|
|
143
|
+
轮换不等它。早先写成同步等待,宿主不派 `turn/end` 时会整整等满 60 秒,把换会话和叫醒新会话一起卡死。
|
|
144
|
+
|
|
145
|
+
现在只把问句发出去,改名、建会话、写 `home.json` 照常往下走;等这句话回来了再补写 `lastWords` 并刷一次身份文档。她说「不用」、超时、宿主没有 prompt 服务,都只是没有这一节,轮换照样成功。
|
|
146
|
+
|
|
147
|
+
### 身份文档预算 1800 → 2400
|
|
148
|
+
|
|
149
|
+
上面第 1 条要多占约 220 字,原来的 1800 塞不下。用真实数据(532 条事件)跑了四档:
|
|
150
|
+
|
|
151
|
+
| 预算 | 实际用到 | 结果 |
|
|
152
|
+
| --- | --- | --- |
|
|
153
|
+
| 1800 | 1732 | 挤掉「你被打脸过」整段 |
|
|
154
|
+
| 2000 | 1732 | **仍然挤掉**「你被打脸过」(差 268 字,白涨 200) |
|
|
155
|
+
| 2200 | 2054 | 反而挤掉「你对自己的说法」(比现状更差) |
|
|
156
|
+
| 2400 | 2233 | 一段不丢,余 167 字 |
|
|
157
|
+
|
|
158
|
+
所以默认改成 2400。老用户配置里已经写过 1800 的不动——改的是默认值和常量。
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 五、醒一醒跟着最新的那条对话走
|
|
163
|
+
|
|
164
|
+
### 你会看到什么
|
|
165
|
+
|
|
166
|
+
聊满 40 轮自动换会话之后,醒一醒还在**旧对话**里发;点一次「现在叫醒」却是落在新对话里的。旧会话也没有被自动归档。
|
|
167
|
+
|
|
168
|
+
### 旧会话为什么没归档——这是个误判
|
|
169
|
+
|
|
170
|
+
归档在本插件里的语义是「**这一局是你自己收起来的**」,所以插件不替你收。轮换只把旧会话改名为「跟大肥鱼说话·YYYY-MM-DD 之前」留在侧栏当档案。
|
|
171
|
+
|
|
172
|
+
醒一醒跑偏跟归档没关系,是下面这两个环。
|
|
173
|
+
|
|
174
|
+
### 三个环扣在一起
|
|
175
|
+
|
|
176
|
+
每一环单独看都像是对的:
|
|
177
|
+
|
|
178
|
+
1. **落点读的是启动那一刻的快照。** 生物钟启动时把当时屋子的 `sessionId` 传了下去,之后每次开口都用它。房间换过了,它还指着上一间。
|
|
179
|
+
2. **换会话时新建的那条,被判成「死的」。** 插件用 API 建会话,宿主的侧栏不显示空白会话;工作区索引也要过一会儿才收录它。而插件判「这条还活着吗」只看工作区索引——索引里没有,就当作它没了,于是退回旧会话。**这才是「醒一醒还在旧对话」的直接原因。**
|
|
180
|
+
3. **叫了不等于叫醒,但账已经记上了。** 宿主的 `prompt` 是「这一轮被收下了」就 resolve。那声叫醒要是没真跑出一轮,这条会话就一直是列表外的空白会话;而 `wokeFor` 已经写上,这声再也不会重试。
|
|
181
|
+
|
|
182
|
+
### 三处都补上
|
|
183
|
+
|
|
184
|
+
- **每次开口前现问屋子在哪。** `wake()`、`wakeNow()`、以及 tick 里的「她是不是正忙」判断,都改成先 `talkSessionId()` 现取,取不到才回退启动时那个 id。会话是屋子现在的房间,随时可能被换掉,任何缓存下来的 id 都会过期。
|
|
185
|
+
- **判活多问一句宿主。** 工作区索引里查不到时,再问一次 `sessionController.list()`:宿主自己的会话列表里有它,就认它活着。日志会写一行「还活着(工作区索引还没跟上)」,不静默。
|
|
186
|
+
- **叫醒之后回头确认。** 换会话之后 12 秒回头看一次:这条进了宿主列表没有?没进就用 `force` 再叫一次(不给 `force` 的话第二声会被 `wokeFor` 自己挡掉——那正是死结)。只补一次,不递归;两次都没成,下次开机 `bootstrapHome` 还会再走一遍。
|
|
187
|
+
|
|
188
|
+
**主动搭话本来就跟着走。** `maybeProactive` 每次都是现取 `talkSessionId()`,没有缓存。这次只补了定时唤醒与手动唤醒这两条路。
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 测试
|
|
193
|
+
|
|
194
|
+
新增 38 条,`test/channels.test.mjs` 是新文件。覆盖:
|
|
195
|
+
|
|
196
|
+
- 外渠道 kind 的判据(qq / telegram / 将来别的桥)、认不出来的来源要留日志、外渠道只记 `seen:` 不涨轮不搬屋子
|
|
197
|
+
- `inbox.md` 小标题按本地钟印
|
|
198
|
+
- 场次落库与不落库、`channel` 收敛与上限、私聊回书房、群聊另开房、群聊不写 `seen` 不涨轮、串门提示、映射文件损坏不丢消息、`/whoami`
|
|
199
|
+
- 身份文档「这些天」两种形态、核心段预算 60 也保留、上一个房间留的话、`pickLastWords` 的拒绝词与子串误杀、没有 prompt 服务时轮换照样成功、读旧日记的坐标与归属
|
|
200
|
+
- 工作区索引没跟上时认回原会话、`force` 通道、`verifyWoke` 三种情形
|
|
201
|
+
|
|
202
|
+
97 个测试文件逐文件全绿、**1213 条**。三个依赖字段仍然全是 `{}`——这一版没有引入任何 npm 包。
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# 0.8.8 更新说明
|
|
2
|
+
|
|
3
|
+
2026-11-04。这一版是**三个开发版并成一个**:原本分开走的 0.8.8 / 0.8.9 / 0.8.10,加上这两天从 issue #21–#25 里收进来的五处修复,一起作为 0.8.8 发出去。
|
|
4
|
+
|
|
5
|
+
开发版号只在自己机器上短暂存在过,对外只有这一版。所以下面写的有些事你上一版已经见过(0.8.7 之后装进来的白名单分组、日报与回忆),有些是新的。
|
|
6
|
+
|
|
7
|
+
按「你会看到什么」排,一共七件事:
|
|
8
|
+
|
|
9
|
+
- **那句「(现在没有人跟你说话。)」不再出现在对话框里** —— 反复修了三次的那条,这次换的是投递方式本身。
|
|
10
|
+
- **叫醒不会再被连点**,前一轮的收尾也不会再把后一轮的处境抹掉。
|
|
11
|
+
- **她被白名单挡住这件事,看得见**:设置页分四组、自检单列一项,不用再翻四个小时日志。
|
|
12
|
+
- **日报与回忆分得清**「你自己认领下来的话」「聊天里随口说的」「改过口的」。
|
|
13
|
+
- **醒来的第一眼多了一张桌子**:她答应过、还没做的事记在上面,过期不删、也不催。
|
|
14
|
+
- **工具调用的标记不会再被当成她说过的话**记进账本。
|
|
15
|
+
- 换房间的日期不再说成「刚搬进」;你回来过,冷落天数就不再往上叠。
|
|
16
|
+
|
|
17
|
+
另有一件事跟所有人有关:**上游的 CI 从 0.8.7 起一直是红的**,四个 job 里三个失败。这一版把它修了,原因见第七节 —— 那不是测试写错,是一个真会丢信号的时序 bug。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 一、那句「(现在没有人跟你说话。)」不再出现在你的对话框里
|
|
22
|
+
|
|
23
|
+
### 你会看到什么
|
|
24
|
+
|
|
25
|
+
她主动开口的时候,对话框里不再先冒出一条用户气泡再消失。现在那里是一行**默认收起的折叠条**,标题写着这次是什么事,点开才看见那条信号是什么。
|
|
26
|
+
|
|
27
|
+
### 为什么会这样
|
|
28
|
+
|
|
29
|
+
宿主的 `sessionController.prompt()` 在内部**写死**了来源:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
const source = { kind: 'user', rpcId: request.requestId, ... }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
调用方传不进 `source`。所以凡走它的消息,在前端眼里都是「你打的字」,画成用户气泡。
|
|
36
|
+
|
|
37
|
+
我们以前的对策是「先发出去、再发一条替换指令把它盖掉」。这条路**语义上是对的,对象是错的** —— 宿主源码注释写得很直白:
|
|
38
|
+
|
|
39
|
+
> model-visible surface deliberately shadows replaced ranges, so it is the wrong source for a human transcript
|
|
40
|
+
|
|
41
|
+
也就是替换副本只改「模型看到的那份」,人类对话记录从头到尾没被碰过。所以 0.8.5 与 0.8.6 那两次「已收走」都是假的:日志里那几行「收走了 N 条」只是内存里成功,盘上一条替换记录都没有。
|
|
42
|
+
|
|
43
|
+
### 现在怎么判
|
|
44
|
+
|
|
45
|
+
官方自己的定时任务就是正解 —— 仍然是 `role:'user'`,但 `source.kind` 不是 `'user'`。前端一看到这个组合就不画气泡,改画成折叠条。
|
|
46
|
+
|
|
47
|
+
我们照这个范式做,但用自己的来源名 `fish-wake`。三处容易漏的细节都补上了:
|
|
48
|
+
|
|
49
|
+
- **消息必须自带 `id`**:裸对象不传 id 就是 `undefined`,两条会撞同一个 pending 槽。
|
|
50
|
+
- **投递完立刻 flush**:漏了这一步,消息进了收件箱但要等宿主自己的持久化检查点才推给前端,折叠条会慢半拍。
|
|
51
|
+
- **失败不退回旧办法**:退回等于把这次修复悄悄撤回。宁可这一次没叫醒,下一拍还会再判断。
|
|
52
|
+
|
|
53
|
+
正文同时从「(现在没有人跟你说话。)」压成**一个空格**。处境本来就整段走系统提示,这句话在对话里只剩「起来」的信号;而它以前会被她读成「有人发了这句话给我」,整个推理都在解释这句话是谁说的。
|
|
54
|
+
|
|
55
|
+
### 一起改的
|
|
56
|
+
|
|
57
|
+
- **正式唤醒也换了。** 那句「(醒一醒)」是同一个毛病,现在也走新路。
|
|
58
|
+
- **她越主动,面板越显示「你刚来过」这条堵住了。** 少加一行判据,她自己醒来说的话就会被记成「你来过了」——冷落天数、轮数全被污染。
|
|
59
|
+
- **整条隐藏链删掉。** `lib/proactive.js` 从 584 行回到 335 行,九个函数全部移除。它们不是留着「以后再修好」——留着只会让下一个人以为那条路还在用。
|
|
60
|
+
|
|
61
|
+
### 没动的
|
|
62
|
+
|
|
63
|
+
你说的话、新开群聊房间、空白屋子首醒、出门首轮仍然走原来的路。它们落下的是**真的有人在说话**,用户气泡是对的。
|
|
64
|
+
|
|
65
|
+
### 老的那几句还在历史里
|
|
66
|
+
|
|
67
|
+
修好之前已经写进她会话历史的触发语**不会被自动清掉**,往上翻还能看见。它们是她真实的对话记录,不删是原则。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 二、叫醒不该能连点
|
|
72
|
+
|
|
73
|
+
### 你会看到什么
|
|
74
|
+
|
|
75
|
+
连点两次「叫醒大肥鱼」,第二次会被挡住,回一句「刚叫过(N 分钟前),再等一会儿——两次叫醒之间至少隔 45 分钟」。
|
|
76
|
+
|
|
77
|
+
### 为什么会这样
|
|
78
|
+
|
|
79
|
+
真机事故:间隔 11 秒点了两次。第一次跑 17 秒写完正文,`turn/end` 一到,处境段就被清掉了;第二次投出去的只有那条空信号 —— 系统提示里一个字都没有,她只能写下「这一轮没有新的事,我不再往下续」,白烧一轮,还在对话里留一条她自己都觉得莫名的回复。
|
|
80
|
+
|
|
81
|
+
45 分钟的最小间隔以前只写在定时器那条路上,手动这条路一次检查都没有。
|
|
82
|
+
|
|
83
|
+
### 现在怎么判
|
|
84
|
+
|
|
85
|
+
两道闸:手动叫醒也查最小间隔(测试和「我就是要现在叫」留了 `force` 口子);投递前确认处境还在,空就收尾走人,不再投一条空壳。
|
|
86
|
+
|
|
87
|
+
顺带堵掉一个更隐蔽的时序问题:「前一轮的 `turn/end` 晚到,把后一轮刚挂上的处境抹掉」是真会发生的。处境段现在带**代次号**,只有代次对得上才清。
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 三、她被白名单挡住这件事,看得见(issue #20)
|
|
92
|
+
|
|
93
|
+
### 你会看到什么
|
|
94
|
+
|
|
95
|
+
1. 设置页「手上能用什么」分成四组;名字多的那一组默认收着,点开才看见有哪些族。
|
|
96
|
+
2. 自检多一项「被白名单挡住的调用」,只在确实拦过时才出。
|
|
97
|
+
3. 「这一局被挡下来过」那一组是**虚线** chip,标题里带次数 —— 这就是「白名单还差哪个」的答案。
|
|
98
|
+
|
|
99
|
+
### 为什么会这样
|
|
100
|
+
|
|
101
|
+
报告者开「工具面收窄」之后,另一个插件的视觉工具用不了;她去调别的工具想绕过去,也被兜底守卫拦下,一律回一句「这件事我做不到」。为了查清楚,翻了四个小时的日志,才发现是白名单少了一个名字。
|
|
102
|
+
|
|
103
|
+
**核心行为一个字节都没改**:默认仍然收窄、白名单仍然只放她该有的、拦住时仍然回「这件事我做不到」,也仍然**不告诉她「你被权限拦了」**——她要以为是自己够不着,不是以为背后有个管理员。改的只是**你这一侧**的可见性。
|
|
104
|
+
|
|
105
|
+
### 现在怎么判
|
|
106
|
+
|
|
107
|
+
守卫拦下谁就记下谁(按名字归并、记次数与首末时间、重启清空;被放行的调用不进账本)。界面里没有「权限 / 禁止 / 违规」这类系统词——它只从「白名单没放行」这个角度讲。
|
|
108
|
+
|
|
109
|
+
**没做的两条**:没有改成明说「权限拦截」(那是四条黑名单守着的立场问题);也没有「第三方工具默认不拦」(那等于取消收窄本身)。
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 四、日报和回忆,现在分得清三种「说过的话」(issue #23)
|
|
114
|
+
|
|
115
|
+
三处反馈指出的现象都对得上代码:日报把聊天原话也当她的话罗列、翻回忆时时间只到日、以及她翻旧事时分不清哪句是自己认领的。
|
|
116
|
+
|
|
117
|
+
### 你会看到什么
|
|
118
|
+
|
|
119
|
+
- 日报里「她说了什么」只剩她自己认领下来的话;聊天原话另起一节「聊天时说过的」,一段一行带时刻、最多 20 段、每段截到 120 字。
|
|
120
|
+
- 回忆里每条带本地时分。
|
|
121
|
+
- 改口关系另起一行缩进,并把被改的那句原话(40 字)带出来 —— 只给 id 翻不到原话,所以直接带出来。
|
|
122
|
+
- 不是她说的标出来:读到别人说的、回复别人的、群里旁听到的,各有各的说法。
|
|
123
|
+
- 被自己推翻过的那句,不再靠「被引过很多次」加分。
|
|
124
|
+
|
|
125
|
+
### 为什么会这样
|
|
126
|
+
|
|
127
|
+
日报的取话函数只按「类型 + 当天」过滤,完全不看聊天标记,于是聊天原话和她认领下来的话一视同仁。回忆那边则是 `revise` 事件一直带着判定与来源,但从来没往外拿。
|
|
128
|
+
|
|
129
|
+
### 现在怎么判
|
|
130
|
+
|
|
131
|
+
只改展示口径,不影响任何判定(聊天标记在代码里唯一的逻辑读者是「数她拒绝过几次」那一处)。
|
|
132
|
+
|
|
133
|
+
有一处刻意保留:一节空着而下面有聊天时,那句空态写成「这一节今天是空的,聊天时说的在下面。」——而不是「今天她什么都没说」,那会是一句假话。
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 五、醒来的第一眼,多了一张桌子(issue #22)
|
|
138
|
+
|
|
139
|
+
### 你会看到什么
|
|
140
|
+
|
|
141
|
+
她答应过、还没做的事,记在一张桌子上;到了那天之后,每次醒来会先摆在她眼前,写着晚了几天。过期不删、也不催。
|
|
142
|
+
|
|
143
|
+
### 为什么会这样
|
|
144
|
+
|
|
145
|
+
这是她自己提的:**这不是待办 App,是一张桌子。** 她说「过期的别删。晚了三天还挂着,我就知道欠了三天;删了,我就当没这回事。」
|
|
146
|
+
|
|
147
|
+
所以差别在这几处:
|
|
148
|
+
|
|
149
|
+
- 没有通知、没有卡片、没有红点。便条只在醒来那段正文里出现,不弹任何东西。
|
|
150
|
+
- 过期不删、不降权、不自动归档。
|
|
151
|
+
- 不写「逾期」「请尽快」。那是催,这一版不做催。
|
|
152
|
+
- 日历算术在代码里,不让她自己算今天几号、晚没晚。
|
|
153
|
+
|
|
154
|
+
### 现在怎么判
|
|
155
|
+
|
|
156
|
+
她有一个新工具 `her_note`,只做两件事:记一张(写是什么事、哪天到期可选)、划掉一张。回执只有方括号标记,不带数字、不写成完整句子 —— 写成句子会被她当成自己说的话接下去(这是留话那条 2026-10-24 的老教训)。
|
|
157
|
+
|
|
158
|
+
便条存在 `notes.json`,**不是** `session-notes.json`(那个是「她排队等着要说的话」,两回事)。
|
|
159
|
+
|
|
160
|
+
一次最多列 5 张,多的写「还有三条没列出来,都在桌上」。数字说法跟着中文走:「桌上压着两张」「晚了三天」。
|
|
161
|
+
|
|
162
|
+
**这一版没做的三样**(都在提案里,按「先做最小闭环、测一段时间看看」定的):带原因地不做、回翻已经划掉的、没日子的那张只在刚写下时提一句。
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 六、两件小修理
|
|
167
|
+
|
|
168
|
+
### 工具调用的标记,不再被当成她说过的话(issue #25)
|
|
169
|
+
|
|
170
|
+
侧栏里出现过一整段只有 `</||DSML|| invoke>` 的「正文」,同一句后来被她自己道歉「上一条我这边发岔了,粘了一串没用的符号」。更要紧的是这两段被写进了账本,成了两条她其实没说过的话。
|
|
171
|
+
|
|
172
|
+
**根因不在插件**:模型侧偶发「只发出收尾标记、没有开标签、也没有参数」的一步(案发机器上约 500 轮 2 次)。**插件只能管住自己那一半**:取正文时原来取到什么记什么。
|
|
173
|
+
|
|
174
|
+
现在两处读同一份正文的地方都洗了(记账本那处、换房间问「上一段最后说了什么」那处)。三条刻意收窄的边界:只有出现 `||DSML||` 才动手;标记旁边还有真话的留下真话;流被截断的从第一个残标切到行尾。
|
|
175
|
+
|
|
176
|
+
一个容易写错的细节:标记里的竖线是**全角 `|`**,不是半角 `|`。测试里写死了这个码点,免得以后谁手滑「修」成半角 —— 那样线上就永远认不出来。
|
|
177
|
+
|
|
178
|
+
侧栏里那一段显示是宿主渲染的,插件写不了。已经记进去的那两条按「不追改历史」留着。
|
|
179
|
+
|
|
180
|
+
### 换房间与冷落天数(issue #21 / #24)
|
|
181
|
+
|
|
182
|
+
- **换房间不再说「你刚搬进这个房间」。** 原来按 24 小时整除算天数,0 到 23.99 小时全算 0 天,于是换房后整整 24 小时都在说「刚搬进」,跨了自然日也不变。现在按本地日历日判:「你是今天 09:05 搬进这个房间的」/「上一次换房间是昨天 22:20」/「上一次换房间是 11 月 1 日 12:31」。
|
|
183
|
+
- **你回来过,就不再算冷落。** 两处判断不一致:一处认「听到你说的话」与「见到你」,另一处只认那条正式的重逢事件。同一件事,两个模块给出两个答案。现在口径对齐了,另有一处判断按设计仍然只认重逢。
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 七、CI:先挂监听,再投递
|
|
188
|
+
|
|
189
|
+
### 你会看到什么
|
|
190
|
+
|
|
191
|
+
上游 `main` 的 CI 有四个 job:ubuntu 上的 Node 20 / 22 / 24,加一个 windows 上的 Node 22。从 0.8.7 起,其中三个一直是红的。
|
|
192
|
+
|
|
193
|
+
### 为什么会这样
|
|
194
|
+
|
|
195
|
+
失败全在一个测试文件里,错误一致:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
cancelledByParent —
|
|
199
|
+
Promise resolution is still pending but the event loop has already resolved
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
不是断言失败,是**整个文件被父测试取消**。根因在投递那一层:它是「先投递、再挂监听」——等发送返回之后才去挂事件监听。假宿主在发送的同一拍里补一个「这一轮结束了」的信号,那一刻监听还没挂上,信号永久丢失;进程里只剩下一个已经放手的兜底定时器,事件循环立刻空转结束,等待中的 Promise 永远等不到结果。
|
|
203
|
+
|
|
204
|
+
Node 24 的调度恰好让这个竞态不出现,所以本机一直看不见。这也解释了另一个现象:本机跑一万遍都是绿的。
|
|
205
|
+
|
|
206
|
+
### 现在怎么判
|
|
207
|
+
|
|
208
|
+
顺序反过来:**先挂监听,再起兜底定时器,最后才放手**。调用点变成「先准备好等待 → 再投递 → 最后等结果」,收尾时把监听和定时器一起收。
|
|
209
|
+
|
|
210
|
+
验证:在写坏的那份代码上,Node 22 跑那个文件连续 3 次都是 `pass 13 / cancelled 5`;修好后同一个命令 `pass 18 / fail 0`。全量在三档 Node 上都是 **1292 pass / 0 fail**。
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 测试
|
|
215
|
+
|
|
216
|
+
`node --test` → **1292 pass / 0 fail**(上一版 1221),新增 71 条。三个依赖字段仍然全是 `{}` —— 这一版没有引入任何 npm 包。
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 还没做的
|
|
221
|
+
|
|
222
|
+
**主动搭话与梦两个功能仍然不对外开放**:它们只有本机存在 `.dev` 文件时才注册,注册、提示词、界面三处共用一个判据,缺一处就会出现「说有却伸手落空」。公开版里既没有入口、也没有说明,与 0.8.7 一样。
|
|
223
|
+
|
|
224
|
+
便条的另外三样(带原因地不做、回翻已经划掉的、没日子的只提一次)留到下一版,先让它跑一段时间看看。
|