dsh-deepseek-web-login 0.6.24 → 0.6.26

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
@@ -2,6 +2,78 @@
2
2
 
3
3
  本项目大致遵循语义化版本;日期为本地时间。
4
4
 
5
+ ## 0.6.26 — 2026-10-01
6
+
7
+ **修复:新开一个窗口、只发一句话,网页端却显示「修改」+ `2 / 2`。**
8
+
9
+ ### 根因:内部请求占用了对话的会话槽
10
+
11
+ 时序(新窗口的第一句话):
12
+
13
+ 1. DSH 先发一次 `session-title`(**内部请求,不带结构化 promptParts**)
14
+ 2. 它与对话共用同一个 DSH sessionId ⇒ 此前共用同一个 slotKey
15
+ 3. 于是 `session-title` 先建会话、发出一条**根消息**(`parent=null`,内容就是那段
16
+ `Create a concise title…`);用户的真实消息随后**复用**这个会话,但内部请求不传 parts、
17
+ 没有链 ⇒ `decideFeed` 只能 `detach('no-chain')` ⇒ **又是一条根消息**
18
+ 4. 服务端把这两条看成**同一条用户消息的两个版本** ⇒ 网页端渲染成 `1 / 2`、`2 / 2` + 「修改」,
19
+ 而且标题 prompt 直接暴露在用户的对话里
20
+
21
+ ### 修法
22
+
23
+ 新增 `requestSlotKey(auth, params)`,**两个调用点(`openCompletion` / `streamWebCompletion`)
24
+ 统一使用**(键必须同源,否则链会写在一个键、读另一个键):
25
+
26
+ | 请求 | 归属键 |
27
+ |---|---|
28
+ | 带 `promptParts`(真正的对话) | `账号\|DSH会话` |
29
+ | 不带 `promptParts`(`session-title` / 压缩等) | `internal\|账号` |
30
+
31
+ 分开之后,对话槽里的第一条消息**永远**是这个窗口真正发出的那一条 —— 根消息只有一条。
32
+
33
+ ### 顺带
34
+
35
+ 「自动换号」的提示补上一句:**每换一次号,网页端就会多出一个新会话**(旧会话属于旧账号)。
36
+ 用户两次被"同一个窗口又多一个会话"搞懵,都是因为不知道中间换过号。
37
+
38
+ ### 验证
39
+
40
+ - 新增 2 条用例(端到端 + 会话分槽),并修正 4 处 fixture —— 真实 chat 请求必须带 `promptParts`,
41
+ 否则会被当成内部请求(这本身就是修复要引入的区分)
42
+ - 变异反向验证:把 `requestSlotKey` 改回"共用槽" ⇒ 两条用例都变红、`rc=1`
43
+ - `tsc --noEmit`、`npm run test`(60/60)、`check-smoke`、`check-bundle` 全通过
44
+
45
+ ## 0.6.25 — 2026-10-01
46
+
47
+ **新增:投喂决策留痕(`feed-decisions.jsonl`)—— 把"这一轮为什么没走增量"变成可查的事实。**
48
+
49
+ ### 为什么要有
50
+
51
+ 判据只在**原因变化时**打 `链式投喂退回全量重发(原因=X)`,而**宿主日志不落盘**。
52
+ 2026-10-01 两次排查("每轮都发全量"、"同一个窗口又多一个会话")都被这一点卡住,
53
+ 最后只能往**已安装的产物**里插桩 —— 那是能查,但每次都要占一个发版周期。
54
+
55
+ ### 做了什么
56
+
57
+ 每一轮决策追加一行到 `~/.dsh/web-login/feed-decisions.jsonl`(环形保留 300 条,超 256 KB 自动裁剪):
58
+
59
+ | 字段 | 含义 |
60
+ |---|---|
61
+ | `reason` | `chained`(真的只发增量)或退回全量的具体原因 |
62
+ | `reused` | 这一轮的网页端会话是不是复用来的 |
63
+ | `session` / `account` | 会话与账号键前 8 位 —— **切号会让 `account` 变**,一眼看出"多出来的会话"是不是换号造成的 |
64
+ | `chainLen` / `entriesLen` | 链里已发的条目数 / 本轮条目数 |
65
+ | `tailSame` | **链尾是否仍在本轮的同一位置** —— `canExtendChain` 第二条判据的直接答案,`false` 就是"DSH 改写了历史"的实锤 |
66
+
67
+ ⚠️ 只记**结构性事实**(长度 / 原因 / 是否复用),不记任何内容。
68
+
69
+ ### 验证
70
+
71
+ - 新增一条端到端用例:跑三轮(起链 → 严格追加 → 历史被改写),
72
+ 断言落盘三条、`reason` 依次为 `new-session` / `chained` / `not-appended`,
73
+ 且 `tailSame` 为 `null` / `true` / `false`
74
+ - 变异反向验证:把 `tailSame` 写死成 `null` ⇒ 用例变红、`rc=1`
75
+ - `tsc --noEmit`、`npm run test`(60/60)、`check-smoke`、`check-bundle` 全通过
76
+
5
77
  ## 0.6.24 — 2026-10-01
6
78
 
7
79
  **修复:浏览器代理传输层不支持 `FormData` ⇒ 图片全部上传失败。**
package/lib/client.js CHANGED
@@ -1695,7 +1695,7 @@ background:var(--bg2);white-space:pre-wrap;font-size:12px}
1695
1695
  const paintAutoSwitch = () => {
1696
1696
  const minutes = currentSwitchMinutes();
1697
1697
  swValue.textContent = minutes === 0 ? "关闭" : `${minutes} 分钟`;
1698
- const base = minutes === 0 ? "关闭时不会自动换号 —— 当前账号一直用到你手动切换为止。" : `每 ${minutes} 分钟换到账号库里的下一个可用账号(失效或正在受限的会跳过;可用的不足两个就不换)。换号会让投喂链断掉:下一轮要全量重发,历史图也要重新上传 —— 间隔越短,这个代价出现得越频繁。`;
1698
+ const base = minutes === 0 ? "关闭时不会自动换号 —— 当前账号一直用到你手动切换为止。" : `每 ${minutes} 分钟换到账号库里的下一个可用账号(失效或正在受限的会跳过;可用的不足两个就不换)。换号会让投喂链断掉:下一轮要全量重发,历史图也要重新上传 —— 间隔越短,这个代价出现得越频繁。另外要注意:每换一次号,网页端就会多出一个新会话(旧会话是在旧账号名下的,新账号看不到它),当前窗口也会从头开始 —— 在意网页端会话数量的,把它设成「关闭」。`;
1699
1699
  const last = lastAutoSwitchInfo;
1700
1700
  const why = last?.reason === "recently-throttled" ? ",原账号刚被限流" : last?.reason === "current-unusable" ? ",原账号不可用" : "";
1701
1701
  swHint.textContent = last ? `${base}上次自动换号:${shortTime(last.at)}(${last.from} → ${last.to}${why})。` : base;