@michengai/dsh-btw 0.1.0 → 0.1.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.
Files changed (24) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/CHANGELOG.zh-CN.md +11 -0
  3. package/README.md +22 -87
  4. package/README.zh-CN.md +22 -85
  5. package/assets/branding/dsh-btw-banner.png +0 -0
  6. package/lib/client.js +34 -7
  7. package/lib/index.js +113 -35
  8. package/lib/types/client/bubbles.d.ts +4 -1
  9. package/lib/types/locales.d.ts +2 -0
  10. package/lib/types/server/jobs.d.ts +16 -1
  11. package/package.json +1 -2
  12. package/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/00-/351/230/205/350/257/273/345/257/274/350/210/252.md +0 -12
  13. package/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/02-/345/275/223/345/211/215/347/212/266/346/200/201.md +0 -16
  14. package/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/03-/345/276/205/345/212/236/344/270/216/351/230/273/345/241/236.md +0 -11
  15. package/docs/01-/345/275/223/345/211/215/345/267/245/344/275/234/I005-/351/246/226/346/254/241/345/217/221/345/270/203/344/270/216/345/217/257/344/277/241/345/217/221/345/270/203/351/205/215/347/275/256/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -27
  16. package/docs/05-/345/267/245/347/250/213/344/272/244/344/273/230/01-/350/207/252/345/212/250/345/217/221/345/270/203.md +0 -58
  17. package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I001-/344/270/200/346/254/241/346/200/247/346/227/201/351/227/256/346/260/224/346/263/241/344/270/216/350/276/223/345/205/245/345/216/206/345/217/262/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -44
  18. package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I001-/344/270/200/346/254/241/346/200/247/346/227/201/351/227/256/346/260/224/346/263/241/344/270/216/350/276/223/345/205/245/345/216/206/345/217/262/01-/351/252/214/346/224/266/350/256/260/345/275/225.md +0 -32
  19. package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I002-/345/256/236/351/231/205DSH/346/217/222/344/273/266/346/216/245/345/205/245/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -45
  20. package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I002-/345/256/236/351/231/205DSH/346/217/222/344/273/266/346/216/245/345/205/245/01-/346/265/213/350/257/225/347/216/257/345/242/203/351/232/224/347/246/273.md +0 -38
  21. package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I002-/345/256/236/351/231/205DSH/346/217/222/344/273/266/346/216/245/345/205/245/02-/345/256/242/346/210/267/347/253/257/346/263/250/345/205/245/344/270/216/346/260/224/346/263/241/345/256/275/345/272/246/344/277/256/345/244/215.md +0 -28
  22. package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I003-/345/256/277/344/270/273/345/244/226/350/247/202/344/270/216/345/233/275/351/231/205/345/214/226/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -47
  23. package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I003-/345/256/277/344/270/273/345/244/226/350/247/202/344/270/216/345/233/275/351/231/205/345/214/226/01-/350/247/206/350/247/211/351/252/214/346/224/266.md +0 -35
  24. package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I004-/344/273/223/345/272/223/344/270/216/350/207/252/345/212/250/345/217/221/345/270/203/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -38
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ## [0.1.1] - 2026-09-06
6
+
7
+ - Limit each session to 20 side-question bubbles. At capacity, close the oldest completed bubble before accepting a new question; failed closure preserves answers and the current input with a localized message.
8
+ - Show localized retry messages when cleanup fails during closure and keep host error details in logs.
9
+ - Preserve completed answers when resource cleanup fails, with close retries still available.
10
+ - Bound startup cancellation and cleanup waits while retaining ownership of late child agents. Cleanup errors no longer block plugin unload, and unreleased children remain protected from tool execution.
11
+ - Retain recent bubbles per session so questions in another session cannot evict answers. Failed cleanup during eviction keeps the bubble available for retry.
12
+ - Format the community notice consistently as a blockquote in both READMEs.
13
+
3
14
  ## [0.1.0] - 2026-09-06
4
15
 
5
16
  - Add one-shot `/btw` questions that inherit completed turns from the main session and answer without executing tools.
@@ -1,5 +1,16 @@
1
1
  # 更新日志
2
2
 
3
+ ## [未发布]
4
+
5
+ ## [0.1.1] - 2026-09-06
6
+
7
+ - 限制每个会话最多保留 20 个旁问气泡;满额先关闭最旧的已完成气泡,成功后接收新问题,失败则保留答案和当前输入并显示中英文提示。
8
+ - 关闭清理失败时显示本地化重试提示,宿主错误详情仅记录日志。
9
+ - 修复资源清理失败覆盖已完成答案的问题,保留关闭重试能力。
10
+ - 为启动取消和资源清理增加有界等待,继续接管迟到的子代理;卸载异常不再阻塞插件退出,未释放子代理仍受工具保护。
11
+ - 按会话保留最近的旁问气泡,避免其他会话的提问挤掉答案;淘汰时清理失败会保留气泡供重试。
12
+ - 将中英文 README 的社区说明统一为引用块格式。
13
+
3
14
  ## [0.1.0] - 2026-09-06
