dsh-deepseek-web-login 0.6.25 → 0.6.27
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 +88 -0
- package/lib/client.js +1 -1
- package/lib/client.js.map +1 -1
- package/lib/index.js +48 -14
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,94 @@
|
|
|
2
2
|
|
|
3
3
|
本项目大致遵循语义化版本;日期为本地时间。
|
|
4
4
|
|
|
5
|
+
## 0.6.27 — 2026-10-02
|
|
6
|
+
|
|
7
|
+
**修复:早先发过的图片被反复重挂在后面的每条消息上("我这轮没发图,网页端却又有图")。**
|
|
8
|
+
|
|
9
|
+
### 取证过程(这次没有猜)
|
|
10
|
+
|
|
11
|
+
1. 用户截图:那条 `哈哈哈哈` 消息上挂着两个图片框,但 DSH 里它**没有**图。
|
|
12
|
+
2. 先确认"图是不是真实存在":写工具解 DSH 会话日志(`dev/scan-session-images.mjs`)——
|
|
13
|
+
`session.v4.jsonl.zstd` 是 **zstd 多帧**(一个 70KB 文件 34 帧),而 `zstdDecompressSync()`
|
|
14
|
+
**只解第一帧**,只拿到 session header ⇒ "含 image 的行数"永远是 0(**假阴性**)。
|
|
15
|
+
逐帧解压后确认:会话里确实有 **2 条带图的用户消息**("还不错哎"、"这个能看见了吧")。
|
|
16
|
+
3. 既然图是真的,问题就变成"**为什么它出现在不含图的那一轮**"—— 于是回到代码。
|
|
17
|
+
|
|
18
|
+
### 根因
|
|
19
|
+
|
|
20
|
+
`sentRefIds`("服务端已见过哪些图"的账本)原本是**一个全局 Set + 一个 `sentRefIdsSession` 变量**:
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
if (sentRefIdsSession !== sessionId) { sentRefIds = new Set(); sentRefIdsSession = sessionId }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
两个窗口交错时它们会互相覆盖:
|
|
27
|
+
|
|
28
|
+
1. 窗口 A 的请求进来 → 记成 A
|
|
29
|
+
2. 窗口 B 的请求进来(A 还没返回)→ **清空**,记成 B(A 的账没了)
|
|
30
|
+
3. A 的下一轮进来 → 发现 `sentRefIdsSession` 不是自己 → **再清一次**
|
|
31
|
+
|
|
32
|
+
⇒ A 里**早就发出去过**的图,每轮都被当成"没发过"⇒ 反复重发 ⇒ 网页端把它们挂到了后面每条消息上。
|
|
33
|
+
|
|
34
|
+
### 修法
|
|
35
|
+
|
|
36
|
+
账本改成 **`Map<sessionId, Set<key>>`**:每个会话各记各的,并发的窗口互不影响。
|
|
37
|
+
会话退役(`retireSession`)与全量清理(`disposeSessionReuse`)时销掉对应条目,避免 Map 跟着开过的窗口一直涨。
|
|
38
|
+
|
|
39
|
+
### 验证
|
|
40
|
+
|
|
41
|
+
- 新增用例:**两个窗口交错**(A 首轮 → B 首轮 → A 续轮 → B 续轮),
|
|
42
|
+
断言各自的图都只发一次、续轮不再重发
|
|
43
|
+
- 变异反向验证:把账本改回"会话一变就清空" ⇒ 用例变红、`rc=1`
|
|
44
|
+
- 另修两条产物断言(它们引用的是已被替换的旧变量形态;意图不变)
|
|
45
|
+
- `tsc --noEmit`、`npm run test`(60/60)、`check-smoke`、`check-bundle` 全通过
|
|
46
|
+
|
|
47
|
+
### 新增诊断工具
|
|
48
|
+
|
|
49
|
+
- `dev/scan-session-images.mjs`:扫一个会话日志,报"历史里到底有没有图片"
|
|
50
|
+
- `dev/dump-image-messages.mjs`:把带图的消息连同文本一起打出来(判断图该挂在哪一轮)
|
|
51
|
+
- `dev/trace-session-images.mjs`:列出图片出现在日志的第几行
|
|
52
|
+
|
|
53
|
+
## 0.6.26 — 2026-10-01
|
|
54
|
+
|
|
55
|
+
**修复:新开一个窗口、只发一句话,网页端却显示「修改」+ `2 / 2`。**
|
|
56
|
+
|
|
57
|
+
### 根因:内部请求占用了对话的会话槽
|
|
58
|
+
|
|
59
|
+
时序(新窗口的第一句话):
|
|
60
|
+
|
|
61
|
+
1. DSH 先发一次 `session-title`(**内部请求,不带结构化 promptParts**)
|
|
62
|
+
2. 它与对话共用同一个 DSH sessionId ⇒ 此前共用同一个 slotKey
|
|
63
|
+
3. 于是 `session-title` 先建会话、发出一条**根消息**(`parent=null`,内容就是那段
|
|
64
|
+
`Create a concise title…`);用户的真实消息随后**复用**这个会话,但内部请求不传 parts、
|
|
65
|
+
没有链 ⇒ `decideFeed` 只能 `detach('no-chain')` ⇒ **又是一条根消息**
|
|
66
|
+
4. 服务端把这两条看成**同一条用户消息的两个版本** ⇒ 网页端渲染成 `1 / 2`、`2 / 2` + 「修改」,
|
|
67
|
+
而且标题 prompt 直接暴露在用户的对话里
|
|
68
|
+
|
|
69
|
+
### 修法
|
|
70
|
+
|
|
71
|
+
新增 `requestSlotKey(auth, params)`,**两个调用点(`openCompletion` / `streamWebCompletion`)
|
|
72
|
+
统一使用**(键必须同源,否则链会写在一个键、读另一个键):
|
|
73
|
+
|
|
74
|
+
| 请求 | 归属键 |
|
|
75
|
+
|---|---|
|
|
76
|
+
| 带 `promptParts`(真正的对话) | `账号\|DSH会话` |
|
|
77
|
+
| 不带 `promptParts`(`session-title` / 压缩等) | `internal\|账号` |
|
|
78
|
+
|
|
79
|
+
分开之后,对话槽里的第一条消息**永远**是这个窗口真正发出的那一条 —— 根消息只有一条。
|
|
80
|
+
|
|
81
|
+
### 顺带
|
|
82
|
+
|
|
83
|
+
「自动换号」的提示补上一句:**每换一次号,网页端就会多出一个新会话**(旧会话属于旧账号)。
|
|
84
|
+
用户两次被"同一个窗口又多一个会话"搞懵,都是因为不知道中间换过号。
|
|
85
|
+
|
|
86
|
+
### 验证
|
|
87
|
+
|
|
88
|
+
- 新增 2 条用例(端到端 + 会话分槽),并修正 4 处 fixture —— 真实 chat 请求必须带 `promptParts`,
|
|
89
|
+
否则会被当成内部请求(这本身就是修复要引入的区分)
|
|
90
|
+
- 变异反向验证:把 `requestSlotKey` 改回"共用槽" ⇒ 两条用例都变红、`rc=1`
|
|
91
|
+
- `tsc --noEmit`、`npm run test`(60/60)、`check-smoke`、`check-bundle` 全通过
|
|
92
|
+
|
|
5
93
|
## 0.6.25 — 2026-10-01
|
|
6
94
|
|
|
7
95
|
**新增:投喂决策留痕(`feed-decisions.jsonl`)—— 把"这一轮为什么没走增量"变成可查的事实。**
|
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;
|