@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.
- package/CHANGELOG.md +11 -0
- package/CHANGELOG.zh-CN.md +11 -0
- package/README.md +22 -87
- package/README.zh-CN.md +22 -85
- package/assets/branding/dsh-btw-banner.png +0 -0
- package/lib/client.js +34 -7
- package/lib/index.js +113 -35
- package/lib/types/client/bubbles.d.ts +4 -1
- package/lib/types/locales.d.ts +2 -0
- package/lib/types/server/jobs.d.ts +16 -1
- package/package.json +1 -2
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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.
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -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
|
|
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**:
|
|
22
|
-
- **Answers only**:
|
|
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
|
-
- **
|
|
25
|
-
- **
|
|
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
|
-
-
|
|
53
|
-
-
|
|
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
|
-
|
|
57
|
+
Examples use the `web` profile; replace it with your target profile. Disable other plugins that provide `/btw` before installing.
|
|
60
58
|
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
103
|
+
### Usage Notes
|
|
133
104
|
|
|
134
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
53
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`
|
|
172
|
-
|
|
173
|
-
运行 `npm run test:browser` 验证气泡、取消、窄屏、Markdown、主题切换、对比度、语言切换及不再拦截上下键。
|
|
127
|
+
`npm run check` 执行类型检查、测试和构建。构建后可运行 `npm run test:browser` 做浏览器测试,本地需要 Microsoft Edge。`npm run dev` 提供使用模拟回答的界面预览。
|
|
174
128
|
|
|
175
|
-
|
|
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
|
-
|
|
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
|
|
|
Binary file
|
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
|
-
|
|
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
|
-
|
|
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) };
|