Repository navigation
Releases: cyjyyd/dsh-ssh-tui
Release list
0.8.4
0.8.4 — Input Ownership & Release Hardening (Stable)
稳定版:
0.8.4-rc.2观察期结束后的正式转正。 包版本为0.8.4;产品代码与最终 RC.2
相同,只有版本元数据和发布材料变化,没有新增功能、兼容范围变化或存储迁移。
正式 tag、GitHub Release 与 npmlatest在最终四腿 CI 和规范 tarball 验证通过后创建;
next同步到0.8.4,不再停留在旧候选。
0.8.4-rc.2的候选说明与收据保留在
release-notes-0.8.4-rc.2.md;0.8.4-rc.1的历史记录保留在
release-notes-0.8.4-rc.1.md,不追溯改写。
dsh-ssh-tui现在以 DSH Relay 为产品名展示;npm 包名与安装命令没有变化。
版本状态
0.8.4-rc.1的 annotated tag 是a2645c9134646f83d8a5db758743476285f9840b,其指向的提交是
0e1342a9f4a7cbb922971891e9b26a311a7c7026(2026-10-17)。0.8.4-rc.2的 annotated tag 是aa1688264c9ae246eb20651caced38bfd69d68d1,其指向的提交是
5795f6052cdaba565b0db9bf1982aa622f40ac05(2026-10-24),已作为候选发布到 npmnext。0.8.4从该 RC.2 转正;稳定版只把包版本改为0.8.4并更新发布材料,没有产品代码变化。
Stable 维护来源是release/0.8.4,不是main;main上的 W-4 Completion 或 W-5 Search Screen
不属于本线。- 正式发布后 npm
latest和next都指向0.8.4。RC.2 的 tarball 哈希和测试收据保留为历史,
不作为0.8.4稳定 tarball 的哈希。
本版包含(RC.1 之后)
FI1 — 首次安装的 Agent Plane 自动补齐
首次向导保存后、以及旧或不完整 profile 启动时,TUI 只补当前组合缺少的 Agent Plane 工具行;
已存在的行不重复写入,写入前保留备份,重复启动保持幂等。若行已经写入但当前进程尚未组合它们,
界面会明确提示重启,而不是把“已保存”误报成“已生效”。这不是安装钩子,也不会把所有缺失行
无条件重写。
WIN-CI1.H3 — RTT 探针的 Drain 不再丢弃按住的 Esc
RTT/liveness 探针开始时,输入过滤器可能正扣着一个单字节,因为它还可能是光标应答 CSI ... R
的开头。TerminalInputPump.drain() 不再重置过滤器并丢掉这个字节,而是保留它并重新安排释放:
如果后续应答到达,字节合并为应答;如果窗口结束仍不完整,就按原有规则交还为键入。切开的应答
仍不会被误认成 Escape。
APPR.BP1 — AI Reviewer 传递真实授权 Session
Reviewer 的 GenerateOptions 现在携带授权 Agent 的真实 sessionId。同一 Session 的复核共享
身份,不同 Session 或子 Agent 不共享。Reviewer prompt、400-token budget、15 秒 deadline、
reasoning effort、授权策略、缓存与 fail-closed 行为均未改变;该修复不写入用户对话记录。
DOC.R1 — /doctor 纳入已注册 Adapter
doctorRouting() 保留 llm-pi-ai.providers,并合并 ctx.llm.listProviders() 返回的实际注册
Adapter ID,去重后用于路由自洽性检查。已注册的 deepseek-official 不再被误报为“不在已配置
路由里”;第三方 Provider 缺失、已配置 Provider 的非法模型、以及子代理跨 Provider Family
仍会继续告警。LLM 服务缺失或 listProviders() 抛错时回退到已配置集合,/doctor 不会崩溃。
“已注册或已配置”只表示路由存在,不表示网络或 API 调用成功。官方模型目录的实时校验仍是
OPEN:routeModels 为空时继续按“目录未知”处理,不会伪造 Model Missing。
兼容性
声明窗口与 0.8.4-rc.1 相同,没有新增兼容声明:
>=0.2.0-rc.1 <0.2.1 || >=0.2.1-rc.2 <0.2.2
0.2.1-rc.2仍是默认开发线。0.2.0-rc.1/0.2.0-rc.2继续保留兼容表态与 CI 腿。0.1.7线自0.8.4-rc.1起为incompatible。0.1.5及更早从0.8.0起不再支持。
验证记录
0.8.4 与最终 RC.2 的产品代码相同;以下测试收据来自 RC.2 的最终验证,稳定版只改版本和发布材料:
npm run build:通过。npm run typecheck:通过。npm test:1540 项,1536 通过,0 失败,4 跳过。npm run freeze/npm run freeze:input:通过。npm run bench:五个动作均为增量绘制,0 次整屏清屏。npm run bench:report -- --check:通过。scripts/artifact-smoke.mjs:RC.2 候选 tarball 通过,包清单 252 files。scripts/upgrade-smoke.mjs --from 0.8.4-rc.1 --pack与--from 0.8.3 --pack:通过
(manifest、bundle 入口、凭据与会话存储均保留)。- 最终
0.8.4的四腿 CI、规范 tarball 的 bytes / SHA1 / SHA256 / integrity 与 Registry
回读结果记录在独立发布回执中;本文件不把 RC.2 的哈希写成稳定 tarball 的哈希。
Windows Terminal、conhost、IME 中文输入、多行中文粘贴、鼠标复制与 SSH 恢复仍按
release.md 的人工验收清单执行;H-2 的 conhost / ASCII 边框确认已由维护者完成,
其余未实际人工执行的项目仍保持 PENDING,不引用旧版本结果冒充本版验收。自动化 PASS 不是
人工 Gate PASS。
已知限制
- 官方模型目录校验仍 OPEN:
/doctor可以看到已注册的官方 Provider,但尚未异步读取
llm.listModels('deepseek-official')来校验官方模型成员;自定义官方 Catalog 缺少所选模型时
仍可能漏诊。 - 过滤框对非 BMP 字符的限制(未修):picker 的过滤框仍以
text.length === 1判单字符,
'🚀'.length === 2,因此星平面字符在过滤框中会被丢弃。Composer、借用字段与向导字段正常。 - 陈旧
lib/的打包风险(未修):tsc不清理旧输出,prepack也不跑clean;发布必须从
干净树构建。artifact-smoke的清单检查是纪律证据,但脚本本身不阻止脏树打包。 - 时间敏感的 Windows 单元测试:
session-list.test.mjs中 150ms mock 竞速用例在慢跑器上可能
失败;这是测试 oracle 问题,不是产品行为,本版未修。 - ConPTY 无法覆盖的帧级断言:ConPTY 会重写行寻址并自行插入清屏,逐行寻址/不清屏类检查在
Windows 上标记为NOT_MEASURED;功能断言照常执行。 - 发布后 registry 比对仍是人工步骤:发布前自动化 pack → 清单 → 安装 → 探针;发布后从
registry 取回 tarball、核对 shasum/integrity 与lib/仍按docs/release.md执行。
安装
稳定版走 latest,当前正式版本为 0.8.4:
dsh plugin --profile tui add dsh-ssh-tui@latest
dsh --profile tui@next 也已同步到 0.8.4;需要固定版本时可显式使用 dsh-ssh-tui@0.8.4。
历史候选仍可按精确版本安装:
# 历史 RC.2
dsh plugin --profile tui add dsh-ssh-tui@0.8.4-rc.2
# 历史 RC.1
dsh plugin --profile tui add dsh-ssh-tui@0.8.4-rc.1已安装 0.8.3、0.8.4-rc.1 或 0.8.4-rc.2 的用户可以升级;配置、凭据和会话记录不需要迁移。
0.8.4-rc.2
0.8.4-rc.2 — First-install, Input & Doctor Reliability Candidate
发布通道:仅 npm
next,不得触及latest。
此文档是候选版的发版说明;它本身不表示已经创建 Tag、GitHub Release 或完成 npm 发布。
候选来源:从release/0.8.4@127617b99101820ff548f6fa8b61f319b5084d24切出的独立发行准备分支。DSH Relay 仍使用 npm 包名
dsh-ssh-tui,没有改名、没有存储格式迁移。
为什么需要 RC.2
0.8.4-rc.1(2026-10-17)已经作为 prerelease 发布;它提供 Reader → Router → Owner 输入归属整理,以及首次安装向导、工作区粘贴和发布 tarball 的 CI 门禁。
此后维护分支进行了四项实际运行时修复,因此 RC.2 不是 RC.1 的无变化重打包,不能沿用 RC.1 的字节、哈希、测试或人工验收收据。
RC.1 之后的修复
FI1 — First-install Agent Plane Repair
全新安装或旧的不完整 Profile 可能缺少 tool-ask-user 与 present 的 Agent Plane 组合行。TUI 现在能检测并定向补齐缺失项,写前备份、不覆盖已存在行、重复启动幂等。已写入但本进程尚未组合的工具会明确提示重启才生效,不把保存成功误报为运行时就绪。
WIN-CI1.H3 — RTT Probe 不再丢弃 Esc
Launcher 的 TerminalInputPump.drain() 原先会把正在等待判别的 ESC 通过 filter.reset() 丢弃:用户按下 Esc 后,恰逢 RTT/存活探测,/view 选择器可能无法取消。现在保留暂存字节、重新安排释放;完整或分片的光标应答仍由过滤器识别,不以强制转发半截应答换取按键恢复。
APPR.BP1 — Reviewer 绑定授权 Session
AI Reviewer 的直接 llm.stream() 调用携带 request.agent.session.id,保留同一会话身份、隔离不同 Agent Session 和子代理。Reviewer prompt、400-token 输出预算、15 秒 deadline、reasoningEffort: off、授权策略、缓存与 fail-closed 保持原样。未对完整真实线上审批延迟作保证。
DOC.R1 — 官方 Provider 路由诊断
/doctor 的路由事实来自 llm-pi-ai.providers 与实际注册的 ctx.llm.listProviders() 的并集。官方 deepseek-official 已注册时不再被误报为“既未注册也未配置”,仍保留第三方非法模型与子代理不同族警告。注册不等于联网调用成功,官方模型目录的实时成员校验仍为 OPEN。
未纳入本候选
- W-4 Completion / W-5 Search Screen(
main开发线),未进入release/0.8.4; - PR #8 的独立 InputReader 粘贴尾部 Esc 边界修复(慢粘贴 payload ESC 语义权衡仍未收口);
- PR #9 的 Host/Relay 诊断插桩;
- 官方模型目录实时校验、Reviewer 延迟预算重设计,以及其他未获授权的兼容范围变化。
兼容范围
与 RC.1 一致:
>=0.2.0-rc.1 <0.2.1 || >=0.2.1-rc.2 <0.2.2
默认宿主开发线为 0.2.1-rc.2,兼容测试仍覆盖 0.2.0-rc.1 / 0.2.0-rc.2。0.1.7 及更旧宿主不在本候选支持范围内。
发布前证据门槛
以下证据必须绑定最终候选 Commit 与其 tarball,不能沿用 RC.1 或 Stable 的旧收据:
| Gate | 必需结果 |
|---|---|
| 源码与包身份 | package.json 和 lockfile 同为 0.8.4-rc.2;候选 Git SHA 冻结 |
| 四条 CI | Linux 三线及 Windows test-windows 全部 SUCCESS |
| 三道阻塞门 | Fresh-home 向导、Paste Probe、Packed Artifact 在默认 Linux 和 Windows 真实执行 |
| 产物可重复性 | 干净构建、npm pack 文件清单、bytes、shasum、integrity 与候选 SHA 对应 |
| 升级验证 | upgrade-smoke --from 0.8.4-rc.1 --pack;并核对 0.8.3 → RC.2 |
| Windows 人工 | 真正 Windows Terminal / conhost、IME、中文粘贴、复制、重连、首次安装,H-1~H-11 逐项 PASS |
| 最终发布许可 | 维护者明确授权 Tag / GitHub prerelease / npm publish --tag next |
本文件不预先给出未生成的 RC.2 tarball 哈希、文件数或 CI 成功结论,人工 Gate 亦不得由 ConPTY 自动探针代替。
安装与发布通道
在 RC.2 正式发布之前,registry 的可用版本请以实际 packument 为准;既有历史记录中 latest 为 0.8.3,next 为 0.8.4-rc.1。
获得正式 RC.2 发布授权后,候选用户才能通过 dsh plugin --profile tui add dsh-ssh-tui@0.8.4-rc.2 或更新后的 @next 安装。正式 0.8.4 发布另走 latest,不在这次候选动作范围内。
历史候选记录:0.8.4-rc.1。正式稳定版准备说明:0.8.4。
v0.8.4-rc.1 — 输入归属 + 发布安全门(候选版)
0.8.4-rc.1 — Input Ownership & Release Hardening Candidate
发布方式:只上
next。latest保持 0.8.3(候选版不占latest)。
上一次发版:0.8.3(2026-10-13,latest+next)。本文件描述的是候选版,
是否转正由独立审核与后续发版决定。本版同时是:输入可靠性的修复批次、内部输入路由的结构整理、以及发布安全门的加固。
与 0.8.3 相比有 runtime 行为变化(见「修复」),这一点如实写在最前面;本轮发布准备
只为跨线兼容加了一处垫片(见「跨宿主线的兼容垫片」),没有别的 runtime 改动。
dsh-ssh-tuiis now presented as DSH Relay.
The npm package name and the install command have not changed.
dsh-ssh-tui现在以 DSH Relay 为产品名展示;npm 包名与安装命令没有变化。
没有协议变更、没有配置迁移、没有数据格式变更。升级即生效,不需要任何迁移动作。
概览
0.8.4-rc.1 的主线是输入归属:一次终端 read 里往往不止一个键(粘贴、合并读取、带尾巴的序列),
0.8.3 及以前由一串顺序判断逐段消费它们,于是同一类缺陷反复出现在不同 Owner 上——Screen 背后的草稿被
粘贴改写、Surface 的编辑键去改看不见的草稿、一次 read 里的第二个键被丢掉。这一版把这条路径拆成三层
(Reader 分帧 → Router 判定归属 → Owner 执行),并把每一层的行为用轨迹、归属矩阵与变异测试钉住。
同时,0.8.3 的真实事故(首次安装时向导收不下粘贴的 API Key)暴露了发布流程本身的缺口:CI 从来没有
跑过"未配置 home 的首启向导",也从来没有跑过 npm 发布包本身。这一版把两者都变成阻塞门。
修复
一次 read 不是一个键(W-3a / W-3a.1)
终端的粘贴、合并读取、以及"序列 + 尾巴"都会把多个键塞进同一次 read。此前合并 read 里的第二个键会被
整段匹配失败吞掉(two arrows in one read 两个都没移动、PgUp 连按只滚一次、改绑键与其它键同 read
时绑定丢失)。现在 Reader 把 read 切成单元(完整序列、Alt 和弦、控制字节、CR/LF、一段文本),
每个单元各问一次归属,于是"同一次 read"与"分两次 read"到达同一位置、同一终态。
Screen 的边界在单元内部同样成立:关闭 Screen 的那个字符之后的同一单元剩余部分被丢弃,而不是
落到它背后的 Composer(真实入口是粘贴尾巴 \x1bq)。
- 提交:
f7e5849(W-3a)、f44b56c(W-3a.1) - 证据:
tests/input-reader.test.mjs(成对轨迹)、tests/input-ownership-regression.test.mjs、
tests/input-reader-traces.json(参考轨迹 72 → 111 条;两条是本次切分用例,一条只改handleInputText
的拼接形状、消费者调用与终态不变)
Surface 的编辑键归 Surface,编辑的是读者看得见的那一行(W-3b.1)
Surface 打开时,Home/End/Delete 与 kitty 的复制序列此前被放行给 Composer:Home/End 移动看不见的
草稿光标,Delete 编辑它。现在这几个单元归 Surface——有借用字段就编辑屏幕上那一行,没有就消费掉。
复制键在 Surface 下保持无操作(与它原来的可观察行为一致)。
- 提交:
5ad6290 - 证据:
tests/input-ownership-matrix.test.mjs、tests/interaction-surface.test.mjs
借用字段的终端光标跟随它自己(W-3b.1a)
绘制时终端光标取的是隐藏草稿的列号,于是借用字段(自由文本问题)里光标会落在错误位置——中文与
宽字符下尤其明显。现在光标跟随正在绘制的字段。
- 提交:
fd3f638 - 证据:
tests/borrowed-field-caret.test.mjs(真实终端网格)
输入一个 emoji 会直接崩溃(W-3b.1b)
在 Composer 里输入 🚀、𠮷 等非 BMP 字符会抛 RangeError: Maximum call stack size exceeded:
判定"这是不是一段文本"用的是 UTF-16 长度(🚀.length 是 2,但它是一个码点),于是同一个码点被无限次
送回自己。借用字段与确认框同样命中。现在按码点判定,ASCII 热路径零分配;多字符单元仍逐码点重新判定
(yes 在确认框上仍然由第一个字符作答、随后的 es 仍交给它当时的 Owner)。
同一根因的第二处(借用字段的 grapheme 编辑切的是隐藏草稿而不是借用字段)一并修掉。
- 提交:
d0db720 - 证据:
tests/unicode-input-safety.test.mjs(12 条:Composer/借用字段/拒绝方、UTF-8 逐字节分片、
粘贴与键入等价、grapheme 编辑),与修复前构建 40 个场景逐字节等价
一个 Surface 借走字段后,不再改写 Composer 的历史与草稿(W-3b.3a)
两条实际可达的越权路径:借用字段里的退格 / Delete / Ctrl+U / Ctrl+K / 粘贴会经
leaveHistoryBrowse() 结束 Composer 的历史浏览,并把正在浏览的那条历史写进 historyDraft——
读者原先未发送的草稿再也回不来;Surface 打开时 Alt+f / Alt+F / Alt+/ 会把 /find 直接写进
看不见的草稿。现在历史浏览只由 Composer 自己的字段结束,/find 种子只写进 Composer 拥有的字段
(Surface 内该和弦被消费),Alt+1…4 / Alt+n/p 的既有语义保持不变。
- 提交:
48efd24 - 证据:
tests/surface-state-isolation.test.mjs(真实 ↑ 进入浏览的完整流程)、
scripts/input-freeze-audit.mjs新增composer history写入类与historyGuards检查
反馈行不再随消息长度漂移(W-3b.2a)
/copy 之类的回执此前是状态行上最低优先级的一组,放得下就画在第一行、放不下才退到最后一行——
同一条消息的行号由它的长度与窗口宽度决定(真实报告:"copy 提示跑到第一行")。现在回执恒在身份行,
状态行只留仪表;警告(notice)仍优先于回执。
- 提交:
915578f - 证据:
tests/feedback-routing.test.mjs(短/长回执、notice、160/100/60/40 列) - Ctrl+L 的重绘归属经独立审核后保留双 Painter(
KEEP_DUAL),合同与理由见 AD-22 §10;
证据tests/ctrl-l-repaint.test.mjs(一次有意的完整清屏,之后回到增量)
粘贴的结束标记被切成两半(W-1 补)
终端的粘贴用 ESC[200~ … ESC[201~ 包起来,而字节流不保证按 read 边界对齐:ESC[201~ 可能分成
…ESC[20 和 1~ 两次到达。麻烦在于 ESC[20 同时是"半个光标应答"的形状——输入过滤器会先把它扣住,
30ms 后当打字放行。于是后半截到达时已经没有开头,Reader 的粘贴状态永远关不掉:此后每个键都被当作
粘贴内容,Esc 关不掉对话框,/exit 也退不出去(只能杀进程)。
这条不是本版引入的:0.8.3 的内联实现有同样的跨 read 粘贴状态与同样的朴素切分,所以同样中招;只是要
"结束标记恰好跨两次 read 到达"才触发,之前没人撞上。本版新增的 tui-paste-probe.mjs 在 CI 上撞上了
(/view 对话框里的 Esc 不再生效),才把它逼出来。
修法两处,各管一半:
-
输入过滤器(
TerminalInputFilter):粘贴流量里的尾巴不再被当成半个应答扣留——设备应答不会出现在
括号粘贴里面; -
Reader(
InputReader):把切开的标记留到下一读补齐;另有一只看门狗盯整段粘贴——2 秒内没有任何
粘贴流量就认定这次粘贴已经结束(终端的 close marker 有可能被直接吞掉,ConPTY 就会吞掉半个 CSI),
把状态放回"不在粘贴中",而不是让键盘继续当粘贴内容。 -
证据:
tests/input-reader.test.mjs(跨 read 补齐 + 悬空尾巴放行)、tests/terminal-input.test.mjs
(粘贴尾巴不扣留)、scripts/tui-paste-probe.mjs的 "split-marker" 步骤(粘贴后 Esc 仍必须是键)
跨宿主线的兼容垫片(0.2.1-rc.2)
/subagents kill <id> 调用 subagent 服务的 drainContinuableChildren(parent, ids);0.2.1-rc.2 把它
改名为 drainChildren(parent, ids)(同义、签名一致),旧名不再存在。窗口里同时有两条线,所以调用点
改走 src/dsh-compat.ts 的 drainChildSubagents():它按宿主实际拥有的名字调用,两者都没有时解析为
Promise.resolve()。这是本版唯一一处为兼容而改的 runtime 代码。
- 证据:
npm run typecheck在 0.2.1-rc.2 上 0 error;0.2.0 两条腿在 CI 上同样编译(它们只认旧名)。
内部结构
- Reader(
src/input-reader.ts,新文件,W-1):解码、探测回复过滤、ESC 组装、粘贴边界、单元切分。 - Router(
src/input-router.ts,新文件,W-2):route(kind, unit, ownershipView) → screen | surface | composer | workspace,纯函数、无状态;handleInputText变成 Reader → Router → owner 三行能读完。 - 共享执行体的合同:
composerUnit仍是 Composer 与 Surface 的共用执行体,经必要性审核后保留
(KEEP_SHARED_WITH_INVARIANTS,S-1…S-6,AD-20 §19)。本版没有做 executor split,也不打算在
0.8.4 里做;完整拆分留给后续的独立结构轮。 - W-3b ownership / input-routing 合同已收口(CLOSED):G1–G4 证据(Surface × 共享体敏感键、
options question / filtering picker 进归属矩阵、两条/find种子路径、Ctrl+N/P 的焦点环记录)
与 M1–M7 变异全部实测为红。 - 参考轨迹 111 条(两条新增;
paste-split-close-marker的拼接形状改变,消费者调用与终态不变):
新增只追加,旧读法以台账形式记录在
tests/input-reader.test.mjs的GAP_FIXTURES/W3A1_FIXTURES/W3B1_FIXTURES/W3B1B_FIXTURES。
发布安全门(Release Hardening)
这一版新增三道阻塞门,在 CI 的默认腿(0.2.1-rc.2)与 test-windows 上各跑一次:
-
首次安装向导(
probe-home.mjs --probe --unconfigured --script tui-setup-probe.mjs):
在真实 PTY / ConPTY 上用未配置的 home 启动 → 向导自动出现 → 列表步 → 字段步 →
括号粘贴一段 API Key → 字段收下且全程掩码(掩码按•计数断言,密钥既不出现在画面、
也不出现在原始字节流里)→ resize → Esc 交还工作区 → 写入凭据后再启动不再出现向导 →
resumed 首启保存:走完九步按y,不需要任何额外按键工作区自己回来。 -
发布产物冒烟(
scripts/artifact-smoke.mjs):npm pack出真实 tarball → 检查包内清单
(必须有package.json/lib/index.js/lib/tui.js/cordis.patch.yml/README.md/
LICENSE;不得有src/tests/scripts/.github/本地缓存/嵌套 tgz/node_modules)→
把 tarball 装进仓库之外的临时 home(不是link:<repo>)→ 证明dependencies记录的是
file:<tgz>、安装实体不是指向 checkout 的符号链接、realpath 在 profile 内且不等于仓库 →
--dump-config通过 → 在上述安装产物上再跑一遍向导探针、工作区探针与粘贴探针。 -
粘贴探针(
probe-home.mjs --probe --script tui-paste-probe.mjs):世界不是只靠打字——0.8.2 的事故
是粘贴进不了字段,而当时的探针只会打字。向导那一半在第 1 道门里;这一道补工作区那一半:
单行粘贴落进 Composer、多行粘贴只插入不提交、Ctrl+U 清空、4 kB 粘贴不崩不闪、
Screen / picker 打开时粘贴不漏进层、也不落到背后的 Composer、粘贴里的\x1bq尾巴保持字面
(不当作 Esc 关掉 Screen)、结束标记被切成两半后键盘仍然可用(这一步在 ConPTY 上跳过并说明原因:
它会把半个 CSI 直接吞掉,切不出这个形状;那里的"粘贴后 Esc 仍然生效"由别的步骤覆盖)。
负向证明:把 Reader 的
paste 分帧关掉(isPasteRead()恒 false),
该探针立刻判红("多行粘贴没进 Composer"——即粘贴被当成按键执行了)。
另外:
scripts/upgrade-smoke.mjs(基础设施):装上一个已发布版本 → 写凭据与会话 → 装候选 →
断言 bundle 条目、凭据、会话存储存活、--dump-config仍通过。同版本互换不算升级,脚本会明确
拒绝(--allow-same-version才跑机制,并把自己标成 reinstall smoke);真正的升级验证是版本
bump 之后的必跑项,本版即按此执行(见发版记录)。- 三道门都有负向证明:清单门对"缺
lib/tui.js"与"夹带tests/"两种坏 tarball 均判红;
向导门在"home 明明是未配置的、向导却没出现"时从 SKIP 改为 FAIL(0.8.1 那类首启缺陷不再能伪装成绿);
粘贴门在 Reader 的 paste 分帧被关掉时判红(粘贴被当成按键执行)。
实测(本候选版准备时,本机 100×20)
场景 毫秒 字节 寻址行 整屏清屏
流式输出时的一次重绘 1.68 501 3 0
等待卡片时钟走一格 1.13 465 3 0
报告屏翻一页(PgDn / PgUp) 1.40 2457 17 0
菜单里移动一次光标 0.91 588 4 0
向导字段里打一个字符 0.19 140 1 0
五个动作合计 0 次整屏清屏(npm run bench,OK: every measured action was incremental)。
毫秒列是本机数字,只有字节 / 寻址行 / 清屏数是跨机器可比的合同值。README 里的实测块由
npm run bench:report 单独跑一次生成(因此毫秒列与本表可能相差零点几毫秒,字节/行数/清屏数逐项相同),
npm run bench:report -- --check 校验提交的数字与当前树一致。
同一轮的本机收据:
npm run typecheck→ 0 errornpm test→ 1509 项 · 1505 通过 · 0 失败 · 4 跳过npm run freeze/npm run freeze:input→ 全绿(八条不变量 + 七条退役路径;输入归属无基线超限)- 参考轨迹 111 条(2 条新增)
scripts/artifact-smoke.mjs→ PASS(0.8.4-rc.1· 250 文件 · 约 1.53 MB;在装好的 tarball 上依次跑
向导探针、工作区探针与粘贴探针)scripts/upgrade-smoke.mjs --from 0.8.3 --pack→ PASS(真实0.8.3 → 0.8.4-rc.1)scripts/verify-batch.mjs --home <新建的探针 home>→ RESULT: PASS:typecheck、1509 项套件、
十类真 PTY/ConPTY 探针(含tui-unmeasured-probe的两条 leg 与tui-paste-probe)、footer 帧检查全过,
无SKIP:npm run bench:report -- --check→ PASS(README 两个语种的实测块与当前树一致)- CI → 四条腿全 success:
test (0.2.1-rc.2)/test (0.2.0-rc.2)/test (0.2.0-rc.1)/test-windows。
默认腿与test-windows上的三道阻塞门(首启向导、粘贴、发布产物冒烟)都是实际执行并 success,不是
skip;unmeasured探针在两条腿上也已转绿。具体的 run 号与提交写在docs/release.md的发版记录里——
这份说明随包发布,把 run 号钉进来只会在下一次改动里立刻过期。
兼容性
声明窗口本版变了,两条线一进一出:
>=0.2.0-rc.1 <0.2.1 || >=0.2.1-rc.2 <0.2.2
- 新增
0.2.1-rc.2(上游next):整族随之移动到这条线,族的 root pins 各前进一个 patch
(cordis 4.0.5、include 1.0.10、loader 1.0.6、group 1.0.5),默认线(提交的 manifest / 锁 /
test-windows)改为它。 - 摘除
0.1.7线(0.1.7-rc.1/0.1.7-rc.2):dshReleases改为incompatible、两条 CI 腿删除。
原因是实打实的范围判断而不是喜新:0.2.1-rc.2 移动了族的 root pins,并把 subagent 的 wind-down 调用
改名(drainContinuableChildren→drainChildren——签名一致,但旧名在新宿主上不存在);
一个构建同时面向两代 seam,就得在类型面上各写一份分支。0.2.0 的两个 rc 仍在窗口内,仍各有一条腿。 0.1.5及更早仍不支持(dshReleases保留incompatible表态)。0.2.1-alpha.x不在声明承诺内:承诺的是跑过并表态的 rc。- 默认线现在是
0.2.1-rc.2;0.2.0-rc.2与0.2.0-rc.1继续跑既有的兼容性回归。
已知限制
- 过滤框对非 BMP 字符的限制(R4,未修):picker 的过滤框以
text.length === 1判单字符,
'🚀'.length === 2,因此星平面字符在过滤框里被丢弃。与上面已修复的 Composer emoji 崩溃不是同一件
事——Composer / 借用字段 / 向导字段输入 emoji 都是正常的。属"过滤字段模型",留待后续。 - 陈旧
lib/的打包风险(未修):tsc不清理旧输出、prepack也不跑clean,从脏lib/
打包会带上已删除源文件的陈旧模块(实测:本机 252 文件,干净重建与 CI 都是 249)。本版发布必须从
干净树打包;artifact-smoke的清单检查是这道纪律的证据,但脚本本身不阻止脏树打包。 - 一个时间敏感的 Windows 单元测试:
tests/session-list.test.mjs的
"a title is cached and painted as soon as it re...
0.8.3 — 紧急修复:向导收得下粘贴,也走得掉
0.8.3 发版说明
发布方式:
latest+next。 上一次发版:0.8.2(2026-10-05,latest+next)。
本版是紧急修复:0.8.2 把首启向导改成 Screen 之后,向导的输入与退场出了三个缺陷,
都在"首次安装 //setup"这条路上。没有协议变更、没有配置变更、没有数据损坏;
升级即修复,不需要任何迁移动作。
头等事:向导收得下粘贴,也走得掉
三个缺陷都是 0.8.2 引入的(0.8.1 的向导借工作区的输入行,所以既收得到粘贴、也不需要自己退场)。
1. 粘贴的 API Key 进不了字段,只能手敲
终端粘贴(Ctrl+V / Cmd+V / 右键,即括号粘贴)此前落到了向导 Screen 背后那个看不见的工作区草稿里:
向导字段一个字都收不到,而粘贴并没丢——它会留在草稿里,Esc 离开向导后在输入行现形,按回车就会被当成
一条消息发出去。受影响的是所有做括号粘贴的终端(Windows Terminal、iTerm2、GNOME Terminal、WezTerm、
kitty、VS Code 终端、tmux……),也就是几乎所有现代终端;受影响的是每一个字段步骤(提供商过滤、
Provider ID、Base URL、API Key、模型列表、上下文窗口)。
现在粘贴归字段所有:字段步骤直接落进字段(行尾换行折叠掉,多行按分隔拼成一行),没有字段的步骤
(模型多选、确认)不接收,两种情况都不再改写工作区草稿。老控制台不发括号粘贴时,多行 burst 也按粘贴处理。
2. 走完向导没有退回工作区
保存成功后向导仍然占着屏,读者要随便按一个键才退得出去(那个键的"Screen 背后没有向导 → 关掉它"兜底
才是真正的退场)。保存本身就是退场,现在与 Esc 一致:按 y 的同一帧,工作区回来。
同一处的失败路径也修了:保存失败时错误写在了一个立刻被丢弃的状态上——Screen 留着、错误没了、填好的答案
也没了,下一次按键还把这套流程当作没发生过。现在失败保留向导与错误,改完可以直接重试。
向导自己的提示行会随 Screen 消失,所以"配置完成,已记住 X / Y"现在落在转写行里(凭据那一行是
footer 回显,会过期),回到工作区后还读得到。
3. 一次读取里的多个按键(顺带修掉的同类)
一次终端读取里的多个按键此前被当成一个键:text\r(慢链路上的快速输入、终端合并写入)会收下文字却
丢掉那个回车,步骤停住不动——读者只能再按一次回车。现在一次读取按 composer 一直以来的规则拆开:
转义序列保持完整、CRLF 算一个回车、控制字节各自成键、可打印文本连成一段(所以没有括号粘贴的
"yes" 仍然不会替读者按下确认)。
兼容
上游兼容窗口与 0.8.2 相同:0.2.0-rc.1 / 0.2.0-rc.2 / 0.1.7-rc.1 / 0.1.7-rc.2。
0.1.5 及更早的宿主不再支持。 本版不改协议、不加命令、不动上游接口,也不改任何配置文件的形状。
安装 / 升级
dsh plugin --profile tui add dsh-ssh-tui@latest装完退出再启动。/status 可确认当前版本;装不上先看 /doctor。
更新时请显式带 @latest,否则已有 lockfile 可能继续沿用旧版本。
实测数字(本版,100×20)
| 动作 | 毫秒(中位) | 字节 | 寻址行 | 全屏清屏 |
|---|---|---|---|---|
| 流式 delta | 1.67 | 501 | 3 | 0 |
| 等待卡时钟 | 1.12 | 465 | 3 | 0 |
| 报告屏翻页 | 1.21 | 2457 | 17 | 0 |
| 菜单光标移动 | 0.90 | 588 | 4 | 0 |
| 向导字段打字 | 0.19 | 140 | 1 | 0 |
复现:npm run bench;README 里的同一张表由 npm run bench:report 从这次真跑生成。
本版没有触碰绘制路径,数字与 0.8.2 同一量级。
已知问题
- 向导字段没有左右光标键,粘贴总是追加在末尾(掩码字段 + 中间光标这条组合目前不存在)。
未测那一格在 Windows 本机看不到(ConPTY 代答光标位置),由管道父进程形状的探针覆盖。- 完整已知问题与人工验收清单见
checkpoints.md的 0.8.2 一节。
v0.8.2
0.8.2 发版说明
发布方式:
latest+next。 上一次发版:0.8.1(2026-09-30,latest+next)。
本版由 0.8.2-rc.1(2026-10-04 只上next)沉淀一周后转正:候选版期间没有收到新的回归报告,
CI 五条腿与全部真 PTY 探针持续全绿,Windows / ConPTY 实机人工验收也已完成(见
checkpoints.md「Windows 实机轮」)。内容与 0.8.2-rc.1 完全相同,
逐项说明见release-notes-0.8.2-rc.1.md。
dsh-ssh-tuiis now presented as DSH Relay.
The npm package name and the install command have not changed.
dsh-ssh-tui现在以 DSH Relay 为产品名展示;npm 包名与安装命令没有变化。
发布坐标仍是dsh-ssh-tui,安装命令仍是
dsh plugin --profile tui add dsh-ssh-tui@latest。依据:decisions/brand-dsh-relay.md。
头等事:全新安装第一次就会出现配置向导(此前根本不会出现)
0.8.1 及以前,一台没有任何配置的机器上装好插件、启动 dsh --profile tui,看到的是一个空工作区、
一行提示都没有,模型调用必然失败——配置向导永远不会自动出现。原因是前端拉起的 Host 永远带
--resume=<id>(连它自己刚铸的新 id 也带),而首启判定把 resume 当成"这台机器配置过"。
0.8.2 起只看凭据存不存在。详见候选版说明第一节。
另外两件用户直接看得见的事
- resume 不再有一段 1–3 秒黑屏:进屏序列现在跟着第一帧一起发出,加载提示全程在屏
(20k 事件实测:清屏与落地同一 tick,此前中间有 3.5 秒全黑)。 - 思考卡片不再发白:
reasoning角色此前只写属性、不写颜色,"暗"完全靠 faint;不实现 faint 的终端
会退回默认前景色。四个调色板现在各自给出 muted 颜色。 - 链路那一格不再把"绘制节奏"当延迟:终端不回
CSI 6n时显示未测/n/a,不再印一个看起来像
测量值的 160ms。
兼容
上游兼容窗口与 0.8.1 相同:0.2.0-rc.1 / 0.2.0-rc.2 / 0.1.7-rc.1 / 0.1.7-rc.2。
0.1.5 及更早的宿主不再支持。 本版不改协议、不加命令、不动上游接口。
安装 / 升级
dsh plugin --profile tui add dsh-ssh-tui@latest装完退出再启动。/status 可确认当前版本;装不上先看 /doctor。
更新时请显式带 @latest,否则已有 lockfile 可能继续沿用旧版本。
实测数字(本版,100×20)
| 动作 | 毫秒(中位) | 字节 | 寻址行 | 全屏清屏 |
|---|---|---|---|---|
| 流式 delta | 1.47 | 501 | 3 | 0 |
| 等待卡时钟 | 1.26 | 465 | 3 | 0 |
| 报告屏翻页 | 1.40 | 2457 | 17 | 0 |
| 菜单光标移动 | 1.09 | 588 | 4 | 0 |
| 向导字段打字 | 0.25 | 140 | 1 | 0 |
复现:npm run bench;README 里的同一张表由 npm run bench:report 从这次真跑生成。
已知问题
见候选版说明的「已知限制」一节。要点:Windows / ConPTY 实机人工验收已完成(12 项清单;
两处 ConPTY 盲区在探针里显式 SKIP 并在 Linux 腿上真跑,SKIP 不算通过);未测 那一格在
Windows 本机看不到(ConPTY 代答光标位置),由管道父进程形状的探针覆盖。
v0.8.2-rc.1
0.8.2-rc.1 发版说明(候选版)
发布方式:只上
next。 这是候选版(RC):latest保持 0.8.1,只有显式装@next或指定
版本号的人才会拿到它。上一次发版:0.8.1(2026-09-30,latest+next)。
这一版是架构收尾 + 稳定性:把 0.8.x 期间分批落地的终端架构(screen / 表示管线 / 工作区区域)
冻结下来,并修掉两处只有全新安装或弱链路才会遇到的问题。
头等事:全新安装第一次就会出现配置向导(此前根本不会出现)
0.8.1 及以前,一台没有任何配置的机器上装好插件、启动 dsh --profile tui,看到的是:
一个空工作区、一行都没有的提示,模型调用必然失败 —— 配置向导永远不会自动出现。
原因不在向导本身(它是好的),而在触发它的判断:前端拉起后台 Host 时永远带上
--resume=<id>(连它自己刚生成的那个新 id 也带),而首启判定把 --resume 当成"这台机器配置过",
于是直接跳过了首启流程。
0.8.2 起这条判断只看凭据存不存在:没有凭据、环境里也没有 key,就是首启,向导自动打开(Enter 走步骤、
Esc 随时退出),并且工作区会同时留下一行说明告诉你怎么手动重开 /setup。第二次启动不会再出现。
这同时修掉一个连带问题:向导不再借用 composer 的输入框。0.8.1 里进入向导会清掉你已经打了一半的草稿;
现在向导有自己的字段、自己的光标,工作区里的草稿、历史、正在进行的输入一个字都不动。
工具卡与输入法:两处"画面串行"的修复
- 工具卡内容含多行代码时不再窜到输入区。
bash卡片的标题就是命令行原文,遇到 heredoc(<<'PY')会带
着换行进入"一行"——终端因此真的换了行,而这一版之前按绝对行号绘制的帧随后把下面每一行都写错位:卡片内容
落在输入框和页脚上,甚至两段文字拼在同一行。现在标题行会把换行折成空格,单行裁剪器也不再把换行交给终端。 - Windows Terminal + 微软输入法:预输入不再在处理中卡片那一行闪烁。终端把输入法的预输入画在光标处,
而"写一行"会把光标留在该行末尾;跑一轮时实时区域每 tick 重画又正好在输入框上方,所以光标停在处理中卡片
上。现在每写完一行就把光标交还输入框,预输入只会出现在它该在的地方。
resume 不再黑屏,思考卡片不再"发白",q 真的能关掉报告
- resume 进入时的 1–3 秒黑屏。前端先在备用屏打出"正在载入历史会话…",relay 接入时宿主又写了一次
?1049h——终端已经在备用屏时这个序列会清屏,而重建会话日志期间按设计不合成任何帧,于是那块加载
提示被擦掉、屏幕空着,直到历史重建完成(实测 20k 事件:9.1 s 清屏、12.6 s 首帧落地,中间 3.5 s 全黑)。
现在这条"进屏"序列跟着第一帧一起发出,同一 tick 内清屏并重画;没有东西要等的重连行为不变。 - 思考卡片的文字不再发白。"思考 / 已思考"这一角色此前只写
2;3两个属性、不写颜色——"暗"完全
靠 faint,而不实现 faint 的终端(报告的 256 色终端)于是退回默认前景色,看起来和旁边的回复一样亮。
四个调色板现在各自给出 muted 颜色,属性保留给支持的终端(mono仍然只用属性)。 q/Q能关掉报告。inspectClosesOn一直声明接受它们("全屏报告理应响应 q"),但可打印字符的
提示分支在它之前返回,于是这两个键只会打印"这是报告视图,按 Esc 返回"。
状态行不再把"绘制节奏"当成"链路延迟"
现场报告(/diag):Linux + xterm-256color + SSH,探测 未知(终端未回 DSR)——这台终端多数时候不回答
CSI 6n,于是状态行常年显示 SSH ○○○○ 160ms。两个部分要分开看:
- 空心圆是对的:没测到往返就是
unknown,不该假装知道链路好坏。 160ms是错的:那不是延迟,是回退的绘制节奏(探测不到时用的安全默认值)。它出现在"延迟"该在的
位置上,读者无法分辨"离我 160ms"和"从来没测过"。这一格现在改为SSH ○○○○ 未测/SSH ○○○○ n/a;
测到过的链路照旧显示真实值,绘制节奏仍然出现在/diag那一行、并且写明是"绘制"。
另外两个"看起来像 bug"的现象是设计使然,不是缺陷:
- 有回 DSR 时数值也不怎么动:显示的是最近 3 次测量的中位数,重测间隔 20 秒;稳定的 SSH 链路上
中位数本来就该是一条直线。Windows 本机之所以看得见跳动,是因为那些数字只有 1–12ms,同样抖动在两位数
里显得大。 - 连续 3 次探测失败后不再每 20 秒重试:退避到 10 分钟——避免"不回答 DSR 的终端被永远每 20 秒扫一次"。
命令提示会自己让位(排队提示除外)
命令的确认("已复制 412 字")与警告(命令失败)从此显示约 6 秒后自动让位,恢复原来的遥测行;按 Enter
提交仍会立刻清掉。唯一的例外是"运行中已提交,将在下个步骤生效"这条:它显示到那条消息真正被提交
为止——它是"你有话在排队"的唯一标记,提前消失等于把排队这件事藏起来。
弱链路 / SSH:画面更稳,流式不再整屏重画
这一版把 0.8.x 期间逐块落地的渲染架构冻结下来,对远程、跳板机、弱网最直接的三条:
- 流式输出不再每个 tick 整屏重画:正在生成的回复是一个投影,不再是往历史里追加行,所以每个
token 批次只重画它自己那几行。实测(100×20):一次流式 delta 寻址 3 行 / 478 字节 / 0 次全屏清屏。 - 报告不再污染对话记录:
/status、/help、/doctor、/subagents、/diag现在是独立的屏,
打开不改变历史窗口位置,翻页只重画正文,Esc 回到你原来的位置,也不会在重连后被重新画出来。 - 断线重接后表示一致:提问、审批、计划三种卡片从会话日志折叠出来,因此掉线重接、换窗口、
--resume之后读到的状态与当时一致,而不是"重连后多出一张卡"或"状态回到未知"。
/clear 只清可见视图
/clear 不删除任何东西:不删会话日志、不动模型上下文、不动计划 artifact。它只是把可见视图切到
当前时刻之后(并且用一行提示告诉你这件事)。/resume 进来仍然是完整历史。/find 只在你看得见的
范围里搜索,这一点也在提示里说明。
计划与实时处理的抢占关系
计划条(Plan Dock)不再被"处理中"的实时区域盖住:实时区域现在是自己的行,而不是画在历史上面。
计划条高度上限为工作区一半,所以它不会把输入框和光标挤出屏幕(中文输入法的预输入串也不会再落到
计划卡上)。终端很矮时计划条按三档降级(完整 / 紧凑 / 极简),不会直接消失。
反馈去哪了("我执行了命令但没有任何提示")
- 控制面的确认(切主题、切视图、preset 写入…)进页脚,显示到下一次输入为止;
- 警告与失败进身份行(原来
目录:那一行),不盖住你正在读的内容; - 只有会改变历史的事实才进对话记录。
页脚挤不下时确认会退到身份行显示,所以再也不会有"执行了但什么都没有"。
上游 400:现在会告诉你它是什么
command-code 这类思考模式网关偶尔会成串拒收请求(实测:一次长会话里 13 个失败轮次中 12 个是这种),
以前你只看到一行原始错误。现在:
- 明确说出网关拒收的是请求本身、不是你的 key;
- 指名已知的那一族缺陷(上游 #1780 / #231,思考模式的 reasoning 回传);
- 给出可操作的两条绕法(该路线
/effort off,或换同一网关的 command-code-messages); - 不会自动重试 —— 实测这类失败会成串出现(连续 8 轮),重发通常值得,但同一个请求确定性失败时
自动重试会白花一整段上下文。什么时候重发由你决定。
数字(本版实测,100×20)
| 动作 | 毫秒(中位) | 字节 | 寻址行 | 全屏清屏 |
|---|---|---|---|---|
| 流式 delta | 1.43 | 501 | 3 | 0 |
| 等待卡时钟 | 1.25 | 465 | 3 | 0 |
| 报告屏翻页 | 1.19 | 2457 | 17 | 0 |
| 菜单光标移动 | 0.97 | 588 | 4 | 0 |
| 向导字段打字 | 0.22 | 140 | 1 | 0 |
发版候选(c2fe073)自检,逐条可复现:
| 自检 | 结果 | 命令 |
|---|---|---|
| 冻结不变量 | 八条全绿 · 七条退役路径 0 命中 | npm run freeze |
| 性能基线 | 上表五行 · 全部 0 次整屏清屏 | npm run bench |
| 全套测试(Linux) | 1376 项 · 1372 通过 · 0 失败 · 4 跳过 | npm test |
| 全套测试(Windows 实机) | 1376 项 · 1365 通过 · 0 失败 · 11 跳过(跳过均为平台互斥用例) | npm test |
| 打包 | 243 个文件 · tarball 1.4 MB · 解包 4.2 MB · shasum 92b7f517… · 无凭据 / 无 home / 无 sqlite |
npm pack --dry-run |
跳过只代表"这台机器上没跑",不代表通过:verify-batch 见到 SKIP: 就收尾 INCOMPLETE(见
release.md)。
兼容
上游兼容窗口与 0.8.1 相同(0.2.0-rc.1 / 0.2.0-rc.2 / 0.1.7-rc.1 / 0.1.7-rc.2)。
这一版不改协议、不加命令、不动上游接口。
安装 / 升级
dsh plugin --profile tui add dsh-ssh-tui@0.8.2-rc.1装完退出再启动。/status 可确认当前版本;装不上先看 /doctor。
已知限制
- Windows / ConPTY 实机人工验收:已完成(2026-10-03,维护者)。用一份单独安装的 CLI dsh 逐条走过
12 项清单,逐项结论见checkpoints.md「Windows 实机轮」。分三类:
可自动化的部分全绿(verify-batch --home <dir>:12 PASS · 2 SKIP · 0 FAIL);两处 ConPTY 盲区
明确打印SKIP:并附理由(ConPTY 自己回CSI 6n,"慢链路"与"终端静默但没断连"这两个形状它扮演不了);
没有自动化、只能人眼的两目已由维护者确认通过——微软拼音 IME 组合、多行中文粘贴(清单与走法见
release.md「发布前人工门槛」)。两处SKIP是覆盖边界,不是通过:它们
在 Linux 腿上是真跑的(link第一段、cut的"静默终端"那一半),在 ConPTY 上才无信号。 未测那一格在 Windows 本机看不到:ConPTY 代答光标位置,本机链路芯片因此永远是"已测量"。
该形状由scripts/tui-unmeasured-probe.mjs用管道父进程覆盖(两条腿:不回话读未测、每条必答读
真值),已进 CI 两条腿。- 拖放探针此前约 1/7 概率误报(探针自身与自己的
/status重试竞态,不是产品行为)。本版已修,
并连跑 20 次验证。 --resume与首启的关系见上文:本版修的是"新机器不显示向导",不是"resume 不再触发向导"。- 依赖旧版 inbox 通知(inbox P3)与新协议的命令不在本版范围内。
v0.8.1
发布方式:
latest+next。 上一次发版:0.8.0(2026-09-29,next,随后提前提升为latest)。
这是一版修稳定性的补丁:桌面版装上它不会再把宿主带崩;终端侧的几处"残留"也一并清掉。
头等事:桌面版装了不会再弄坏宿主
0.8.0 在没有终端的进程里是抛错退出插件的。桌面版 Harness 的启动器是 Electron-as-Node(GUI 子系统,
没有 console),于是插件管理把它显示成"1 entry did not activate / 插件失败",在桌面版上还会把 GUI 带崩。
0.8.1 起按"能不能画"分四种情形,只有真能画时才挂载:
| 情形 | 行为 |
|---|---|
| 本进程就是分离的 TUI Host | 照常服务(它的终端在随后接入的那个窗口里) |
| stdin 与 stdout 都是 TTY | 照常挂载 TUI |
桌面版启动器(Electron-as-Node / app.asar / DeepSeek Harness.exe) |
不挂载任何东西(不取锁、不装定时器、不查更新),记一行说明,措辞是"此处不启用"而不是报错 |
其它没有终端的情形(管道 … | tee、cron、服务) |
同上,文案换成"没有终端"。退出码 0 |
需要"没终端就报错"的场合(脚本断言某个终端 profile 真能起来)在 profile 行加 requireTerminal: true
——那是唯一会抛错的路径,而且只在显式要求时。桌面版用户不需要这个插件:用应用本身或 dsh web;
想在终端里用它,用 npm 安装的 CLI(npm i -g @deepseek-ai/dsh)在真实终端/SSH 里跑 dsh --profile tui。
新增:宿主自带终端控件时,可以把这个 TUI 当子进程用
桌面版自己不需要它,但"宿主会开终端控件"(PTY 面板、xterm.js、GUI 里嵌的终端)是另一条路:
声明 DSH_TUI_DISPLAY=stdio(或 --display stdio),插件就不再要求 TTY——画面走 stdout、按键走 stdin、
面板尺寸由宿主用 CSI 8 ; rows ; cols t 报告(管道没有 resize 事件也没有 SIGWINCH)。协议、父进程
必须做的五件事、Node 最小示例与限制写进 display-mode.md(这一版起随包发布)。
终端侧修掉的三处"残留"
- 会卡住的"处理中"卡片:渲染缓存把"活尾巴"(等待卡、流式缓冲、还没有回复的突发摘要)收进了
最后一行的缓存条目,于是每一帧都把那一份冻结在旧秒数的副本重放出来。真实会话里攒到过四条
(0s / 12s / 19s / 30s)夹在工具卡中间,0s那条永远不动。现在缓存条目只包含它自己那行画的内容。 - 退出后提示符旁的
①—…“”·•Ⅰ与^[[25;1R:歧义字形探针打印的那行自己没有擦(调用方可能一帧
都没画),而它的光标回答要一个往返才回来——慢链路上那时终端已经交还给 shell,tty 就把回答回显成了
文本。现在探针自己擦行、归还终端前多留一个往返吞掉回答、并且不再对已经交还的终端探测;探针期间
暂存 Host 的输出,避免擦除打在刚画好的帧上。 - 极简视图:单文件编辑卡不再重复自己的标题与 diffstat;失败的工具行补回状态球(
●),与其它卡片行一致。
性能与渲染(0.8.0 之后陆续落地)
- 每行渲染缓存 + 逐行脏比较:5000 行会话的整帧重画从约 1050ms 降到约 22ms,按键帧从约 1040ms 降到约 13ms;
多行帧用DEC 2026同步刷新,避免慢链路上一行行"落下来"的撕裂(这就是本插件存在的意义的那个不闪屏)。 - 歧义宽度字形(
①–⑩、—、“”、CJK 标点…)改为问终端:接入时打印探针并用CSI 6n读回列号,
答案随帧下发给绘制的 Host;终端不回答就退回按 locale 推断,DSH_TUI_NO_GLYPH_PROBE=1可整体关掉探针。
上游兼容
| 通道 | 版本 | 表态 |
|---|---|---|
latest / next |
0.2.0-rc.2 |
compatible:本版默认线(干净树安装、tsc 0 错、套件 1086 项 1082 通过 / 0 失败 / 4 跳过、真机探针全过;桌面版基线) |
| 线内 | 0.2.0-rc.1 |
compatible:typecheck + 套件 |
| 线内 | 0.1.7-rc.2 |
compatible:完整探针 |
| 线内 | 0.1.7-rc.1 |
compatible:typecheck + 套件 |
| 已摘 | 0.1.5-* 及更早 |
incompatible(明确表态,请求升级到 0.1.7-rc 或 0.2.0-rc) |
声明窗口:>=0.1.7-rc.1 <0.1.8 || >=0.2.0-rc.1 <0.2.1。CI 四条腿:0.2.0-rc.2(默认,完整探针)、
0.1.7-rc.2(完整)、0.2.0-rc.1 与 0.1.7-rc.1(typecheck + 套件)、外加 Windows leg(ConPTY 真机)
——两条腿新增无 PTY 的管道探针(scripts/tui-stdio-probe.mjs)。
安装 / 升级
dsh plugin --profile tui add dsh-ssh-tui@latest # 0.8.1装完退出再启动。/status 可确认当前版本;装不上先看 /doctor。
已知限制
- 0.1.5 及更早不再支持(见上)。
- 桌面版应用本身仍然不能跑终端 profile(它的启动器没有 console)——这是上游的事,见
docs/desktop.md。本版保证的是
"装了它不会把宿主弄坏",以及"宿主愿意开终端控件时能用"。 - 管道宿主必须自己报告尺寸变化(
CSI 8 ; rows ; cols t),否则会停在启动时声明的COLUMNS/LINES。 - 真实 Windows 上仍只能人工确认的两条:宿主那个控制台窗口确实是隐藏的;
%USERPROFILE%与$HOME
不一致的账户行为。
0.8.0
头等事:跟上宿主的 0.2.0-rc 线,同时摘掉 0.1.5
上游把 next 指向 0.2.0-rc.1。这条线保留了 0.1.7 引入的两处结构(设置表单由 loader entry 自己的
Config 投影、终端 profile 在进程级组合代理),所以插件源代码一行没改就跑了:类型面 0 错、七条真机探针
全过。真正需要改的是三处"宿主代际判定"——它们原来用 0.1.7 前缀判断,把 0.2.x 读成了旧代,会让
profile 挂错行。现在统一读一个按版本元组比较的共用规则。
0.1.5 这条线不再支持。 还在 0.1.5 的用户会从商店读到明确的 incompatible(而不是沉默),
请先升级宿主到 0.1.7-rc 或 0.2.0-rc 再装这一版。
401/403 不再混为一谈,并可自动重试一次
宿主的 llm/retry 不重试 AUTH 类错误,所以一次提供商侧的瞬时 403 会直接终结整轮,而屏幕上只有
permission_error 一行。实测过一次:同一会话 37 轮在同一条路由上成功,失败只有间隔 45 秒的两次,
随后同一把 key 打同一模型 200。
- 失败时补一行来源判断:
提供商拒绝了请求(HTTP 403)…{ENV} 已配置(意思是别改 key,重发即可)
或本机没有可用的 {ENV}(意思是先去配)。判据是真的解析一次凭据,不是猜。 /retryauth on|off(默认关):只在"提供商拒绝 + 本机有凭据 + 这一轮还没重试过"时自动重发上一条
消息一次。默认关是因为重试会把整段上下文再发一遍,长会话很贵。- 排查三步(三种错误信封、两条文件检查、一条直连
curl)写进docs/remote-ops.md§4.8;
附带一个会误导人的坑:GET /provider/v1/models是公开端点,带不带 key 都 200,不能用它验鉴权。
上游兼容
| 通道 | 版本 | 表态 |
|---|---|---|
latest |
0.1.7-rc.2 |
compatible:完整 CI 腿 |
next |
0.2.0-rc.1 |
compatible:本版默认线(干净树安装、tsc 0 错、套件 1021 通过 / 0 失败、七条真机探针) |
| 0.1.7 线 | 0.1.7-rc.1 |
compatible:typecheck + 套件 |
| 已摘 | 0.1.3-alpha.* / 0.1.5-* |
incompatible(明确表态,范围不再收;请求升级到 0.1.7-rc 或 0.2.0-rc) |
CI 四条腿:0.2.0-rc.1(默认,6 条探针)、0.1.7-rc.2(完整)、0.1.7-rc.1(类型+套件)、Windows(ConPTY 真机)。
验证细节见 docs/upstream-0.2.0.md。
安装 / 升级
dsh plugin --profile tui add dsh-ssh-tui@next # 0.8.0(宿主 0.2.0-rc 或 0.1.7-rc 都可以)
dsh plugin --profile tui add dsh-ssh-tui@latest # 0.7.4(仍是默认;10-01 之后 latest 会变成 0.8.0)装完退出再启动。/status 可确认当前版本;装不上先看 /doctor。
发布方式
这一版只上 next:latest 仍是 0.7.4,等 2026-10-01 再把 0.8.0 提升为 latest。
宿主已升到 0.2.0-rc 线的用户现在就可以用 @next 装。
热度基线(发版当日)
- 14 天窗口(2026-09-29):views 278(169 unique)· clones 1585(371 unique)
- referrers:github.com 56 · Google 12 · Bing 2 · npmjs.com 2 · chatgpt.com 1 · dshfind.com 1
- npm 上周(09-21→09-27)1059 次下载;0.7.3 单版本 380 次;仓库 ★ 2
0.7.4
头等事
这一版是「把已经做出来的东西做对」:0.7.3 之后落的六个功能各自被真机与对抗式审查拧了一遍,
修掉了 8 个实际缺陷(含两个只有读代码才看得见的:现场思考卡被挤出焦点环、覆盖层里的复制
拿到的是另一份正文)。行为上最明显的变化是回复变成了可选中的卡片。
回复可以选中、可以复制(本版主线)
/copy 取的是"被选中的那一行",而可选列表里从来没有回复——所以只要按过一次 ↑/↓ 选中
任何卡片,/copy 就再也拿不到回复,最新回复只是"没有选中时"的兜底。现在:
- 空输入
↑/↓在回复与卡片之间按屏幕顺序走,选中的那一行带▶标记;Alt+4直接选中最新回复。 /copy(或Ctrl+Shift+C)复制的是选中的那一条的原文——不是折行后的屏幕文本;
复制后保留选中,连按两次拿到的是同一条。- 在选中的回复上按
Enter:打开「回复全文」(可滚动、Esc返回),长回复里的表格与代码块
不再被窗口宽度切碎。覆盖层里也能复制,取的是屏幕上那份正文,并在覆盖层底部回显「已复制 …」。 - 工具卡 / 子代理 / 改动卡的「全文」覆盖层里,复制键取的同样是屏幕上那份正文(工具全文、那份 diff、
子代理日志),不再是卡片摘要。 - 拖选照旧:被选中回复第一行前面的
▶是装饰,拖过它不会把标记复制进去。
底栏与链路
- 链路质量在会话运行中持续重测并按中位数读数:进会话那一瞬间的网络抖动不再把整场会话钉在最低档
(绘制节奏与每帧字节预算都跟着这档走)。 - 选择器把「可接入 / 已占用」两个词固定下来,并且在首帧之前就把"已经挂掉的窗口"判完——列表不会先
显示「已占用」再改口。判定走一次真实往返(光标探针),因为断掉的 SSH 链路会让 launcher 还连着显示通道。 - 接管(把别人的窗口接过来)现在要确认,
Esc是取消而不是退出。
空回合不再被误读为完成
上游网关偶尔只回思考、finish_reason: stop、正文为零(实测某网关 10/35 个回合如此),
宿主如实组装成"只有思考的助手消息",状态栏显示完成/空闲。现在转录里会补一行明确提示
(含 finish_reason: stop 与"按 Enter 继续"),回放/中断/报错的回合不提示。
上游兼容
dsh声明窗口:>=0.1.7-rc.1 <0.1.8(next)与本条线并行;0.1.7-rc.2已按完整流程验过并声明
compatible(干净树安装、tsc0 错、套件全绿、六个真 PTY 探针全过),CI 里单独一条腿。- CI 现有 5 条腿:
0.1.7-rc.1(默认,提交的 manifest)、0.1.7-rc.2、0.1.5-rc.3、0.1.5-rc.1
(只跑类型与单元)、test-windows(ConPTY 真机)。默认腿新增一条 Reply copy probe:真 PTY 跑一轮
脚本化模型,断言拖选复制、/find高亮、以及选中回复后按复制键拿到原文。
安装 / 升级
dsh plugin --profile tui add dsh-ssh-tui@0.7.4装完退出再启动。/status 可确认当前版本;装不上先看 /doctor。宿主在 0.1.5 线上的用户可以继续用 0.7.4
(0.1.5 仍在本版声明窗口里);下一次(0.8.0)会随上游 0.2.0-rc 线调整支持面。
热度基线(发版当日)
- 14 天窗口(2026-09-29):views 278(169 unique)· clones 1585(371 unique)
- referrers:github.com 56 · Google 12 · Bing 2 · npmjs.com 2 · chatgpt.com 1 · dshfind.com 1
- npm 上周(09-21→09-27)1059 次下载;0.7.3 单版本 380 次;dshfind 卡片 0.7.3;仓库 ★ 2
- 与前一周相比:views −15% · clones −14%
0.7.3
这一版的头等事是跟上宿主的 0.1.7-rc 线:0.1.7 的 launcher 会拒绝声明不兼容的插件,所以 0.7.2 在那条线上装不上。同一个版本里还落地了 Windows 的地基与 P1 四条,以及两个实测出来的可靠性修复。
支持 0.1.7-rc.1(next)
0.1.7 换了三处结构,插件现在两条线共用一份代码:
- 设置接缝重写:
settings.get/installSection/settings.document全部删除,表单由 loader entry 自己的Config投影,只有.volatile()字段可读可写,命名空间 = entry id。插件把自己的段落落成三条 entry,/setup、/mode、/doctor在两条线上都能读写。 - presets 改名:复数包没有 0.1.7 版本,拆成
dsh-agent-preset(组合行)+dsh-agent-preset-registry(服务);终端不再有 roster,/mode fix在 0.1.7 上写的是persona/tool-ask-user/present三行。 - 声明窗口:
dsh.compatibility.dsh与 14 条 peer 纳入>=0.1.7-rc.1 <0.1.8。launcher 会逐条读 peer 范围(optional 也算),所以这一步是升级路径的硬前提,不是收尾动作。
真机核验:同一份 src/ 对着两条线 tsc 各 0 错;mock 轮次探针真跑一轮并完成拖选复制与 /find;settings.yaml 四段迁移在 PTY 下全部落到新 entry。
不再支持 0.1.2-rc
0.1.2 与 0.1.5 属于同一个 API 代,但兼容层里专为它保留的分支一直占着维护成本。现在:
- 范围去掉
>=0.1.2-rc.1 <0.1.6,dshReleases把0.1.2-rc.1标成incompatible(保留条目,让还在那条线的用户看到明确结论,而不是"没有表态"); - 只服务该 API 代的兼容分支与测试固定装置(
Session.events、裸 headerlist()、inspect()、images标志、durableassistant/chunk回放链……)一并删除。
还在 0.1.2-rc 的用户:请先把 dsh 升到 0.1.5-rc.1 及以上(推荐 latest 或 next),再装本插件;0.7.2 仍可继续使用。用 0.1.2 宿主写下的旧会话在新版本里仍能正常回放文本,只是底栏不再为它们重建 TTFT / tok/s。
Windows:从"用户发现"变成"CI 发现"
- 真 ConPTY 端到端进 CI:
test-windows腿驱动真 TUI(boot、断连重连、关窗、终端矩阵),不再只跑单元测试。 - 文件权限不再是空操作:
icacls /inheritance:r /grant:r落到 env 文件、.credentials.yaml、OAuth token、锁/套接字目录与宿主 stderr 日志。(顺带:POSIX 下宿主 stderr 日志过去没有任何 mode,是 0644,现在 0600。) - 宿主活过"关掉终端窗口":宿主改由系统 PowerShell
Start-Process -WindowStyle Hidden启动——它有自己的隐形控制台、不在启动器的KILL_ON_JOB_CLOSEjob 里,关窗带不走它,回合继续跑完;没有 PowerShell 的机器回退到直接 spawn,并会在会话里明确提示这是回退路径。 - 终端能力矩阵:Windows Terminal / conhost / VTE 系(GNOME、XFCE)/ Konsole / xterm / tmux / screen / Linux 控制台 / dumb 一张表,探针逐种起 TUI 断言实际发出的序列。
可靠性
- 深
DSH_HOME下显示通道起不来:AF_UNIX 的sun_path只有 107 字节(macOS 103),而过去只把会话标签截到 80 字符、从不校验整条路径——地址一超长listen()就 EINVAL,用户只看到一句host display socket did not appear,最容易被误判成"宿主没起来"。现在按字节预算截断(非 ASCII 的 home 也按字节算),浅目录逐字节保持原名,深到放不下时直接说明原因。注意默认~/.dsh距上限只有 1 字节,所以这不是罕见配置。 - 0.1.7 的 DeepSeek 适配器不加载:那条 CI 腿过去用
--legacy-peer-deps安装,连 peer 一起跳过——37 个 peer-only 包缺失,base 里 25 行(llm-deepseek、tool-fs、subagent…)没有 fiber,loader 把 import 失败静默吞掉。改为用overrides把整族钉在该线并正常安装,该腿的 busy-drop 探针也重新跑起来了。
安装 / 升级
dsh plugin --profile tui add dsh-ssh-tui@latest装完退出再启动。/status 可确认当前版本;装不上先看 /doctor。宿主是 0.1.7-rc 的用户需要 0.7.3 及以上(0.7.2 会被 launcher 拒绝加载)。
已知限制
- 真实 Windows 上仍只能人工确认的两条:宿主那个控制台窗口确实是隐藏的;
%USERPROFILE%与$HOME不一致的账户行为。 - 尚未做(计划 0.7.4):非 UTF-8 控制台/ locale 的 ASCII 回退渲染;安装/校验脚本去 bash 化;面向普通用户的 Windows 文档。
热度基线(发版当日)
snapshot appended: github ok · npm ok · dshfind ok · store /root/heat-0.7.3.jsonl
- 14 天窗口(2026-09-24):views 278(169 unique)· clones 1585(371 unique)
- referrers:github.com 56 · Google 12 · Bing 2 · npmjs.com 2 · chatgpt.com 1 · dshfind.com 1
- npm 上周(09-15→09-21)1070 次下载;dshfind 卡片 0.7.2;仓库 ★ 2