dsh-input-traffic 0.1.0
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/LICENSE +21 -0
- package/README.en.md +202 -0
- package/README.md +202 -0
- package/cordis.patch.yml +11 -0
- package/lib/client.js +920 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +5 -0
- package/lib/types/client/freeze-button.d.ts +10 -0
- package/lib/types/client/freeze-button.d.ts.map +1 -0
- package/lib/types/client/freeze-button.js +51 -0
- package/lib/types/client/freeze-button.js.map +1 -0
- package/lib/types/client/freeze-store.d.ts +24 -0
- package/lib/types/client/freeze-store.d.ts.map +1 -0
- package/lib/types/client/freeze-store.js +24 -0
- package/lib/types/client/freeze-store.js.map +1 -0
- package/lib/types/client/hide-enter-row.d.ts +17 -0
- package/lib/types/client/hide-enter-row.d.ts.map +1 -0
- package/lib/types/client/hide-enter-row.js +5 -0
- package/lib/types/client/hide-enter-row.js.map +1 -0
- package/lib/types/client/index.d.ts +25 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +80 -0
- package/lib/types/client/index.js.map +1 -0
- package/lib/types/client/locales.d.ts +54 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/locales.js +96 -0
- package/lib/types/client/locales.js.map +1 -0
- package/lib/types/client/steer-queue-dock.d.ts +59 -0
- package/lib/types/client/steer-queue-dock.d.ts.map +1 -0
- package/lib/types/client/steer-queue-dock.js +320 -0
- package/lib/types/client/steer-queue-dock.js.map +1 -0
- package/lib/types/index.d.ts +11 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +5 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/invariant.d.ts +12 -0
- package/lib/types/invariant.d.ts.map +1 -0
- package/lib/types/invariant.js +12 -0
- package/lib/types/invariant.js.map +1 -0
- package/package.json +96 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-input-traffic contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<strong>Three-tier input traffic control for the DeepSeek Harness Web GUI</strong>
|
|
3
|
+
</p>
|
|
4
|
+
<p align="center">
|
|
5
|
+
<a href="README.md">中文</a> · <strong>English</strong>
|
|
6
|
+
</p>
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-263146?style=flat-square"></a>
|
|
9
|
+
<img alt="Public beta" src="https://img.shields.io/badge/status-public%20beta-7da1de?style=flat-square">
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
# dsh-input-traffic
|
|
13
|
+
|
|
14
|
+
> While the agent is busy, "interrupt" and "queue" are no longer mutually exclusive: red interrupts and sends now, yellow inserts at the next turn, green queues until the end — all three coexist. Near DeepSeek peak pricing hours, one click freezes the session; resume later during off-peak pricing.
|
|
15
|
+
|
|
16
|
+
A cordis client plugin assembled via the `dsh plugin` command and a bundle patch — no dsh source changes, no PR required.
|
|
17
|
+
|
|
18
|
+
## What it does
|
|
19
|
+
|
|
20
|
+
- **Three tiers coexist**: while the agent is busy, every input lands in a waiting area first, then you choose when it enters the conversation — no longer a single "interrupt" or a single "queue":
|
|
21
|
+
- 🔴 **Red (now)**: interrupt the current turn and send immediately — the running generation stops and the message is processed and answered right away;
|
|
22
|
+
- 🟡 **Yellow (next)**: insert at the next natural turn — the current action (tool call / ongoing generation) finishes first, no interruption;
|
|
23
|
+
- 🟢 **Green (later)**: queue until the whole logic has finished — processed after all previously queued actions complete (the default).
|
|
24
|
+
- **Yellow is reversible**: pressing green on an already-steered (yellow) message revokes the insertion and pulls it back to the queue.
|
|
25
|
+
- **Queued content stays editable**: messages already in the queue can be edited in place — the multi-line editor auto-grows with the content so long messages stay fully visible (Enter saves / Shift+Enter newline / Esc cancels); they can also be **pulled back into the composer for editing** (back-filled draft, then resubmitted).
|
|
26
|
+
- **Queue management**: messages in the waiting area can be **moved up / down** to reorder, removed, or cleared with the queue-level "cancel and clear".
|
|
27
|
+
- **Edits are never lost**: if saving an edit fails (the agent already claimed the message), the edited content automatically moves back to the composer; an occupied draft is never overwritten.
|
|
28
|
+
- **Peak-hour freeze**: a "Freeze session" button on the composer's right — near DeepSeek peak pricing hours (09:00-12:00, 14:00-18:00) it pauses API consumption: the current turn finishes naturally, then the unsent queue is frozen; "Resume session" continues during off-peak hours.
|
|
29
|
+
- **Official behavior takeover**: while the plugin is mounted, the official "busy-Enter behavior" settings row is hidden (Enter stays queue-later).
|
|
30
|
+
|
|
31
|
+
## UI preview
|
|
32
|
+
|
|
33
|
+
Layout sketch of the waiting area and the freeze button in a session page:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
┌─ Composer ───────────────────────────────────── Send ── [❄ Freeze] ─┐
|
|
37
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
38
|
+
┌─ Waiting area (three-tier planning dock) ───────────────────────────┐
|
|
39
|
+
│ ┌ 2 queued messages 🗑 Cancel & clear ┐ │
|
|
40
|
+
│ │ 🟢 queued First message preview… ↑ ↓ Pull Edit Remove │ │
|
|
41
|
+
│ │ 🟢 queued Second message preview… ↑ ↓ Pull Edit Remove │ │
|
|
42
|
+
│ │ Editing: the multi-line editor auto-grows (up to ~8 rows) │ │
|
|
43
|
+
│ │ Enter saves · Shift+Enter newline · Esc cancels │ │
|
|
44
|
+
│ └───────────────────────────────────────────────────────────────────┘ │
|
|
45
|
+
└───────────────────────────────────────────────────────────────────────┘
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## The three tiers
|
|
49
|
+
|
|
50
|
+
| Tier | Color | Semantics | Underlying mechanism (existing dsh RPCs) |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| **later** (default) | Green | Queue: processed after all previously queued actions finish; green on an already-steered message **revokes the insertion** | Enter default queue → `agent.followup()` (next-turn); revoke = `updateQueue(remove)` + `send(text)` |
|
|
53
|
+
| **next** | Yellow | Insert at the next natural turn: after the current action finishes | `updateQueue(id, { kind: 'steer' })` → `agent.steer()` (next-step boundary) |
|
|
54
|
+
| **now** | Red | Interrupt and send: stop the current turn, the message is processed immediately | `cancel()` → `updateQueue(remove)` (avoids the inbox duplicate-insertion rejection) → `send(text)` (re-submit, wakes the driver immediately) |
|
|
55
|
+
|
|
56
|
+
> Why red is cancel + remove + resend: the harness inbox rejects inserting a message that is already pending; steering the original message after an interrupt would be rejected and strand the message (see FAQ).
|
|
57
|
+
|
|
58
|
+
## Session freeze / resume (peak-hour pause)
|
|
59
|
+
|
|
60
|
+
The "Freeze session / Resume session" button on the composer's right (beside the send button) pauses API consumption near DeepSeek peak pricing hours:
|
|
61
|
+
|
|
62
|
+
- **Freeze**: the current turn is **not interrupted** — it finishes naturally, then the unsent queue is frozen (detached from the waiting area); the dock shows a "Frozen" banner;
|
|
63
|
+
- **Resume**: the frozen messages re-enter the queue and the agent continues in FIFO order;
|
|
64
|
+
- Engine: freeze = detach every queued row via `updateQueue(remove)` (plain-text copies kept in the plugin store); the driver stops naturally once the current turn ends with no pending work; resume = re-submit via `send(text)`, waking the driver;
|
|
65
|
+
- Note: queued messages containing non-text content (images) cannot be re-sent and are released by the freeze (they do not come back).
|
|
66
|
+
|
|
67
|
+
## Queue management
|
|
68
|
+
|
|
69
|
+
Each waiting-area message (while not frozen) offers:
|
|
70
|
+
|
|
71
|
+
| Action | Description |
|
|
72
|
+
|---|---|
|
|
73
|
+
| Move up / down | Reorder the FIFO queue (the whole queue is rebuilt in the new order; disabled while any image message is queued) |
|
|
74
|
+
| Edit in composer | Back-fill the message into the composer draft and remove it from the queue for editing |
|
|
75
|
+
| Edit / remove | Edit the queued content in a multi-line editor / cancel the message |
|
|
76
|
+
| Red / yellow / green planning | See "The three tiers" |
|
|
77
|
+
| Cancel and clear | Stop the current run and remove every queued message |
|
|
78
|
+
|
|
79
|
+
When editing a queued message (inline):
|
|
80
|
+
|
|
81
|
+
- **Auto-grow**: the editor grows with the content in real time; long messages expand fully, up to about 8 rows, then scroll internally;
|
|
82
|
+
- **Shortcuts**: `Enter` saves, `Shift+Enter` inserts a newline, `Esc` cancels (composition input is protected from accidental saves);
|
|
83
|
+
- **Failure fallback**: if the save fails because the agent already claimed the message (e.g. "started sending"), the edited content automatically moves back to the composer with a notice — **nothing is lost**; the back-fill only happens when the composer is empty, so an existing draft is never overwritten.
|
|
84
|
+
|
|
85
|
+
## Installation
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
# Assemble (git or local path)
|
|
89
|
+
dsh plugin --profile web add /absolute/path/to/dsh-input-traffic
|
|
90
|
+
# (after git install, build in the profile's node_modules: npm install --legacy-peer-deps && npm run build)
|
|
91
|
+
|
|
92
|
+
# Confirm the composed tree contains the new row
|
|
93
|
+
dsh web --dump-config | grep -B1 -A2 'input-traffic'
|
|
94
|
+
|
|
95
|
+
# Restart dsh web — required! A running instance does not hot-load the bundle layer
|
|
96
|
+
dsh web
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Local build and tests:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
npm install --legacy-peer-deps # the @deepseek-ai client package chain is incomplete on npm; toolchain only
|
|
103
|
+
npm run build # tsc (lib/types) + tsdown (lib/index.js + lib/client.js)
|
|
104
|
+
node examples/verify-assembly.mjs # 12 assembly assertions
|
|
105
|
+
npm test # 32 vitest component tests
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Usage
|
|
109
|
+
|
|
110
|
+
1. While the agent is busy, type and send — the message enters the **waiting area** (green queue by default);
|
|
111
|
+
2. Press a planning button on the message:
|
|
112
|
+
- 🟡 Yellow = interject — insert after the current action finishes;
|
|
113
|
+
- 🔴 Red = interrupt — stop the current action, the message is processed right away;
|
|
114
|
+
- 🟢 Green = keep queued (the default); on an already-steered message, green revokes it back to the queue;
|
|
115
|
+
3. Reorder / re-edit: use move up/down, "Edit in composer", or the multi-line inline editor (Enter saves, Shift+Enter newline);
|
|
116
|
+
4. Near peak hours: press "Freeze session" on the composer's right; the session pauses after the current turn; press "Resume session" during off-peak hours to continue.
|
|
117
|
+
|
|
118
|
+
## FAQ
|
|
119
|
+
|
|
120
|
+
### After an interrupt the message gets no reply / the conversation stalls
|
|
121
|
+
|
|
122
|
+
Fixed (historical issue). Root cause: the harness inbox rejects inserting a message that is already pending — steering the original message right after an interrupt was rejected with `"message is already pending"`, stranding the message and stopping the driver. The current implementation is `cancel → remove → resend` (the text re-submitted as a fresh message), so the interrupted message is processed and answered immediately. If it still happens, rebuild the plugin and restart dsh web.
|
|
123
|
+
|
|
124
|
+
### Where does the content go after a failed edit save?
|
|
125
|
+
|
|
126
|
+
It is not lost. When the save fails (the agent already claimed the message), the edited content automatically moves back to the composer with an "Edit failed; the content was moved back to the composer" notice; if the composer already has a draft, nothing is back-filled and only the failure is reported.
|
|
127
|
+
|
|
128
|
+
### The "busy-Enter behavior" settings row is missing
|
|
129
|
+
|
|
130
|
+
Expected — the plugin hides it and pins Enter to green queue; a stale preference cannot leak behind the hidden row.
|
|
131
|
+
|
|
132
|
+
### Queued messages disappeared after freezing
|
|
133
|
+
|
|
134
|
+
Expected — the freeze detaches the queue into the plugin store (removed from the waiting area); they return on resume. Refreshing the page loses the frozen queue; avoid refreshing while frozen.
|
|
135
|
+
|
|
136
|
+
### Move up/down is disabled
|
|
137
|
+
|
|
138
|
+
Reordering is disabled while any queued message contains non-text content (images cannot be re-sent). Same for "Edit in composer".
|
|
139
|
+
|
|
140
|
+
### Interrupt / interject buttons are disabled
|
|
141
|
+
|
|
142
|
+
Red and yellow are disabled while the agent is idle — an idle agent would process the message immediately anyway, so planning is not needed.
|
|
143
|
+
|
|
144
|
+
## Uninstall
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
dsh plugin --profile web remove dsh-input-traffic
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Restart dsh web afterwards to restore the official queue dock and the "busy-Enter behavior" settings row.
|
|
151
|
+
|
|
152
|
+
## Compatibility and privacy
|
|
153
|
+
|
|
154
|
+
- Requires DeepSeek Harness with the web profile; verified on Windows/macOS/Linux dsh web.
|
|
155
|
+
- A browser-side (client) plugin only — every operation goes through existing dsh RPCs (`session.prompt` / `session.updateQueue` / `session.cancel`); **no official source changes**.
|
|
156
|
+
- The plugin reads no data beyond session state and uploads nothing; the frozen queue lives only in browser memory.
|
|
157
|
+
- The contract types are declared locally in `src/types/contracts.d.ts` (the npm dsh client chain is incomplete) and mirror the harness sources at build-verification time.
|
|
158
|
+
|
|
159
|
+
## Architecture
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
src/
|
|
163
|
+
├── index.ts # node half (loader entry, empty apply)
|
|
164
|
+
├── invariant.ts # takeover invariants
|
|
165
|
+
├── types/contracts.d.ts # local @deepseek-ai/* contract declarations
|
|
166
|
+
└── client/
|
|
167
|
+
├── index.ts # browser half apply: busyEnter pinned to queue + three slot registrations
|
|
168
|
+
├── steer-queue-dock.tsx # three-tier planning dock (shadows conversation.input.dock id queue)
|
|
169
|
+
├── freeze-button.tsx # freeze/resume button (conversation.input.right)
|
|
170
|
+
├── freeze-store.ts # shared freeze state (composer button ↔ dock banner)
|
|
171
|
+
├── hide-enter-row.tsx # settings-row hiding (shadows settings.general.item id composer-enter)
|
|
172
|
+
├── locales.ts # steer dictionaries (zh/en)
|
|
173
|
+
└── *.module.css
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- **Slot shadowing**: list slots render the lowest priority per cell — the same id at priority -1 overrides the official entries (QueueDock, EnterBehaviorRow).
|
|
177
|
+
- **Build chain**: tsdown mirrors harness `packages/client/tsdown.client.ts` semantics (`__ModuleLoader__.load` banner, lightningcss-inlined CSS Modules, platform externals table, bundle purity gate).
|
|
178
|
+
- **Consumer contract**: `conversation.updateQueue / cancel / send / input.for(actx).notify / actions.setDraft` (official ui-conversation service, verified against api-proxy.ts).
|
|
179
|
+
- **Auto-growing editor**: `resizeEditor` (a pure export of steer-queue-dock.tsx) resets the textarea height and grows it by `scrollHeight`; a CSS `max-height` caps the growth and the editor scrolls internally.
|
|
180
|
+
|
|
181
|
+
## Real-environment verification (Windows, 2026-08-17)
|
|
182
|
+
|
|
183
|
+
End-to-end browser verification on a live `dsh web`, zero application console errors:
|
|
184
|
+
|
|
185
|
+
| Item | Result |
|
|
186
|
+
|---|---|
|
|
187
|
+
| Assembly | Composed tree contains the `input-traffic` row; plugin tab shows mounted/enabled; `/plugins/dsh-input-traffic/client.js` 200 |
|
|
188
|
+
| Settings-row hiding | The "busy-Enter behavior" row is absent (zero DOM matches) |
|
|
189
|
+
| Red now | Interrupted message is processed immediately: the agent replies to it explicitly and continues; no stranded intermediate state |
|
|
190
|
+
| Yellow next + green revoke | After interjecting, green pulls the message back to the queue |
|
|
191
|
+
| Freeze / resume | Current turn finishes naturally without interruption, queue frozen with banner; resume drains everything in FIFO order |
|
|
192
|
+
| Queue editing (multi-line / fallback) | Covered by component tests (32/32 green); real-environment re-check pending |
|
|
193
|
+
|
|
194
|
+
## References
|
|
195
|
+
|
|
196
|
+
- [dsh-plugin-creation-convention.md](../dsh-plugin-creation-convention.md) (workspace root) — the dsh plugin creation convention this plugin follows
|
|
197
|
+
- Semantics reference: [dsh-traffic-light](https://github.com/yimeng-dev/dsh-traffic-light) (desktop session-status traffic light)
|
|
198
|
+
- Harness anchors: `packages/client/AGENTS.md`, `packages/client/tsdown.client.ts`, `packages/client/web/src/platform.ts`, `packages/bundle/web-app/cordis.patch.yml`, `packages/client/ui-conversation/src/client/queue/QueueDock.tsx`, `packages/host/apiproxy/src/api-proxy.ts`
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<strong>给 DeepSeek Harness Web GUI 一个三档输入交通管制</strong>
|
|
3
|
+
</p>
|
|
4
|
+
<p align="center">
|
|
5
|
+
<strong>中文</strong> · <a href="README.en.md">English</a>
|
|
6
|
+
</p>
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-263146?style=flat-square"></a>
|
|
9
|
+
<img alt="Public beta" src="https://img.shields.io/badge/status-public%20beta-7da1de?style=flat-square">
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
# dsh-input-traffic
|
|
13
|
+
|
|
14
|
+
> 智能体忙碌时不再只有"打断"或"排队"二选一:红色打断立即输入、黄色下一轮插入、绿色排队到最后,三档并存;邻近 DeepSeek 高峰收费时段可一键冻结会话,错峰再恢复继续。
|
|
15
|
+
|
|
16
|
+
无需修改 dsh 源码、无需提 PR:`dsh plugin` 命令组装 + bundle patch 装配的 cordis client 插件。
|
|
17
|
+
|
|
18
|
+
## 它能做什么
|
|
19
|
+
|
|
20
|
+
- **三档插入并存**:智能体忙碌时,每一条输入都先进入等待区,再按需选择何时进入对话——不再只有一个"打断"或只有一个"排队":
|
|
21
|
+
- 🔴 **红色(now)**:打断当前轮次并立即输入——当前生成停止,消息作为新输入被 agent 立刻处理并回复;
|
|
22
|
+
- 🟡 **黄色(next)**:下一自然轮插入——不打断当前执行,当前正在进行的动作(工具调用 / 本轮生成)结束后插入;
|
|
23
|
+
- 🟢 **绿色(later)**:待整个逻辑执行完成后输入——排队等待,上一轮输入的所有动作都结束后再处理(默认状态)。
|
|
24
|
+
- **黄色可逆**:对已插话(黄色)的消息点绿色按钮,可撤销插入、收回排队状态。
|
|
25
|
+
- **排队内容可再编辑**:已经排在队列中的消息可以直接在队列里编辑——多行编辑区随内容自动扩展,长消息也能完整查看与修改(Enter 保存 / Shift+Enter 换行 / Esc 取消);也可**打回输入框再编辑**(回填 composer 修改后重新发送)。
|
|
26
|
+
- **队列管理**:等待区的消息可以**上移 / 下移调整顺序**、删除,以及队列级「取消并清空」。
|
|
27
|
+
- **编辑不丢内容**:编辑保存失败(消息已被 agent 认领)时,编辑内容自动退回主输入框,不会丢失;主输入框已有内容时不覆盖。
|
|
28
|
+
- **高峰期冻结**:输入框右侧「冻结会话」按钮——邻近 DeepSeek 高峰收费时段(9:00-12:00、14:00-18:00)时主动暂停 API 消耗:当前轮次自然完成后暂停,未发送队列冻结保存;「恢复会话」后在非高价时间继续处理。
|
|
29
|
+
- **接管官方行为**:插件生效时,官方设置面板的「繁忙时 Enter 键行为」设置行不再显示(Enter 行为固定为绿色排队)。
|
|
30
|
+
|
|
31
|
+
## 界面预览
|
|
32
|
+
|
|
33
|
+
等待区与冻结按钮在会话页面中的布局示意:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
┌─ 输入区 ──────────────────────────────── 发送 ── [❄ 冻结会话] ─┐
|
|
37
|
+
└────────────────────────────────────────────────────────────────┘
|
|
38
|
+
┌─ 排队等待区(3 档规划 dock)────────────────────────────────────┐
|
|
39
|
+
│ ┌ 2 条排队消息 🗑 取消并清空 ┐ │
|
|
40
|
+
│ │ 🟢 排队 第一条消息内容预览… ↑ ↓ 打回 编辑 删除 │ │
|
|
41
|
+
│ │ 🟢 排队 第二条消息内容预览… ↑ ↓ 打回 编辑 删除 │ │
|
|
42
|
+
│ │ 编辑中:多行文本区随内容自动扩展(上限约 8 行) │ │
|
|
43
|
+
│ │ Enter 保存 · Shift+Enter 换行 · Esc 取消 │ │
|
|
44
|
+
│ └──────────────────────────────────────────────────────────────┘ │
|
|
45
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 三档语义
|
|
49
|
+
|
|
50
|
+
| 档位 | 颜色 | 语义 | 底层机制(dsh 现有 RPC 组合) |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| **later**(默认) | 绿 | 排队:上一轮输入的动作都结束后再处理;对已插话(黄色)的消息点绿 = **撤销插入,收回排队** | Enter 默认 queue → `agent.followup()`(next-turn);收回 = `updateQueue(remove)` + `send(text)` |
|
|
53
|
+
| **next** | 黄 | 下一自然轮插入:当前正在执行的动作结束后插入 | `updateQueue(id, { kind: 'steer' })` → `agent.steer()`(next-step 步骤边界) |
|
|
54
|
+
| **now** | 红 | 打断并输入:停止当前轮次,消息立即被处理 | `cancel()` → `updateQueue(remove)`(避免 inbox 重复插入拒绝)→ `send(text)`(重新提交,唤醒 driver 立即处理) |
|
|
55
|
+
|
|
56
|
+
> 红色为什么是 cancel + remove + resend:harness 的 inbox 禁止重复插入同一条消息,打断后直接 steer 会被拒绝导致消息滞留(详见「常见问题」)。
|
|
57
|
+
|
|
58
|
+
## 会话冻结 / 恢复(高峰期暂停)
|
|
59
|
+
|
|
60
|
+
输入框右侧(发送按钮旁)的「冻结会话 / 恢复会话」按钮,用于即将进入 DeepSeek 高峰收费时段时暂停 API 消耗:
|
|
61
|
+
|
|
62
|
+
- **冻结**:当前轮次**不打断**、自然完成后暂停;未发送队列被冻结保存(从等待区移除);等待区显示「已冻结」横幅;
|
|
63
|
+
- **恢复**:冻结的排队消息重新入队,agent 按 FIFO 继续处理;
|
|
64
|
+
- 引擎实现:冻结 = 逐条 `updateQueue(remove)` 分离队列(纯文本副本存于插件 store),当前轮次完成后 driver 因无 pending 自然停止;恢复 = 逐条 `send(text)` 重新提交并唤醒 driver;
|
|
65
|
+
- 注意:含非文本内容(图片)的排队消息无法重发,冻结时会随队列释放(不会恢复)。
|
|
66
|
+
|
|
67
|
+
## 队列管理
|
|
68
|
+
|
|
69
|
+
等待区每条消息(未冻结时)提供:
|
|
70
|
+
|
|
71
|
+
| 操作 | 说明 |
|
|
72
|
+
|---|---|
|
|
73
|
+
| 上移 / 下移 | 调整 FIFO 顺序(整个队列按新顺序重建;含图片消息时禁用) |
|
|
74
|
+
| 打回输入框编辑 | 消息内容回填 composer 输入框并从队列移除,编辑后重新发送 |
|
|
75
|
+
| 编辑 / 删除 | 多行文本区直接修改排队内容 / 取消该消息 |
|
|
76
|
+
| 红 / 黄 / 绿规划 | 见「三档语义」 |
|
|
77
|
+
| 取消并清空 | 停止当前执行并清空全部排队消息 |
|
|
78
|
+
|
|
79
|
+
编辑排队消息(行内编辑)时:
|
|
80
|
+
|
|
81
|
+
- **自动扩展**:编辑区随内容实时增高,长消息完整展开,上限约 8 行,超出后内部滚动;
|
|
82
|
+
- **快捷键**:`Enter` 保存,`Shift+Enter` 换行,`Esc` 取消(中文输入法组合期间不会误保存);
|
|
83
|
+
- **失败兜底**:保存时若消息已被 agent 认领(如「已经开始发送」),编辑内容自动退回主输入框并提示,**不会丢失**;仅当主输入框为空时回填,已有草稿不被覆盖。
|
|
84
|
+
|
|
85
|
+
## 安装
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
# 组装(git 或本地路径)
|
|
89
|
+
dsh plugin --profile web add /absolute/path/to/dsh-input-traffic
|
|
90
|
+
# (git 安装后需在 profile 的 node_modules 内现场构建:npm install --legacy-peer-deps && npm run build)
|
|
91
|
+
|
|
92
|
+
# 确认组合树包含新行
|
|
93
|
+
dsh web --dump-config | grep -B1 -A2 'input-traffic'
|
|
94
|
+
|
|
95
|
+
# 重启 dsh web —— 必做!运行中实例不热载 bundle 层
|
|
96
|
+
dsh web
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
本地构建与测试:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
npm install --legacy-peer-deps # @deepseek-ai client 包链在 npm 上不完整,仅装工具链
|
|
103
|
+
npm run build # tsc(lib/types)+ tsdown(lib/index.js + lib/client.js)
|
|
104
|
+
node examples/verify-assembly.mjs # 12 项装配断言
|
|
105
|
+
npm test # 32 项 vitest 组件测试
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 使用方式
|
|
109
|
+
|
|
110
|
+
1. 智能体忙碌时直接输入并发送,消息**统一进入等待区**(默认绿色排队);
|
|
111
|
+
2. 在等待区对消息点规划按钮:
|
|
112
|
+
- 🟡 黄色 = 插话——当前动作结束后插入;
|
|
113
|
+
- 🔴 红色 = 打断——立即中断当前动作,消息随后被处理;
|
|
114
|
+
- 🟢 绿色 = 保持排队(当前默认态);对已插话的消息点绿 = 收回排队;
|
|
115
|
+
3. 需要调整顺序 / 修改内容:用上移下移、打回输入框编辑或多行编辑(Enter 保存、Shift+Enter 换行);
|
|
116
|
+
4. 邻近高峰时段:点击输入框右侧「冻结会话」,当前轮次完成后自动暂停;非高价时间点「恢复会话」继续。
|
|
117
|
+
|
|
118
|
+
## 常见问题
|
|
119
|
+
|
|
120
|
+
### 打断后消息没有回复 / 对话停住
|
|
121
|
+
|
|
122
|
+
历史问题(已修复)。根因:harness 的 inbox 禁止重复插入同一条消息——打断后直接对原消息执行 steer 会被 `"message is already pending"` 拒绝,消息滞留在队列、agent 停摆。当前实现改为 `cancel → remove → resend`(新消息重新提交),打断消息会立即被 agent 处理并回复。若仍遇到,请确认插件为最新构建并重启 dsh web。
|
|
123
|
+
|
|
124
|
+
### 编辑保存失败后,内容去哪了?
|
|
125
|
+
|
|
126
|
+
不会丢。保存失败(消息已被 agent 认领)时,编辑内容会自动退回主输入框并弹出「编辑失败,内容已退回主输入框」提示;主输入框已有内容时不回填,仅提示编辑失败。
|
|
127
|
+
|
|
128
|
+
### 设置面板里找不到「繁忙时 Enter 键行为」
|
|
129
|
+
|
|
130
|
+
正常——插件接管后该设置行被隐藏,Enter 行为固定为绿色排队(旧偏好不会在隐藏的设置行背后继续生效)。
|
|
131
|
+
|
|
132
|
+
### 冻结后排队消息消失了
|
|
133
|
+
|
|
134
|
+
正常——冻结会把队列保存到插件内存(从等待区移除),恢复后重新出现。刷新页面会丢失冻结队列,请避免冻结后刷新。
|
|
135
|
+
|
|
136
|
+
### 上移/下移按钮不可用
|
|
137
|
+
|
|
138
|
+
队列中含图片等非文本消息时,排序会禁用(图片消息无法重发)。打回输入框编辑同理。
|
|
139
|
+
|
|
140
|
+
### 打断/插话按钮不可用
|
|
141
|
+
|
|
142
|
+
智能体空闲(未运行)时红黄两档禁用——空闲时消息本来就会被立即处理,无需规划。
|
|
143
|
+
|
|
144
|
+
## 卸载
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
dsh plugin --profile web remove dsh-input-traffic
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
卸载后重启 dsh web,即恢复官方 queue dock 与「繁忙时 Enter 键行为」设置行。
|
|
151
|
+
|
|
152
|
+
## 兼容性与隐私
|
|
153
|
+
|
|
154
|
+
- 需要已安装 DeepSeek Harness 并使用 web profile;在 Windows / macOS / Linux 的 dsh web 上验证。
|
|
155
|
+
- 插件为纯浏览器侧(client)插件,所有操作均通过 dsh 现有 RPC(`session.prompt` / `session.updateQueue` / `session.cancel`)完成,**不改动任何官方源码**。
|
|
156
|
+
- 插件不读取、不上传任何会话内容以外的数据;冻结队列仅保存在本机浏览器内存。
|
|
157
|
+
- 类型契约在 `src/types/contracts.d.ts` 本地声明(npm 上 dsh client 包链不完整),构建时以 harness 源码核实为准。
|
|
158
|
+
|
|
159
|
+
## 架构
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
src/
|
|
163
|
+
├── index.ts # node half(loader 行入口,空 apply)
|
|
164
|
+
├── invariant.ts # 接管不变量说明
|
|
165
|
+
├── types/contracts.d.ts # @deepseek-ai/* 平台面本地类型声明
|
|
166
|
+
└── client/
|
|
167
|
+
├── index.ts # browser half apply:busyEnter 固定 queue + 三处 slot 注册
|
|
168
|
+
├── steer-queue-dock.tsx # 三档规划等待区(shadowing conversation.input.dock id queue)
|
|
169
|
+
├── freeze-button.tsx # 冻结/恢复按钮(conversation.input.right)
|
|
170
|
+
├── freeze-store.ts # 冻结状态共享 store(composer 按钮 ↔ dock 横幅)
|
|
171
|
+
├── hide-enter-row.tsx # 设置行隐藏(shadowing settings.general.item id composer-enter)
|
|
172
|
+
├── locales.ts # steer 字典(zh/en)
|
|
173
|
+
└── *.module.css
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- **slot shadowing**:list 型 slot 同 id + 更低 priority(-1)覆盖官方条目(QueueDock、EnterBehaviorRow)。
|
|
177
|
+
- **构建链**:tsdown 复制 harness `packages/client/tsdown.client.ts` 语义(`__ModuleLoader__.load` banner、CSS Modules lightningcss 内联、平台模块 external 表、bundle purity gate)。
|
|
178
|
+
- **消费方契约**:`conversation.updateQueue / cancel / send / input.for(actx).notify / actions.setDraft`(官方 ui-conversation service,api-proxy.ts 核实)。
|
|
179
|
+
- **编辑区自动扩展**:`resizeEditor`(steer-queue-dock.tsx 导出的纯函数)把 textarea 高度重置后按 `scrollHeight` 生长,CSS `max-height` 封顶后内部滚动。
|
|
180
|
+
|
|
181
|
+
## 真实环境验证(Windows,2026-08-17)
|
|
182
|
+
|
|
183
|
+
`dsh web` 真实启动后浏览器端到端验证,全程控制台零应用错误:
|
|
184
|
+
|
|
185
|
+
| 验证项 | 结果 |
|
|
186
|
+
|---|---|
|
|
187
|
+
| 插件装配 | 组合树含 `input-traffic` 行;插件页签「已挂载已启用」;`/plugins/dsh-input-traffic/client.js` 200 |
|
|
188
|
+
| 设置行隐藏 | 设置面板「繁忙时 Enter 键行为」行不存在(DOM 全量搜索零匹配) |
|
|
189
|
+
| 红色 now | 打断后消息立即被处理:agent 明确回复被打断消息并继续;无滞留中间态 |
|
|
190
|
+
| 黄色 next + 绿色撤回 | 插话后点绿收回排队,消息回到等待区 |
|
|
191
|
+
| 冻结 / 恢复 | 当前轮次自然完成不打断、队列冻结保存、横幅提示;恢复后 FIFO 全部处理完成 |
|
|
192
|
+
| 队列编辑(多行 / 失败退回) | 组件测试覆盖(32 项全绿);真实环境复核待做 |
|
|
193
|
+
|
|
194
|
+
## 参考
|
|
195
|
+
|
|
196
|
+
- [dsh-plugin-creation-convention.md](../dsh-plugin-creation-convention.md)(workspace 根部)——本插件遵循的 dsh 插件创建流程规约
|
|
197
|
+
- 语义参考:[dsh-traffic-light](https://github.com/yimeng-dev/dsh-traffic-light)(Session 运行状态红绿灯提示)
|
|
198
|
+
- harness 锚点:`packages/client/AGENTS.md`、`packages/client/tsdown.client.ts`、`packages/client/web/src/platform.ts`、`packages/bundle/web-app/cordis.patch.yml`、`packages/client/ui-conversation/src/client/queue/QueueDock.tsx`、`packages/host/apiproxy/src/api-proxy.ts`
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# dsh-input-traffic bundle patch。
|
|
2
|
+
#
|
|
3
|
+
# 将本 client 插件作为一行追加进 profile 装配树。dsh-client-modules 的 node
|
|
4
|
+
# half 扫描 Loader entries 中声明了 `dsh.client` 的包,把本包组合进
|
|
5
|
+
# window.__DSH_BOOT__ 并服务 /plugins/dsh-input-traffic/client.js。
|
|
6
|
+
#
|
|
7
|
+
# 纯新增行,不替换任何官方行(插件行为经 slot shadowing 在浏览器侧接管,
|
|
8
|
+
# 见 src/client/steer-queue-dock.tsx 与 hide-enter-row.tsx)。
|
|
9
|
+
- insert:
|
|
10
|
+
- id: input-traffic
|
|
11
|
+
name: dsh-input-traffic
|