4
15
 
5
16
  - 新增 `/btw` 一次性旁问,继承主会话已完成回合,只回答、不执行工具。
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="assets/branding/dsh-btw-banner.png" alt="DSH BTW" width="100%">
3
+ </p>
4
+
1
5
  <div align="center">
2
6
 
3
7
  # DSH BTW
@@ -12,23 +16,19 @@
12
16
 
13
17
  </div>
14
18
 
15
- Type `/btw your question` in the existing chat composer to get an answer based on your conversation's context. Answers appear in independent bubbles above the composer, ready for a quick explanation, recap, or follow-up question.
16
-
17
- DSH BTW is a community-maintained plugin for DSH Web and desktop apps that host the Web client. It does not occupy the sidebar, require Codex UI, or open a separate page.
19
+ > DSH BTW is a community-maintained DeepSeek Harness plugin, not an official DeepSeek AI product. Type `/btw your question` in the current conversation to get a context-aware answer in an independent bubble without interrupting the main task. Supports DSH Web and desktop apps that include DSH Web.
18
20
 
19
21
  ## Features
20
22
 
21
- - **Context-aware questions**: each question starts a one-shot child agent that inherits completed turns from the main session.
22
- - **Answers only**: all tools are disabled. The agent cannot read new files, browse the web, run commands, or edit code.
23
+ - **Context-aware questions**: explain concepts, recap conclusions, or ask a quick question about the current conversation.
24
+ - **Answers only**: no reading new files, browsing the web, running commands, or editing code.
23
25
  - **Independent answer bubbles**: render Markdown, copy answers, collapse, expand, and close individual bubbles. Multiple answers can stay visible.
24
- - **Separate from the main task**: answers are not added to the main model's history, and previous side questions do not become context for later ones.
25
- - **Cancellation and cleanup**: closing a running bubble cancels only that question. Cleanup failures remain visible so closing can be retried.
26
+ - **Keep the main task going**: side questions are answered separately and do not add answers to the main conversation.
27
+ - **Cancel anytime**: close a bubble while it is answering to cancel that question.
26
28
  - **Themes and languages**: follow DSH light and dark themes and switch between Chinese and English UI.
27
29
 
28
30
  ## Screenshots
29
31
 
30
- These screenshots were provided by the user from the actual DSH dark interface. Model responses shown in them illustrate the bubble UI only.
31
-
32
32
  ### Side questions in stock DSH
33
33
 
34
34
  The main conversation stays in place, with the side answer above the composer.
@@ -49,72 +49,43 @@ Each answer has its own copy, collapse or expand, and close controls in the uppe
49
49
 
50
50
  ## Prerequisites
51
51
 
52
- - A working DeepSeek Harness Web installation with `dsh` available in PowerShell.
53
- - The development baseline is DSH `0.1.2-rc.1`. The host must provide a `fork` child agent with context inheritance, tool filtering, and persona support.
54
- - Source builds require Node.js 22+ and npm.
55
- - Examples use the `web` profile; replace it with your target profile.
52
+ - An installed copy of DeepSeek Harness. The supported baseline is `0.1.2-rc.1`.
53
+ - Node.js 22+ and `dsh` available in your terminal.
56
54
 
57
55
  ## Installation
58
56
 
59
- Install from npm, local source, or a built package.
57
+ Examples use the `web` profile; replace it with your target profile. Disable other plugins that provide `/btw` before installing.
60
58
 
61
- Source repository: <https://github.com/MichengAI/dsh-btw>. Download `.tgz` packages from [Releases](https://github.com/MichengAI/dsh-btw/releases).
62
-
63
- ### From npm
59
+ ### From npm (Recommended)
64
60
 
65
61
  ```powershell
66
62
  [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
67
63
  $OutputEncoding = [System.Text.Encoding]::UTF8
68
64
 
69
65
  dsh plugin --profile web add @michengai/dsh-btw@latest --registry=https://registry.npmjs.org/
70
- dsh --profile web --dump-config
71
66
  ```
72
67
 
73
- Use `@0.1.0` instead of `@latest` to pin the first release. Reload DSH after installation as described below.
74
-
75
- ### From local source
76
-
77
- Run from the project root:
78
-
79
- ```powershell
80
- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
81
- $OutputEncoding = [System.Text.Encoding]::UTF8
82
-
83
- npm ci --ignore-scripts
84
- npm run build
85
- dsh plugin --profile web add . --ignore-scripts
86
- dsh --profile web --dump-config
87
- ```
88
-
89
- Confirm that the configuration includes `michengai-btw`. A local directory installation reads the package metadata and `cordis.patch.yml`; do not copy `lib` separately.
90
-
91
68
  ### From a package archive
92
69
 
