dsh-connect-doubao 0.4.0 → 0.4.1

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
@@ -4,6 +4,49 @@
4
4
 
5
5
  ---
6
6
 
7
+ ## 0.4.1 — 2026-11-05
8
+
9
+ > 「工作任务」模型接入批次:把豆包客户端里那三个**只能在工作任务模式跑**的条目
10
+ > (2.1 Lite `seed-lite-7b`、2.1 Pro `5`、自动 `9`)真正接到 DSH 里,走的正是会员/
11
+ > 活动付费额度。上一版把它们整个藏起来,理由是「外部客户端注册不进 CUA 运行时」——
12
+ > 那个理由对了一半:运行时确实接不进去,但它不是必需的,`tool_local` 的三条 HTTP
13
+ > 接口就够把一轮任务回合走完。新增 4 例回归测试(另改写 2 例旧的「必须隐藏」断言),
14
+ > 全仓 189 例。
15
+
16
+ ### 新增
17
+
18
+ - **豆包 2.1 Lite / 2.1 Pro / 自动 进入模型选择器** —— 选中任一条,请求自动切到
19
+ 「工作任务」模式(`mode_id: "3"`、`agent_mode: 1`、`need_deep_think: 10001`、
20
+ 新会话 `conversation_mode: 2`、`model_extra_params.input_token_limit: "256k"`,
21
+ `seed-lite-7b` 另带 `reasoning_effort`),`general_task_param` 带 `task_input_json`。
22
+ 实机跑通:Lite 与 Pro 各一次纯文本回合,约 5.8 s,`server_model_tag` 为 `38`。
23
+ - **`tool_local` 应答器** —— 任务模式下服务端会按 `${unique_key}:${device_id}:${aid}:message:${seq}`
24
+ 的顺序下发本地工作区指令;插件并发轮询 `msg/ack`,遇到 `command: 6003`(`cmd_call`,
25
+ 如 `local.workspace.apply`)就回 `upload_command_result` + `msg/complete`,遇到
26
+ `command: 6004`(`agent_finished`)收尾。**这是解开 `710010702` 的全部原因。**
27
+ - **`CHUNK_DELTA` 增量帧** —— 任务模式的正文增量以无 `block_type` 包裹的
28
+ `{ text, id }` 形式下发,按 `id` 分流到 `reasoning_content` / 正文。
29
+
30
+ ### 修复
31
+
32
+ - **选 `seed-lite-7b` / `5` 会被静默换成「豆包 快速」** —— `findDoubaoModel` 只在内置
33
+ 清单里查,查不到就回落到 `'0'`。现在三条任务模型都在清单里,不再回落。
34
+ - **`msg/ack` 批量查询必失败** —— `{ msg_ids: [0..6] }` 会整体报
35
+ `{"code":710010702}`,因为批里只要含一个服务端还没生成的 `seq`,整批就废。
36
+ 现在严格单条轮询,逐 `seq` 递进。
37
+ - **自动发现把工作任务模型全部隐藏** —— 上一版按名称前缀一律排除。现在改为只排除
38
+ `DOUBAO_UNAVAILABLE_MODELS`(仅剩「即将下线」的 `4`),并按内置清单补齐,避免本机
39
+ IndexedDB 只缓存了对话子集时任务模型消失。
40
+
41
+ ### 已知边界
42
+
43
+ - 纯文本、不触发本地工具的小回合,服务端会在 `ext` 里标 `general_agent_task_mode: "simple"`
44
+ 与 `mode_downgrade_reason: "success"`,此时**不扣**活动赠送额度(实测调用前后
45
+ `usedAmount` 恒为 782)。要消耗付费额度,得让它真的执行多步工具/文档任务。
46
+ - `msg/ack` 的 `seq` 没有可查询的上界,应答器在流结束后最多再补 8 次轮询即退出。
47
+
48
+ ---
49
+
7
50
  ## 0.4.0 — 2026-11-05
8
51
 
9
52
  > 稳定性与额度准确性批次:让「回答」在中断时说实话,让「停止」真的停下上游,让凭据不再经过
package/README.en.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # DSH Connect Doubao
2
2
 
3
3
  > Connect the sign-in **already held by the local Doubao desktop app** to DeepSeek
4
- > Harness as a Seed-family model provider, plus a **read-only** quota view
5
- > (5-hour window / weekly window, with reset countdowns).
4
+ > Harness: **chat models** and **「工作任务」 (task-mode) models** (2.1 Lite / 2.1 Pro /
5
+ > Auto, which spend the paid membership quota), **Seedream image generation**, plus a
6
+ > **read-only** quota panel (5-hour window / weekly window / activity grant, each with
7
+ > a reset countdown). Windows x64 only — credential decryption relies on Windows DPAPI.
6
8
 
