强制折叠每轮工作过程,答案照常显示并可一键展开
29.7/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/11/1 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/gurio-wine/dsh-fold-it-up.git状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 40 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-fold-it-up(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=22.12.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/9/21 23:54:42
安全扫描自动 · 静态分析源码v2.7.0🟢 扫描:未发现敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 折叠每轮工作过程
- 答案照常显示
- 一键展开
README
由 DeepSeek 最新模型翻译生成dsh-fold-it-up
强制的整轮折叠:一轮工作结束(或已经结束过的旧轮次)之后,它的工作过程收成一行,答案照常显示;点这一行即可展开、再点收起。
装进 DSH 的 Web 界面后,打开任意会话——包括你在这个插件存在之前就聊完的历史会话,以及点「加载更早」取回来的那些页——每一轮都已经折好了。
▸ 10 次工具调用 · 3 条消息已上传工坊,发布版本 1.0.8。
...
(那一行就是 DSH 自己的折叠行,文案、字体、主题色、间距全部来自产品本身。)
为什么需要它
DSH 自带「轮次过程折叠」,但它只在一整套前置条件同时成立时才折叠。用户能撞上的那一条是长会话:
processWindowReady = 会话是紧凑视图
&& 该轮已有定稿答案
&& 该轮已关闭
&& !historyIncomplete ← 这一条
historyIncomplete 来自会话窗口是否还有更早的历史(界面上的「加载更早」)。DSH 首屏只取 50 条消息(PAGE_MESSAGES),所以只要一次会话超过两三个来回,这个条件就永久为真:该折叠的一轮也不折,历史轮次也不折,界面上看就是「折叠功能时灵时不灵」。
展开完整文档(README 剩余 1 节)
dsh-fold-it-up
强制的整轮折叠:一轮工作结束(或已经结束过的旧轮次)之后,它的工作过程收成一行,答案照常显示;点这一行即可展开、再点收起。
装进 DSH 的 Web 界面后,打开任意会话——包括你在这个插件存在之前就聊完的历史会话,以及点「加载更早」取回来的那些页——每一轮都已经折好了。
▸ 10 次工具调用 · 3 条消息已上传工坊,发布版本 1.0.8。
...
(那一行就是 DSH 自己的折叠行,文案、字体、主题色、间距全部来自产品本身。)
为什么需要它
DSH 自带「轮次过程折叠」,但它只在一整套前置条件同时成立时才折叠。用户能撞上的那一条是长会话:
processWindowReady = 会话是紧凑视图
&& 该轮已有定稿答案
&& 该轮已关闭
&& !historyIncomplete ← 这一条
historyIncomplete 来自会话窗口是否还有更早的历史(界面上的「加载更早」)。DSH 首屏只取 50 条消息(PAGE_MESSAGES),所以只要一次会话超过两三个来回,这个条件就永久为真:该折叠的一轮也不折,历史轮次也不折,界面上看就是「折叠功能时灵时不灵」。
本插件接管了那一格的渲染(conversation.chat.node 的 turn-process),自己判断折叠:
折 ⟺ 该轮已关闭 且 这一轮有过程行
没有历史完整性开关,也没有「模型是否给出了总结」这一层判断。
判定读的是渲染结果,不是 store
这是本插件最要紧的一条设计,也是它修掉的第二个 bug。
Chat store 的快照和屏幕上真实渲染的行会不一致。 点「加载更早」时,React 把新的一页分几批提交进 DOM,于是有一小段时间:DOM 里已经有这些行,而当前 store 快照还没有描述它们。如果折叠范围是按 store 算、再往 DOM 上贴,那么「store 不认识的行」就会被算成不该隐藏,结果是——
实测:某一轮的过程行在一次 pass 之后从 94 行涨到 116 行,那次 pass 只折了 18 行,剩下 102 行留在屏幕上。
所以现在:
- 行集合与顺序来自座位元素本身(ChatNodeSeat 给每个渲染节点写的 data-chat-turn / data-chat-flow-kind / data-chat-anchor-key);
- 哪些行属于过程范围,用座位自己写的 data-turn-process-member——那是官方投影的产物,不需要重新推导,也不可能和屏幕上的东西矛盾;
- 官方标记缺席时(座位因自己的原因拒绝了折叠,例如该轮还在跑),退回读折叠行上发布的 processStartSeq / answerAnchorSeq 序列区间——走哪条路只由座位自己的成员标记决定,注入上下文的归属不参与选路(它进隐藏集,但不替这一轮挑分支);
- store 只提供每个 key 的内容(文本块、step),行集合和顺序一概不听它的。
判定和写入在同一次 pass 里完成,每次都是重新读一遍当前的列。
上下文注入行:它们不属于任何一轮
第三个 bug 是用户报的「上下文注入类还留在外面不被折叠」。
注入的上下文(上下文注入 @deepseek-ai/dsh-system-prompt、上下文注入 skill-catalog、graph-memory 的回忆块……)在自己的座位上没有轮次号。 它们的 user/message 事件没有 turn 字段,于是 Location 是 unresolved,座位渲染出 data-chat-turn="null"——它们不属于任何分组。所以它们不是被「排除」在折叠之外,而是从来没有进入过任何一次判定。
两个事实叠加才让它一直看得见:
1. 官方那份「与过程无关的 kind」清单里没有 context(system-prompt / user / steering / turn-process / turn-error / turn-max-tokens / turn-tail 才有)——也就是说官方几何本来就会在范围里隐藏它们;
2. 但官方给行打过程标记的那一刻,需要 processWindowReady,而那个开关要求 !historyIncomplete。长会话里它恒为假,于是这些行连标记都拿不到。
于是本插件的做法是:按座位在 DOM 里的位置,把无轮次的 context 行归给紧跟在它下面的那一轮(也就是它被注入进去的那一轮);尾部没有后续轮次时归给上面那一轮。然后
- 有官方标记时:这些行按成员对待,和同一轮的其它过程行一起折;
- 官方标记缺席时:退回序列区间判定,context 行不以序列号参与,而是以「它被归给了这一轮」参与。
归给哪一轮是可读的:pass 会给每一行写 data-folditup-turn,上下文行的归属就写在它自己的 DOM 上。这也让「这一行到底属于谁」可以被外部断言,而不必重新推导一遍规则。
顺带一个边界:一行只在它位于该轮答案之前时才会被隐藏,所以「上一轮答案之后、下一轮用户消息之前」的注入行跟着上一轮折(它属于上一轮),而不会被算进下一轮。
正在思考的那一轮会把自己折起来
第四个 bug 是用户报的「处于思考时,过程就会被折叠,直到进入到下一步」。
根因是「已定稿答案」这个判据在轮次运行中并不成立。 官方 latestAnswer(turn-process.ts)只接受最后一步的 assistant 节点、且该节点已带 finalNode;但运行中「最后一步」正是正在流式输出的那一步,它还没定稿,于是官方投影仍然把上一步的定稿回答当作边界发布出去——answerAnchorSeq 指向的是一条在模型正在写的那一行之上的行。于是:
1. 过程标记非空、answerFor 能解析、区间比较全部成立 → 折叠条件成立;
2. 折叠行出现,把过程折掉,而仍在增长的那一行被当成答案留在外面;
3. 那一步定稿、下一步开始时投影重算,折叠又消失 → 用户看到的「折一下再弹开」。
第二个放大器:textLen 原本把 reasoning 也算进回答正文。纯思考的一步因此能通过 isAnswerRow,折叠行甚至会显示成「运行中 · 思考」。官方的 hasAssistantReplyContent 恰好把 reasoning 排除在外。
修法是补上官方那道关闭门,并且让 textLen 只数正文:
- textLen 只累计 text 块,reasoning 是过程材料;
- 一轮的「已关闭」由它自己的行说明:turn-tail 在 turn/end 时发布(不论什么原因),turn-error / turn-max-tokens 与它同时发布。分组里出现这三种 kind 之一,就是 DOM 自己对 turnClosed 的陈述;
- foldTurn 第一件事就是这道门:没有关闭 → 一行都不折,foldable 为 false,折叠行不渲染。
关闭门必须排在其它判据之前,因为它是唯一无法从别处补救的事实:运行中的一轮发布的边界已经移动过了,后面所有区间比较都建立在过期前提上。
报错结束的轮次:没有答案,通知行就是边界
第六个 bug 是「回复过程中报错,这一轮就不折了」。
根因是「必须找到答案行」这条旧判据在错误轮次上永远不成立。 官方投影的 latestAnswer 要求定稿答案,而报错或触顶的那一轮没有定稿答案:answerStep / answerAnchorSeq 恒为 null,座位因此也拿不到 data-turn-process-member 标记。旧逻辑要求「必须有答案行可留」才折,于是 foldable 为 false;而插件影子了官方渲染器,false 时连折叠控件都不渲染——用户看到的就是「不折」,而且最该收起来的失败现场,恰恰摊得最开。
判定它不需要任何新事实,只需要本来就有那道关闭门:turn-tail 在 turn/end 时发布(不论什么原因),turn-error / turn-max-tokens 与它同时发布。所以:
- 组内第一个 closing 行(turn-error / turn-max-tokens / turn-tail)就是这一轮的边界:它上方的过程行——含注入上下文——全部折进去;
- 通知行及其下方的一切保持可见;这一轮若没有已定稿的正文,就没有任何行被标记为答案(data-fold-it-up-answer 不出现);
- 真答案存在时行为不变:答案优先,closing 边界只是没有答案时的兜底;
- 分支选择只认座位自己写下的成员标记——注入上下文行可以进隐藏集,但不再能劫持分支选择;运行中的轮次(还没有 closing 行)照旧完全不折。
顺带两个实测事实:真实产品里错误轮次正在流式的那一步会被结算清掉,所以报错前没写过正文的错误轮次折叠后可见 assistant 行数是 0——这是合格状态,不是缺陷;而报错前已有定稿正文的那一轮,那段正文就是答案行(它位于通知行之下时除外),照常标记、照常可见。
行为细节
| 场景 | 行为 |
| 一轮正常结束 | 折叠,末条回答保持可见 |
| 正在思考 / 正在跑工具的那一轮 | 完全不折(没有 turn-tail),过程照常实时显示 |
| 点「加载更早」取回旧页 | 那批旧轮次立刻折好 |
| 打开历史会话 | 已结束的轮次全都折好 |
| 被中断、没有回答的轮次 | 照折:过程收进折叠行,没有任何行被标记为答案(没有正文回答可留) |
| 上下文注入行 | 与该轮的过程一起折叠;展开时原样回来 |
| 轮次报错 / 触顶 | 过程照折,错误行与提示行保持可见,并充当该轮的收尾边界 |
| 首条人类消息 | 永不隐藏 |
| 刷新页面 / 切换会话 | 重新折起(展开状态不持久化,这是刻意的) |
| Ctrl+F 搜索 | 折叠的行仍能被浏览器找到并自动展开(hidden="until-found") |
安装
把仓库放到本地任意目录(本插件没有任何依赖,也没有安装步骤:client.js / index.js / cordis.patch.yml 都是入库的生成物,不需要构建工具链):
git clone https://github.com/gurio-wine/dsh-fold-it-up.git
然后把它装进 Web profile(profile 名按你的实际配置来,默认是 web):
dsh plugin --profile web add <刚 clone 下来的目录>
最后按 Ctrl+R 刷新页面 —— 客户端插件随页面加载,不需要重启 App。
卸载:
dsh plugin --profile web remove dsh-fold-it-up
再刷新页面即可;会话记录不受影响。
实现
| 文件 | 作用 |
| src/logic.js | 全部决策 + 整列 pass:分组、范围、答案、隐藏 |
| src/browser.js | 浏览器半边(作者态 ESM):影子渲染器、控制器、DOM 读写原语 |
| client.js | 生成物:按模块系统的 bundle 协议包装好的浏览器半边 |
| index.js | 生成物:宿主半边(刻意什么也不做) |
| cordis.patch.yml | 生成物:把包挂进 profile 的那一行 |
几个刻意的选择:
1. 影子注册。priority: -1 拿下这个 keyed 单元(官方是 0,同一格优先级最低者渲染)。包级 boot-graph 插件不过 runner 的 guard,所以这里的 -1 是真实语义,不是自动分配的。
2. 复用产品自己的折叠行。真实那一行就是产品注册进槽位账本的原件:slots.entriesOfSlot('conversation.chat.node') 的 StoredEntry.component 上挂着 Chat 包自己注册在 turn-process 键上的 TurnProcessNodeView,插件把格子影到自己名下之后,行的渲染直接借账本里那个组件——文案走 chat 命名空间既有词条,样式(CSS module 类名)在产品构建期就已绑定。不取源码、不编译、不碰任何类名哈希,产品改版不需要跟着同步维护。借不到时退回内置等价实现(自带 .dsh-fold-it-up- 样式),__FOLDITUP__ 里的 {kind:'row', source} 如实记下用的是哪个。(早先版本试图从 source map 的 sourcesContent 取 TurnProcessNodeView.tsx 现场编译——但那是 TSX 原文,JSX 与类型标注在语法层就进不了 new Function,那条路从未真正工作过,已整套移除。)
3. 隐藏用官方同一套机制。给同一批 wrapper 元素挂 hidden="until-found"——这正是 ChatNodeSeat 自己用的属性,因此「隐藏的行不占列间距」的节奏和 find-on-page 都保持不变。
4. 展开状态从行元素上读回。折叠行自己会写 data-open;pass 读它来决定这一轮要不要展开。这一点踩过坑:data-open 写在行上,而索引里拿到的是外面的座位包装元素,读错对象会让每一次点击都变成空操作。
5. 一个稳定的锚点元素。组件在「还不知道该不该折」和「已经折好」两种状态下都渲染同一个容器元素,控制器从它解析当前的 transcript 列。它必须一直存在:折好之后那一行本身就不渲染了。
6. 监听 DOM,而不只是监听 store。列上挂 MutationObserver——一页被提交进来时,负责渲染那一轮行的那次 React 提交不会通知这个插件,但会改动 DOM。
7. 每个 session 一个控制器。主会话和 sidebar 里的子代理会同时拥有各自的 transcript、store 和 DOM 列;controller、observer 和 publication 不跨 session 共享,关闭子代理后也会释放它的监听。
8. 无构建步骤的构建。tools/build.mjs 是一个字面量的 ESM→bundle 转换器(本包自己写的四种语法形式),把 src/.js 内联进 client.js,平台模块(react、@deepseek-ai/dsh-client-store)留给 boot 模块表。生成物入库,装插件不需要任何工具链。
诊断:页面里 globalThis.__FOLDITUP__ 保存最近 200 条生命周期记录(注册是否拿到格子、每次 pass 读到的列规模与分组数、折叠行用的是产品原件还是内置行——{kind:'row', source})。每一行上还有两个由 pass 写下的属性:data-folditup-seq(该行在 store 里的排序位置,取不到就没有)与 data-folditup-turn(管这一行的轮次——注入上下文行的归属只写在这里)。
验证
node --test tools/*.test.mjs # 纯逻辑 + 生成物契约
node tools/build.mjs --check # 生成物是否为最新
node tools/verify-live.mjs --url <带 token 的启动 URL> --session <id> --toggle
node tools/verify-load-older.mjs --url <带 token 的启动 URL> --session <id> --rounds 3
node tools/probe-stream.mjs --url <带 token 的启动 URL> --text "<提示词>" # 边跑边采样
tools/verify-live.mjs 用独立无头 Edge(独立 profile、独立调试端口,不碰你在用的窗口)打开真实会话,直接读真实 DOM:
- 每个已关闭轮次:恰好 1 行可见的 assistant(已标记的答案)、控制器未被隐藏、没有漏出来的过程行;无答案标记的轮次走另一支——报错 / 触顶 / 中断结束时它是 0 行答案、0 行可见 assistant(本来就没有答案),但通知行之上不许有漏出来的过程行,通知行下方的可见内容是收尾本身,不算泄漏;
- 该轮的注入上下文行如果还看得见,就是失败(这一条以前漏掉了,因为断言把 context 当成了「本来就该可见」的 kind);
- 每一行隐藏都必须是 hidden="until-found";
- --toggle:点开 → 隐藏数归零、过程行与注入行重新可见;再点 → 恢复原样;
- --expect builtin 跑同一套断言的反面(基线:过程行全部可见);
- 任何 console.error / 未捕获异常都会打印出来。
tools/verify-load-older.mjs 专测上一个回归:每一轮都重新加载页面、点一次「加载更早」、等新页提交,然后断言
- 没有任何一轮「有折叠控件却还露着过程行」,也没有任何一轮「渲染了过程行却完全没有折叠控件」;
- 每个折好的轮次至多 1 行可见 assistant、且注入上下文行为 0(报错结束的轮次是 0 行)——否则一个空转录也会「零失败」通过。
tools/probe-stream.mjs 专测「运行中不许折」:用浏览器自己的输入管线真的发一轮,从发出前到关闭后每 60ms 采样一次 DOM,把采样压成状态段后断言
- 关闭之前不存在任何折叠控件、也不存在任何被隐藏的行(premature 必须为空);
- 关闭之后才出现折叠控件、且隐藏行数 > 0(报错轮次关闭后同样折,可见 assistant 为 0 属正常)。
静态检查只看已结束的轮次,所以「折一下再弹开」这种运行中的抖动在它们眼里是隐形的——这个探针就是补这一块。
实测(web profile):
真实 DOM 上刚刚跑完的一轮 7 行:system-prompt / user / 折叠行 / 2 × context / 答案 / 尾部 2 行 context 均为 hidden="until-found" 展开 → 2 行 context 回来;收起 → 又都藏好大历史会话(有「加载更早」) 4 turn(s) inspected; 0 failure(s) turn 22/23/24 各带 4/6/5 行 context,全部折好,0 泄漏展开 → hidden 0 / 11 行 assistant 可见;收起 → hidden 20 / 1 行点「加载更早」2 轮 页面从 90 行长到 201 行,5 个轮次全部折好(隐形轮 99 行),0 failure 同一会话 4 轮(关闭门修复后) 4 turn(s) inspected; 0 failure(s) 点开 → hidden 0 / 11 行 assistant 可见;收起 → hidden 20 / 1 行边跑边采样(关闭门修复前) premature 1 段:t+11.8s–13.0s 折叠控件已出现、3 行已隐藏,而该轮尚无尾部;被当作「答案」留在外面的是「运行中 思考 I」边跑边采样(关闭门修复后) premature 0 段;关闭前 controllers=0 / hidden=0,关闭后才折
同一会话在未装插件的实例上过程行全部可见——也就是用户报的那个「就是不折叠」。
其余工具:
- tools/scan-context.mjs <session.v3.jsonl.zstd>:从会话日志里把注入的上下文事件与每一轮的过程范围 [turn/start, 答案) 对出来,回答「这些行到底在不在范围里」;
- tools/pick-session.mjs:按「无轮次上下文行数 / 轮次数」挑选值得实测的会话;
- tools/analyze-turns.mjs <session.v3.jsonl.zstd>:从会话日志重建每一轮,按官方判定打印哪些轮「本该折但没折」及原因;
- tools/probe-rows.mjs --url <URL> [--session <id>]:把一页的每一行按轮次打印出来(kind / 轮次 / 归属 / 成员标记 / 是否隐藏 / 文案),排查时先看它;
- tools/probe-ask.mjs --url <URL> --text "...":用浏览器自己的输入管线在实例里真的发一轮,然后打印折叠结果——没有可用的历史会话时用这个造一个;
- tools/probe-stream.mjs --url <URL> --text "...":同上,但在整轮进行中每 60ms 采样一次 DOM,输出压成状态段并列出「关闭前就折叠」的段——运行中抖动只能用它看见;
- tools/probe-routes.mjs --url <URL>:Web 端没有会话级 URL,这个探针把这件事查清楚(页面地址、localStorage 键、侧栏会话行长什么样),顺带说明「打开指定会话」只能靠侧栏或用 dsh.sessions.current 预置;
- tools/session-log.mjs:多帧 zstd 会话日志的解码(DSH 的 .zstd 是上千个拼接帧,Node 的 zstdDecompressSync 只解第一帧)。
已知边界
- 只对 Web 界面生效;TUI / headless 没有这个话题。
- 展开状态不跨刷新保留(刻意:装上、刷新、切会话都应该看到折叠)。
- 折叠行是 DSH 自己的行,所以它的文案是「工具调用 / 消息 / 已思考」;工作时长显示在轮次尾部的用量面板里,本插件不去改那一行。
- 「加载更早」还没点的时候,更早的轮次根本不在 DOM 里,无从折叠;把那些行取回来之后它们会立刻折好。
- 注入上下文的归属是「它下面的那一轮」。这符合几何(一行只在它位于该轮答案之前时才会被隐藏),但如果将来 DSH 改成把注入行渲染在别的位置,这条规则需要跟着改——data-folditup-turn 就是为了一眼看出归属而存在的。
- 与另一个同类第三方插件(抢同一格)不能共存;同一格里优先级最低者胜出,本插件是 -1。
许可证
MIT © 2026 Gurio预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
gurio-wine/dsh-fold-it-up
强制折叠每轮工作过程,答案照常显示并可一键展开
相关插件 · 对话 / 记忆
上下文中枢oceanbase/powercontext
上下文跟着工作走 —— 跨会话、跨智能体、跨人。
★ 1,487已汉化DSH
73
智能体记忆库plastic-labs/honcho
Honcho 是记忆基础设施,用来构建有状态的智能体。这些智能体能理解不断变化的人、智能体、群组、项目和想法。
★ 7,894已汉化DSH
71.7
长程智能体控制面loopx-project/loopx
给 Agent 一个目标,让工作持续向前。
★ 6,358已汉化DSH
71.3
智能体上下文数据库volcengine/OpenViking
给 AI 智能体用的上下文数据库
★ 41,379已汉化DSH
71.3
长效机器人团队Fei2411/DS-Bot
DS Bot 是一个基于 DeepSeek Harness简称 dsh的 bot 插件,插件将 dsh 改造为 bot…
★ 5,084已汉化DSH
71.1
可携带记忆层EverMind-AI/EverOS
为什么选择 EverOS
★ 13,778已汉化DSH
71.1
这是你的仓库?认领条目