93
- Build an archive with `npm pack` from the project root, then install it:
70
+ Download a `.tgz` from [Releases](https://github.com/MichengAI/dsh-btw/releases). Run this command from the download directory, replacing the filename with your downloaded version:
94
71
 
95
72
  ```powershell
96
73
  [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
97
74
  $OutputEncoding = [System.Text.Encoding]::UTF8
98
75
 
99
- npm pack
100
76
  dsh plugin --profile web add .\michengai-dsh-btw-0.1.0.tgz --ignore-scripts
101
- dsh --profile web --dump-config
102
77
  ```
103
78
 
104
- For an existing `.tgz`, substitute its path in the installation command. Disable other plugins that claim `/btw` before installing.
105
-
106
79
  ### Reloading
107
80
 
108
- DSH Codex Desktop may automatically reload its service when plugin configuration changes. Install or update after current tasks have finished. Other DSH Web launch methods require a manual restart.
109
-
110
- Rebuilding a linked plugin does not reload the backend. The current host's `patchReload: live` watches configuration, while backend HMR with `root: []` does not watch plugin JavaScript. The frontend can show new labels while the backend still serves the old command directory. Use the desktop app's reload action, or restart the relevant `dsh web` process, after tasks finish. Refreshing the browser alone does not update backend code.
81
+ Install or update after current tasks finish, as desktop apps may reload automatically. If the change has not taken effect, use the desktop app's reload action or restart the `dsh web` service. Refreshing the browser alone is not enough.
111
82
 
112
83
  ## Usage
113
84
 
114
85
  In a conversation with existing context, enter:
115
86
 
116
87
  ```text
117
- /btw Why does the proposed design use a one-shot child agent?
88
+ /btw Summarize the proposed plan in one sentence.
118
89
  ```
119
90
 
120
91
  | Goal | Action |
@@ -129,25 +100,10 @@ In a conversation with existing context, enter:
129
100
 
130
101
  Use the regular conversation to run commands, edit code, or continue the main task. BTW can only answer using existing context.
131
102
 
132
- Arrow-key input history belongs to Codex UI. BTW does not collect input history, access history storage, or bind arrow keys. Installing BTW alone adds side questions only.
103
+ ### Usage Notes
133
104
 
134
- ## Compatibility and Boundaries
135
-
136
- The development baseline is DSH `0.1.2-rc.1`. The host needs commands, tools, subagents, and a fork provider supporting tool filtering, persona, and inherited context. Requests fail when these capabilities are unavailable. The client needs conversation, input-trigger, chat, api-remotes, locale, and host theme tokens.
137
-
138
- The child agent receives an empty tool allowlist. An execution guard also rejects all tools, including `run_code` and tools registered within child scopes.
139
-
140
- Forks inherit completed turns, excluding the turn currently being generated. Disposing a child agent does not delete host audit logs. Command results are not added to the main model history, but the host may retain command and child-agent records.
141
-
142
- Internal commands `btw-run` and `btw-close` are hidden from the command directory. They use the host's session RPC rather than adding HTTP endpoints. Users only need `/btw` and the bubble controls.
143
-
144
- `btw-run` accepts JSON `{ id, question, locale? }`. Supported locales are `zh` and `en`; omission defaults to Chinese for older clients, and other languages fall back to English. The response uses the host command's `{ kind, text }` format. `btw-close` accepts the raw request identifier; cleanup errors retain the request's language.
145
-
146
- Each question is limited to 8,000 characters, with a 90-second timeout and at most eight concurrent requests.
147
-
148
- Bubbles are stored in client memory only and are not restored after a refresh. Appearance follows the current DSH theme, independently of the operating system's dark-mode preference. UI translations update through the host locale service; server messages use the language at submission time. Model answers and original external errors are not translated.
149
-
150
- Local installation and outstanding acceptance checks are tracked in [Current Status](docs/00-交接入口/02-当前状态.md). Runtime and simulated-host tests do not replace actual DSH model and interaction checks.
105
+ - Each question is independent and uses completed content from the main conversation at the time you ask. It does not include an answer still being generated or previous side questions.
106
+ - Bubbles are not restored after refreshing the page. Copy any answers you want to keep first.
151
107
 
152
108
  ## Uninstallation
153
109
 
@@ -170,34 +126,13 @@ npm ci --ignore-scripts
170
126
  npm run check
171
127
  ```
172
128
 
173
- `npm run check` runs type checking, unit and release-script tests, and a build. `npm run dev` provides a simulated host with mock answers for development, without connecting to a real model. DSH remains the actual plugin entry point. Theme CSS and LocaleRuntime come from the matching official development dependencies.
129
+ `npm run check` runs type checking, tests, and a build. After building, use `npm run test:browser` for browser tests; Microsoft Edge is required locally. `npm run dev` provides a UI preview with mock answers.
174
130
 
175
- `npm run test:browser` checks bubbles, cancellation, narrow layouts, Markdown, themes, contrast, language switching, and the absence of arrow-key interception.
176
-
177
- Build before running browser tests. Playwright manages `npm run dev` automatically and uses Microsoft Edge locally and Chromium in CI. `tests/host.test.ts` loads the host from the `node_modules` directory specified by `DSH_RUNTIME_ROOT`, defaulting to `.dsh/profiles/node_modules` under the user's home directory. The two tests are explicitly skipped when that host is absent; they do not read credentials or configuration. CI points at this project's development dependencies and executes the runtime tests.
178
-
179
- `tests/command-visibility.test.ts` uses a real Typert Registry, Gateway, and command runtime to check directory filtering, execution, and unload restoration. An installed desktop runtime can also be tested by setting `DSH_DESKTOP_RUNTIME_ROOT`. Passing these tests does not prove that a running DSH process has reloaded the latest build.
131
+ To install from source, run `dsh plugin --profile web add . --ignore-scripts` from the project directory, then reload DSH.
180
132
 
181
133
  ## References and Credits
182
134
 
183
- The side-question interaction was informed by [JasonQQ/dsh-btw-plugin](https://github.com/JasonQQ/dsh-btw-plugin), the close lifecycle by [kaieye/dsh-AIR](https://github.com/kaieye/dsh-AIR), and boundary handling by the Pi plugin below. This plugin is implemented independently.
184
-
185
- References from [`@narumitw/pi-btw` 0.57.0](https://pi.dev/packages/@narumitw/pi-btw) and its [source](https://github.com/narumiruna/pi-extensions/tree/b4981b29604945d67ce2ce04e0f769c62bce1f20/packages/pi-btw):
186
-
187
- - Snapshot context at question time instead of continuously following the main task. BTW uses a fork of completed turns.
188
- - Recheck cancellation and reject late answers. BTW checks cancellation and request identity on both client and server.
189
- - Do not write answers back to the main task by default. BTW only displays independent bubbles and does not bind history-navigation keys.
190
-
191
- Pi calls the model directly without tools; BTW uses one-shot child agents. Pi's full-screen TUI, multi-turn recovery, and write-back features are not included.
192
-
193
- ## Project Documentation
194
-
195
- The engineering documents are maintained in Chinese:
196
-
197
- - [Reading Guide](docs/00-交接入口/00-阅读导航.md)
198
- - [Current Status](docs/00-交接入口/02-当前状态.md)
199
- - [Outstanding Work](docs/00-交接入口/03-待办与阻塞.md)
200
- - [Automated Testing and Publishing](docs/05-工程交付/01-自动发布.md)
135
+ Interaction design was informed by [dsh-btw-plugin](https://github.com/JasonQQ/dsh-btw-plugin), [dsh-AIR](https://github.com/kaieye/dsh-AIR), and [pi-btw](https://pi.dev/packages/@narumitw/pi-btw). This plugin is implemented independently.
201
136
 
202
137
  ## License
203
138
 
package/README.zh-CN.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="assets/branding/dsh-btw-banner.png" alt="DSH BTW" width="100%">
3
+ </p>
4
+
1
5
  <div align="center">
2
6
 
3
7
  # DSH BTW
@@ -12,23 +16,19 @@
12
16
 
13
17
  </div>
14
18
 
15
- 在原有聊天输入框输入 `/btw 问题`,根据当前会话的已有上下文获取回答。答案显示在输入框上方的独立气泡中,方便临时解释概念、回顾结论或追问原因。
16
-
17
- DSH BTW 是社区维护的 DSH Web 插件,也可用于承载 Web 客户端的桌面容器。不占用侧边栏,不依赖 Codex UI,无需打开独立页面。
19
+ > DSH BTW 是社区维护的 DeepSeek Harness 插件,并非 DeepSeek AI 官方产品。在当前会话中输入 `/btw 问题`,即可基于已有上下文获得独立气泡回答,不打断主任务。支持 DSH Web 及集成 DSH Web 的桌面应用。
18
20
 
19
21
  ## 功能概览
20
22
 
21
- - **上下文旁问**:每次提问创建一次性子代理,继承主会话已完成回合。
22
- - **只回答,不执行**:禁用全部工具,不读取新文件、联网、运行命令或修改代码。
23
+ - **上下文旁问**:根据当前会话内容解释概念、回顾结论或回答临时问题。
24
+ - **只回答,不执行**:不会读取新文件、联网、运行命令或修改代码。
23
25
  - **独立答案气泡**:支持 Markdown、复制、折叠、展开和关闭,可同时查看多条旁问。
24
- - **主任务保持独立**:答案不回填主模型历史,上一条旁问也不会成为下一条的上下文。
25
- - **取消与清理**:关闭运行中的气泡只取消对应旁问;清理失败时保留提示,支持再次关闭。
26
+ - **不打断主任务**:旁问单独作答,答案不会写入主会话。
27
+ - **随时取消**:关闭正在回答的气泡即可取消该旁问。
26
28
  - **主题与国际化**:跟随 DSH 浅色、深色主题,界面支持中文和英文切换。
27
29
 
28
30
  ## 界面预览
29
31
 
30
- 以下为用户提供的实际 DSH 深色界面截图,图中模型回答仅用于展示气泡效果。
31
-
32
32
  ### 原生 DSH 中的旁问
33
33
 
34
34
  主会话保留在原来的位置,旁问答案位于输入框上方。
@@ -49,71 +49,42 @@ DSH BTW 是社区维护的 DSH Web 插件,也可用于承载 Web 客户端的
49
49
 
50
50
  ## 前置条件
51
51
 
52
- - 已能正常使用 DeepSeek Harness Web,并且可在 PowerShell 中执行 `dsh`。
53
- - 开发基线为 DSH `0.1.2-rc.1`,宿主需要支持继承上下文的 `fork` 子代理、工具过滤及 persona。
54
- - 从源码构建需要 Node.js 22+ 和 npm。
55
- - 以下命令使用 `web` profile,请按实际环境替换。
52
+ - 已安装 DeepSeek Harness,适配版本为 `0.1.2-rc.1`。
53
+ - Node.js 22+,可在终端执行 `dsh`。
56
54
 
57
55
  ## 安装
58
56
 
59
- 支持从 npm、本地源码或构建包安装。
60
-
61
- 源码仓库:<https://github.com/MichengAI/dsh-btw>。也可从 [Releases](https://github.com/MichengAI/dsh-btw/releases) 下载 `.tgz` 安装包。
57
+ 以下命令使用 `web` profile,请按实际环境替换。安装前停用其他提供 `/btw` 命令的插件。
62
58
 
63
- ### 从 npm 安装
59
+ ### 从 npm 安装(推荐)
64
60
 
65
61
  ```powershell
66
62
  [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
67
63
  $OutputEncoding = [System.Text.Encoding]::UTF8
68
64
 
69
65
  dsh plugin --profile web add @michengai/dsh-btw@latest --registry=https://registry.npmjs.org/
70
- dsh --profile web --dump-config
71
- ```
72
-
73
- 将 `@latest` 替换为 `@0.1.0` 可固定首个版本。安装后按下文说明重新加载 DSH。
74
-
75
- ### 从本地源码安装
76
-
77
- 在本项目根目录执行:
78
-
79
- ```powershell
80
- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
81
- $OutputEncoding = [System.Text.Encoding]::UTF8
82
-
83
- npm ci --ignore-scripts
84
- npm run build
85
- dsh plugin --profile web add . --ignore-scripts
86
- dsh --profile web --dump-config
87
66
  ```
88
67
 
89
- 检查配置中是否包含 `michengai-btw`。本地目录安装会读取包信息和 `cordis.patch.yml`,无需手工复制 `lib`。
90
-
91
68
  ### 从安装包安装
92
69
 
93
- 在本项目根目录执行 `npm pack` 生成安装包,然后安装:
70
+ [Releases](https://github.com/MichengAI/dsh-btw/releases) 下载 `.tgz` 文件,在下载目录执行以下命令,将文件名替换为实际版本:
94
71
 
95
72
  ```powershell
96
73
  [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
97
74
  $OutputEncoding = [System.Text.Encoding]::UTF8
98
- npm pack
99
75
  dsh plugin --profile web add .\michengai-dsh-btw-0.1.0.tgz --ignore-scripts
100
- dsh --profile web --dump-config
101
76
  ```
102
77
 
103
- 已有 `.tgz` 文件时,直接将安装命令中的路径替换为该文件路径。安装前停用其他占用 `/btw` 的插件。
104
-
105
78
  ### 重新加载
106
79
 
107
- DSH Codex Desktop 可能在检测到插件变更后自动重载服务,请在当前任务结束后安装或更新。其他 DSH Web 启动方式需要自行重启。
108
-
109
- 本地开发链接的 JS 重新构建不等于后端已经重载。当前宿主的 `patchReload: live` 仅监听配置,后端 HMR 的 `root: []` 不监听插件 JS;前端可能已出现新文案,后端仍运行旧命令目录。升级后须等任务结束,重新加载实际使用的后端:桌面版使用其“重新加载”,独立 `dsh web` 则重启该命令对应的进程。仅刷新浏览器不能更新后端。
80
+ 请在当前任务结束后安装或更新,桌面应用可能自动重载。若未生效,使用桌面应用的“重新加载”,或重启 `dsh web` 服务;仅刷新浏览器不够。
110
81
 
111
82
  ## 使用
112
83
 
113
84
  在已有上下文的会话中输入:
114
85
 
115
86
  ```text
116
- /btw 刚才这个方案为什么采用一次性子代理?
87
+ /btw 用一句话总结刚才的方案
117
88
  ```
118
89
 
119
90
  | 目标 | 操作 |
@@ -128,25 +99,10 @@ DSH Codex Desktop 可能在检测到插件变更后自动重载服务,请在
128
99
 
129
100
  若需要执行命令、修改代码或继续主任务,请通过普通会话提交。BTW 只能依据已有上下文作答。
130
101
 
131
- 上下键输入历史已迁至 Codex UI。本插件不再采集输入历史、访问历史存储或绑定上下键;单独安装只提供 BTW 旁问。
132
-
133
- ## 兼容与边界
134
-
135
- 开发基线为 DSH `0.1.2-rc.1`。需要宿主装配 commands、tools、subagents 及 fork provider,且 provider 必须支持工具过滤、persona 与上下文继承;缺少能力时拒绝启动。客户端需要 conversation、input-trigger、chat、api-remotes、locale 模块及宿主主题令牌。
136
-
137
- 子代理使用空工具白名单,并由执行层 guard 拒绝全部工具,包括 `run_code` 和子作用域自注册工具。
138
-
139
- fork 继承的是已完成回合,主任务正在生成的回合不包含在内。一次性子代理的释放不等于删除宿主的审计日志。命令结果不加入主模型历史,但宿主仍可保存命令及子代理记录。
140
-
141
- 内部传输命令为 `btw-run` 和 `btw-close`,从命令目录中隐藏;用户只需使用 `/btw` 和气泡关闭按钮。它们使用宿主会话 RPC,未增加 HTTP 接口。
142
-
143
- `btw-run` 的 JSON 请求为 `{ id, question, locale? }`,`locale` 支持 `zh`、`en`,省略时兼容旧客户端按中文处理;其他语言回退英文。返回仍为宿主命令的 `{ kind, text }`。`btw-close` 继续接受原始请求标识,清理错误保留该请求的语言。
144
-
145
- 单次问题最多 8000 字符、90 秒超时、最多同时处理 8 个请求。
102
+ ### 使用说明
146
103
 
147
- 气泡仅保存在客户端内存中,刷新页面后不恢复。外观跟随 DSH 当前主题,不单独读取系统深色偏好。界面语言通过宿主 locale 服务即时切换,服务器提示使用提交时的语言;模型回答和外部原始错误不翻译。
148
-
149
- 本机安装状态和待验收事项统一记录在[当前状态](docs/00-交接入口/02-当前状态.md),运行时与模拟宿主测试不能代替实际 DSH 的模型和交互验收。
104
+ - 每次旁问相互独立,参考的是提问时主会话已完成的内容,不包含正在生成的回答或之前的旁问。
105
+ - 刷新页面后气泡不会保留,需要保存的答案请先复制。
150
106
 
151
107
  ## 卸载
152
108
 
@@ -168,32 +124,13 @@ npm ci --ignore-scripts
168
124
  npm run check
169
125
  ```
170
126
 
171
- `npm run check` 执行类型检查、单元测试和构建。`npm run dev` 仅提供开发用模拟宿主,回答为模拟数据,不连接真实模型;实际使用入口始终是 DSH。主题 CSS LocaleRuntime 来自同版本官方开发依赖。
172
-
173
- 运行 `npm run test:browser` 验证气泡、取消、窄屏、Markdown、主题切换、对比度、语言切换及不再拦截上下键。
127
+ `npm run check` 执行类型检查、测试和构建。构建后可运行 `npm run test:browser` 做浏览器测试,本地需要 Microsoft Edge。`npm run dev` 提供使用模拟回答的界面预览。
174
128
 
175
- 浏览器测试需要先构建,Playwright 自动管理 `npm run dev`,本地默认使用已安装的 Microsoft Edge,CI 使用 Chromium。`tests/host.test.ts` `DSH_RUNTIME_ROOT` 指定的 `node_modules` 读取宿主;默认读取用户目录下 `.dsh/profiles/node_modules`,不存在时明确跳过这 2 项测试,不会读取凭据或配置。CI 指向本项目开发依赖,实际执行运行时用例。
176
-
177
- `tests/command-visibility.test.ts` 使用真实 Typert Registry、Gateway 和命令运行时验证 RPC 目录、执行及卸载恢复;同时检查本机可用的桌面运行时,其目录可用 `DSH_DESKTOP_RUNTIME_ROOT` 覆盖。通过该测试仍需确认运行中的 DSH 已重新加载构建产物。
129
+ 从源码安装时,在项目目录执行 `dsh plugin --profile web add . --ignore-scripts`,然后重新加载 DSH。
178
130
 
179
131
  ## 参考与致谢
180
132
 
181
- 参考 [JasonQQ/dsh-btw-plugin](https://github.com/JasonQQ/dsh-btw-plugin) 的旁问交互、[kaieye/dsh-AIR](https://github.com/kaieye/dsh-AIR) 的关闭流程,以及下述 Pi 插件的边界处理,代码独立实现。
182
-
183
- 参考 [`@narumitw/pi-btw` 0.57.0](https://pi.dev/packages/@narumitw/pi-btw) 的 [源码](https://github.com/narumiruna/pi-extensions/tree/b4981b29604945d67ce2ce04e0f769c62bce1f20/packages/pi-btw):
184
-
185
- - 将旁问上下文限定为提问时的快照,避免跟随主任务持续变动。本插件通过 fork 继承已完成回合。
186
- - 取消后再次检查信号,拒收迟到答案。本插件前后端都按请求标识检查取消状态。
187
- - 默认不回填主任务。本插件只展示独立气泡,不绑定历史导航按键。
188
-
189
- Pi 使用无工具的直接模型调用;本插件仍按约定使用一次性子代理。未引入其全屏 TUI、多轮恢复或回填主任务功能。
190
-
191
- ## 项目文档
192
-
193
- - [阅读导航](docs/00-交接入口/00-阅读导航.md)
194
- - [当前状态](docs/00-交接入口/02-当前状态.md)
195
- - [待办与阻塞](docs/00-交接入口/03-待办与阻塞.md)
196
- - [自动测试与发布](docs/05-工程交付/01-自动发布.md)
133
+ 交互设计参考 [dsh-btw-plugin](https://github.com/JasonQQ/dsh-btw-plugin)[dsh-AIR](https://github.com/kaieye/dsh-AIR) [pi-btw](https://pi.dev/packages/@narumitw/pi-btw),代码独立实现。
197
134
 
198
135
  ## 许可证
199
136
 
package/lib/client.js CHANGED
@@ -393,6 +393,7 @@ var zh = {
393
393
  "error.empty": "\u8BF7\u8F93\u5165\u65C1\u95EE\u5185\u5BB9\u3002",
394
394
  "error.length": "\u65C1\u95EE\u6700\u591A\u652F\u6301 8000 \u4E2A\u5B57\u7B26\u3002",
395
395
  "error.capacity": "\u6B63\u5728\u5904\u7406\u7684\u65C1\u95EE\u8F83\u591A\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
396
+ "error.bubbleCapacity": "\u5F53\u524D\u4F1A\u8BDD\u7684\u65C1\u95EE\u6C14\u6CE1\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u8BF7\u5148\u5173\u95ED\u65E7\u6C14\u6CE1\uFF1B\u5173\u95ED\u5931\u8D25\u7684\u6C14\u6CE1\u53EF\u91CD\u8BD5\u3002",
396
397
  "error.close": "\u5173\u95ED\u5931\u8D25\uFF0C\u53EF\u91CD\u8BD5\u3002{detail}",
397
398
  "error.requestLength": "\u65C1\u95EE\u5185\u5BB9\u8FC7\u957F\u3002",
398
399
  "error.request": "\u65C1\u95EE\u8BF7\u6C42\u65E0\u6548\u3002",
@@ -404,7 +405,8 @@ var zh = {
404
405
  "error.timeout": "\u65C1\u95EE\u8D85\u65F6\uFF0C\u5DF2\u53D6\u6D88\u3002",
405
406
  "error.incomplete": "\u65C1\u95EE\u672A\u5B8C\u6210\uFF08{reason}\uFF09\u3002{detail}",
406
407
  "error.noText": "\u6A21\u578B\u672A\u8FD4\u56DE\u6587\u5B57\u56DE\u7B54\u3002",
407
- "error.cleanup": "\u8D44\u6E90\u6E05\u7406\u5931\u8D25\uFF0C\u8BF7\u518D\u6B21\u5173\u95ED\u91CD\u8BD5\u3002{detail}"
408
+ "error.cleanup": "\u8D44\u6E90\u6E05\u7406\u5931\u8D25\uFF0C\u8BF7\u518D\u6B21\u5173\u95ED\u91CD\u8BD5\u3002{detail}",
409
+ "error.cleanupTimeout": "\u8D44\u6E90\u91CA\u653E\u4ECD\u672A\u5B8C\u6210\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u5173\u95ED\u3002"
408
410
  };
409
411
  var en = {
410
412
  "locale.id": "en",
@@ -428,6 +430,7 @@ var en = {
428
430
  "error.empty": "Enter a side question.",
429
431
  "error.length": "Side questions are limited to 8,000 characters.",
430
432
  "error.capacity": "Too many side questions are running. Try again shortly.",
433
+ "error.bubbleCapacity": "This session has reached its bubble limit. Close older bubbles first; retry any failed closures.",
431
434
  "error.close": "Could not close. Try again. {detail}",
432
435
  "error.requestLength": "The side question request is too long.",
433
436
  "error.request": "Invalid side question request.",
@@ -439,7 +442,8 @@ var en = {
439
442
  "error.timeout": "The side question timed out and was cancelled.",
440
443
  "error.incomplete": "The side question did not complete ({reason}).{detail}",
441
444
  "error.noText": "The model returned no text.",
442
- "error.cleanup": "Cleanup failed. Close again to retry. {detail}"
445
+ "error.cleanup": "Cleanup failed. Close again to retry. {detail}",
446
+ "error.cleanupTimeout": "Resources are still being released. Try closing again shortly."
443
447
  };
444
448
  function translate(locale = "zh") {
445
449
  const dictionary = locale === "zh" ? zh : en;
@@ -455,6 +459,7 @@ var CLOSE_COMMAND = "btw-close";
455
459
  var MAX_QUESTION_LENGTH = 8e3;
456
460
 
457
461
  // src/client/bubbles.ts
462
+ var MAX_BUBBLES_PER_SESSION = 20;
458
463
  var BubbleStore = class {
459
464
  constructor(transport, t = translate()) {
460
465
  this.transport = transport;
@@ -464,21 +469,42 @@ var BubbleStore = class {
464
469
  listeners = /* @__PURE__ */ new Set();
465
470
  active = /* @__PURE__ */ new Map();
466
471
  closing = /* @__PURE__ */ new Map();
472
+ admitting = /* @__PURE__ */ new Set();
473
+ disposed = false;
467
474
  getSnapshot = () => this.snapshot;
468
475
  subscribe = (listener) => {
469
476
  this.listeners.add(listener);
470
477
  return () => this.listeners.delete(listener);
471
478
  };
472
- ask(sessionId, question) {
479
+ /** 返回已接收请求的标识;满额先等待旧气泡关闭,失败时拒绝且不发送新问题。 */
480
+ async ask(sessionId, question) {
481
+ if (this.disposed) throw new Error(this.t("error.stopped"));
473
482
  if (!question.trim()) throw new Error(this.t("error.empty"));
474
483
  if (question.length > MAX_QUESTION_LENGTH) throw new Error(this.t("error.length"));
484
+ if (this.admitting.has(sessionId)) throw new Error(this.t("error.bubbleCapacity"));
485
+ if (this.active.size >= 8) throw new Error(this.t("error.capacity"));
486
+ const sessionBubbles = this.snapshot.filter((item) => item.sessionId === sessionId);
487
+ if (sessionBubbles.length >= MAX_BUBBLES_PER_SESSION) {
488
+ const oldest = sessionBubbles.find((item) => !this.active.has(item.id) && !this.closing.has(item.id) && !item.closeFailed);
489
+ if (!oldest) throw new Error(this.t("error.bubbleCapacity"));
490
+ this.admitting.add(sessionId);
491
+ let timer;
492
+ try {
493
+ await Promise.race([this.close(oldest.id), new Promise((_, reject) => {
494
+ timer = setTimeout(() => reject(new Error(this.t("error.bubbleCapacity"))), 5e3);
495
+ })]);
496
+ } finally {
497
+ clearTimeout(timer);
498
+ this.admitting.delete(sessionId);
499
+ }
500
+ }
501
+ if (this.disposed) throw new Error(this.t("error.stopped"));
502
+ if (this.snapshot.filter((item) => item.sessionId === sessionId).length >= MAX_BUBBLES_PER_SESSION) throw new Error(this.t("error.bubbleCapacity"));
475
503
  if (this.active.size >= 8) throw new Error(this.t("error.capacity"));
476
504
  const id = crypto.randomUUID();
477
505
  const controller = new AbortController();
478
506
  this.active.set(id, controller);
479
- const settled = this.snapshot.filter((item) => !this.active.has(item.id)).slice(-19);
480
- const running = this.snapshot.filter((item) => this.active.has(item.id));
481
- this.publish([...settled, ...running, { id, sessionId, question, answer: "", phase: "answering" }]);
507
+ this.publish([...this.snapshot, { id, sessionId, question, answer: "", phase: "answering" }]);
482
508
  void Promise.resolve().then(() => this.transport.run(sessionId, id, question, controller.signal)).then((result) => {
483
509
  if (controller.signal.aborted || this.closing.has(id)) return;
484
510
  this.update(id, { phase: result.kind === "success" ? "done" : "error", answer: result.kind === "success" ? result.text : "", error: result.kind === "error" ? result.text : void 0 });
@@ -509,6 +535,7 @@ var BubbleStore = class {
509
535
  return operation;
510
536
  }
511
537
  async dispose() {
538
+ this.disposed = true;
512
539
  await Promise.all(this.snapshot.map((item) => this.close(item.id)));
513
540
  }
514
541
  update(id, patch2) {
@@ -13394,7 +13421,7 @@ function apply(ctx) {
13394
13421
  submit: async (args, _actx, images) => {
13395
13422
  if (images.length) return { kind: "error", text: t("error.images") };
13396
13423
  try {
13397
- store.ask(sessionId, args.trim());
13424
+ await store.ask(sessionId, args.trim());
13398
13425
  return { kind: "success" };
13399
13426
  } catch (error) {
13400
13427
  return { kind: "error", text: error instanceof Error ? error.message : String(error) };