7
9
  [![npm version](https://img.shields.io/npm/v/dsh-connect-doubao.svg)](https://www.npmjs.com/package/dsh-connect-doubao)
8
10
  [![license](https://img.shields.io/npm/l/dsh-connect-doubao.svg)](./LICENSE)
@@ -33,12 +35,14 @@ Without a signed-in Doubao app the plugin fails loudly rather than silently.
33
35
 
34
36
  ## Features
35
37
 
36
- - **Seed-family models** — the 6 entries the Doubao desktop app publishes (table below),
37
- exposed as a DSH model provider.
38
+ - **Two groups of Doubao models** — two 对话-mode entries (快速 / 2.1 Turbo·专家) and
39
+ three 「工作任务」-mode entries (2.1 Lite `seed-lite-7b`, 2.1 Pro `5`, Auto `9`).
40
+ The task models spend the **paid membership quota**; selecting one switches the
41
+ request into the full task-mode shape automatically.
38
42
  - **Three reasoning-effort levels** — low / mid / high, mapped to the measured upstream
39
43
  `reasoning_effort` values **3 / 4 / 5**.
40
- - **Read-only quota panel** — 5-hour and weekly windows as two bars, each with a
41
- **reset countdown**; manual refresh plus a 5-minute automatic poll.
44
+ - **Read-only quota panel** — 5-hour, weekly and activity-grant windows as bars, each
45
+ with a **reset countdown**; manual refresh plus a 5-minute automatic poll.
42
46
  - **Model checklist** — ticked entries appear in DSH's model picker and are sent as
43
47
  `model_item_key`.
44
48
  - **Account status line** — distinguishes signed in / not signed in / sign-in could
@@ -72,7 +76,10 @@ works everywhere):
72
76
  - ✅ **All six catalog entries render** — 豆包 快速 / 2.1 Lite (`0921 新版`) /
73
77
  2.1 Turbo (`专家`) / 2.1 Pro / 2.1 Turbo (`即将下线`) / 自动, with badges matching
74
78
  the desktop app and a count of `已启用 6 ↑`.
75
- - ✅ **A 「豆包(Doubao)」 group appears in DSH's model picker** with the six entries
79
+ > This screenshot is from **0.4.0**: back then the task-mode models were only
80
+ > rendered, not wired up, and the badges used the client's own names. Since 0.4.1
81
+ > they carry the `工作任务` badge and actually answer; the screenshot is not re-shot.
82
+ - ✅ **A 「豆包(Doubao)」 group appears in DSH's model picker** with the entries
76
83
  listed individually. This is the **end-to-end evidence that provider registration
77
84
  and the catalog both work**.
78
85
  - ✅ **The checklist is interactive** — one checkbox per row plus `刷新` / `全选` /
@@ -84,7 +91,7 @@ works everywhere):
84
91
 
85
92
  - `doctor` passes all five checks: 29 cookies decrypted with 9/9 required session
86
93
  cookies present, the account accepted upstream, the shim bound to a loopback port with
87
- a wrong bearer refused (401), and both quota windows readable.
94
+ a wrong bearer refused (401), all three quota windows readable, and 6 models offered.
88
95
  - Reasoning effort low=3 / mid=4 / high=5 was read by **diffing the configuration the
89
96
  client re-serialises when the level is switched**, then independently confirmed
90
97
  against `reasoning_effort_config` in the client's own config.
@@ -104,14 +111,18 @@ the document the server hands the app) — not reconstructed by guesswork. The
104
111
  `model_item_key` **is** the DSH model id, on purpose: one string instead of a
105
112
  translation layer that could only be wrong.
106
113
 
107
- | id | name | badge | reasoning |
108
- | --- | --- | --- | --- |
109
- | `0` | 豆包 快速 | — | ✅ |
110
- | `seed-lite-7b` | 豆包 2.1 Lite | `0921 新版` | ✅ |
111
- | `3` | 豆包 2.1 Turbo | `专家` | ✅ |
112
- | `5` | 豆包 2.1 Pro | — | ✅ |
113
- | `4` | 豆包 2.1 Turbo | `即将下线` | ✅ |
114
- | `9` | 自动 | — | ✅ |
114
+ | id | name | badge | mode | reasoning |
115
+ | --- | --- | --- | --- | --- |
116
+ | `0` | 豆包 快速 | — | 对话 | ✅ |
117
+ | `3` | 豆包 2.1 Turbo | `专家` | 对话 | ✅ |
118
+ | `seed-lite-7b` | 豆包 2.1 Lite | `工作任务` | 工作任务 | ✅ |
119
+ | `5` | 豆包 2.1 Pro | `工作任务` | 工作任务 | ✅ |
120
+ | `9` | 自动 | `工作任务` | 工作任务 | ✅ |
121
+
122
+ The three `工作任务` rows switch the request into task mode (`mode_id: "3"`,
123
+ `agent_mode: 1`, `need_deep_think: 10001`) and are answered by the plugin's own
124
+ `tool_local` responder — see 「工作任务」 mode below. They are the entries that spend
125
+ the **paid membership quota**.
115
126
 
116
127
  **Reasoning effort**: low = `3`, mid = `4`, high = `5`. These values were read by
117
128
  **diffing the configuration the client re-serialises when the level is switched**,
@@ -119,7 +130,8 @@ then independently confirmed against `reasoning_effort_config` in the client's o
119
130
  config. They are observed, not inferred.
120
131
 
121
132
  > 「即将下线」 is not a property of the Turbo model: the app publishes **two** Turbo
122
- > entries and only the retiring one carries that badge.
133
+ > entries and only the retiring one carries that badge. The retiring one (`4`) is
134
+ > the only entry withheld from the picker.
123
135
 
124
136
  ---
125
137
 
@@ -139,7 +151,13 @@ DSH (pi-ai provider)
139
151
  (`bot_id` + `reasoning_effort`).
140
152
  3. **Streaming** — upstream's three-line frames are parsed into pi-ai's event stream;
141
153
  tool calls, compaction and permissions stay Harness-owned.
142
- 4. **Read-only routes** — `/plugins/dsh-connect-doubao/quota` and `/catalog` serve quota
154
+ 4. **Task mode** — for the `工作任务` entries, `doubao-upstream` additionally runs a
155
+ `tool_local` responder: it polls the server's `msg/ack` **one `msg_id` at a time**
156
+ (a batch containing a not-yet-generated seq fails the whole batch with
157
+ `710010702`), answers `cmd_call` via `upload_command_result` + `msg/complete`, and
158
+ stops on `agent_finished`. Incremental `CHUNK_DELTA` frames are split into
159
+ thinking/answer by their block id.
160
+ 5. **Read-only routes** — `/plugins/dsh-connect-doubao/quota` and `/catalog` serve quota
143
161
  and catalog from an **explicit allow-list** (no upstream field is passed through),
144
162
  and refuse non-loopback origins.
145
163
 
@@ -257,22 +275,34 @@ npm run check # all of the above
257
275
  1. **It uses a non-public interface.** Doubao publishes no third-party API; this
258
276
  plugin reverses the behaviour of its own client. Any upstream change can break it,
259
277
  without notice.
260
- 2. **`'4'` (豆包 2.1 Turbo, "即将下线") is unverified.** The other 5 entries were
261
- exercised in real conversations.
262
- 3. **There is no response-side field that proves "the server really used that model".**
278
+ 2. **`'4'` (豆包 2.1 Turbo, "即将下线") is unverified** — and withheld from the
279
+ picker. The other 5 entries were exercised in real conversations, including both
280
+ task-mode models.
281
+ 3. **Task mode works, but whether a turn is billed depends on what it did.** In task
282
+ mode the server hands each turn to `Agent-GeneralTask`, and the plugin runs a
283
+ `tool_local` responder alongside (`msg/ack` → `upload_command_result` →
284
+ `msg/complete`) so the local workspace commands complete instead of failing with
285
+ `710010702`. **For a plain-text turn that triggers no local tool, the server marks
286
+ the round `general_agent_task_mode: "simple"` with `mode_downgrade_reason: "success"`,
287
+ and the activity-grant `usedAmount` does not move** (measured: unchanged at 782
288
+ across a call — i.e. downgraded, not billed). To actually spend the paid quota, the
289
+ turn has to do multi-step tool/document work. This is a measurement, not a promise.
290
+ 4. **There is no response-side field that proves "the server really used that model".**
263
291
  The upstream SSE carries an opaque marker in `message.ext` (observed as `38`); it is
264
292
  **not** a version number or a tier, and ByteDance has never published its meaning.
265
293
  What this plugin can establish is that it sends `model_item_key` with the request
266
294
  (confirmed by packet capture). How the server interprets that key is the server's
267
- own business.
268
- 4. **Windows only** — credential decryption relies on DPAPI. `User Data` does not
295
+ own business — sending an invented key still returns HTTP 200, so the field is not
296
+ validated. What 0.4.1 offers is **the whole task-mode request shape for a given
297
+ key** (verified to answer), not a guarantee that Pro rather than Lite did the work.
298
+ 5. **Windows only** — credential decryption relies on DPAPI. `User Data` does not
269
299
  have to live on `C:` (see the five layers above); a browser-only host has no
270
300
  native folder dialog, so the picker button reports that instead of failing
271
301
  silently — use `DSH_DOUBAO_PROFILE` there.
272
- 5. **Risk-control sliders cannot be automated.** When one fires, send one message in
302
+ 6. **Risk-control sliders cannot be automated.** When one fires, send one message in
273
303
  the Doubao desktop app to clear it.
274
- 6. **No image input.** The provider sends text blocks only.
275
- 7. **Single account.** No account pool, no region switching, no check-in.
304
+ 7. **No image input.** The provider sends text blocks only.
305
+ 8. **Single account.** No account pool, no region switching, no check-in.
276
306
 
277
307
  ---
278
308
 
package/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # DSH Connect Doubao
2
2
 
3
- > 把**本机豆包(Doubao)桌面客户端已有的登录态**接入 DeepSeek Harness,作为 Seed 系列模型 provider,
4
- > 并在设置面板里提供一个**只读**的额度概览(5 小时限额 / 周额度 + 重置倒计时)。
3
+ > 把**本机豆包(Doubao)桌面客户端已有的登录态**接入 DeepSeek Harness:**对话模型**与
4
+ > **「工作任务」模型**(2.1 Lite / 2.1 Pro / 自动,走会员付费额度)、**Seedream 生图**,
5
+ > 外加一块**只读**额度看板(5 小时限额 / 周额度 / 活动赠送额度,含重置倒计时)。
6
+ > 仅 Windows x64 —— 凭据解密依赖 Windows DPAPI。
5
7
 
6
8
  [![npm version](https://img.shields.io/npm/v/dsh-connect-doubao.svg)](https://www.npmjs.com/package/dsh-connect-doubao)
7
9
  [![license](https://img.shields.io/npm/l/dsh-connect-doubao.svg)](./LICENSE)
@@ -30,10 +32,12 @@ Chromium cookie(通过 Windows DPAPI 在本进程内解密),把它当作
30
32
 
31
33
  ## 功能特性
32
34
 
33
- - **豆包对话接入** —— 对话模式真正可用的两个条目(见下表),作为 DSH 的模型 provider。
35
+ - **两组豆包模型** —— 对话模式两条(快速 / 2.1 Turbo·专家)与「工作任务」模式三条
36
+ (2.1 Lite `seed-lite-7b`、2.1 Pro `5`、自动 `9`)。任务模型走的是**会员付费额度**,
37
+ 选中即自动切到任务模式的完整请求形态。
34
38
  - **生图(Seedream 5.0)** —— 模型选择器里选「豆包 生图」,回复即以 Markdown 图片返回真实生成图,可直接下载落盘。2026-11-02 实测端到端通过。
35
39
  - **推理强度三档** —— 低 / 中 / 高,对应上游 `reasoning_effort` 实测值 **3 / 4 / 5**。
36
- - **只读额度面板** —— 5 小时限额与周额度两条进度条,各带**重置倒计时**;设置面板里手动刷新 + 5 分钟自动刷新。
40
+ - **只读额度面板** —— 5 小时限额、周额度、活动赠送额度三条进度条,各带**重置倒计时**;设置面板里手动刷新 + 5 分钟自动刷新。
37
41
  - **模型勾选清单** —— 勾选的条目会出现在 DSH 的模型选择器中,请求时按 `model_item_key` 指定模型。
38
42
  - **账号状态行** —— 区分「已登录 / 未登录 / 登录信息读不出来」等状态,各自给出不同的处置建议。
39
43
  - **独立 loopback shim** —— 对话请求走一个只监听 `127.0.0.1` 的本地端点,带每进程共享密钥,任何非本机请求一律拒绝。
@@ -59,6 +63,8 @@ Chromium cookie(通过 Windows DPAPI 在本进程内解密),把它当作
59
63
  `周额度 已用 9% · 5d 21h 后重置`、`账号状态: 已登录`、`每 5 分钟自动刷新`、配置目录路径。
60
64
  - ✅ **模型清单六个条目全部渲染** —— 豆包 快速 / 2.1 Lite(`0921 新版`)/ 2.1 Turbo(`专家`)/
61
65
  2.1 Pro / 2.1 Turbo(`即将下线`)/ 自动,角标与桌面包一致,计数显示 `已启用 6 ↑`。
66
+ > 这张截图来自 **0.4.0**:当时工作任务模型只被渲染出来、还没接通,徽章取客户端原名。
67
+ > 0.4.1 起它们带 `工作任务` 徽章并且真的能对话,截图尚未重拍。
62
68
  - ✅ **模型选择器里出现「豆包(Doubao)」分组** —— 六个条目逐一列出。
63
69
  这是 **provider 注册成功 + catalog 生效** 的端到端证据。
64
70
  - ✅ **勾选交互可用** —— 每行一个勾选框,配 `刷新` / `全选` / `全不选` / `保存` 四个按钮。
@@ -67,8 +73,8 @@ Chromium cookie(通过 Windows DPAPI 在本进程内解密),把它当作
67
73
 
68
74
  ### 代码级 / 抓包级证据(非截图)
69
75
 
70
- - `doctor` 四段检查全绿:凭据 29 cookie 解密且 9/9 必需会话 cookie 齐备、账号被上游接受、
71
- shim 绑定回环端口且错误 bearer 返回 401、两个额度窗口可读。
76
+ - `doctor` 五项检查全绿:凭据 29 cookie 解密且 9/9 必需会话 cookie 齐备、账号被上游接受、
77
+ shim 绑定回环端口且错误 bearer 返回 401、三个额度窗口可读、模型发现开放 6 个。
72
78
  - 推理强度 低=3 / 中=4 / 高=5:由**抓取客户端切换档位时重新序列化的配置 diff**读出,
73
79
  并由客户端配置里的 `reasoning_effort_config` 独立确认。
74
80
  - 请求体带 `model_item_key` 与 `reasoning_effort`:抓包确认。
@@ -89,20 +95,40 @@ Chromium cookie(通过 Windows DPAPI 在本进程内解密),把它当作
89
95
  | 对话 | 豆包 快速(`0`)、豆包 2.1 Turbo「专家」(`3`) |
90
96
  | 工作任务 | 自动(`9`)、2.1 Lite(`seed-lite-7b`)、2.1 Pro(`5`)、Turbo(`3`) |
91
97
 
92
- **本插件只提供「对话」模式的两条**:
98
+ **本插件提供这两组里的五条**(外加生图别名):
93
99
 
94
- | id | 名称 | 徽章 | 推理强度 |
95
- | --- | --- | --- | --- |
96
- | `0` | 豆包 快速 | — | ✅ |
97
- | `3` | 豆包 2.1 Turbo | `专家` | ✅ |
100
+ | id | 名称 | 徽章 | 模式 | 推理强度 |
101
+ | --- | --- | --- | --- | --- |
102
+ | `0` | 豆包 快速 | — | 对话 | ✅ |
103
+ | `3` | 豆包 2.1 Turbo | `专家` | 对话 | ✅ |
104
+ | `seed-lite-7b` | 豆包 2.1 Lite | `工作任务` | 工作任务 | ✅ |
105
+ | `5` | 豆包 2.1 Pro | `工作任务` | 工作任务 | ✅ |
106
+ | `9` | 自动 | `工作任务` | 工作任务 | ✅ |
98
107
 
99
- 「工作任务」的四条**故意不列**:它们的执行侧是豆包客户端进程内的 CUA 运行时
100
- (`tasks-mode-resource` + `sbox`),外部客户端注册不进去,每一轮都会得到
101
- `710010702`。与其在模型选择器里摆出选不中的条目,不如不摆。
108
+ **「工作任务」三条为什么能用了。** 上一版把它们整个隐藏,理由是执行侧是豆包客户端进程内的
109
+ CUA 运行时(`tasks-mode-resource` + `sbox`),外部客户端注册不进去,每一轮必然得到
110
+ `710010702`。那个判断只对了一半:CUA 进程确实接不进去,但**它不是必需的** —— 服务端
111
+ 真正等待的只是三条 HTTP 应答:
112
+
113
+ ```
114
+ POST /alice/office/tool_local/msg/ack # 轮询本地指令
115
+ POST /alice/office/tool_local/upload_command_result # 回传执行结果
116
+ POST /alice/office/tool_local/msg/complete # 标记该指令完成
117
+ ```
118
+
119
+ 服务端按 `${unique_key}:${device_id}:${aid}:message:${seq}` 顺序下发指令(`seq = 0,1,2,…`):
120
+ `command: 6003`(`cmd_call`,如 `local.workspace.apply`)需要回传结果,
121
+ `command: 6004`(`agent_finished`)表示这一轮结束。插件在流式生成期间并发跑一个应答器
122
+ 把这些走完,于是任务回合不再落到 `710010702`。**实测 2026-11-05 跑通,Lite 与 Pro
123
+ 各一次纯文本回合。**
124
+
125
+ > ⚠️ **`msg/ack` 必须单条查询。** `{ msg_ids: [0,1,2,3,4,5,6] }` 会整体失败并返回
126
+ > `{"code":710010702,"msg":"系统错误"}` —— 批里只要含一个服务端尚未生成的 `seq`,
127
+ > 整批就废。这是逆向时最容易踩的坑,`tests/` 里没有钉它(需要真实上游才能复现)。
102
128
 
103
129
  **推理强度**:低 = `3`、中 = `4`、高 = `5`(由客户端切换档位时重新序列化的配置 diff 读出,
104
130
  并由配置里的 `reasoning_effort_config` 独立确认)。深度思考开关本身走
105
- `need_deep_think: 0`,档位只由 `reasoning_effort` 携带 —— 这是 2026-10-22 CDP 抓包更正的。
131
+ `need_deep_think: 0`;任务模式下抓到的真实请求固定发 `10001`,档位仍只由 `reasoning_effort` 携带。
106
132
 
107
133
  ---
108
134
 
@@ -146,7 +172,9 @@ Chromium cookie(通过 Windows DPAPI 在本进程内解密),把它当作
146
172
  `0x22` 是 Latin-1、`0x63` 是 UTF-16LE。**实测确认 `0x63` 的长度字段是字节数而不是字符数**
147
173
  —— 按字符数会正好读多一倍,模型名后面会吃掉下一个字段的字节。
148
174
  - 客户端给「工作任务」专属模型的名称自带前缀(`工作任务 Pro`、`工作任务 Auto`),
149
- 所以模式归属直接从缓存里就能读出来:带前缀的一律不开放(它们需要 App 内的 CUA 运行时)。
175
+ 所以模式归属直接从缓存里就能读出来。**但带前缀不再等于隐藏**:0.4.1 起它们会被
176
+ 标记成 `taskMode` 并正常开放,只有 `DOUBAO_UNAVAILABLE_MODELS` 里的条目(目前仅
177
+ 「即将下线」的 `4`)被排除。缓存里缺了某条任务模型时,会从内置清单补齐。
150
178
  - 缓存读不到、或解析结果不可信时,**回落到内置清单**,绝不清空用户的选择器。
151
179
  - 结果缓存 10 分钟;卡片上的「刷新」(POST)会丢弃记忆重新读盘。
152
180
  - `doctor` 第 5 项会报告这次到底是从缓存发现的、还是回落的。
@@ -154,7 +182,7 @@ Chromium cookie(通过 Windows DPAPI 在本进程内解密),把它当作
154
182
  当前这台机器上的实测输出:
155
183
 
156
184
  ```
157
- [ ok ] models 客户端缓存 5 个模型,开放 3 个(「工作任务」专属的按名字前缀排除)
185
+ [ ok ] models 客户端缓存 5 个模型,开放 6 个(仅排除「即将下线」等不可用条目)
158
186
  ```
159
187
 
160
188
  ## 实验性:工具调用(默认关闭)
@@ -309,7 +337,7 @@ dsh-connect-doubao-probe --prompt "hi" --model 5 --thinking high --json
309
337
  --timeout <ms> 超时(默认 120000)
310
338
  ```
311
339
 
312
- `doctor` 输出四段独立结论:凭据 / 账号 / provider 装配 / 额度。
340
+ `doctor` 输出五项独立结论:凭据 / 账号 / provider 装配 / 额度 / 模型发现。
313
341
  凭据只报**数量**,不报值。
314
342
 
315
343
  ---
@@ -350,21 +378,24 @@ npm run check # 以上全部
350
378
  5. **风控滑块无法自动处理。** 触发后需去豆包客户端手动发一条消息完成验证。
351
379
  6. **图片输入未提供。** 本 provider 只发送文本块,不转发图片。
352
380
  7. **单账号。** 没有账号池、没有区域切换、没有签到。
353
- 8. **「工作任务」跑不了。** `agent_mode` 的取值是 `2 = 普通对话`、`1 = 工作任务`(CDP 抓包实测,
354
- 2026-10-22)。任务模式下服务端会把每一轮交给 `Agent-GeneralTask`,而它的执行运行时是豆包
355
- 客户端进程里的 CUA(`tasks-mode-resource` + `sbox`),外部客户端注册不进去,必然回
356
- `710010702`。所以插件固定走对话模式;请求体里的 `general_task_param` 发的是客户端在普通
357
- 对话下的默认值 `{"server_do_compare":true}`。
358
- 9. **`model_item_key` 服务端根本不校验,所以选不了模型。** 三条证据(2026-10-23):
381
+ 8. **「工作任务」能跑,但扣不扣费取决于这一轮做了什么。** `agent_mode` 的取值是
382
+ `2 = 普通对话`、`1 = 工作任务`(CDP 抓包实测,2026-10-22)。任务模式下服务端把每一轮交给
383
+ `Agent-GeneralTask`,并由插件并发跑一个 `tool_local` 应答器(`msg/ack` →
384
+ `upload_command_result` → `msg/complete`)把本地指令走完,所以不再必然回 `710010702`。
385
+ **但纯文本、不触发本地工具的小回合,服务端会在 `ext` 里标
386
+ `general_agent_task_mode: "simple"` 与 `mode_downgrade_reason: "success"`,此时活动赠送
387
+ 额度的 `usedAmount` 不变**(实测调用前后恒为 782,即「走降级、不计费」)。想让任务模型
388
+ 真正消耗付费额度,得让它执行多步工具/文档任务。这一条是实测结论,不是产品承诺。
389
+ 9. **`model_item_key` 服务端不校验,所以「选中哪个模型」只能靠模式与账号决定。** 三条证据
390
+ (2026-10-23):
359
391
  1. CDP 抓到 App 选 2.1 Pro 时的真实请求:`model_item_key: "5"` + `mode_id: "3"` +
360
- `agent_mode: 1` + `general_task_param.runtime_type: 2`(本地电脑)。前三项我们能复制,
361
- 最后一项是 App 进程内的 CUA 运行时,复制不了 —— 这正是 `710010702` 的来源。
392
+ `agent_mode: 1` + `general_task_param.runtime_type: 2`(本地电脑)。
362
393
  2. 用 `mode_id=3` + `key=5` 在对话模式发请求:正常回答,但与 `key=0` 无可测差异
363
394
  (耗时 15.1–17.6 s、长度 311–340 字,均在噪声范围内)。
364
395
  3. **真正的对照**:把三处 `model_item_key` 全部改成一个不存在的值直接发送,服务端仍返回
365
396
  200 与完整正常流,没有任何错误帧 —— 它不校验这个字段。
366
- 所以「在 DSH 里指定 2.1 Pro / 2.1 Lite」做不到:模型由服务端按产品/模式/账号决定。
367
- 真要用这两个模型,只能在豆包客户端自己的「工作任务」里用。
397
+ 所以 0.4.1 能提供的是**「工作任务模式 + 该 key」这一整组请求形态**(实测能出答案),
398
+ 而不是「服务端一定会用 Pro 而不是 Lite 来算」。要更强的模型,仍建议在豆包客户端里确认。
368
399
  10. **本插件的设置不走宿主 settings,存在自己的 JSON 文件里。**
369
400
  `~/.dsh/dsh-connect-doubao.settings.json`,由插件自己写入(先写临时文件再原子重命名),
370
401
  读取也不依赖任何宿主服务在线。原因是宿主这条路对本插件**完全关闭**:它的 fiber 永远停在
@@ -1,4 +1,4 @@
1
- import { T as classifyDoubaoFailure, f as DoubaoUpstreamError } from "./doubao-upstream-BdIak5wD.js";
1
+ import { O as classifyDoubaoFailure, f as DoubaoUpstreamError } from "./doubao-upstream-DEeYQzAG.js";
2
2
  //#region src/cli-doctor.d.ts
3
3
  type CheckStatus = 'ok' | 'fail' | 'skip';
4
4
  interface DoctorCheck {
package/lib/cli-doctor.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { H as PROFILE_ENV, I as REQUIRED_COOKIE_NAMES, K as listDoubaoProfiles, U as describeProfileLocation, V as loadDoubaoCredentials, _t as discoverDoubaoModels, ct as DOUBAO_PROVIDER_DISPLAY_NAME, i as formatDuration, j as probeAccount, m as DoubaoUpstreamError, n as fetchQuota, o as millisUntilReset, ot as DOUBAO_MODELS, r as findWindow, st as DOUBAO_PROVIDER, w as classifyDoubaoFailure } from "./doubao-quota-Do37dMMj.js";
3
- import { g as createDoubaoAdapter, i as doubaoQuotaView, m as createDoubaoShim, n as DOUBAO_QUOTA_PATH, p as liveModels, u as discoverAllCachedModels } from "./doubao-status-CeXUM4Cy.js";
2
+ import { D as classifyDoubaoFailure, G as PROFILE_ENV, K as describeProfileLocation, P as probeAccount, W as loadDoubaoCredentials, Y as listDoubaoProfiles, bt as discoverDoubaoModels, dt as DOUBAO_PROVIDER_DISPLAY_NAME, i as formatDuration, lt as DOUBAO_MODELS, m as DoubaoUpstreamError, n as fetchQuota, o as millisUntilReset, r as findWindow, ut as DOUBAO_PROVIDER, z as REQUIRED_COOKIE_NAMES } from "./doubao-quota-BYF0-RMF.js";
3
+ import { g as createDoubaoAdapter, i as doubaoQuotaView, m as createDoubaoShim, n as DOUBAO_QUOTA_PATH, p as liveModels, u as discoverAllCachedModels } from "./doubao-status-DI2Rj0U_.js";
4
4
  //#region src/cli-doctor.ts
5
5
  /**
6
6
  * `doctor` — verify the whole stack end to end without spending inference quota.
@@ -160,7 +160,7 @@ async function runDoctor() {
160
160
  checks.push({
161
161
  name: "models",
162
162
  status: offered.length > 0 ? "ok" : "fail",
163
- detail: cached.length > 0 ? `客户端缓存 ${cached.length} 个模型,开放 ${offered.length} 个(「工作任务」专属的按名字前缀排除)` : `读不到客户端缓存,回落到内置清单(${offered.length} 个)`,
163
+ detail: cached.length > 0 ? `客户端缓存 ${cached.length} 个模型,开放 ${offered.length} 个(仅排除「即将下线」等不可用条目)` : `读不到客户端缓存,回落到内置清单(${offered.length} 个)`,
164
164
  data: {
165
165
  source: cached.length > 0 ? "client-cache" : "static-fallback",
166
166
  cached: cached.map((entry) => `${entry.key}=${entry.name}${entry.taskOnly ? "[工作任务]" : ""}`),
package/lib/cli-probe.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { M as streamChat, U as describeProfileLocation, V as loadDoubaoCredentials, bt as findDoubaoModel, i as formatDuration, j as probeAccount, m as DoubaoUpstreamError, n as fetchQuota, o as millisUntilReset, ot as DOUBAO_MODELS, r as findWindow, xt as findReasoningLevel } from "./doubao-quota-Do37dMMj.js";
2
+ import { Ct as findDoubaoModel, F as streamChat, K as describeProfileLocation, P as probeAccount, W as loadDoubaoCredentials, i as formatDuration, lt as DOUBAO_MODELS, m as DoubaoUpstreamError, n as fetchQuota, o as millisUntilReset, r as findWindow, wt as findReasoningLevel } from "./doubao-quota-BYF0-RMF.js";
3
3
  //#region src/cli-probe.ts
4
4
  /**
5
5
  * Doubao smoke probe.
package/lib/client.js CHANGED
@@ -1060,7 +1060,7 @@ window.__ModuleLoader__.load({
1060
1060
  "account.profile": "配置目录",
1061
1061
  "models.title": "模型",
1062
1062
  "models.enabledCount": "已启用 {count} ↑",
1063
- "models.routingNote": "清单直接读自豆包客户端的缓存,豆包上线新模型时会自动出现,不需要等插件更新。「工作任务」专属的条目(2.1 Pro / Lite / Auto)由客户端内的运行时执行,外部客户端接不进去,已自动隐藏",
1063
+ "models.routingNote": "清单直接读自豆包客户端的缓存,豆包上线新模型时会自动出现,不需要等插件更新。带「工作任务」徽章的条目(2.1 Lite / 2.1 Pro / 自动)走任务模式与会员付费额度,插件会自动完成本地指令应答",
1064
1064
  "models.refresh": "刷新",
1065
1065
  "models.refreshing": "刷新中…",
1066
1066
  "models.save": "保存",
@@ -1132,7 +1132,7 @@ window.__ModuleLoader__.load({
1132
1132
  "account.profile": "Profile path",
1133
1133
  "models.title": "Models",
1134
1134
  "models.enabledCount": "{count} enabled ↑",
1135
- "models.routingNote": "Only the 对话-mode entries are offered (快速 / 专家). The 「工作任务」-only models (2.1 Pro / 2.1 Lite) are executed by the Doubao desktop app runtime, which an external client cannot join",
1135
+ "models.routingNote": "The list is read from the Doubao client cache, so new models appear without a plugin update. Entries badged 「工作任务」 (2.1 Lite / 2.1 Pro / Auto) run in task mode against the paid membership quota; the plugin answers the local workspace commands for you",
1136
1136
  "models.refresh": "Refresh",
1137
1137
  "models.refreshing": "Refreshing…",
1138
1138
  "models.save": "Save",