drscrewdriver/dsh-input-traffic

drscrewdriver/dsh-input-traffic

三档(now/next/later)输入规划 dock:以红/黄/绿色优先级条取代忙碌 Enter 排队栏,普通 Enter 排入 later,支持多行原位编辑、拖拽排序与会话级冻结/恢复;冻结按钮经 stopNextTurn/resume RPC + 输入框锁定,桥接另一独立插件 [dsh-session-guard](https://github.com/drscrewdriver/dsh-session-guard)(可在 dsh-market 安装)实现按会话真实冻结锁定,并配合其高峰全局自动暂停,在 DeepSeek 高价时段停止会话、错峰自动恢复。

UI 增强cordisdeepseek-harnessdsh

给 DeepSeek Harness Web GUI 一个三档输入交通管制 English · 中文 · 日本語 · 한국어

dsh-input-traffic

  • English README
  • 中文 README
  • 日本語 README
  • 한국어 README
  • Installation guide
  • 中文安装指南
  • 日本語インストールガイド
  • 한국어 설치 안내
  • Changelog
  • 日本語 changelog
  • 한국어 changelog
▼ DSH 版本适配
本插件(compat/0.1.5 线,0.2.12-beta.1 起)仅支持 DSH v0.1.5+。
| DSH 版本 | 状态 | 说明 |
| --- | --- | --- |
| ≥ 0.1.5-alpha.1 | ✅ 支持 | 面向 0.1.5-rc.1 / rc.2 验证(类型契约已对照 dsh-v0.1.5-rc.2) |
| < 0.1.5-alpha.1 | ⚠️ 不支持 | 请留在 main 线(0.2.11-beta.1,支持 0.1.2+)或更早版本 |
- 分界点在 0.1.5:Session V3(surface node)、dockkit Sidebar 重写、Lexical composer 是 0.1.5 的结构性变更。本插件的三个 slot 锚点(conversation.input.dock / conversation.input.right / settings.general.item)在 0.1.5 全部保留,服务面(send / cancel / updateQueue / blocks.set)无破坏记录,因此代码零结构改动,仅收窄支持分段。
- 队列条位置:注册 order: 1000,排在 conversation.input.dock 带内所有已知条目(todo 0 / goal 10 / 官方 queue 20 / dsh-perm-gate 提示 30)之后,紧贴输入卡片。DSH 没有 "last" 语义,这是约定而非结构性保证——第三方插件若注册更大的 order 仍可能插到它下面。
▼ DSH 0.1.5 已知回归(宿主侧,非本插件 bug)
| 问题 | 对本插件的影响 | 讨论 |
| --- | --- | --- |
| fork 会话继承父会话「已入队未执行」的 prompt 并自动重跑 | 正是本插件管理的等待队列:fork 出的子会话可能重放父会话排队中的消息 | #6314 / #6197 |
| composer 迁移 Lexical 后 IME 组合期 Enter 被当换行、浏览器翻译插件打崩输入框 | 影响「拉回编辑器回填草稿」与发送路径 | #6231 / #6052 |
| 阻塞式工具等待期间 steer 无法抢占(最长约 10 分钟) | 黄色「下一轮插入」档位在该窗口不生效,用户可能误以为是插件问题 | #6030 |
| 升级旧 profile 后 client combo 缓存陈旧,插件树整体不激活 | 排障第一步:强制刷新浏览器(清缓存)再排查 | #5999 / #6374 |
兼容性说明: v0.2.9 已包含日语(ja)和韩语(ko)字典,但当前官方 DSH 只通过 LocaleRuntime 提供 zh 和 en。在原版 DSH 中选择 ja 或 ko 会失败,并提示 locale "" is not registered。需要等待官方 DSH 增加对应 locale ID 后才能正常使用。高级用户可以维护 DSH fork,在 packages/client/locale/src/locale-settings.ts 更新 LOCALE_IDS,在 packages/client/locale/src/client/index.ts 更新 LOCALES 标签,并补齐核心字典和测试,然后重新构建并运行 fork 版本。仅修改本插件无法扩展 DSH 的全局 locale 列表。
智能体忙碌时不再只有"打断"或"排队"二选一:红色打断立即输入、黄色下一轮插入、绿色排队到最后,三档并存;邻近 DeepSeek 高峰收费时段可一键冻结会话,错峰再恢复继续。

无需修改 dsh 源码、无需提 PR:dsh plugin 命令组装 + bundle patch 装配的 cordis client 插件。

💡 为什么推荐「冻结会话」:DeepSeek 已于 2026-08-17 实行峰谷计费——高峰时段(北京时间 9:00-12:00、14:00-18:00)单价为闲时(其余时段,含午间、夜间、周末与节假日)的 2 倍。长跑型会话若跨越高价窗口,手动冻结暂停 API 消耗、错峰再恢复,费用最多可省 50%。
目前建议搭配:配合一般提醒插件(如 dsh-notify,到点桌面提醒「该冻结/该恢复」)与计费统计插件(如 dsh-deepseek-usage、dsh-cost-tracker、dsh-billing-balance,核对冻结前后的实际花费),形成「提醒 → 冻结 → 错峰恢复 → 对账」的省钱闭环。
  • 支持的 DSH 宿主: 0.1.0-rc.2 0.1.0-rc.3 0.1.0-rc.6 0.1.0-rc.7 0.1.0-rc.8 0.1.1-rc.1 0.1.1-rc.2 0.1.2-rc.1 0.1.5-rc.1 0.1.5-rc.2 0.1.5-rc.3 0.1.7-rc.1 0.1.7-rc.2 0.2.0-rc.1 0.2.0-rc.2 0.2.1-rc.1 0.2.1-rc.2 0.2.1-rc.3(覆盖 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 共 7 条线 18 个 rc)
  • 运行时已验证: 0.1.0-rc.2, 0.1.0-rc.3, 0.1.0-rc.6, 0.1.0-rc.7, 0.1.0-rc.8, 0.1.1-rc.1, 0.1.1-rc.2, 0.1.2-rc.1, 0.1.5-rc.1, 0.1.5-rc.2, 0.1.5-rc.3, 0.1.7-rc.1, 0.1.7-rc.2, 0.2.0-rc.1, 0.2.0-rc.2(证据见 .compat-results/)

它能做什么

实际运行截屏(Windows,dsh web):

深色模式(自动跟随系统 / dsh 暗色主题):排队等待区 + 输入行右侧「冻结会话」按钮。

浅色模式 + 已冻结状态:高峰时段冻结后队列被冻结保存,输入行按钮切换为「恢复会话」,错峰再行处理。

  • 三档插入并存:智能体忙碌时,每一条输入都先进入等待区,再按需选择何时进入对话——不再只有一个"打断"或只有一个"排队":
  • 🔴 红色(now):打断当前轮次并立即输入——当前生成停止,消息作为新输入被 agent 立刻处理并回复;
  • 🟡 黄色(next):下一自然轮插入——不打断当前执行,当前正在进行的动作(工具调用 / 本轮生成)结束后插入;
  • 🟢 绿色(later):待整个逻辑执行完成后输入——排队等待,上一轮输入的所有动作都结束后再处理(默认状态)。
  • 黄色可逆:对已插话(黄色)的消息点绿色按钮,可撤销插入、收回排队状态。
  • 排队内容可再编辑:已经排在队列中的消息可以直接在队列里编辑——多行编辑区随内容自动扩展,长消息也能完整查看与修改(Enter 保存 / Shift+Enter 换行 / Esc 取消);也可打回输入框再编辑(回填 composer 修改后重新发送)。
  • 队列管理:等待区的消息可以上移 / 下移调整顺序、删除,以及队列级「取消并清空」。
  • 编辑不丢内容:编辑保存失败(消息已被 agent 认领)时,编辑内容自动退回主输入框,不会丢失;主输入框已有内容时不覆盖。
  • 高峰期冻结:输入框右侧「冻结会话」按钮——邻近 DeepSeek 高峰收费时段(9:00-12:00、14:00-18:00)时主动暂停 API 消耗:当前轮次自然完成后暂停,未发送队列冻结保存;「恢复会话」后在非高价时间继续处理。
  • 会话隔离冻结:冻结队列按 sessionId 隔离——冻结一个会话不影响其他会话(各自独立的 frozen 标志与 detached 队列),多会话并行时互不串台(见「会话冻结 / 恢复」)。
  • 接管官方行为:插件生效时,官方设置面板的「繁忙时 Enter 键行为」设置行不再显示(Enter 行为固定为绿色排队)。
  • 日夜自动适配:队列框与冻结按钮全部改用 dsh 官方语义 token(--dsw-alias-*),自动跟随系统深色模式 / dsh 暗色主题——深色下自动变为深灰底 + 白色反色字,无需任何设置(见「日夜模式」)。

界面预览

等待区与冻结按钮在会话页面中的布局示意:

┌─ 输入区 ──────────────────────────────── 发送 ── [❄ 冻结会话] ─┐
└────────────────────────────────────────────────────────────────┘
┌─ 排队等待区(3 档规划 dock)────────────────────────────────────┐
│ ┌ 2 条排队消息                                     🗑 取消并清空 ┐ │
│ │ 🟢 排队   第一条消息内容预览…          ↑ ↓ 打回 编辑 删除      │ │
│ │ 🟢 排队   第二条消息内容预览…          ↑ ↓ 打回 编辑 删除      │ │
│ │   编辑中:多行文本区随内容自动扩展(上限约 8 行)              │ │
│ │   Enter 保存 · Shift+Enter 换行 · Esc 取消                    │ │
│ └──────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

三档语义

档位 · 颜色 · 语义 · 底层机制(dsh 现有 RPC 组合) later(默认) · 绿 · 排队:上一轮输入的动作都结束后再处理;对已插话(黄色)的消息点绿 = 撤销插入,收回排队 · Enter 默认 queue → agent.followup()(next-turn);收回 = updateQueue(remove) + send(text) next · 黄 · 下一自然轮插入:当前正在执行的动作结束后插入 · updateQueue(id, { kind: 'steer' }) → agent.steer()(next-step 步骤边界) now · 红 · 打断并输入:停止当前轮次,消息立即被处理 · cancel() → updateQueue(remove)(避免 inbox 重复插入拒绝)→ send(text)(重新提交,唤醒 driver 立即处理)

红色为什么是 cancel + remove + resend:harness 的 inbox 禁止重复插入同一条消息,打断后直接 steer 会被拒绝导致消息滞留(详见「常见问题」)。

会话冻结 / 恢复(高峰期暂停)⭐ 推荐

省钱定位:这是本插件面向 DeepSeek 峰谷计费(2026-08-17 生效)的核心推荐功能——高峰时段(9:00-12:00、14:00-18:00)单价翻倍、闲时半价。手动冻结把不紧急的生成「暂停」到闲时再恢复,直接规避高价窗口,长跑型会话最多省一半费用。
建议搭配:一般提醒插件(如 dsh-notify)在进入/离开高峰时段时提醒你手动冻结/恢复;计费统计插件(如 dsh-deepseek-usage、dsh-cost-tracker、dsh-billing-balance)在冻结前后核对实际花费。

输入框右侧(发送按钮旁)的「冻结会话 / 恢复会话」按钮,用于即将进入 DeepSeek 高峰收费时段时暂停 API 消耗:

  • 冻结:当前轮次不打断、自然完成后暂停;队列与冻结解耦——冻结只停止 agent 消费(执行/插入/追加),等待队列保持可见且完全可操作(排序、编辑、删除、设定红/黄/绿插入档位),与未冻结无明显区别;
  • 恢复:按修改后的队列重新入队,按每条预定的档位执行(红=打断并立即处理,黄=插话,绿=排队),agent 按 FIFO 继续处理;
  • 会话隔离:冻结状态与 detached 队列按 sessionId 键控(src/client/freeze-store.ts 的 Map)——A 会话冻结不影响 B 会话的 banner/按钮/队列;编辑只重渲染对应会话的消费者;
  • 引擎实现:冻结 = 逐条 updateQueue(remove) 分离队列(含档位的副本存于插件 store,按 sessionId 隔离),当前轮次完成后 driver 因无 pending 自然停止;冻结期间对队列的修改(文本/顺序/档位)实时写回 store;恢复 = 先 await sessionGuard.resume(sessionId)(被打断回合的自然下一步先发生),再逐条 send(text) 重新提交并唤醒 driver(红色档位的条目先 cancel() 再发送);
  • 注意:含非文本内容(图片)的排队消息无法重发,冻结时会随队列释放(不会恢复)。

借助 dsh-session-guard 实现会话级锁定

冻结按钮触发时经 sessionGuard 桥(src/client/session-guard-bridge.ts → POST /session-guard/rpc { action: stopNextTurn|resume, sessionId })透传服务端做会话级锁定;同时在前端 raise composer block(conversation.blocks.set),输入框变 inert、回车不再漏进对话:

  • 引用组件指向:
  • src/client/freeze-button.tsx — 冻结/恢复控制(slot conversation.input.right);
  • src/client/steer-queue-dock.tsx — 三档规划等待区 + 冻结横幅/冻结列表(slot conversation.input.dock id queue);
  • src/client/freeze-store.ts — 会话级冻结状态 store(Map,供按钮 ↔ dock 共享);
  • src/client/session-guard-bridge.ts — session-guard RPC 透传(fail-open,未装则静默);
  • src/client/index.ts — slot 注册 + composer block 注入(conversation.blocks.set)。
  • 作用域对比:本插件冻结按钮 = 会话级(按 sessionId 锁那一个会话);session-guard 的高峰自动门 / step 门 = 全局或会话级暂停(入峰拉门、退峰自动放行)。

分工(重要):本插件只负责「排」,session-guard 只负责「停」

· 本插件(input-traffic) · session-guard 职责 · 用户输入进哪条队列、什么档位、何时被消费 · 何时允许推进(step / 回合 / 请求) DSH 原语 · next-step / next-turn 两条待处理队列 + updateQueue(steer\ · remove\ · edit) / send / cancel · agent/pre-step(step 门)、agent.cancel({keepInbox:true}) + goals.pause、agent/request hold 冻结 · 摘 queued + steering 行(保留档位)+ composer block · stopNextTurn:先释放 step 门再做回合级暂停(否则两边互等) 恢复 · 按档位重投(红 cancel+send / 黄 steer / 绿 send) · resume:从暂停点续跑

两条队列的物理含义:next-step = 「与工具返回同级的下一个 step,仍在同一个 turn 内」;next-turn = 「新的回合」。所以黄档是「插到当前回合的下一步」,绿档是「等回合结束后再开一轮」——这正是三档能被 API 在同一次 turn 中途看到的原因。

队列管理

等待区每条消息(未冻结时)提供:

操作 · 说明 上移 / 下移 · 调整 FIFO 顺序(整个队列按新顺序重建;含图片消息时禁用) 拖拽排序 · 按住行直接拖动到目标位置(原生 HTML5 DnD,无额外依赖);与箭头按钮同样走服务端重建 打回输入框编辑 · 消息内容回填 composer 输入框并从队列移除,编辑后重新发送 编辑 / 删除 · 多行文本区直接修改排队内容 / 取消该消息 红 / 黄 / 绿规划 · 见「三档语义」 取消并清空 · 两步确认后停止当前执行并清空全部排队消息(首次点击弹出「确认清空?」)

排序的并发保护:重建期间若某条消息已被 agent 认领(queue-item-not-found),本次排序立即中止且不重发,提示「队列已变化,本次排序已取消」——绝不会把变化中的队列排乱。

等待区的展开状态会记忆:手动收起/展开后,下次打开插件保持同样状态。

编辑排队消息(行内编辑)时:

  • 自动扩展:编辑区随内容实时增高,长消息完整展开,上限约 8 行,超出后内部滚动;
  • 快捷键:Enter 保存,Shift+Enter 换行,Esc 取消(中文输入法组合期间不会误保存);
  • 失败兜底:保存时若消息已被 agent 认领(如「已经开始发送」),编辑内容自动退回主输入框并提示,不会丢失;仅当主输入框为空时回填,已有草稿不被覆盖。

日夜模式(自动适配深色)

队列框与冻结按钮的配色不再使用自绘颜色,全部改为引用 dsh 官方语义 token(--dsw-alias-*:bg-layer-* / border-l* / label-* / interactive-bg-hover / state-success|warn|error-primary)。这些 token 由 dsh 主题系统统一维护:

  • 自动跟随:dsh 默认主题偏好为「跟随系统」——系统切到深色模式(或在 dsh 设置里手动选暗色主题)时,body[data-ds-dark-theme] 生效,token 自动切换为深色盘,队列框变为深灰底 + 白色反色字,三档色用官方暗色适配值(对比度由 dsh 保证);
  • 无需配置:插件本身不监听系统、不新增设置项——深浅完全交给 dsh 主题,白天外观保持不变;
  • 范围:等待区 dock(含冻结横幅 / 冻结列表)与输入框右侧「冻结会话」按钮。

安装

# 方式一:从 npm 安装(推荐,稳定发布)
#   (profile 是 pnpm workspace root,add 需带 -w 参数)
dsh plugin --profile web add dsh-input-traffic -w

# 方式二:GitHub 直装(drscrewdriver fork 专属,先试用新功能)
#   (本仓库未提交 lib/,装后需在 profile 内现场构建:
#    cd ~/.dsh/profiles/web/node_modules/dsh-input-traffic && npm install --legacy-peer-deps && npm run build)
dsh plugin --profile web add github:drscrewdriver/dsh-input-traffic#main

# 方式三:本地路径组装
# dsh plugin --profile web add /absolute/path/to/dsh-input-traffic -w

# 确认组合树包含新行
dsh web --dump-config | grep -B1 -A2 'input-traffic'

# 重启 dsh web —— 必做!运行中实例不热载 bundle 层
dsh web
⚠️ GitHub 网络可达性:github: 直装需要能连通 github.com;网络受限时请先配置可用代理或镜像加速,否则 add 会在拉取阶段卡住。

本地构建与测试:

npm install --legacy-peer-deps   # @deepseek-ai client 包链在 npm 上不完整,仅装工具链
npm run build                    # tsc(lib/types)+ tsdown(lib/index.js + lib/client.js)
node examples/verify-assembly.mjs  # 12 项装配断言
npm test                         # 50 项 vitest 组件测试
npm run lint                     # ESLint(src + tests,flat config)
npm run verify                   # 一体化门禁:lint + test + build + verify-assembly

开发(TDD + Lint)

本项目按 TDD(测试驱动开发)维护:先写失败用例,再实现到全绿。

npm run tdd        # vitest watch:改动即重跑,红→绿闭环

流程:

  1. 在 tests/ 新增/修改用例(红:确认新行为尚未实现);
  2. npm run tdd 观察失败;
  3. 在 src/ 最小实现(绿);
  4. npm run verify 全绿后提交(lint + 50 测试 + 构建 + 12 项装配断言)。

Lint 说明:

npm run lint       # ESLint flat config(eslint.config.mjs)
npm run lint:fix   # 自动修复可修复项
  • 范围:src/ 与 tests/(TypeScript + React);构建产物 lib/ 忽略;
  • 规则:@typescript-eslint/recommended + react-hooks 最佳实践;未使用变量报错(下划线前缀 _ 可豁免)。

使用方式

  1. 智能体忙碌时直接输入并发送,消息统一进入等待区(默认绿色排队);
  2. 在等待区对消息点规划按钮:
  • 🟡 黄色 = 插话——当前动作结束后插入;
  • 🔴 红色 = 打断——立即中断当前动作,消息随后被处理;
  • 🟢 绿色 = 保持排队(当前默认态);对已插话的消息点绿 = 收回排队;
  1. 需要调整顺序 / 修改内容:用上移下移、打回输入框编辑或多行编辑(Enter 保存、Shift+Enter 换行);
  2. 省钱关键(推荐):邻近高峰时段(9:00-12:00、14:00-18:00)点击输入框右侧「冻结会话」,当前轮次完成后自动暂停,避开高价窗口;闲时点「恢复会话」继续。可配合提醒 / 计费统计插件使用(见上文「推荐」)。

常见问题

打断后消息没有回复 / 对话停住

历史问题(已修复)。根因:harness 的 inbox 禁止重复插入同一条消息——打断后直接对原消息执行 steer 会被 "message is already pending" 拒绝,消息滞留在队列、agent 停摆。当前实现改为 cancel → remove → resend(新消息重新提交),打断消息会立即被 agent 处理并回复。若仍遇到,请确认插件为最新构建并重启 dsh web。

编辑保存失败后,内容去哪了?

不会丢。保存失败(消息已被 agent 认领)时,编辑内容会自动退回主输入框并弹出「编辑失败,内容已退回主输入框」提示;主输入框已有内容时不回填,仅提示编辑失败。

设置面板里找不到「繁忙时 Enter 键行为」

正常——插件接管后该设置行被隐藏,Enter 行为固定为绿色排队(旧偏好不会在隐藏的设置行背后继续生效)。