dsh-deepseek-web-login 0.6.3 → 0.6.5

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,60 @@
2
2
 
3
3
  本项目遵循大致语义化版本;日期为本地时间。
4
4
 
5
+ ## 0.6.5 — 2026-09-27
6
+
7
+ > 修复 0.6.3 的重试策略**形状**错误:它会让「按本地退避重试」算出 `NaN`,把整轮打成 UNKNOWN 错误。
8
+
9
+ ### 修复
10
+
11
+ - **`providerRetryPolicy` 的返回值改成扁平字段**(0.6.3 包了一层 `backoff`)。dsh-llm 的取法是
12
+ `adapter.providerRetryPolicy(p) ?? resolveRetryPolicy(…)` —— 我们一返回对象,右侧那个"会把 `backoff`
13
+ 展开成扁平字段"的规范化就**不会执行**,而运行期的消费方读的正是扁平字段
14
+ (`policy.initialDelayMs / maxDelayMs / jitterRatio`)。实测 DSH 会话日志里 `llm/retry` 事件的
15
+
16
+ ```
17
+ "policyKey":"[\"normal\",5,[\"EMPTY_RESPONSE\",\"RATE_LIMIT\",…],null,null,null]"
18
+ ```
19
+
20
+ 后三项是 `null` = `undefined` —— 两个后果,第二个是致命的:
21
+
22
+ 1. `providerRetryAfterMs > maxDelayMs` 变成 `X > undefined` = **恒 false** ⇒「要等太久就放弃」失效
23
+ (连封禁一天的解除时间也会被照单等下去);
24
+ 2. 失败**不带** provider 延迟时(例如 `EMPTY_RESPONSE`)走本地退避 `initialDelayMs * 2**n`,
25
+ `undefined` ⇒ **`NaN`** ⇒ DSH 写会话事件时拒收非有限数 ⇒ 整轮以
26
+ `UNKNOWN: session event "llm/retry" carries non-JSON-serializable data` 结束。
27
+
28
+ 实测 2026-09-27 17:33:51:一轮里第一次重试(限流,带 2s)正常成功;第二次重试
29
+ (`EMPTY_RESPONSE`,无 provider 延迟)触发该错误 ⇒ 现象就是"任务突然自己停了"。
30
+
31
+ ### 测试
32
+
33
+ - `check-llm-retry.mjs` 补四条**意图级**断言:策略扁平键齐全且**无 `backoff` 嵌套**、
34
+ **回放 DSH 的 `retryPolicyKey` 不许出现 `null`**、**回放 `localDelay` 必须算出有限正数**、
35
+ 超长解除时间仍须被判「等太久」而放弃。
36
+ - 反向验证 3 步全部如期红:改回嵌套 → 6 条红;`maxDelayMs` 调小到 10s → 2 条;抽掉 `initialDelayMs` → 4 条。
37
+
38
+ ## 0.6.4 — 2026-09-27
39
+
40
+ > 文档版本:把「封号归因」那一节带到 npm 包页面上(纯文档,**无代码改动**)。
41
+
42
+ ### 文档
43
+
44
+ - README(中/英)新增 **「关于『装了这个插件之后封号了』」**,放在「免责声明」之前:
45
+ 把上下文里各部分的量级摆出来,再给自查顺序。
46
+
47
+ | 来源 | 量级 | 由谁决定 |
48
+ |---|---|---|
49
+ | 本插件协议指令 | **3,045 字符 ≈ 950 token** | 本插件(多个版本未变) |
50
+ | 工具目录 | 0.6.2 起长尾压成一行,同口径 **-50%** | 你装了多少工具插件 |
51
+ | `~/.dsh/prompt-inject.md` + `AGENTS.md` | **49,185 字节 ≈ 1.2 万 token(协议的 16 倍)** | 你的全局注入 |
52
+
53
+ 另附维护者实测:连续 5 天无封禁;单日 178 次请求中 6 次限流、全部在退避后自动恢复。
54
+
55
+ ### 说明
56
+
57
+ - 仅为让 npm 包页面的 README 与仓库同步;代码与产物逻辑与 0.6.3 一致。
58
+
5
59
  ## 0.6.3 — 2026-09-27
6
60
 
7
61
  > 限流之后**任务自己接下去**,不用再手点「继续」。
