dsh-codex-subscription 3.0.0-beta.5 → 3.0.0-beta.7

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/README.en.md CHANGED
@@ -18,17 +18,122 @@ Sign in with ChatGPT Plus / Pro to use GPT-6 and other Codex models, quota, web
18
18
  [![MIT](https://img.shields.io/badge/license-MIT-111111.svg)](LICENSE)
19
19
  [![Star](https://img.shields.io/github/stars/WSL043/dsh-codex-subscription?style=flat&logo=github&label=Star)](https://github.com/WSL043/dsh-codex-subscription/stargazers)
20
20
 
21
- [Features](#feature-overview) · [Install](#install) · [Daily use](#inside-the-plugin) · [Examples](#from-sketch-to-image) · [User guide](docs/GUIDE.en.md) · [Update and uninstall](#update-and-uninstall)
21
+ [Install](#install) · [Highlights](#highlights) · [Phone remote control](#phone-remote-control-beta) · [Examples](#from-sketch-to-image) · [All features](#all-features) · [User guide](docs/GUIDE.en.md) · [Troubleshooting](#troubleshooting)
22
22
 
23
23
  </div>
24
24
 
25
- <p align="center"><img src="docs/assets/product-account-demo-en.png" width="1100" alt="Codex subscription account and quota interface with model choices, quota windows and alerts; demonstration data"></p>
25
+ <p align="center"><img src="docs/assets/hero-en.webp" width="1100" alt="Your subscription, now in DSH: the whale-girl mascot and the Codex subscription account quota interface"></p>
26
26
 
27
- ## Feature overview
27
+ ## Prepare DSH
28
+
29
+ Verified against DSH `0.2.0-rc.2` (`latest` and `next` channels); the minimum supported version is `0.1.7-rc.2`. Please upgrade older DSH releases first.
30
+
31
+ This plugin supports the latest DeepSeek Harness release recorded in its package metadata and requires a ChatGPT account that currently has Codex access.
32
+
33
+ - **Recommended: the official desktop app.** DeepSeek now ships a [DeepSeek Harness desktop app](https://www.deepseek.com/harness/) (macOS on Apple Silicon and 64-bit Windows; preview, open source). It needs no Node.js setup: install it, then add this plugin from the Plugins page as described below. The desktop app, the web app and the CLI share one DSH plugin system; this plugin's end-to-end acceptance is mainly run on the web app, so please [open an issue](https://github.com/WSL043/dsh-codex-subscription/issues) if the desktop app misbehaves.
34
+ - Need Linux, or prefer a community portable build? Use [DSH-Portable](https://github.com/WSL043/DSH-Portable), a community portable desktop distribution for Windows, macOS, and Linux.
35
+ - Prefer the official route? Follow the [DeepSeek Harness run guide](https://github.com/deepseek-ai/deepseek-harness#run).
36
+
37
+ ## Install
38
+
39
+ ### Install from the Plugins page (recommended)
40
+
41
+ 1. Open **Plugins → Add plugin** in DSH.
42
+ 2. Paste this package name, with its version, into the **Package name or address** field:
28
43
 
29
- Subscription access, model controls, quota management, and image creation in one DSH workflow. Each capability is listed below; model access and quota depend on your account.
44
+ ```text
45
+ dsh-codex-subscription@2.6.8
46
+ ```
30
47
 
31
- ### Models and search
48
+ 3. Click **Install** and wait for completion. Follow the page instructions; save your work before restarting if requested.
49
+ 4. Open **Settings → Codex**, sign in to ChatGPT, then select a Codex model in your conversation.
50
+
51
+ Why the version is included: DSH's plugin page only installs versions that are at least 24 hours old, so the bare package name can install an older release during the first day after a new one, and that older release may not support your DSH. With the version you get exactly this release; a day after release the bare `dsh-codex-subscription` also resolves to the latest stable release. Other version numbers are on the [releases page](https://github.com/WSL043/dsh-codex-subscription/releases).
52
+
53
+ <details>
54
+ <summary>Terminal installation (with an existing dsh command)</summary>
55
+
56
+ ```sh
57
+ dsh plugin --profile web add dsh-codex-subscription@2.6.8
58
+ ```
59
+
60
+ Follow the restart instructions, then sign in under **Settings → Codex**. Both the Plugins page and the terminal use DSH's installation management.
61
+
62
+ </details>
63
+
64
+ <details>
65
+ <summary>Headless tasks</summary>
66
+
67
+ After signing in and selecting a Codex model in Web, install the same plugin in the Headless profile:
68
+
69
+ ```sh
70
+ dsh plugin --profile headless add dsh-codex-subscription@2.6.8
71
+ dsh --profile headless "Reply with only the word: ok"
72
+ ```
73
+
74
+ </details>
75
+
76
+ ## Highlights
77
+
78
+ ### Sign in with your subscription and use Codex models
79
+
80
+ Sign in to ChatGPT under **Settings → Codex** in DSH, then pick GPT-6 and the other models in your account catalog from any conversation, set the reasoning and speed levels, and use subscription web search. No API key. Models and quota depend on your account.
81
+
82
+ ### Quota at a glance
83
+
84
+ Short-window and weekly quota, reset times, extra Credits and reset cards live in settings; the composer can keep the remaining quota in view and warn you below a threshold.
85
+
86
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/ui-quota-en-dark.png"><img src="docs/assets/ui-quota-en-light.png" width="560" alt="Quota card: weekly quota and reset time, with usage overview and where the allowance went below"></picture></p>
87
+
88
+ <details>
89
+ <summary>Usage overview and where the allowance went: lifetime usage, streaks, a daily heatmap and the weekly breakdown</summary>
90
+ <br>
91
+ <p align="center"><img src="docs/assets/usage-overview-en.png" width="460" alt="Usage overview and where the allowance went in the quota card: lifetime tokens, streaks, a half-year daily heatmap and the weekly allowance split by model, surface and source"></p>
92
+ </details>
93
+
94
+ ### Everyday controls, right in the composer
95
+
96
+ Pick the model, the reasoning level and the speed tier while the remaining quota stays in view, without opening settings for every request.
97
+
98
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/ui-composer-en-dark.png"><img src="docs/assets/ui-composer-en-light.png" width="760" alt="Composer: permission, sketch, remaining quota, the lightning mark for Fast mode and the model picker"></picture></p>
99
+
100
+ ### Phone remote control (Beta)
101
+
102
+ Pair the ChatGPT mobile app's Codex section with a QR code and the DSH on your computer shows up as a host on your phone. DeepSeek and any other DSH model work from the phone:
103
+
104
+ - follow, start, continue, interrupt and archive conversations; search conversations and keywords inside one;
105
+ - switch model, reasoning level and permission (workspace write / full access);
106
+ - approvals and questions pop up on the phone; if it drops, they pop up again when you reopen it, otherwise they go back to the computer;
107
+ - replies appear as they are written; commands, file edits with diffs, the task list, web searches, connector calls and generated images use the phone's native views;
108
+ - send photos, @ search files on the computer, / pick DSH skills, view changes with read-only git, and start chats in a real folder picker;
109
+ - DSH workspaces appear as projects; fork a conversation, see usage limits, lifetime usage and context usage; where DSH has `/compact` (0.2.0-rc.2 onwards), compact the context from the phone;
110
+ - model errors show on the phone; paired phones are listed in settings and can be removed.
111
+
112
+ Off by default: **Settings → Codex → Models & runtime → Phone remote control**. The computer must stay on, online and running DSH. This is a ChatGPT capability OpenAI has not opened to third parties and may change at any time, hence Beta. Currently in the pre-release: `dsh-codex-subscription@3.0.0-beta.7`.
113
+
114
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/ui-remote-en-dark.png"><img src="docs/assets/ui-remote-en-light.png" width="560" alt="Phone remote control in settings: the switch, pairing QR code and manual code, paired phones"></picture></p>
115
+
116
+ ### Images and sketching (Beta)
117
+
118
+ Generate images with your subscription, circle a region and write a note to keep editing, or open the sketch board to draw yourself or let the agent draw, then use the sketch as the reference for the finished image.
119
+
120
+ <p align="center"><img src="docs/assets/annotate-en.webp" width="900" alt="Image preview: a region above the chimney is marked with a note, then editing continues with the note"></p>
121
+
122
+ Examples are in [From sketch to image](#from-sketch-to-image); the switches are under **Settings → Codex → Images & sketch**.
123
+
124
+ ### ChatGPT connectors (Beta)
125
+
126
+ Let DeepSeek or any other DSH model use the apps you connected in ChatGPT: GitHub, Gmail, Google Drive, Calendar and more. Models search first, then call, and every change asks for your approval. Off by default; turn it on under Models & runtime.
127
+
128
+ [Usage details, limitations and optional components →](docs/GUIDE.en.md)
129
+
130
+ ## All features
131
+
132
+ Model access and quota depend on your account.
133
+
134
+ <details>
135
+ <summary><b>Models and search</b> (13)</summary>
136
+ <br>
32
137
 
33
138
  | Feature | What you can do | Availability / setup |
34
139
  | --- | --- | --- |
@@ -46,7 +151,11 @@ Subscription access, model controls, quota management, and image creation in one
46
151
  | **Context budget** | Use standard, extended, or custom budgets per model | Limited by the model catalog |
47
152
  | **GPT-Reserve** | Select the reserve model when your account offers it | Experimental; no promise of a separate reserve quota |
48
153
 
49
- ### Accounts and quota
154
+ </details>
155
+
156
+ <details>
157
+ <summary><b>Accounts and quota</b> (10)</summary>
158
+ <br>
50
159
 
51
160
  | Feature | What you can do | Availability / setup |
52
161
  | --- | --- | --- |
@@ -61,9 +170,11 @@ Subscription access, model controls, quota management, and image creation in one
61
170
  | **Redeem reset cards** | Use an available reset card after confirmation | Confirmation and waiting safeguards; server decides the result |
62
171
  | **Retry after reset** | Wait for a confirmed short-window reset before continuing | Off by default; waiting can be stopped |
63
172
 
64
- <p align="center"><img src="docs/assets/usage-overview-en.png" width="460" alt="Usage overview and where the allowance went in the quota card: lifetime tokens, streaks, a half-year daily heatmap and the weekly allowance split by model, surface and source"></p>
173
+ </details>
65
174
 
66
- ### Images and sketching
175
+ <details>
176
+ <summary><b>Images and sketching</b> (12)</summary>
177
+ <br>
67
178
 
68
179
  | Feature | What you can do | Availability / setup |
69
180
  | --- | --- | --- |
@@ -80,11 +191,15 @@ Subscription access, model controls, quota management, and image creation in one
80
191
  | **Agent drawing** | Ask the agent to draw with `@sketch`, follow progress, and stop it; the agent can draw whole stages in SVG, imported as editable native strokes | Beta; off by default, requires the canvas |
81
192
  | **Result preview for the model** | Return a canvas image after drawing for subsequent review | Beta; off by default, adds image input usage |
82
193
 
83
- ### Advanced capabilities and maintenance
194
+ </details>
195
+
196
+ <details>
197
+ <summary><b>Advanced capabilities and maintenance</b> (9)</summary>
198
+ <br>
84
199
 
85
200
  | Feature | What you can do | Availability / setup |
86
201
  | --- | --- | --- |
87
- | **Phone remote control** | Drive the DSH on your computer from the Codex section of the ChatGPT mobile app: start, continue and archive conversations, switch model and permissions, answer approvals and questions; DeepSeek and any other DSH model work | Beta (3.0.0-beta.5); off by default; Settings → Codex → Models & runtime → Phone remote control, pair by QR or manual code |
202
+ | **Phone remote control** | Drive the DSH on your computer from the Codex section of the ChatGPT mobile app: start, continue and archive conversations, switch model and permissions, answer approvals and questions; DeepSeek and any other DSH model work | Beta (3.0.0-beta.7); off by default; Settings → Codex → Models & runtime → Phone remote control, pair by QR or manual code |
88
203
  | **Codex independent subtasks** | Reuse subscription sign-in and workspace permissions; select allowed models and reasoning levels | Beta; DSH by default, Codex requires the optional component and model-selection setup |
89
204
  | **Subtask component management** | Install, disable, or uninstall the official Codex subtask component from settings | Managed by supported DSH hosts; no extra login |
90
205
  | **Cloud context compaction** | Continue long conversations using Codex compaction while retaining DSH history and native compaction | Beta / experimental; off by default, enabled requests use SSE |
@@ -94,58 +209,9 @@ Subscription access, model controls, quota management, and image creation in one
94
209
  | **Support diagnostics** | Inspect prerequisites and recent operation results, and copy a short report (about 1 KB; the full one can be copied separately) without credentials or conversation text | Settings → Maintenance; unverified does not mean broken |
95
210
  | **Forecast cache cleanup** | Inspect and clear local quota forecast history | Keeps sign-in, drafts, originals, and shared dependencies |
96
211
 
97
- [Usage details, limitations, and optional components →](docs/GUIDE.en.md)
98
-
99
- ## Prepare DSH
100
-
101
- Verified against DSH `0.2.0-rc.2` (`latest` and `next` channels); the minimum supported version is `0.1.7-rc.2`. Please upgrade older DSH releases first.
102
-
103
- This plugin supports the latest DeepSeek Harness release recorded in its package metadata and requires a ChatGPT account that currently has Codex access.
104
-
105
- - **Recommended: the official desktop app.** DeepSeek now ships a [DeepSeek Harness desktop app](https://www.deepseek.com/harness/) (macOS on Apple Silicon and 64-bit Windows; preview, open source). It needs no Node.js setup: install it, then add this plugin from the Plugins page as described below. The desktop app, the web app and the CLI share one DSH plugin system; this plugin's end-to-end acceptance is mainly run on the web app, so please [open an issue](https://github.com/WSL043/dsh-codex-subscription/issues) if the desktop app misbehaves.
106
- - Need Linux, or prefer a community portable build? Use [DSH-Portable](https://github.com/WSL043/DSH-Portable), a community portable desktop distribution for Windows, macOS, and Linux.
107
- - Prefer the official route? Follow the [DeepSeek Harness run guide](https://github.com/deepseek-ai/deepseek-harness#run).
108
-
109
- ## Install
110
-
111
- ### Install from the Plugins page (recommended)
112
-
113
- 1. Open **Plugins → Add plugin** in DSH.
114
- 2. Paste this package name, with its version, into the **Package name or address** field:
115
-
116
- ```text
117
- dsh-codex-subscription@2.5.9
118
- ```
119
-
120
- 3. Click **Install** and wait for completion. Follow the page instructions; save your work before restarting if requested.
121
- 4. Open **Settings → Codex**, sign in to ChatGPT, then select a Codex model in your conversation.
122
-
123
- Why the version is included: DSH's plugin page only installs versions that are at least 24 hours old, so the bare package name can install an older release during the first day after a new one, and that older release may not support your DSH. With the version you get exactly this release; a day after release the bare `dsh-codex-subscription` also resolves to the latest stable release. Other version numbers are on the [releases page](https://github.com/WSL043/dsh-codex-subscription/releases).
124
-
125
- <details>
126
- <summary>Terminal installation (with an existing dsh command)</summary>
127
-
128
- ```sh
129
- dsh plugin --profile web add dsh-codex-subscription@2.5.9
130
- ```
131
-
132
- Follow the restart instructions, then sign in under **Settings → Codex**. Both the Plugins page and the terminal use DSH's installation management.
133
-
134
212
  </details>
135
213
 
136
- <details>
137
- <summary>Headless tasks</summary>
138
-
139
- After signing in and selecting a Codex model in Web, install the same plugin in the Headless profile:
140
-
141
- ```sh
142
- dsh plugin --profile headless add dsh-codex-subscription@2.5.9
143
- dsh --profile headless "Reply with only the word: ok"
144
- ```
145
-
146
- </details>
147
-
148
- ## Inside the plugin
214
+ ## Interface demo
149
215
 
150
216
  <details>
151
217
  <summary><b>Interface demo (animation, click to expand)</b>: models, prompts, images, annotation, sketch board, quota and resets</summary>
@@ -153,32 +219,6 @@ dsh --profile headless "Reply with only the word: ok"
153
219
  <p align="center"><img src="docs/assets/readme-demo-en.webp" width="1100" alt="Demo: sign in with ChatGPT, pick a subscription model, ask, generate an image, view and annotate it, continue on the sketch board, and check quota and resets"></p>
154
220
  </details>
155
221
 
156
- ### Everyday controls, right in the composer
157
-
158
- Choose a model, adjust reasoning, switch speed tiers, and check remaining quota without opening settings for every request.
159
-
160
- <p align="center"><img src="docs/assets/composer-quota-en.png" width="800" alt="DSH composer with GPT-6-Luna, reasoning controls and remaining quota"></p>
161
-
162
- ### Control DSH from your phone (Beta)
163
-
164
- Turn on **Settings → Codex → Models & runtime → Phone remote control** and pair by scanning the QR code in the Codex section of the ChatGPT mobile app. This computer's DSH then appears in the phone's host list:
165
-
166
- - browse, start, continue, interrupt and archive DSH conversations from the phone, with DeepSeek, Codex or any other DSH model;
167
- - switch model, reasoning level and permissions (workspace write / full access) from the phone;
168
- - when DSH needs a tool approval or asks you a question, it pops up on the phone; if the phone dropped, it is asked again when you return, otherwise the computer handles it;
169
- - replies appear on the phone as they are written;
170
- - commands, file edits (with diffs), the task list and generated images use the phone's native views, and file-edit approvals show the diff;
171
- - DSH workspaces appear as projects on the phone: start chats in them, fork a conversation, see usage limits and context usage;
172
- - on the phone, @ searches files on the computer, skills can be picked, and the changes view works (read-only git); conversations can be searched, keywords found inside a conversation, and your lifetime usage viewed;
173
- - Settings lists the paired phones and lets you remove any of them;
174
- - send photos from the phone (with or without text), and images DSH produces show up on the phone;
175
- - the phone folder picker browses real folders on this computer, and a new chat can start in one of them;
176
- - model errors are shown on the phone.
177
-
178
- Off by default. The computer must stay on, online and running DSH. This uses a ChatGPT capability OpenAI has not opened to third parties and may change at any time, hence Beta. Currently available in the pre-release `dsh-codex-subscription@3.0.0-beta.5`.
179
-
180
- <p align="center"><img src="docs/assets/remote-control-en.png" width="560" alt="Official desktop app settings: phone remote control connected, showing a pairing QR code and manual code"></p>
181
-
182
222
  ### Accounts and preferences, in one place
183
223
 
184
224
  Open **Settings → Codex** to manage accounts and quota. Enable other features as your tasks need them.
@@ -268,9 +308,9 @@ The face shape, hair flow, lace, hand and speech-bubble lettering are all closer
268
308
  <details>
269
309
  <summary>View Images & sketch settings</summary>
270
310
 
271
- <p align="center"><img src="docs/assets/real-creative-en.png" width="564" alt="Images and sketch settings: generation, editing, model choice, and separate canvas and agent controls"></p>
311
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/ui-creative-en-dark.png"><img src="docs/assets/ui-creative-en-light.png" width="564" alt="Images and sketch settings: generation and editing, creative shortcuts, requested model and quality"></picture></p>
272
312
 
273
- Settings shown directly as cropped screenshots from the actual DSH interface.
313
+ Settings shown directly as cropped screenshots from the actual DSH interface, following the light or dark theme.
274
314
 
275
315
  </details>
276
316
 
@@ -278,14 +318,6 @@ Layers, native curves, brushes, undo/redo, and shortcuts are supported. Save mul
278
318
 
279
319
  [Image editing and draft storage →](docs/GUIDE.en.md#image-generation-and-editing-beta)
280
320
 
281
- <a id="codex-subtask-runtime"></a>
282
-
283
- ## Optional capabilities
284
-
285
- Normal subscription chat needs no Codex CLI. For **Codex independent subtasks**, use Install component under Models & runtime; disabling and uninstalling are available in the same place. Other experimental options remain opt-in.
286
-
287
- [Optional components, storage cleanup, and the full user guide →](docs/GUIDE.en.md)
288
-
289
321
  ## Update and uninstall
290
322
 
291
323
  Find this plugin on the DSH **Plugins** page and use its update or uninstall action. Follow any restart instructions. Uninstalling this plugin does not remove other plugins.
@@ -294,7 +326,7 @@ Find this plugin on the DSH **Plugins** page and use its update or uninstall act
294
326
  <summary>Terminal commands</summary>
295
327
 
296
328
  ```sh
297
- dsh plugin --profile web update dsh-codex-subscription@2.5.9
329
+ dsh plugin --profile web update dsh-codex-subscription@2.6.8
298
330
  ```
299
331
 
300
332
  Run only when you want to uninstall:
@@ -307,13 +339,6 @@ dsh plugin --profile web remove dsh-codex-subscription
307
339
 
308
340
  ## Troubleshooting
309
341
 
310
- <details>
311
- <summary>DSH 0.2.0-rc.2 reports the plugin as incompatible?</summary>
312
-
313
- DSH 0.2.0-rc.2 did not exist when any release before 2.2.10 was published, and a published package's compatibility declaration cannot be changed. Install 2.2.10 or later (a bare package name can resolve to an older release during the first 24 hours, so enter `dsh-codex-subscription@2.2.10`) from the **Plugins** page, or run `dsh plugin --profile web update dsh-codex-subscription@2.5.9`, then restart DSH.
314
-
315
- </details>
316
-
317
342
  <details>
318
343
  <summary>How does this relate to the official Codex bundle (dsh-hooks-codex) in the plugin list?</summary>
319
344
 
@@ -344,15 +369,12 @@ Diagnostics exclude credentials, account identifiers, raw responses, and full lo
344
369
 
345
370
  </details>
346
371
 
347
- ## Current icon
348
-
349
- The current plugin icon and its appearance in DSH’s Installed list.
350
-
351
- <p align="center"><img src="icon-subscription.webp" width="200" height="200" alt="Current Codex Subscription plugin icon"></p>
372
+ <details>
373
+ <summary>Do I need the Codex command line?</summary>
352
374
 
353
- <p align="center"><img src="docs/assets/plugin-installed-en.png" width="800" alt="Codex Subscription plugin card in the DSH Installed list"></p>
375
+ Not for ordinary subscription chat. Only if you want **Codex independent subtasks**: use "Install component" under Settings → Codex → Models & runtime; disabling and uninstalling are in the same place. Other experimental options are opt-in. [Optional components, storage cleanup and the full guide →](docs/GUIDE.en.md)
354
376
 
355
- <sub>Captured in DSH 0.1.7-alpha.2.</sub>
377
+ </details>
356
378
 
357
379
  ## Positioning and boundaries
358
380
 
package/README.md CHANGED
@@ -18,17 +18,122 @@
18
18
  [![MIT](https://img.shields.io/badge/license-MIT-111111.svg)](LICENSE)
19
19
  [![Star](https://img.shields.io/github/stars/WSL043/dsh-codex-subscription?style=flat&logo=github&label=Star)](https://github.com/WSL043/dsh-codex-subscription/stargazers)
20
20
 
21
- [功能](#功能一览) · [安装](#安装) · [日常使用](#界面一览) · [作品案例](#从草图到作品) · [使用指南](docs/GUIDE.zh-CN.md) · [更新与卸载](#更新与卸载)
21
+ [安装](#安装) · [核心功能](#核心功能) · [手机远程控制](#手机远程控制beta) · [作品案例](#从草图到作品) · [全部功能](#全部功能) · [使用指南](docs/GUIDE.zh-CN.md) · [常见问题](#常见问题)
22
22
 
23
23
  </div>
24
24
 
25
- <p align="center"><img src="docs/assets/product-account-demo.png" width="1100" alt="Codex 订阅账号与额度界面:模型选择、双周期额度与提醒;使用演示数据"></p>
25
+ <p align="center"><img src="docs/assets/hero-zh.webp" width="1100" alt="已有的订阅,在 DSH 接着用:鲸鱼少女吉祥物与 Codex 订阅账号额度界面"></p>
26
26
 
27
- ## 功能一览
27
+ ## 准备 DSH
28
+
29
+ 已验证兼容 DSH `0.2.0-rc.2`(`latest` 与 `next` 通道),最低支持 `0.1.7-rc.2`;更早的 DSH 请先升级。
30
+
31
+ 本插件支持软件包元数据中记录的最新版 DeepSeek Harness,并需要一个当前具有 Codex 使用资格的 ChatGPT 账户。
32
+
33
+ - **推荐:官方桌面端**。DeepSeek 已发布 [DeepSeek Harness 桌面版](https://www.deepseek.com/harness/)(macOS Apple Silicon、Windows 64 位,预览版、开源),无需配置 Node.js,安装后按下文在插件页面添加本插件即可。桌面端与网页版、命令行使用同一套 DSH 插件系统;本插件的端到端验收主要在网页版上完成,桌面端遇到问题请[提交 issue](https://github.com/WSL043/dsh-codex-subscription/issues);
34
+ - 需要 Linux,或想要社区便携分发:使用 [DSH-Portable](https://github.com/WSL043/DSH-Portable)。这是面向 Windows、macOS 和 Linux 的社区便携桌面分发;
35
+ - 想按官方方式运行:查看 [DeepSeek Harness 官方说明](https://github.com/deepseek-ai/deepseek-harness#run)。
36
+
37
+ ## 安装
38
+
39
+ ### 在插件页面安装(推荐)
40
+
41
+ 1. 打开 DSH 的 **插件 → 添加插件**。
42
+ 2. 在 **包名或地址** 输入框中粘贴下面的包名(已带版本号):
43
+
44
+ ```text
45
+ dsh-codex-subscription@2.6.8
46
+ ```
47
+
48
+ 3. 点击 **安装**,等待安装完成;按页面提示操作,需要重启时先保存工作。
49
+ 4. 打开 **设置 → Codex 订阅**,登录 ChatGPT,然后在会话中选择 Codex 模型。
50
+
51
+ 为什么要带版本号:DSH 的插件页只会安装发布满 24 小时的版本,只填包名时,新版本发布后的第一天可能装到更早的旧版,而旧版不一定兼容你的 DSH。带上版本号可以直接装到这一版;发布一天后只填 `dsh-codex-subscription` 也会装到最新正式版。其他版本号见[发布页](https://github.com/WSL043/dsh-codex-subscription/releases)。
52
+
53
+ <details>
54
+ <summary>终端安装(已能运行 dsh 命令)</summary>
55
+
56
+ ```sh
57
+ dsh plugin --profile web add dsh-codex-subscription@2.6.8
58
+ ```
59
+
60
+ 安装完成后按提示重启 DSH,再到 **设置 → Codex 订阅** 登录。插件页面和终端均由 DSH 管理安装。
61
+
62
+ </details>
63
+
64
+ <details>
65
+ <summary>Headless 任务</summary>
66
+
67
+ 先在 Web 中完成登录并选择一次 Codex 模型,再把同一个插件安装到 Headless profile:
28
68
 
29
- 把订阅接入、模型控制、额度管理和图片创作放在同一个 DSH 工作流里。下面逐项列出插件提供的能力;模型权限和额度以账号实际返回为准。
69
+ ```sh
70
+ dsh plugin --profile headless add dsh-codex-subscription@2.6.8
71
+ dsh --profile headless "只回复:ok"
72
+ ```
73
+
74
+ </details>
75
+
76
+ ## 核心功能
77
+
78
+ ### 用订阅登录,直接用 Codex 模型
79
+
80
+ 在 DSH 的 **设置 → Codex 订阅** 登录 ChatGPT,会话里就能选 GPT-6 等账号目录里的模型,调推理档位和速度档位,并使用订阅联网搜索。不需要 API Key。模型和额度以账号实际返回为准。
81
+
82
+ ### 额度一眼看清
83
+
84
+ 短周期和每周额度、重置时间、额外 Credits 与重置卡都在设置里;输入框旁可以常驻显示剩余额度,低于阈值会提醒。
85
+
86
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/ui-quota-zh-dark.png"><img src="docs/assets/ui-quota-zh-light.png" width="560" alt="额度卡片:每周额度与重置时间,下方可展开使用概览和额度去向"></picture></p>
87
+
88
+ <details>
89
+ <summary>使用概览与额度去向:累计用量、连续天数、每日热力图,以及每周额度花在哪</summary>
90
+ <br>
91
+ <p align="center"><img src="docs/assets/usage-overview.png" width="460" alt="额度卡片里的使用概览与额度去向:累计 token、连续使用天数、最近半年每日用量热力图,以及每周额度按模型、入口、来源的拆分"></p>
92
+ </details>
93
+
94
+ ### 日常操作,留在输入框
95
+
96
+ 选模型、调推理档位、切换速度档位,同时查看剩余额度,无需为每次请求打开设置。
97
+
98
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/ui-composer-zh-dark.png"><img src="docs/assets/ui-composer-zh-light.png" width="760" alt="输入框:权限、草图、剩余额度、闪电标识的 Fast 模式和模型选择"></picture></p>
30
99
 
31
- ### 模型与搜索
100
+ ### 手机远程控制(Beta)
101
+
102
+ 用 ChatGPT 手机 App 的 Codex 扫码配对,这台电脑上的 DSH 就出现在手机的主机列表里,DeepSeek 等任意 DSH 模型都能在手机上用:
103
+
104
+ - 查看、新建、续聊、中断、归档对话,搜索对话和对话内的关键词;
105
+ - 切换模型、推理档位和权限(工作区内修改 / 完全访问);
106
+ - 批准工具调用或回答提问时手机直接弹出;手机掉线,重新打开会再弹一次,否则交回电脑处理;
107
+ - 回复边生成边显示;命令、文件修改(带差异)、任务清单、联网搜索、连接器调用和生成的图片用手机原生视图显示;
108
+ - 发图片,@ 搜电脑上的文件,/ 选 DSH 技能,只读 git 查看改动,用真实的文件夹选择器新建对话;
109
+ - DSH 工作区对应手机上的项目;可以从手机分叉对话,看用量额度、累计用量和上下文用量;DSH 带有 `/compact` 时(0.2.0-rc.2 起)还能从手机压缩上下文;
110
+ - 模型报错会显示在手机上;设置里能看到已配对的手机,随时移除。
111
+
112
+ 默认关闭:**设置 → Codex 订阅 → 模型与运行 → 手机远程控制**。电脑需要保持开机、联网并打开 DSH。这是 OpenAI 尚未向第三方开放的 ChatGPT 能力,随时可能变化,所以标为 Beta,目前在预发布版中提供:`dsh-codex-subscription@3.0.0-beta.7`。
113
+
114
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/ui-remote-zh-dark.png"><img src="docs/assets/ui-remote-zh-light.png" width="560" alt="设置里的手机远程控制:开关、配对二维码和手动码、已配对的手机"></picture></p>
115
+
116
+ ### 图片与草图(Beta)
117
+
118
+ 用订阅生成图片,在图上圈出区域写备注继续修改,或者打开草图画板自己画、让 Agent 画,再以草图为参考生成成品。
119
+
120
+ <p align="center"><img src="docs/assets/annotate-zh.webp" width="900" alt="图片预览:在烟囱上方圈出区域并写下备注,再带着备注继续编辑"></p>
121
+
122
+ 案例见 [从草图到作品](#从草图到作品),开关在 **设置 → Codex 订阅 → 图片与草图**。
123
+
124
+ ### ChatGPT 连接器(Beta)
125
+
126
+ 让 DeepSeek 等任意 DSH 模型使用你在 ChatGPT 里连接的应用:GitHub、Gmail、Google 云端硬盘、日历等。模型先搜索再调用,写操作每次都要你批准。默认关闭,在“模型与运行”里开启。
127
+
128
+ [详细用法、限制与可选组件 →](docs/GUIDE.zh-CN.md)
129
+
130
+ ## 全部功能
131
+
132
+ 模型权限和额度以账号实际返回为准。
133
+
134
+ <details>
135
+ <summary><b>模型与搜索</b>(13 项)</summary>
136
+ <br>
32
137
 
33
138
  | 功能 | 你可以做什么 | 使用方式 / 条件 |
34
139
  | --- | --- | --- |
@@ -46,7 +151,11 @@
46
151
  | **上下文预算** | 按模型使用标准、扩展或自定义上下文预算 | 受模型目录上限约束 |
47
152
  | **GPT-Reserve** | 在账号开放时选择备用模型入口 | 实验性;不承诺独立备用额度 |
48
153
 
49
- ### 账号与额度
154
+ </details>
155
+
156
+ <details>
157
+ <summary><b>账号与额度</b>(10 项)</summary>
158
+ <br>
50
159
 
51
160
  | 功能 | 你可以做什么 | 使用方式 / 条件 |
52
161
  | --- | --- | --- |
@@ -61,9 +170,11 @@
61
170
  | **使用重置卡** | 在确认后主动使用服务端提供的重置卡 | 二次确认与等待保护,由服务端判定结果 |
62
171
  | **重置后重试** | 等待已确认的短时额度重置,再继续请求 | 默认关闭;等待期间可停止 |
63
172
 
64
- <p align="center"><img src="docs/assets/usage-overview.png" width="460" alt="额度卡片里的使用概览与额度去向:累计 token、连续使用天数、最近半年每日用量热力图,以及每周额度按模型、入口、来源的拆分"></p>
173
+ </details>
65
174
 
66
- ### 图片与草图
175
+ <details>
176
+ <summary><b>图片与草图</b>(12 项)</summary>
177
+ <br>
67
178
 
68
179
  | 功能 | 你可以做什么 | 使用方式 / 条件 |
69
180
  | --- | --- | --- |
@@ -80,11 +191,15 @@
80
191
  | **Agent 绘图** | 用 `@sketch` 让 Agent 绘制,查看进度并随时停止;Agent 可用 SVG 整段作画,导入为可编辑原生笔画 | Beta;默认关闭,需同时开启画板 |
81
192
  | **绘图结果回看** | 完成后向模型返回画布预览,帮助后续检查 | Beta;默认关闭,会增加图片输入用量 |
82
193
 
83
- ### 进阶能力与维护
194
+ </details>
195
+
196
+ <details>
197
+ <summary><b>进阶能力与维护</b>(9 项)</summary>
198
+ <br>
84
199
 
85
200
  | 功能 | 你可以做什么 | 使用方式 / 条件 |
86
201
  | --- | --- | --- |
87
- | **手机远程控制** | 在 ChatGPT 手机 App 的 Codex 里直接指挥电脑上的 DSH:新建、续聊、归档对话,切换模型和权限,回答审批和提问,DeepSeek 等任意 DSH 模型都能用 | Beta(3.0.0-beta.5);默认关闭,设置 → Codex 订阅 → 模型与运行 → 手机远程控制,扫码或输入手动码配对 |
202
+ | **手机远程控制** | 在 ChatGPT 手机 App 的 Codex 里直接指挥电脑上的 DSH:新建、续聊、归档对话,切换模型和权限,回答审批和提问,DeepSeek 等任意 DSH 模型都能用 | Beta(3.0.0-beta.7);默认关闭,设置 → Codex 订阅 → 模型与运行 → 手机远程控制,扫码或输入手动码配对 |
88
203
  | **Codex 独立子任务** | 复用订阅登录和工作区权限执行独立任务,可指定允许的模型与推理档位 | Beta;默认 DSH,Codex 需安装可选组件并配置模型选择 |
89
204
  | **子任务组件管理** | 在设置里安装、停用或卸载官方 Codex 子任务组件 | 由支持的 DSH 宿主管理,无需额外登录 |
90
205
  | **云端上下文压缩** | 使用 Codex 云端压缩继续长对话,同时保留 DSH 历史与原生压缩 | Beta / 实验性;默认关闭,开启的请求使用 SSE |
@@ -94,58 +209,9 @@
94
209
  | **支持诊断** | 查看功能前置条件和近期操作结果,一键复制约 1 KB 的精简报告(完整报告可另行复制);不含凭据和会话正文 | 设置 → 维护;未验证不代表故障 |
95
210
  | **预测缓存清理** | 查看并清理本地额度预测历史 | 保留登录、草稿、原图和共享依赖 |
96
211
 
97
- [详细用法、限制与可选组件 →](docs/GUIDE.zh-CN.md)
98
-
99
- ## 准备 DSH
100
-
101
- 已验证兼容 DSH `0.2.0-rc.2`(`latest` 与 `next` 通道),最低支持 `0.1.7-rc.2`;更早的 DSH 请先升级。
102
-
103
- 本插件支持软件包元数据中记录的最新版 DeepSeek Harness,并需要一个当前具有 Codex 使用资格的 ChatGPT 账户。
104
-
105
- - **推荐:官方桌面端**。DeepSeek 已发布 [DeepSeek Harness 桌面版](https://www.deepseek.com/harness/)(macOS Apple Silicon、Windows 64 位,预览版、开源),无需配置 Node.js,安装后按下文在插件页面添加本插件即可。桌面端与网页版、命令行使用同一套 DSH 插件系统;本插件的端到端验收主要在网页版上完成,桌面端遇到问题请[提交 issue](https://github.com/WSL043/dsh-codex-subscription/issues);
106
- - 需要 Linux,或想要社区便携分发:使用 [DSH-Portable](https://github.com/WSL043/DSH-Portable)。这是面向 Windows、macOS 和 Linux 的社区便携桌面分发;
107
- - 想按官方方式运行:查看 [DeepSeek Harness 官方说明](https://github.com/deepseek-ai/deepseek-harness#run)。
108
-
109
- ## 安装
110
-
111
- ### 在插件页面安装(推荐)
112
-
113
- 1. 打开 DSH 的 **插件 → 添加插件**。
114
- 2. 在 **包名或地址** 输入框中粘贴下面的包名(已带版本号):
115
-
116
- ```text
117
- dsh-codex-subscription@2.5.9
118
- ```
119
-
120
- 3. 点击 **安装**,等待安装完成;按页面提示操作,需要重启时先保存工作。
121
- 4. 打开 **设置 → Codex 订阅**,登录 ChatGPT,然后在会话中选择 Codex 模型。
122
-
123
- 为什么要带版本号:DSH 的插件页只会安装发布满 24 小时的版本,只填包名时,新版本发布后的第一天可能装到更早的旧版,而旧版不一定兼容你的 DSH。带上版本号可以直接装到这一版;发布一天后只填 `dsh-codex-subscription` 也会装到最新正式版。其他版本号见[发布页](https://github.com/WSL043/dsh-codex-subscription/releases)。
124
-
125
- <details>
126
- <summary>终端安装(已能运行 dsh 命令)</summary>
127
-
128
- ```sh
129
- dsh plugin --profile web add dsh-codex-subscription@2.5.9
130
- ```
131
-
132
- 安装完成后按提示重启 DSH,再到 **设置 → Codex 订阅** 登录。插件页面和终端均由 DSH 管理安装。
133
-
134
- </details>
135
-
136
- <details>
137
- <summary>Headless 任务</summary>
138
-
139
- 先在 Web 中完成登录并选择一次 Codex 模型,再把同一个插件安装到 Headless profile:
140
-
141
- ```sh
142
- dsh plugin --profile headless add dsh-codex-subscription@2.5.9
143
- dsh --profile headless "只回复:ok"
144
- ```
145
-
146
212
  </details>
147
213
 
148
- ## 界面一览
214
+ ## 界面演示
149
215
 
150
216
  <details>
151
217
  <summary><b>界面演示(动图,点击展开)</b>:选模型、提问、出图、标注、草图、额度与重置</summary>
@@ -153,32 +219,6 @@ dsh --profile headless "只回复:ok"
153
219
  <p align="center"><img src="docs/assets/readme-demo.webp" width="1100" alt="演示:登录 ChatGPT 选订阅模型、提问、用订阅出图、查看与标注、进入草图、查看额度与重置"></p>
154
220
  </details>
155
221
 
156
- ### 日常操作,留在输入框
157
-
158
- 选模型、调推理档位、切换速度档位,同时查看剩余额度。无需为每次请求打开设置。
159
-
160
- <p align="center"><img src="docs/assets/composer-astra-fast.png" width="800" alt="DSH 输入框:GPT-6-Astra Max、闪电标识的 Fast 高速模式与剩余额度"></p>
161
-
162
- ### 手机远程控制 DSH(Beta)
163
-
164
- 打开 **设置 → Codex 订阅 → 模型与运行 → 手机远程控制**,用 ChatGPT 手机 App 的 Codex 扫码配对,这台电脑上的 DSH 就会出现在手机的主机列表里:
165
-
166
- - 在手机上查看、新建、续聊、中断和归档 DSH 对话,DeepSeek、Codex 以及其他 DSH 模型都能用;
167
- - 在手机上切换模型、推理档位和权限(工作区内修改 / 完全访问);
168
- - DSH 需要批准工具调用或向你提问时,手机会直接弹出;手机掉线会在重新打开时再弹一次,否则交回电脑处理;
169
- - 回复边生成边显示在手机上;
170
- - 命令、文件修改(带差异)、任务清单、生成的图片在手机上用原生视图显示,文件修改的审批也能看到差异;
171
- - DSH 工作区对应手机上的项目,可在项目里新建对话,也能从手机分叉对话、看到用量额度和上下文用量;
172
- - 手机上可以用 @ 搜索电脑上的文件、用 / 选择 DSH 技能,也能在手机上查看改动(只读 git);还能搜索对话、在对话内搜关键词,并查看累计用量;
173
- - 设置里可以看到已配对的手机,并能随时移除;
174
- - 在手机上发图片(可配文字),DSH 生成的图片也会显示在手机上;
175
- - 手机的文件夹选择器能浏览这台电脑上真实的文件夹,新对话可以直接在其中开始;
176
- - 模型报错会显示在手机上。
177
-
178
- 默认关闭。电脑需要保持开机、联网并打开 DSH。这是 OpenAI 尚未向第三方开放的 ChatGPT 能力,随时可能变化,所以标为 Beta。目前在预发布版中提供:`dsh-codex-subscription@3.0.0-beta.5`。
179
-
180
- <p align="center"><img src="docs/assets/remote-control.png" width="560" alt="官方桌面版设置页:手机远程控制已连接,显示配对二维码和手动码"></p>
181
-
182
222
  ### 账号与偏好,集中管理
183
223
 
184
224
  在 **设置 → Codex 订阅** 中管理账号和额度;其余功能按任务需要开启。
@@ -297,9 +337,9 @@ SVG 版在“这次只在草图画布上画”之前多加了一句:
297
337
  <details>
298
338
  <summary>查看图片与草图设置</summary>
299
339
 
300
- <p align="center"><img src="docs/assets/real-creative-zh.png" width="564" alt="图片与草图设置:生图编辑、请求型号,以及独立的画板和 Agent 开关"></p>
340
+ <p align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/assets/ui-creative-zh-dark.png"><img src="docs/assets/ui-creative-zh-light.png" width="564" alt="图片与草图设置:图片生成与编辑、创作入口、请求型号与质量"></picture></p>
301
341
 
302
- DSH 实机设置截图。
342
+ DSH 实机设置截图,随浅色 / 深色主题切换。
303
343
 
304
344
  </details>
305
345
 
@@ -307,14 +347,6 @@ DSH 实机设置截图。
307
347
 
308
348
  [图片编辑与草稿保存 →](docs/GUIDE.zh-CN.md#图片生成与编辑beta)
309
349
 
310
- <a id="codex-subtask-runtime"></a>
311
-
312
- ## 按需扩展
313
-
314
- 普通订阅聊天无需 Codex CLI。需要 **Codex 独立子任务** 时,可在“模型与运行”中点击“安装组件”,停用和卸载也在同一处完成。其余实验选项按需开启。
315
-
316
- [可选组件、存储清理与完整使用指南 →](docs/GUIDE.zh-CN.md)
317
-
318
350
  ## 更新与卸载
319
351
 
320
352
  在 DSH **插件** 页面找到本插件,使用更新或卸载操作,完成后按页面提示重启。卸载插件不会删除其他插件。
@@ -323,7 +355,7 @@ DSH 实机设置截图。
323
355
  <summary>终端方式</summary>
324
356
 
325
357
  ```sh
326
- dsh plugin --profile web update dsh-codex-subscription@2.5.9
358
+ dsh plugin --profile web update dsh-codex-subscription@2.6.8
327
359
  ```
328
360
 
329
361
  仅在需要卸载时运行:
@@ -336,13 +368,6 @@ dsh plugin --profile web remove dsh-codex-subscription
336
368
 
337
369
  ## 常见问题
338
370
 
339
- <details>
340
- <summary>DSH 0.2.0-rc.2 提示插件不兼容?</summary>
341
-
342
- 2.2.10 之前的所有版本发布时 DSH 0.2.0-rc.2 还不存在,其兼容声明无法事后修改。请安装 2.2.10 或更高版本(只填包名时,发布满 24 小时前可能装到旧版,请直接填 `dsh-codex-subscription@2.2.10`):在 **插件** 页面更新,或运行 `dsh plugin --profile web update dsh-codex-subscription@2.5.9`,然后重启 DSH。
343
-
344
- </details>
345
-
346
371
  <details>
347
372
  <summary>和官方插件列表里的 Codex 组合包(dsh-hooks-codex)是什么关系?</summary>
348
373
 
@@ -373,15 +398,12 @@ dsh plugin --profile web remove dsh-codex-subscription
373
398
 
374
399
  </details>
375
400
 
376
- ## 当前图标
377
-
378
- 这是当前使用的插件图标,以及它在 DSH「已安装」列表中的实际显示效果。
379
-
380
- <p align="center"><img src="icon-subscription.webp" width="200" height="200" alt="当前 Codex 订阅插件图标"></p>
401
+ <details>
402
+ <summary>需要装 Codex 命令行才能用吗?</summary>
381
403
 
382
- <p align="center"><img src="docs/assets/plugin-installed.png" width="800" alt="DSH 已安装列表中的 Codex 订阅插件卡片"></p>
404
+ 普通订阅聊天不需要。只有想用 **Codex 独立子任务** 时,才在“设置 → Codex 订阅 → 模型与运行”里点“安装组件”,停用和卸载也在同一处。其余实验选项按需开启。[可选组件、存储清理与完整说明 →](docs/GUIDE.zh-CN.md)
383
405
 
384
- <sub>DSH 0.1.7-alpha.2 实机界面。</sub>
406
+ </details>
385
407
 
386
408
  ## 定位与边界
387
409
 
@@ -7,6 +7,7 @@
7
7
  ],
8
8
  "previews": [
9
9
  "0.2.1-alpha.1",
10
- "0.2.1-alpha.2"
10
+ "0.2.1-alpha.2",
11
+ "0.2.1-alpha.3"
11
12
  ]
12
13
  }
package/lib/client.js CHANGED
@@ -9282,7 +9282,7 @@ window.__ModuleLoader__.load({
9282
9282
  }
9283
9283
  //#endregion
9284
9284
  //#region src/version.js
9285
- const PACKAGE_VERSION = "3.0.0-beta.5";
9285
+ const PACKAGE_VERSION = "3.0.0-beta.7";
9286
9286
  //#endregion
9287
9287
  //#region src/client-recovery.js
9288
9288
  async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
package/lib/index.js CHANGED
@@ -12,7 +12,7 @@ import { clientRequestSchema } from "@deepseek-ai/dsh-client-connection";
12
12
  import { lstat, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
13
13
  import { constants, readFileSync, readdirSync, statSync } from "node:fs";
14
14
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
15
- import { pathToFileURL } from "node:url";
15
+ import { fileURLToPath, pathToFileURL } from "node:url";
16
16
  import { defineTool } from "@deepseek-ai/dsh-tools";
17
17
  import * as dshLlm from "@deepseek-ai/dsh-llm";
18
18
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
@@ -54,6 +54,37 @@ function createSettingsAdapter(ctx, config) {
54
54
  };
55
55
  }
56
56
  //#endregion
57
+ //#region src/request-observer.js
58
+ const failureOf = (chunk) => chunk?.reason?.kind === "error" ? chunk.reason.failure : void 0;
59
+ function observed(stream, options, provider) {
60
+ return (async function* () {
61
+ try {
62
+ for await (const chunk of stream(options)) {
63
+ const failure = failureOf(chunk);
64
+ if (failure !== void 0 && !options?.signal?.aborted) provider.noteAdapterFailure?.("failure", options, failure);
65
+ yield chunk;
66
+ }
67
+ } catch (error) {
68
+ if (!options?.signal?.aborted) provider.noteAdapterFailure?.("thrown", options, error);
69
+ throw error;
70
+ }
71
+ })();
72
+ }
73
+ function observeAdapter(adapter, provider) {
74
+ return new Proxy(adapter, { get(target, property) {
75
+ if (property === "stream") return (options) => observed((o) => target.stream(o), options, provider);
76
+ if (property === "prepareCall") return async (...args) => {
77
+ const call = await target.prepareCall(...args);
78
+ return {
79
+ ...call,
80
+ stream: (options) => observed((o) => call.stream(o), options, provider)
81
+ };
82
+ };
83
+ const value = Reflect.get(target, property, target);
84
+ return typeof value === "function" ? value.bind(target) : value;
85
+ } });
86
+ }
87
+ //#endregion
57
88
  //#region src/default-model.js
58
89
  /**
59
90
  * DSH owns the model a freshly created agent starts on. This module is the
@@ -1368,7 +1399,8 @@ const SUPPORTED_RUNTIME_VERSIONS = Object.freeze([
1368
1399
  "0.2.0-rc.1",
1369
1400
  "0.2.0-rc.2",
1370
1401
  "0.2.1-alpha.1",
1371
- "0.2.1-alpha.2"
1402
+ "0.2.1-alpha.2",
1403
+ "0.2.1-alpha.3"
1372
1404
  ]);
1373
1405
  const require = createRequire(import.meta.url);
1374
1406
  const execute = promisify(execFile);
@@ -2126,7 +2158,7 @@ function inheritedOriginalImageRef(session, assetId) {
2126
2158
  }
2127
2159
  //#endregion
2128
2160
  //#region src/version.js
2129
- const PACKAGE_VERSION = "3.0.0-beta.5";
2161
+ const PACKAGE_VERSION = "3.0.0-beta.7";
2130
2162
  const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
2131
2163
  //#endregion
2132
2164
  //#region src/codex-images.js
@@ -3750,7 +3782,7 @@ const SKIP = /* @__PURE__ */ new Set([
3750
3782
  "coverage"
3751
3783
  ]);
3752
3784
  const MAX_ENTRIES = 3e4;
3753
- const MAX_DEPTH = 8;
3785
+ const MAX_DEPTH$1 = 8;
3754
3786
  const BUDGET_MS = 2e3;
3755
3787
  const RESULTS = 50;
3756
3788
  /** Subsequence match with a bonus for runs, word starts and the file name; undefined when the query is not in the text. */
@@ -3814,7 +3846,7 @@ async function searchFiles({ roots, query, folders, now = Date.now }) {
3814
3846
  score: match.score,
3815
3847
  indices: match.indices
3816
3848
  });
3817
- if (directory && !SKIP.has(entry.name) && depth < MAX_DEPTH) queue.push([join(folder, entry.name), depth + 1]);
3849
+ if (directory && !SKIP.has(entry.name) && depth < MAX_DEPTH$1) queue.push([join(folder, entry.name), depth + 1]);
3818
3850
  }
3819
3851
  }
3820
3852
  }
@@ -3937,7 +3969,7 @@ const emptyTurn = () => ({
3937
3969
  completedAt: null,
3938
3970
  durationMs: null
3939
3971
  });
3940
- const textOf = (content) => (content ?? []).map((block) => block?.type === "text" ? block.text : "").filter(Boolean).join("\n");
3972
+ const textOf$1 = (content) => (content ?? []).map((block) => block?.type === "text" ? block.text : "").filter(Boolean).join("\n");
3941
3973
  const inputText = (input) => (Array.isArray(input) ? input : []).map((part) => part?.type === "text" ? part.text : "").filter(Boolean).join("\n");
3942
3974
  /** Skills the phone picked, written the way DSH invokes a skill: a /name gesture ahead of the text. */
3943
3975
  const skillGestures = (input) => (Array.isArray(input) ? input : []).filter((part) => part?.type === "skill" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(String(part.name))).map((part) => `/${part.name}`);
@@ -3964,7 +3996,7 @@ function parseArguments(value) {
3964
3996
  }
3965
3997
  }
3966
3998
  /** What the person typed: DSH appends its own system reminders (skills, context) to user messages. */
3967
- const visibleText = (content) => textOf(content).replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gu, "").trim();
3999
+ const visibleText = (content) => textOf$1(content).replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gu, "").trim();
3968
4000
  /** Text of a user-role message the person wrote; DSH's own injected ones (runtime context, notices) carry another source kind. */
3969
4001
  const typedText = (message) => message.role === "user" && (message.source === void 0 || message.source?.kind === "user") ? visibleText(message.content) : "";
3970
4002
  const lines = (text) => String(text ?? "").split("\n");
@@ -4069,7 +4101,7 @@ function finishTool(item, message, imageUrls) {
4069
4101
  const failed = Boolean(message.isError);
4070
4102
  item.status = failed ? "failed" : "completed";
4071
4103
  if (item.type === "commandExecution") {
4072
- item.aggregatedOutput = textOf(message.content);
4104
+ item.aggregatedOutput = textOf$1(message.content);
4073
4105
  item.exitCode = failed ? 1 : 0;
4074
4106
  } else if (item.type === "imageGeneration") {
4075
4107
  const url = imageBlocks(message).map((block) => imageUrls.get(block.attachment.attachmentId)).find(Boolean);
@@ -4079,7 +4111,7 @@ function finishTool(item, message, imageUrls) {
4079
4111
  item.contentItems = toolOutput(message, imageUrls);
4080
4112
  item.success = !failed;
4081
4113
  } else if (item.type === "mcpToolCall") {
4082
- const text = textOf(message.content);
4114
+ const text = textOf$1(message.content);
4083
4115
  if (failed) item.error = { message: text.slice(0, 500) || "The connector tool failed" };
4084
4116
  else item.result = {
4085
4117
  content: [{
@@ -4093,7 +4125,7 @@ function finishTool(item, message, imageUrls) {
4093
4125
  const imageBlocks = (message) => (message.content ?? []).filter((block) => block?.type === "image" && block.attachment);
4094
4126
  const toolOutput = (message, imageUrls) => [{
4095
4127
  type: "inputText",
4096
- text: textOf(message.content)
4128
+ text: textOf$1(message.content)
4097
4129
  }, ...imageBlocks(message).map((block) => imageUrls.get(block.attachment.attachmentId)).filter(Boolean).map((imageUrl) => ({
4098
4130
  type: "inputImage",
4099
4131
  imageUrl
@@ -4210,7 +4242,7 @@ function thread({ id, title, preview, cwd, createdAt, updatedAt, running, turns
4210
4242
  */
4211
4243
  const PHONE_RETURN_MS = 12e4;
4212
4244
  const ADOPT_WITHIN_MS = 6e5;
4213
- function createDshRemoteControl({ controller, agents, permissions = () => void 0, workspaces = () => void 0, attachments = () => void 0, usage = () => void 0, usageProfile = () => void 0, projections = () => void 0, skills = () => void 0, llm = () => void 0, trace = () => {}, phoneReturnMs = PHONE_RETURN_MS, userAgent }) {
4245
+ function createDshRemoteControl({ controller, agents, permissions = () => void 0, workspaces = () => void 0, attachments = () => void 0, usage = () => void 0, usageProfile = () => void 0, projections = () => void 0, skills = () => void 0, commands = () => void 0, llm = () => void 0, trace = () => {}, phoneReturnMs = PHONE_RETURN_MS, userAgent }) {
4214
4246
  const subscribers = /* @__PURE__ */ new Map();
4215
4247
  const active = /* @__PURE__ */ new Map();
4216
4248
  const queues = /* @__PURE__ */ new Map();
@@ -5019,6 +5051,92 @@ function createDshRemoteControl({ controller, agents, permissions = () => void 0
5019
5051
  "turn/start": (params, { notify }) => titles.has(params?.threadId) ? titleTurn(params, notify) : startTurn(params, notify),
5020
5052
  "turn/interrupt": (params) => {
5021
5053
  service().cancel({ sessionId: params?.threadId });
5054
+ active.get(params?.threadId)?.abort?.abort();
5055
+ return {};
5056
+ },
5057
+ "thread/compact/start": async (params, { notify }) => {
5058
+ const id = params?.threadId;
5059
+ const registry = commands();
5060
+ const agent = await agentFor(id);
5061
+ if (typeof registry?.execute !== "function" || registry.find?.(agent, "compact") === void 0) throw new RpcError(-32601, "This DSH version cannot compact a conversation on request");
5062
+ if (active.has(id)) throw new RpcError(-32602, "A turn is already running in this conversation");
5063
+ subscribe(id, notify);
5064
+ const turn = emptyTurn();
5065
+ const abort = new AbortController();
5066
+ active.set(id, {
5067
+ turn,
5068
+ ended: void 0,
5069
+ abort
5070
+ });
5071
+ emit(id, "turn/started", {
5072
+ threadId: id,
5073
+ turn
5074
+ });
5075
+ emit(id, "thread/status/changed", {
5076
+ threadId: id,
5077
+ status: {
5078
+ type: "active",
5079
+ activeFlags: []
5080
+ }
5081
+ });
5082
+ (async () => {
5083
+ try {
5084
+ const result = (await registry.execute(agent, "/compact", [], abort.signal))?.result;
5085
+ if (result?.kind === "error") throw new Error(result.text || "Compaction failed");
5086
+ if (result?.sourceEventSeq !== void 0 && turn.items.length === 0) {
5087
+ const item = {
5088
+ type: "contextCompaction",
5089
+ id: randomUUID()
5090
+ };
5091
+ turn.items.push(item);
5092
+ emit(id, "item/started", {
5093
+ threadId: id,
5094
+ turnId: turn.id,
5095
+ item,
5096
+ startedAtMs: Date.now()
5097
+ });
5098
+ emit(id, "item/completed", {
5099
+ threadId: id,
5100
+ turnId: turn.id,
5101
+ item,
5102
+ completedAtMs: Date.now()
5103
+ });
5104
+ }
5105
+ turn.status = "completed";
5106
+ } catch (error) {
5107
+ turn.status = abort.signal.aborted ? "interrupted" : "failed";
5108
+ if (turn.status === "failed") {
5109
+ turn.error = {
5110
+ message: String(error?.message ?? "Compaction failed").slice(0, 500),
5111
+ codexErrorInfo: null,
5112
+ additionalDetails: null
5113
+ };
5114
+ emit(id, "error", {
5115
+ threadId: id,
5116
+ turnId: turn.id,
5117
+ willRetry: false,
5118
+ error: turn.error
5119
+ });
5120
+ }
5121
+ }
5122
+ turn.completedAt = seconds(Date.now());
5123
+ active.delete(id);
5124
+ compactions.delete(id);
5125
+ const usageNow = tokenUsage(agent.session);
5126
+ if (usageNow) emit(id, "thread/tokenUsage/updated", {
5127
+ threadId: id,
5128
+ turnId: turn.id,
5129
+ tokenUsage: usageNow
5130
+ });
5131
+ emit(id, "turn/completed", {
5132
+ threadId: id,
5133
+ turn
5134
+ });
5135
+ emit(id, "thread/status/changed", {
5136
+ threadId: id,
5137
+ status: { type: "idle" }
5138
+ });
5139
+ })();
5022
5140
  return {};
5023
5141
  },
5024
5142
  "turn/steer": async (params) => {
@@ -6329,8 +6447,134 @@ function createCodexRpcHandler(coordinator, options = {}) {
6329
6447
  };
6330
6448
  }
6331
6449
  //#endregion
6450
+ //#region src/failure-capture.js
6451
+ const KEEP = /* @__PURE__ */ new Set([
6452
+ "method",
6453
+ "type",
6454
+ "role",
6455
+ "name",
6456
+ "api",
6457
+ "provider",
6458
+ "model",
6459
+ "stopReason",
6460
+ "reasoning",
6461
+ "reasoningEffort",
6462
+ "transport",
6463
+ "mimeType",
6464
+ "kind",
6465
+ "effort",
6466
+ "toolChoice",
6467
+ "cacheRetention",
6468
+ "textVerbosity",
6469
+ "serviceTier"
6470
+ ]);
6471
+ const DROP = /* @__PURE__ */ new Set([
6472
+ "apiKey",
6473
+ "headers",
6474
+ "env",
6475
+ "authorization",
6476
+ "token",
6477
+ "signal",
6478
+ "fetch",
6479
+ "cookie"
6480
+ ]);
6481
+ const MAX_DEPTH = 10;
6482
+ const MAX_ARRAY = 40;
6483
+ const MAX_KEYS = 80;
6484
+ const MAX_NODES = 4e3;
6485
+ const MAX_TEXT$1 = 64;
6486
+ const UNDEFINED_MARKER = "$undefined";
6487
+ function redactRequest(value) {
6488
+ let nodes = 0;
6489
+ const walk = (item, key, depth) => {
6490
+ nodes += 1;
6491
+ if (nodes > MAX_NODES) return { $truncated: "nodes" };
6492
+ if (item === void 0) return { [UNDEFINED_MARKER]: true };
6493
+ if (item === null || typeof item === "boolean") return item;
6494
+ if (typeof item === "number") return Number.isFinite(item) ? item : { $number: String(item) };
6495
+ if (typeof item === "bigint" || typeof item === "symbol") return { $type: typeof item };
6496
+ if (typeof item === "function") return { $type: "function" };
6497
+ if (typeof item === "string") {
6498
+ if (KEEP.has(key) && item.length <= MAX_TEXT$1) return item;
6499
+ return item.length <= MAX_TEXT$1 ? "x".repeat(item.length) : { $text: item.length };
6500
+ }
6501
+ if (depth >= MAX_DEPTH) return { $truncated: "depth" };
6502
+ if (Array.isArray(item)) {
6503
+ const list = [];
6504
+ for (let index = 0; index < Math.min(item.length, MAX_ARRAY); index += 1) list.push(index in item ? walk(item[index], key, depth + 1) : { $hole: true });
6505
+ return item.length > MAX_ARRAY ? [...list, { $more: item.length - MAX_ARRAY }] : list;
6506
+ }
6507
+ if (item instanceof Uint8Array) return { $bytes: item.byteLength };
6508
+ if (typeof item === "object") {
6509
+ const entries = Object.keys(item).filter((name) => !DROP.has(name));
6510
+ const out = {};
6511
+ for (const name of entries.slice(0, MAX_KEYS)) out[name] = walk(item[name], name, depth + 1);
6512
+ if (entries.length > MAX_KEYS) out.$moreKeys = entries.length - MAX_KEYS;
6513
+ return out;
6514
+ }
6515
+ return { $type: typeof item };
6516
+ };
6517
+ return walk(value, "", 0);
6518
+ }
6519
+ //#endregion
6520
+ //#region src/pi-ai-compat.js
6521
+ const versionOf = (specifier) => {
6522
+ try {
6523
+ let path = fileURLToPath(import.meta.resolve(specifier));
6524
+ for (let depth = 0; depth < 6; depth += 1) {
6525
+ path = path.replace(/[\\/][^\\/]+$/u, "");
6526
+ try {
6527
+ const manifest = JSON.parse(readFileSync(`${path}/package.json`, "utf8"));
6528
+ if (typeof manifest.version === "string") return manifest.version;
6529
+ } catch {}
6530
+ }
6531
+ } catch {}
6532
+ };
6533
+ let cached;
6534
+ /** Versions of the pi-ai library and of DSH's adapter this plugin runs against. */
6535
+ function runtimeVersions() {
6536
+ cached ??= {
6537
+ piAi: versionOf("@earendil-works/pi-ai"),
6538
+ adapter: versionOf("@deepseek-ai/dsh-llm-pi-ai")
6539
+ };
6540
+ return cached;
6541
+ }
6542
+ const atLeast087 = (version) => {
6543
+ const match = /^(\d+)\.(\d+)/u.exec(version ?? "");
6544
+ return match !== null && (Number(match[1]) > 0 || Number(match[2]) >= 87);
6545
+ };
6546
+ /** Whether the installed pi-ai understands system messages inside the transcript. */
6547
+ const understandsSystemMessages = (version) => atLeast087(version);
6548
+ const textOf = (content) => typeof content === "string" ? content : Array.isArray(content) ? content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n") : "";
6549
+ /**
6550
+ * Fold the transcript's leading system messages back into `systemPrompt` and `tools`,
6551
+ * the form every pi-ai release accepts. Messages after the first non-system one stay.
6552
+ */
6553
+ function hoistLeadingSystem(context) {
6554
+ const messages = context?.messages;
6555
+ if (!Array.isArray(messages) || messages[0]?.role !== "system") return context;
6556
+ let end = 0;
6557
+ const texts = context.systemPrompt ? [context.systemPrompt] : [];
6558
+ const tools = [...Array.isArray(context.tools) ? context.tools : []];
6559
+ while (messages[end]?.role === "system") {
6560
+ const message = messages[end];
6561
+ const text = [textOf(message.content), ...Object.values(message.sections ?? {}).filter((value) => typeof value === "string")].filter(Boolean).join("\n\n");
6562
+ if (text !== "") texts.push(text);
6563
+ if (Array.isArray(message.toolsAdded)) tools.push(...message.toolsAdded);
6564
+ end += 1;
6565
+ }
6566
+ const { systemPrompt: _systemPrompt, tools: _tools, ...rest } = context;
6567
+ const systemPrompt = texts.join("\n\n");
6568
+ return {
6569
+ ...rest,
6570
+ ...systemPrompt === "" ? {} : { systemPrompt },
6571
+ ...tools.length === 0 ? {} : { tools },
6572
+ messages: messages.slice(end)
6573
+ };
6574
+ }
6575
+ //#endregion
6332
6576
  //#region src/transport-failure.js
6333
- const TRANSIENT_WEBSOCKET_CLOSE = /^WebSocket closed (?:1006|1011|1012|1013)(?:\s|$)/;
6577
+ const TRANSIENT_WEBSOCKET_CLOSE = /^WebSocket closed (?:1000|1006|1011|1012|1013)(?:\s|$)/;
6334
6578
  const GATEWAY_REPLAY_BUFFER_EXCEEDED = /^\s*exceeded request buffer limit while retrying upstream\s*$/;
6335
6579
  function normalizeTransportEvent(event, signal) {
6336
6580
  if (signal?.aborted || event?.type !== "error" || event.reason !== "error") return event;
@@ -6491,6 +6735,36 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
6491
6735
  sessionModel: 0,
6492
6736
  retries: 0
6493
6737
  };
6738
+ const streamFailure = {
6739
+ count: 0,
6740
+ stage: void 0,
6741
+ frame: void 0,
6742
+ request: void 0,
6743
+ adapter: void 0
6744
+ };
6745
+ const noteAdapterFailure = (kind, adapterOptions, failure) => {
6746
+ try {
6747
+ streamFailure.adapter = {
6748
+ kind,
6749
+ ...typeof failure?.code === "string" ? { code: failure.code.slice(0, 40) } : {},
6750
+ message: String(failure?.message ?? "").slice(0, 160),
6751
+ request: redactRequest(adapterOptions)
6752
+ };
6753
+ } catch {
6754
+ streamFailure.adapter = void 0;
6755
+ }
6756
+ };
6757
+ const noteStreamFailure = (stage, error, source) => {
6758
+ streamFailure.count += 1;
6759
+ streamFailure.stage = stage;
6760
+ try {
6761
+ streamFailure.request = source === void 0 ? void 0 : redactRequest(source);
6762
+ } catch {
6763
+ streamFailure.request = void 0;
6764
+ }
6765
+ const frame = typeof error?.stack === "string" ? /([\w.@-]+[\\/][\w.-]+\.[cm]?[jt]sx?):(\d+):(\d+)/u.exec(error.stack.split("\n").slice(1).join("\n")) : null;
6766
+ streamFailure.frame = frame ? `${frame[1].replaceAll("\\", "/")}:${frame[2]}:${frame[3]}` : void 0;
6767
+ };
6494
6768
  const reviewRoute = (model, context) => {
6495
6769
  if (resolveReviewModel() !== "official" || !isDshAutoReview(context)) return void 0;
6496
6770
  review.requests += 1;
@@ -6545,22 +6819,40 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
6545
6819
  };
6546
6820
  return { [Symbol.asyncIterator]: () => replay ??= run() };
6547
6821
  };
6548
- const networkIterable = (factory, options, direct = false) => {
6822
+ const networkIterable = (factory, options, direct = false, source) => {
6549
6823
  let iterator;
6550
6824
  let prepared;
6825
+ let stage = "prepare";
6551
6826
  const step = async (method, value) => {
6552
- const request = await (prepared ??= (direct ? void 0 : connection?.prepare(options)) ?? Promise.resolve({ options }));
6553
- const result = await runNetwork("model", () => {
6554
- iterator ??= factory(direct ? request.options : compaction?.requestOptions(request.options) ?? request.options)[Symbol.asyncIterator]();
6555
- return iterator[method]?.(value) ?? (method === "throw" ? Promise.reject(value) : Promise.resolve({
6556
- done: true,
6557
- value
6558
- }));
6559
- }, direct ? request.network : compaction?.networkOptions(request.network) ?? request.network);
6560
- return result.done ? result : {
6561
- ...result,
6562
- value: normalizeTransportEvent(result.value, request.options?.signal)
6563
- };
6827
+ try {
6828
+ const request = await (prepared ??= (direct ? void 0 : connection?.prepare(options)) ?? Promise.resolve({ options }));
6829
+ if (stage === "prepare") stage = "request";
6830
+ const result = await runNetwork("model", () => {
6831
+ const next = direct ? request.options : compaction?.requestOptions(request.options) ?? request.options;
6832
+ iterator ??= factory({
6833
+ ...next,
6834
+ async onPayload(payload, requestModel) {
6835
+ stage = "payload";
6836
+ return next.onPayload?.(payload, requestModel);
6837
+ }
6838
+ })[Symbol.asyncIterator]();
6839
+ return iterator[method]?.(value) ?? (method === "throw" ? Promise.reject(value) : Promise.resolve({
6840
+ done: true,
6841
+ value
6842
+ }));
6843
+ }, direct ? request.network : compaction?.networkOptions(request.network) ?? request.network);
6844
+ if (!result.done) {
6845
+ if (result.value?.type === "error" && !request.options?.signal?.aborted) noteStreamFailure(stage, void 0, source);
6846
+ else if (stage === "payload") stage = "stream";
6847
+ }
6848
+ return result.done ? result : {
6849
+ ...result,
6850
+ value: normalizeTransportEvent(result.value, request.options?.signal)
6851
+ };
6852
+ } catch (error) {
6853
+ noteStreamFailure(stage, error, source);
6854
+ throw error;
6855
+ }
6564
6856
  };
6565
6857
  return {
6566
6858
  [Symbol.asyncIterator]() {
@@ -6574,9 +6866,16 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
6574
6866
  throw: (error) => iterator ? step("throw", error) : Promise.reject(error)
6575
6867
  };
6576
6868
  };
6577
- const routed = (method, model, context, options) => {
6869
+ const routed = (method, model, transcript, options) => {
6870
+ const context = understandsSystemMessages(runtimeVersions().piAi) ? transcript : hoistLeadingSystem(transcript);
6578
6871
  const route = reviewRoute(model, context);
6579
- const create = () => route === void 0 ? networkIterable((prepared) => provider[method](model, context, prepared), withPreferences(model, options)) : networkIterable((prepared) => provider[method](model, context, prepared), withReview(route, options), true);
6872
+ const source = {
6873
+ method,
6874
+ model,
6875
+ context,
6876
+ options
6877
+ };
6878
+ const create = () => route === void 0 ? networkIterable((prepared) => provider[method](model, context, prepared), withPreferences(model, options), false, source) : networkIterable((prepared) => provider[method](model, context, prepared), withReview(route, options), true, source);
6580
6879
  return isDshAutoReview(context) ? reviewIterable(create) : create();
6581
6880
  };
6582
6881
  return Object.freeze({
@@ -6588,7 +6887,9 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
6588
6887
  getModels,
6589
6888
  stream: (model, context, options) => routed("stream", model, context, options),
6590
6889
  streamSimple: (model, context, options) => routed("streamSimple", model, context, options),
6591
- reviewCounters: () => ({ ...review })
6890
+ reviewCounters: () => ({ ...review }),
6891
+ streamFailure: () => ({ ...streamFailure }),
6892
+ noteAdapterFailure
6592
6893
  });
6593
6894
  }
6594
6895
  Object.freeze([
@@ -7533,6 +7834,15 @@ function safeRequests(network) {
7533
7834
  }
7534
7835
  return result;
7535
7836
  }
7837
+ /** pi-ai and DSH's adapter come from the host; a profile can pair an adapter with an older library, so the report names both. */
7838
+ function libraryVersions() {
7839
+ const { piAi, adapter } = runtimeVersions();
7840
+ const valid = (value) => typeof value === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]{1,32})?$/u.test(value);
7841
+ return {
7842
+ ...valid(piAi) ? { piAi } : {},
7843
+ ...valid(adapter) ? { adapter } : {}
7844
+ };
7845
+ }
7536
7846
  /** Build a support report that deliberately excludes OAuth and account metadata. */
7537
7847
  async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog, connection, compaction, review, inspectionOptions, operations, runtimeManagement, storage, tools }) {
7538
7848
  const collected = {};
@@ -7598,6 +7908,26 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
7598
7908
  "retries"
7599
7909
  ]) : void 0
7600
7910
  ],
7911
+ [
7912
+ "streamFailure",
7913
+ "transport",
7914
+ () => {
7915
+ const value = review?.streamFailure?.();
7916
+ if (!Number.isSafeInteger(value?.count)) return void 0;
7917
+ return {
7918
+ count: value.count,
7919
+ ...[
7920
+ "prepare",
7921
+ "request",
7922
+ "payload",
7923
+ "stream"
7924
+ ].includes(value.stage) ? { stage: value.stage } : {},
7925
+ ...typeof value.frame === "string" && /^[\w.@/-]{1,120}:\d+:\d+$/u.test(value.frame) ? { frame: value.frame } : {},
7926
+ ...value.request && typeof value.request === "object" && JSON.stringify(value.request).length <= 12e4 ? { request: value.request } : {},
7927
+ ...value.adapter && typeof value.adapter === "object" && JSON.stringify(value.adapter).length <= 12e4 ? { adapter: value.adapter } : {}
7928
+ };
7929
+ }
7930
+ ],
7601
7931
  [
7602
7932
  "operations",
7603
7933
  "transport",
@@ -7691,7 +8021,8 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
7691
8021
  node: process.version,
7692
8022
  platform: process.platform,
7693
8023
  arch: process.arch,
7694
- ...hostDshVersion() ? { dsh: hostDshVersion() } : {}
8024
+ ...hostDshVersion() ? { dsh: hostDshVersion() } : {},
8025
+ ...libraryVersions()
7695
8026
  },
7696
8027
  account,
7697
8028
  login,
@@ -7701,6 +8032,7 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
7701
8032
  ...collected.websocket ? { websocket: collected.websocket } : {},
7702
8033
  ...collected.compaction ? { compaction: collected.compaction } : {},
7703
8034
  ...collected.review ? { review: collected.review } : {},
8035
+ ...collected.streamFailure ? { streamFailure: collected.streamFailure } : {},
7704
8036
  ...catalog && ["unavailable", "online"].includes(catalog.source) && [
7705
8037
  "idle",
7706
8038
  "refreshing",
@@ -9988,7 +10320,8 @@ function apply(ctx, config = {}) {
9988
10320
  let resolved;
9989
10321
  try {
9990
10322
  resolved = await resolveAuth();
9991
- } catch {
10323
+ } catch (error) {
10324
+ if (error?.code === "MISSING_CREDENTIAL") throw new dshLlm.LlmError("ChatGPT subscription is not signed in", "MISSING_CREDENTIAL");
9992
10325
  throw new dshLlm.LlmError("ChatGPT subscription authorization failed", "AUTH_FAILED");
9993
10326
  }
9994
10327
  if (typeof resolved?.auth.apiKey !== "string" || resolved.auth.apiKey.length === 0) throw new dshLlm.LlmError("ChatGPT subscription is not signed in", "MISSING_CREDENTIAL");
@@ -9997,7 +10330,7 @@ function apply(ctx, config = {}) {
9997
10330
  auth: adapterAuth,
9998
10331
  resolveAttachments: () => ctx.get?.("attachments")
9999
10332
  });
10000
- ctx.llm.registerAdapter([PROVIDER], compaction.wrapAdapter(adapter));
10333
+ ctx.llm.registerAdapter([PROVIDER], observeAdapter(compaction.wrapAdapter(adapter), provider));
10001
10334
  const currentAgent = () => ctx.get?.("agents")?.currentInitiator?.();
10002
10335
  const codexSearch = createCodexSearchProvider({
10003
10336
  resolvePreferences: () => readCapabilitySettings(settings.get()),
@@ -10047,6 +10380,7 @@ function apply(ctx, config = {}) {
10047
10380
  usageProfile: () => usageProfileReader,
10048
10381
  projections: () => ctx.get?.("sessionProjections"),
10049
10382
  skills: () => ctx.get?.("skills"),
10383
+ commands: () => ctx.get?.("commands"),
10050
10384
  llm: () => ctx.llm,
10051
10385
  trace: (entry) => {
10052
10386
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "3.0.0-beta.5",
3
+ "version": "3.0.0-beta.7",
4
4
  "description": "Use ChatGPT and Codex subscriptions in DeepSeek Harness with OAuth, quota, safe resets, web search, images, and Fast mode",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -76,23 +76,23 @@
76
76
  "node": "^22.19.0 || >=24.0.0"
77
77
  },
78
78
  "peerDependencies": {
79
- "@deepseek-ai/cordis": "4.0.4 || 4.0.5-alpha.1",
80
- "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
81
- "@deepseek-ai/dsh-client-connection": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
82
- "@deepseek-ai/dsh-client-locale": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
83
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
84
- "@deepseek-ai/dsh-client-ui-model-selection": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
85
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
86
- "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
87
- "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
88
- "@deepseek-ai/dsh-client-ui-tool": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
89
- "@deepseek-ai/dsh-credentials": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
90
- "@deepseek-ai/dsh-llm": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
91
- "@deepseek-ai/dsh-llm-pi-ai": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
92
- "@deepseek-ai/dsh-settings": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
93
- "@deepseek-ai/dsh-subagent-codex": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
94
- "@deepseek-ai/dsh-tools": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
95
- "@deepseek-ai/dsh-web": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2",
79
+ "@deepseek-ai/cordis": "4.0.4 || 4.0.5-alpha.1 || 4.0.5-alpha.2",
80
+ "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
81
+ "@deepseek-ai/dsh-client-connection": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
82
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
83
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
84
+ "@deepseek-ai/dsh-client-ui-model-selection": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
85
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
86
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
87
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
88
+ "@deepseek-ai/dsh-client-ui-tool": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
89
+ "@deepseek-ai/dsh-credentials": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
90
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
91
+ "@deepseek-ai/dsh-llm-pi-ai": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
92
+ "@deepseek-ai/dsh-settings": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
93
+ "@deepseek-ai/dsh-subagent-codex": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
94
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
95
+ "@deepseek-ai/dsh-web": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
96
96
  "@deepseek-ai/schemastery": "3.18.1 || ^3.18.2",
97
97
  "@earendil-works/pi-ai": "0.85.1 || 0.87.1 || 1.1.0 || 1.2.0",
98
98
  "react": "^18.2.0",