package/README.en.md CHANGED
@@ -472,6 +472,48 @@ but only when the array itself is closed (a truncated stream must never be repai
472
472
  and an unparsable protocol block is never emitted as answer text again: with no other text in the step it
473
473
  reports a retryable `EMPTY_RESPONSE`, otherwise it appends a one-line notice and logs the raw block.
474
474
 
475
+ ## On "your plugin got my account banned"
476
+
477
+ This attribution shows up from time to time. Before blaming the plugin, compare the **sizes** of what actually
478
+ goes into your context.
479
+
480
+ **What this plugin contributes**
481
+
482
+ | Source | Size | Decided by |
483
+ |---|---|---|
484
+ | Tool protocol instructions (text written by this plugin) | **3,045 chars ≈ 950 tokens** | this plugin; unchanged for many versions |
485
+ | Tool catalog (rendered by this plugin; content comes from the tools DSH hands over) | long-tail tools collapsed to one line since 0.6.2 — measured **-50%** like-for-like | **how many tool plugins you installed** |
486
+ | Above + system header, in a 61-tool setup | ~64k chars ≈ 16k tokens | the bulk is the tool catalog, not the protocol |
487
+
488
+ So the part that can fairly be blamed on this plugin is **those 3,000 characters**.
489
+
490
+ **What actually inflates the context**
491
+
492
+ - **MCP servers** — each one puts its whole tool schema into the context, on **every turn**
493
+ - **Skills** — the more you install, the thicker the system prompt
494
+ - **Jailbreak / "armor-breaking" prompts** — routinely tens of thousands of tokens, and they stay in context
495
+ - **Global injection files** — `~/.dsh/prompt-inject.md` (41 KB on our machine) + `~/.dsh/AGENTS.md` (7 KB),
496
+ sent **in full, every turn** ≈ 49 KB ≈ 12k tokens, i.e. **16×** this plugin's protocol text
497
+
498
+ A bigger context means heavier requests per turn, which makes volume/density heuristics more likely to trip.
499
+ **That part has nothing to do with this plugin, and this plugin cannot control it.**
500
+
501
+ **Maintainer's own experience (reference, not a guarantee)**
502
+
503
+ Five consecutive days of daily use (including tool-call-heavy sessions) with **no ban**; of 178 requests in one
504
+ day, 6 hit the web endpoint's "too frequent" throttle and all recovered after backoff.
505
+ The distinction matters: **throttling is routine and recovers; a ban is a different thing.**
506
+
507
+ **If you suspect you got hit, check in this order**
508
+
509
+ 1. Count your MCP servers and skills — every line is visible in `~/.dsh/profiles/desktop/cordis.patch.yml`
510
+ 2. `wc -c ~/.dsh/prompt-inject.md ~/.dsh/AGENTS.md` — see how large those two files are
511
+ 3. Open the "Token usage" tab and look at per-turn input (our median is around 100k tokens; the tool catalog is a small slice)
512
+ 4. Disable the MCP servers / plugin lines you do not use (`- id: xxx` + `disabled: true`, takes effect after restart)
513
+
514
+ Everything this plugin can do about the risk (gating request density, serializing tool calls, session cleanup,
515
+ slimming the tool catalog) is documented in the sections above. **The rest has to come from your own context.**
516
+
475
517
  ## Disclaimer
476
518
 
477
519
  > ⚠️ **Unofficial.** Not affiliated with, endorsed by, or sponsored by DeepSeek. "DeepSeek" is a trademark of its owner.
package/README.md CHANGED
@@ -643,6 +643,46 @@ DSH Desktop 用的是 `desktop` profile,而注入器的 junction 默认建在
643
643
 
644
644
  同时感谢 DeepSeek Harness 生态与 [dsh-super-injector](https://github.com/yjh051108/dsh-super-injector)(运行时注入 / 侧挂开发链路)。
645
645
 
646
+ ## 关于「装了这个插件之后封号了」
647
+
648
+ 这个归因在群里出现过几次。先把**上下文的各部分量级**摆出来,再判断该怪谁。
649
+
650
+ **本插件这一侧到底占多少**
651
+
652
+ | 来源 | 量级 | 由谁决定 |
653
+ |---|---|---|
654
+ | 工具协议指令(本插件自己写的文本) | **3,045 字符 ≈ 950 token** | 本插件,多个版本未变 |
655
+ | 工具目录(本插件负责渲染,内容来自 DSH 下发的工具) | 0.6.2 起长尾工具压成一行,同口径实测 **-50%** | **你装了多少工具插件** |
656
+ | 以上 + system 头合计(61 个工具的环境) | 约 6.4 万字符 ≈ 1.6 万 token | 大头是工具目录,不是协议 |
657
+
658
+ **能记在本插件账上的,只有那 3,000 字符。**
659
+
660
+ **真正把上下文撑大的,通常是别的东西**
661
+
662
+ - **MCP 服务器**:每接一个,它的工具 schema 就整份进上下文,而且是**每一轮**都在
663
+ - **Skills**:装得越多,system prompt 越厚
664
+ - **越狱 / 破甲类提示词**:这类文本动辄上万 token,且会一直留在上下文里
665
+ - **全局注入文件**:`~/.dsh/prompt-inject.md`(本机实测 41 KB)+ `~/.dsh/AGENTS.md`(7 KB)
666
+ —— 这两个文件是**每轮整份**进 system prompt 的,合计约 49 KB ≈ 1.2 万 token,
667
+ 是本插件协议文本的 **16 倍**
668
+
669
+ 上下文越大 ⇒ 每轮请求越重 ⇒ 越容易撞上风控的体量与密度判定。**这部分与本插件无关,本插件也管不到。**
670
+
671
+ **维护者本机实测(供参考,不是担保)**
672
+
673
+ 连续 5 天日常使用(含工具调用密集的会话)**未被封禁**;单日台账 178 次请求里有 6 次「网页版限流」,
674
+ 全部在退避后自动恢复。区别很重要:**限流是常态、可自动恢复;封禁是另一回事。**
675
+
676
+ **怀疑自己中招,按这个顺序自查**
677
+
678
+ 1. 数一下装了哪些 MCP 服务器、哪些 skill —— `~/.dsh/profiles/desktop/cordis.patch.yml` 里逐行可见
679
+ 2. `wc -c ~/.dsh/prompt-inject.md ~/.dsh/AGENTS.md` —— 看这两个文件的体积
680
+ 3. 打开设置页的「Token 统计」,看单轮输入量级(本机中位数在 10 万 token 上下,工具目录只占其中一小块)
681
+ 4. 把用不到的 MCP / 插件行禁用掉(`- id: xxx` + `disabled: true`,重启生效),再观察
682
+
683
+ 本插件能做的(压密度、串行工具调用、会话清理、长尾工具瘦身)都写在上面「请求节流」等章节里;
684
+ **剩下的大头,只能从你自己的上下文里省。**
685
+
646
686
  ## 免责声明
647
687
 
648
688
  > ⚠️ **非官方项目**:与 DeepSeek 无任何关联,未获其授权、认可或赞助。"DeepSeek" 为其权利人商标。
package/lib/index.js CHANGED
@@ -1566,7 +1566,7 @@ const THROTTLE_JITTER_RATIO = .3;
1566
1566
  /**
1567
1567
  * 我们**可能**上报给调用方(`dsh-llm-retry`)的**最大**限流退避。
1568
1568
  *
1569
- * 🔴 它必须 ≤ 适配器声明的 `providerRetryPolicy().backoff.maxDelayMs`(见 `adapter.ts`):
1569
+ * 🔴 它必须 ≤ 适配器声明的 `providerRetryPolicy().maxDelayMs`(**扁平字段**,见 `adapter.ts`):
1570
1570
  * dsh-llm 的重试策略在 normal 模式下,一旦「提供方要的延迟 > maxDelayMs」就**直接放弃重试**
1571
1571
  * (`return next()`),整轮随即以 error 结束 —— 表现就是"任务停在限流上,要用户手点继续"。
1572
1572
  *
@@ -5784,13 +5784,41 @@ function allowsAutoContinue(purpose) {
5784
5784
  * 声明之后:提供方要的延迟 ≤ `maxDelayMs` 会被**照单等待并重试**(同一个打开的轮次里重跑
5785
5785
  * 失败步骤,历史保持一致),任务自己接下去 —— 重发时"自动换号"的检查点还会换上可用账号。
5786
5786
  *
5787
+ * ---
5788
+ *
5789
+ * 🔴🔴 **形状必须是「扁平」的,不能包一层 `backoff`**(0.6.3 就栽在这上面,0.6.5 修):
5790
+ *
5791
+ * dsh-llm 的取法是 `adapter.providerRetryPolicy(provider) ?? resolveRetryPolicy(undefined, …)`
5792
+ * —— 我们一返回对象,右边的 `resolveRetryPolicy` **就不会执行**,也就**没人把
5793
+ * `backoff: {…}` 展开成扁平字段**。而运行期的消费方(`dsh-llm-retry`)读的是扁平字段:
5794
+ *
5795
+ * ```js
5796
+ * retryPolicyKey(policy) → [policy.mode, policy.maxRetries, […], policy.initialDelayMs,
5797
+ * policy.maxDelayMs, policy.jitterRatio]
5798
+ * localDelay(config, …) → config.initialDelayMs / config.maxDelayMs / config.jitterRatio
5799
+ * ```
5800
+ *
5801
+ * 写成嵌套时的实况(DSH 会话日志里 `llm/retry` 事件的 `policyKey`):
5802
+ * `["normal",5,[…],null,null,null]` ← 后三项全是 null,即三个字段 **undefined**
5803
+ * ⇒ 两个后果,第二个是致命的:
5804
+ * ① `providerRetryAfterMs > maxDelayMs` 变成 `2000 > undefined` = false ⇒ **"要不要放弃"这条
5805
+ * 判断彻底失效**(长封禁的解除时间也会被照单等下去);
5806
+ * ② 失败**不带** provider 延迟时走 `localDelay()`,`initialDelayMs * 2**n` = `NaN`
5807
+ * ⇒ `delayMs: NaN` ⇒ DSH 写会话事件时校验拒收(`walkJsonValue` 拒绝非有限数)
5808
+ * ⇒ 整轮以 **`UNKNOWN: session event "llm/retry" carries non-JSON-serializable data`** 死掉。
5809
+ * 实测 2026-09-27 17:33:51(用户以为是"模型自己停了")。
5810
+ *
5811
+ * ⇒ 结论:**要么返回扁平策略(本实现),要么干脆返回 `undefined` 走 dsh-llm 自己的默认策略**
5812
+ * (`resolveRetryPolicy` 会替我们规范化)。半吊子的中间形态最危险。
5813
+ * `tests/check-llm-retry.mjs` 守这条:断言扁平键齐全、无 `backoff` 嵌套、且 `localDelay` 算得出有限值。
5814
+ *
5787
5815
  * 取值理由:
5788
5816
  * - `initialDelayMs` = `FAILOVER_RETRY_MS`:本地退避起点与"能换号"那条路一致;
5789
5817
  * - `maxDelayMs` = `MAX_THROTTLE_RETRY_MS`(117s):**必须 ≥ 我们可能上报的最大退避**,
5790
5818
  * 否则又回到"要等太久 ⇒ 放弃"。`tests/check-llm-retry.mjs` 守这个不变量;
5791
5819
  * - `maxRetries` = 5(与 dsh-llm 默认一致):**有界** —— 避免账号真被封时无限打请求。
5792
5820
  * 想更激进可以调大,或改 `mode: 'always'`(无尝试上限);那会在坏账号上一直重试,默认不开;
5793
- * - `retryableCodes` 与 dsh-llm 默认一致(含 `RATE_LIMIT`)—— 我们只抛这几个码。
5821
+ * - `retryableCodes` 与 dsh-llm 默认一致(含 `RATE_LIMIT`、`EMPTY_RESPONSE`)—— 我们只抛这几个码。
5794
5822
  */
5795
5823
  const RETRY_POLICY = Object.freeze({
5796
5824
  mode: "normal",
@@ -5802,11 +5830,9 @@ const RETRY_POLICY = Object.freeze({
5802
5830
  "TIMEOUT",
5803
5831
  "TRANSPORT"
5804
5832
  ]),
5805
- backoff: Object.freeze({
5806
- initialDelayMs: FAILOVER_RETRY_MS,
5807
- maxDelayMs: MAX_THROTTLE_RETRY_MS,
5808
- jitterRatio: .2
5809
- })
5833
+ initialDelayMs: FAILOVER_RETRY_MS,
5834
+ maxDelayMs: MAX_THROTTLE_RETRY_MS,
5835
+ jitterRatio: .2
5810
5836
  });
5811
5837
  /** 构造 deepseek-web 适配器(鸭子类型满足 LlmAdapter 契约,无需继承)。 */
5812
5838
  function createAdapter(deps) {
@@ -8994,7 +9020,7 @@ async function checkForUpdate(current, fetchImpl) {
8994
9020
  * 兜底常量与 package.json 的一致性由 `tests/check-smoke.mjs` 守着,不会漂。
8995
9021
  */
8996
9022
  /** 与 package.json 保持一致的兜底版本(由测试保证不会漂)。 */
8997
- const FALLBACK_VERSION = "0.6.3";
9023
+ const FALLBACK_VERSION = "0.6.5";
8998
9024
  let cached;
8999
9025
  /** 本插件版本(如 `0.1.26`)。 */
9000
9026
  function pluginVersion() {