dsh-workflow-map 0.3.2 → 0.3.5

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 CHANGED
@@ -3,6 +3,131 @@
3
3
  `dsh-workflow-map` 的所有重要变更记录在此。
4
4
  格式遵循 [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),版本号遵循 [SemVer](https://semver.org/)。
5
5
 
6
+ ## [0.3.5] - 2026-10-31
7
+
8
+ ### Added
9
+
10
+ - **README 截图区改为按市场评分规则书写的六图画廊**:主界面(深色/浅色)、运行中的单元、分步详情、
11
+ 热注入、设置面板。图片落在 `docs/screenshots/`,相对路径引用,图注含「预览/截图/主界面」等关键词,
12
+ 使插件市场的卡片与安装对话框能自动取到(该市场只认 README 里位于 GitHub 四域名的图片,
13
+ 并按语义与尺寸打分取前 6 张;带 badge/logo 的一律排除)。
14
+ - **标题栏增加手动刷新按钮(`↻`)**。点一次立即重新读取当前会话的视图,不等自动轮询的下一个周期;
15
+ 读取成功后按钮短暂变为「已刷新」,随后恢复图标。它走的是同一条 `GET /dsh-workflow/state` 与同一个
16
+ `If-None-Match` 校验器,不引入第二条数据通道。自动轮询正常工作时无需使用,仅在自动刷新受阻时兜底。
17
+
18
+ ### Fixed
19
+
20
+ - **返回即结束:节点的「已完成」不再由正文是否为空推断**。`nodeView` 发布布尔 `result`(折叠层的 `settled` 标记),
21
+ 工具返回空正文(例如无输出的 `pwsh`)也算已返回。此前浏览器半只按 `resultHead !== ""` 判断,
22
+ 空输出的调用因此永远停在「正在调用」状态。同时 `STATE_VERSION` 升到 5,旧检查点按契约丢弃重建。
23
+ - **结果不是活动**。`foldToolResult` 曾把刚返回的节点重新设为 `active`,面板因此对已经返回的调用声称「正在调用」;
24
+ 现在只有当更新的调用打开了另一个节点时才保留高亮,否则交还给步骤。
25
+ - **未收到 `turn/end` 的回合不再让整张图无限流动**。`view.running` 是回合标志,中断/崩溃/压缩后的会话会把它永久留在 `true`;
26
+ 浏览器半现在以时钟为第二证人:最后一个已折叠事件早于空闲窗口(15 s)即判定为空闲,无时钟(离线预览)时仍信任标志。
27
+ - **重开的行不再继承上一轮的 100 %**。`foldTodos` 把 `completed` 行改回 `in_progress` 时保留了 `percent` 与 `maxPercent`,
28
+ 新行一出现就钉在 100 %,进度条不再移动。现在重开(以及降级为 `pending`)会清空百分比与高水位,`startedAt` 重新计时。
29
+ - **运行中的步骤不再显示 100 %**,且**自报的最新值就是显示值**。`percentFor` 的模型分支此前取「自报」与「高水位」的较大值并允许 100,
30
+ 既让未完成的步骤声称 100 %,也让后续自报被历史高水位压住。高水位仍记录在 `step.maxPercent`,只是不再参与显示。
31
+ - **自动轮询不再因一次慢请求而永久停止**。每个周期开始时会中止上一个未完成的请求,被中止的请求返回
32
+ `status: -1`;旧代码在这里直接 `return`,不重新排程。只要有一次请求慢于轮询间隔(网络抖动、宿主繁忙),
33
+ 循环就此结束,地图停在最后一帧——表现为「地图不会自己更新」。现在中止只让位给已在飞行中的替代请求,
34
+ 两者都结束后照常排程;该规则抽成纯函数 `wfPollContinues()` 并纳入自测。
35
+ - **已结束的 PAE 计划不再改写地图**。`<cwd>/.pae/<sessionId>/orchestrator.json` 一旦写入就长期保留,
36
+ `phase` 为 `completed` / `aborted` 时仍会被当作当前计划读取,用它自己的 `statuses`、`anchorSeqs` 与步骤标题
37
+ 覆盖本轮待办表刚建出的行,于是面板显示的是上一轮的计划。现在计划段新增布尔 `terminal`,为真时仍发布
38
+ (详情页要用它的 `stepReports`),但不再修改任何行的标题与状态;浏览器半的状态判定同样据此让位。
39
+
40
+ ### Changed
41
+
42
+ - 自测口径:离线 10 套合计 1570 条(`selftest-fold` 217、`selftest-host` 54、`selftest-orchestrator` 155、其余同前),
43
+ 浏览器量测 10 套合计 323 条。各套件的输出以退出码与逐条结果为准,本文件不复述结论。
44
+
45
+ ## [0.3.4] - 2026-10-30
46
+
47
+ ### Changed
48
+
49
+ - **宿主要求改由 `engines.dsh` 声明**:`>=0.2.1-rc.1 <0.3.0-0`,覆盖 `0.2.1` 全系列(`rc.1` 至 `rc.5`)与 `0.2.1` 正式版。
50
+ 该区间由宿主在安装与启用期读取(`dsh-app-boot` 的 `evaluatePluginCompatibility()`),不满足即拒绝加载。
51
+ - **删除非标准 `dsh.host` 字段**:宿主包仍由 `peerDependencies` 声明(均为可选 peer),版本闸门只看 `engines.dsh`。
52
+ - **中英 README 增加宿主要求块**:逐版本核验方式与结论、区间写法说明,区间字符串与 `package.json` 的 `engines.dsh` 逐字符一致。
53
+
54
+ ### Added
55
+
56
+ - **`docs/host-compatibility.md`**:区间判据、206 包闭包比对、九个契约点的逐版本证据,以及 `0.2.1-rc.3`(实机运行)
57
+ 与 `0.2.1-rc.1` / `rc.2` / `rc.4` / `rc.5`(契约核验)的区别。
58
+
59
+ ### Fixed
60
+
61
+ - **计划行自带的步骤文件名不再被当成标题的一部分**。PAE 会把计划行渲染成 `1. 标题(限定语)(step-01-x.md)`,
62
+ 折叠时连行尾的文件名一起收进标题,于是这一步的标题比待办表里那一行多一个括号组:`foldTodos` 拿步骤键
63
+ 与表比对时判定「无变化」,视图里留下一条对话正文并不存在的分步(`audit-real` 约束 ①)。
64
+ 现在解析计划行时就剥掉行尾的 `.md` / `.json` 等文件名标注;计划标题改写时同步更新步骤键,
65
+ 旧键归档进 `aliasKeys`(与改名同一条路径),已挂在旧键上的节点随之迁移。
66
+ - **`docs/` 与两份 README 的口语化表述收敛为陈述句**:删去带日期的括号注、第一人称示例与破折号转折,
67
+ 内容不变。
68
+ - **浅色主题下分步卡与背景同色**。宿主在浅色主题里把 `--dsw-alias-bg-layer-1` 与 `-2` 都解析到
69
+ `--dsw-static-neutral-bluish-00`(即 `#fff`),与 `--dsw-alias-bg-base` 同一个值;分步卡当时只填 `bg-layer-2`
70
+ 且边线是 `transparent`,于是整块卡面在白底上不可见。现在卡面带自己的抬升墨色
71
+ (`--wf-lift` / `--wf-lift-2`,深色主题另行覆盖),边线改用 `--dsw-alias-border-l2`,分步、节点、
72
+ 单元三类卡面与悬停/选中态一致。
73
+ - **待办表是步骤生命周期的唯一依据**。`todo/write` 每次重写整张表,但视图同步把行“配对”到旧记录时
74
+ 沿用了那条记录的旧状态:表中写 `pending` 的行显示成 `done`/100%,表中写 `in_progress` 的行显示成
75
+ `pending`(同一会话里两种都出现过)。现在配对后一律按行本身的状态落值,未命名、从未开始、又不拥有
76
+ 任何节点的 `pending` 行会被丢弃,会话不再因为改写过表格而长出空卡。
77
+ - **非运行中的分步不再显示百分比**。`percentFor()` 在进入各条测算路径(自报 / 待办比值 / 基线)之前先判状态:
78
+ 只要不是 `running` 就不出数字。此前 74 步待办(5–99%)带着一个没人更新过的旧数字,看着像停滞。
79
+ - **回合结束时关闭该回合的运行步**。`foldTurnEnd` 以前只翻 `turnActive` 标志,运行步会一直是 `running`;
80
+ 现在按结束时刻补时长并结束该步,唯一的例外是待办表此刻仍标 `in_progress`(那一步确实跨回合继续做)。
81
+ - **检查点版本 3 → 4**:旧检查点里的步骤台账按旧映射生成(错误状态、残留空行、带有不应存在的百分比),
82
+ 按既有契约丢弃后从日志重建。
83
+ - **量测脚本不再依赖 `--dump-dom`**。Edge 156.0.4314.50 的启动器会在毫秒内以退出码 0 退出、把真正的浏览器进程
84
+ 甩成独立进程,于是 `--dump-dom` 写进一个没人持有的 stdout,落盘文件恒为 0 字节(`--version` 也无输出)。
85
+ 新增 `tools/cdp-dump.mjs`:自己拉起无头浏览器、从 `DevToolsActivePort` 拿调试端口、连页面 target、
86
+ `Page.enable` + `Runtime.evaluate(document.documentElement.outerHTML)` 取 DOM,失败必抛;
87
+ 10 个 `tools\measure-*.ps1` 全部改为调它,并新增 `tools\patch-measure-cdp.mjs` 留证改法。
88
+
89
+ ### Tests
90
+
91
+ - **离线自测 10 套 / 1533 条**(0.3.3 为 1515 条)。本轮新增 13 条:`selftest-fold`
92
+ 的 `(r)` 用例 8 条(待办表改回 `pending` 必须降级、改写表后的 `pending` 行不得顶替运行步、
93
+ `in_progress` 行对应的记录即使原来是 `pending` 也要转为运行、未命名空行被丢弃、回合结束仍保留表中
94
+ `in_progress` 的步、非运行行不出数字……),`selftest-panel` 的浅色主题针脚 5 条(抬升变量、深色覆盖、
95
+ 卡面抬升、卡面边线取 token、悬停抬升)。
96
+ - **真实会话日志**:`check-real-injects` 折叠 147 个会话、识别出 19 条热注入记录、failures 0;
97
+ `audit-real` 折叠 148 个会话、629 个步骤,约束 ①(分步必须存在于待办表)与 ②(同一时刻只有一个
98
+ running 步)各 0 违规;`done` 步显示 <100% 的数量为 **0**,非运行步带数字的数量为 **0**(修前 74 条)。
99
+ - **浏览器量测 10 套 / 323 条**(0.3.3 为 308 条):
100
+ `panel 21 · timing 20 · inject 43 · dock 14 · flow 20 · effects 28 · branches 15 · detail 50 · settings 56 · main 56`。
101
+ `main` 从 41 增至 56 的 15 条是本轮新增的主题对比检查:浅色帧的页面底色确实是宿主真值 `#fff`、
102
+ 两种主题下 `--wf-lift` 各自生效、单元卡边线 alpha 与抬升渐变俱在、14 px 颗星徽标靠自身颜色边线可见
103
+ (实测 light/dark 均为 `rgba(…,1)`)。旧的 `card` / `node`(遗留组件)不在单页上,按「不存在不记失败」处理,
104
+ 它们的边线与抬升由 `selftest-panel` 的样式表针脚守住。
105
+ - **`0.2.1-rc.5` 隔离实机**:`--dump-config` 退出码 0、stdout 1234 行、零告警;
106
+ `/dsh-workflow/state`、`/steer`、`/unsteer` 按本插件契约应答(`404 unknown session`、缺参 `400`)。
107
+ 记录与复跑命令见 `docs/host-compatibility.md` §3.1。
108
+
109
+ ## [0.3.3] - 2026-10-29
110
+
111
+ ### Fixed
112
+
113
+ - **编号清单的序号不再丢失,地图不再重复显示步骤**。模型把待办表从 7 行无编号换成 14 行带 `1. `–`14. ` 前缀后,`syncToTodos` 把序号从**已经剥掉前缀的标题**上再解析一次,
114
+ 于是 `planIndexOf` 永远返回 null;创建出的行丢了序号,而 `foldTodos` 按位置配对时又不回写序号,
115
+ 旧行继续带着上一份计划的 `planIndex`。`applyPlanToView` 随后按这个错序号把计划步骤 2/3/4 的标题和状态
116
+ 盖到别的行上。用户看到的是第 3–7 步各出现两次,跑动中的那一格也不是真正的当前步。
117
+ 修法:`planIndex` 从**原始 content** 解析;配对到旧行时把序号按当前表回写;槽位配对遇到序号冲突时让位给按序号匹配。
118
+ 复现判据:修前的视图里 14 行有 5 组标题重名,修后 `planIndex` 1–14 连续且无重名。
119
+ - **计划自带的确认点不再丢失**。`orchestrator.json` 里 `requiresConfirmation: true` 的步骤(该会话第 2/12/13 步)
120
+ 在地图上一个「需确认」标记都没有:`applyPlanToView` 的注释说宿主会合并这一半,但代码里没有这段合并。
121
+ 现在由 `applyPlanToView` 补上,标记的 `kind` 为 `plan`;已 done/failed 的步骤不带标记(与日志侧规则一致),
122
+ 重复应用同一份计划不会叠加。
123
+
124
+ ### Tests
125
+
126
+ - `tools/selftest-fold.mjs` 新增 8 条断言:`(o1)`/`(o2)` 编号清单换表后序号连续、标题不重名、
127
+ 行不继承邻居标题;`(p)` 计划确认点只落在该落的步骤、`kind` 为 `plan`、重复应用不重复、已完成步骤不带标记。
128
+ - 离线自测 **10 套 / 1515 条**(0.3.2 为 1507 条)。
129
+
130
+
6
131
  ## [0.3.1] - 2026-10-28
7
132
 
8
133
  ### Fixed
@@ -10,9 +135,9 @@
10
135
  - **`soft` 配色的同类残留**。0.3.0 已让 `brand` 配色下运行中卡片的外框跟随品牌色,
11
136
  但 `soft` 配色的规则仍只认单页改版之前的选择器(`.wf-step-item > .wf-step-card` / `.wf-node`),
12
137
  于是切到 `soft` 时运行中单元的外框仍是默认彩虹渐变、状态环仍停在品牌蓝、
13
- 卫星圆点与连线的流光也没换色。现在 `soft` 与 `brand` 一样同时命名现役选择器——
14
- `.wf-unit[data-status='running']>.wf-unit-center>.wf-unit-card::after`、`.wf-unit-ring`、
15
- `.wf-sat[data-live='true']::after` 与 `.wf-sat-row[data-live='true'] .wf-sat-line`——
138
+ 卫星圆点与连线的流光也没换色。现在 `soft` 与 `brand` 一样同时命名现役选择器
139
+ (`.wf-unit[data-status='running']>.wf-unit-center>.wf-unit-card::after`、`.wf-unit-ring`、
140
+ `.wf-sat[data-live='true']::after` 与 `.wf-sat-row[data-live='true'] .wf-sat-line`),
16
141
  三处一并跟随 `--wf-soft-1` / `--wf-soft-2`。
17
142
  - 对应的浏览器量测从「记录缺陷」改为「断言修复」:`settings.soft.keepsRainbowRing`
18
143
  换成 `settings.soft.frameMigrated` 与 `settings.soft.ringColor`,两者都用期望色
@@ -25,7 +150,7 @@
25
150
 
26
151
  ### Tests
27
152
 
28
- - 离线自测 **10 套 / 1507 条断言**、浏览器量测 **10 套 / 308 条断言**,全部通过。
153
+ - 离线自测 **10 套 / 1507 条**、浏览器量测 **10 套 / 308 条**。
29
154
 
30
155
  ### Notes
31
156
 
@@ -56,7 +181,7 @@
56
181
  收敛为唯一 running 行;进度上报也归属到待办表里真正 `in_progress` 的那一行。
57
182
  - **地图分步与对话里的待办表不一致**:折叠只增不删,重新规划或改写标题后旧行会残留。
58
183
  现在视图严格按最新待办表产出分步,表里已不存在的行与其节点一并移除。
59
- - **已完成的步显示「0 工具 0 文件」**:三个叠加的问题——按标题匹配导致改写文案后旧行变孤儿、
184
+ - **已完成的步显示「0 工具 0 文件」**:三个叠加的问题。按标题匹配导致改写文案后旧行变孤儿、
60
185
  新建行未写入步骤键、全局节点上限按「丢弃最老」执行而最老的恰属已完成步。
61
186
  现已改为就地改名并迁移节点、统一赋值步骤键、并从节点最多的步开始淘汰(单步永不被清空)。
62
187
  - **被拒绝的审批仍标「需确认」**:审批被拒时该行仍需人工处理,原实现把任何「已决议」都当成已结束。
@@ -66,7 +191,7 @@
66
191
 
67
192
  ### Changed
68
193
 
69
- - **面板布局改为单页**:原先的左右双列(分步列 + 流程列)合并为一个视图——串行主流程在中间,
194
+ - **面板布局改为单页**:原先的左右双列(分步列 + 流程列)合并为一个视图。串行主流程在中间,
70
195
  每步两侧以圆形接口表示该类工具/文件,点击后在右页展开详情。两列之间的跨列曲线随之移除。
71
196
  - **移除「预计剩余时间」**:进度条第二行只保留「N 分钟前估计」(这个数字有多旧),
72
197
  不再预测还需要多久。宿主侧相应的计算与字段一并移除。
package/README.en.md CHANGED
@@ -4,21 +4,49 @@ English · [**简体中文**](README.md)
4
4
 
5
5
  A DeepSeek Harness plugin that renders the current session as an interactive step-by-step workflow map.
6
6
 
7
+ > **Host requirement**
8
+ >
9
+ > - **DSH kernel**: `engines.dsh` = `>=0.2.1-rc.1 <0.3.0-0`, covering the whole `0.2.1` series (`0.2.1-rc.1` through `0.2.1-rc.5`) and the `0.2.1` release.
10
+ > - **Node.js**: `>=20` (`engines.node`).
11
+ > - Host packages are declared as `peerDependencies` (all optional peers, same range as `engines.dsh`); the host is not bundled. Hosts below `0.2.1-rc.1`, or at `0.3.0` and above, are outside the declared range.
12
+ > - Range note: `>=0.2.1-rc.1 <0.3.0-0` carries an explicit prerelease tag on the `0.2.1` tuple, which is what makes it match `0.2.1-rc.x`; a start of `>=0.1.0-rc.1` silently excludes every `0.2.1` prerelease. The upper bound `<0.3.0-0` sorts below every prerelease of `0.3.0`, so the whole `0.3` line (including `0.3.0-rc.1`) stays outside the range.
13
+
14
+ Per-version method and result:
15
+
16
+ | Host version | How it was checked | Result |
17
+ |---|---|---|
18
+ | `0.2.1-rc.3` | Run on a real host: plugin load, panel injection, view routes | Baseline |
19
+ | `0.2.1-rc.1` / `0.2.1-rc.2` / `0.2.1-rc.4` / `0.2.1-rc.5` | Cross-version contract check: closure metadata, per-file SHA-256, contract anchor windows | All nine contract points unchanged |
20
+
21
+ A contract check is not a real-host run: the last four versions were compared against published artifacts and contract anchors only, and never booted. Method and evidence: [`docs/host-compatibility.md`](docs/host-compatibility.md).
22
+
7
23
  Three entry points share one data source and one set of components: the sidebar panel item
8
24
  (`sidebar.panellist`), the main content area (`main`), and the session tab bar (`conversation.view`).
9
25
  The panel folds session events into a step sequence, showing each step's status, progress, tool calls
10
26
  and file operations, and can inject prompts into the next step of a running session.
11
27
 
12
- ![Workflow map: one main flow, type interfaces on both sides, details on the right page](docs/screenshot-detail.png)
28
+ ![Workflow map main screen preview (dark theme): serial main flow, type interfaces on both sides, details on the right page](docs/screenshots/01-main-dark.png)
29
+
30
+ *Main screen (dark): the selected sidebar entry on the left, the serial main flow in the centre (one unit
31
+ card per step, round interfaces on both sides for tool/file types, the running unit highlighted with a
32
+ flowing frame), the step detail page on the right, and the persistent injection bar at the bottom.*
33
+
34
+ ### Screenshots (full preview)
35
+
36
+ | | |
37
+ |---|---|
38
+ | ![Main screen full preview (light theme)](docs/screenshots/02-main-light.png) | ![Running step preview: flowing gradient frame](docs/screenshots/03-running-step.png) |
39
+ | **Main screen · light theme** — same layout, colours follow the host theme | **Running unit** — the flowing frame marks "calling right now" |
40
+ | ![Step detail preview: type list and call records on the right page](docs/screenshots/04-step-detail.png) | ![Hot inject preview: queue a prompt for the next step](docs/screenshots/05-hot-inject.png) |
41
+ | **Step detail** — type list, call arguments and result summary for that step | **Hot inject** — queue a prompt for the next step, revocable before it is claimed |
42
+ | ![Settings panel preview: palette, motion, detail depth and export](docs/screenshots/06-settings.png) | |
13
43
 
14
- *Left: the selected sidebar entry. Centre: the serial main flow (one unit card per step, round interfaces
15
- on both sides for tool/file types; the running unit is highlighted with a flowing frame). Right: the page
16
- opened for a step (type list, step detail). Bottom: the persistent injection bar.*
44
+ All six are offscreen renders of the interface and contain no real session content.
17
45
 
18
46
  ![license](https://img.shields.io/badge/license-MIT-blue)
19
47
  ![platform](https://img.shields.io/badge/platform-DeepSeek%20Harness-brightgreen)
20
48
  ![node](https://img.shields.io/badge/node-%E2%89%A520-informational)
21
- ![version](https://img.shields.io/badge/version-0.3.1-success)
49
+ ![version](https://img.shields.io/badge/version-0.3.5-success)
22
50
 
23
51
  ---
24
52
 
@@ -63,8 +91,6 @@ encoding (animation and colour).
63
91
 
64
92
  ### 2.1 Hot injection (steer / unsteer)
65
93
 
66
- This section states the write semantics, state determination and failure semantics of the injection channel.
67
- All three are derived from session log events; the front end does not guess.
68
94
  **Write semantics**: `POST /dsh-workflow/steer` calls the host's `agent.steer(message)`; the message
69
95
  enters the session inbox queue `next-step` and is claimed by the next step **after the current turn
70
96
  ends**. The call does not trigger `cancel`, so the running turn is not interrupted.
@@ -94,25 +120,25 @@ the capability.
94
120
 
95
121
  ### 2.2 Progress estimation
96
122
 
97
- This section states the percentage priority order, the algorithm and cap of each source, and the definition
98
- of the staleness field. The rule is implemented by `percentFor` in `src/fold.mjs`.
99
-
100
123
  Each step's percentage is resolved by the following **priority** (`percentFor` in `src/fold.mjs`):
101
124
  | Priority | Source | Rule | Cap |
102
125
  |---|---|---|---|
103
126
  | 1 | Todo table `todos` | `round(completed / total * 100)`; when a self-report exists, this measured value is still what is returned | 99 |
104
- | 2 | Model self-report `model` | Only when the todo table has no countable rows: the **larger** of the `stepProgress/report` percentage and the self-report high-water mark | 100 |
127
+ | 2 | Model self-report `model` | Only when the todo table has no countable rows: the most recent `stepProgress/report` percentage | 99 |
105
128
  | 3 | Time baseline `baseline` | `elapsed / median(finished-step durations in this session) * 100` | 90 |
106
129
  | 4 | Plan ordinal `baseline:steps` | Cold start with `current step / total steps * 100` when no durations exist | 90 |
107
130
  | - | `none` | None of the four is available | No number shown |
108
131
 
109
132
  Constraint: an unfinished step never shows 100 %; a finished (done or failed) step is pinned to 100 %.
110
133
 
111
- The choice follows verifiability: the todo table is an objective count, a model self-report is not. So
112
- whenever the todo table has completed rows, the percentage is that measured ratio (both branches of that
113
- `percentFor` expression yield `measured`); the self-report is a fallback only when no row can be measured.
114
- The high-water record `step.maxPercent` is still kept and never rolled back, but it no longer takes part in
115
- computing the displayed value.
134
+ The choice follows verifiability: the todo table is a count of rows, a model self-report has no per-row
135
+ basis. So whenever the todo table has completed rows, the percentage is that ratio (both branches of
136
+ `percentFor` return `measured`); the self-report is a fallback only when no row can be counted, and the
137
+ **most recent** report is used so that a later report can move the value. The high-water record
138
+ `step.maxPercent` is kept and never rolled back, and does not take part in the displayed value.
139
+ When `foldTodos` takes a finished row back to `in_progress`, or demotes it to `pending`, it clears
140
+ `percent` and `maxPercent` and resets `startedAt`: a finished record's number belongs to that closure,
141
+ not to the row that reopens it.
116
142
 
117
143
  A second line under the rail reads `estimated N minutes ago`, i.e. `view.asOf - step.lastEstimateAt`.
118
144
  It describes how **stale** the number is, distinguishing "being updated" from "unchanged for a long time".
@@ -163,7 +189,7 @@ can collapse the host's composer on that page; switching back unmounts the view
163
189
  ## 3. Installation
164
190
 
165
191
  > **Prerequisites for installing from the market**: two conditions must both hold. (1) `dsh-workflow-map`
166
- > is published on npm (currently `0.3.1`). (2) The `awesome-dsh-plugin` PR is merged **and** the market index
192
+ > is published on npm (currently `0.3.4`). (2) The `awesome-dsh-plugin` PR is merged **and** the market index
167
193
  > (`dsh-plugin-catalog`, served via jsDelivr) has refreshed and picked up the entry.
168
194
  > Once both hold, install from the **DSH market** (Plugins > Discover) by searching `workflow`, no terminal needed.
169
195
  > If it is not indexed yet, use the command line below (3.2) to install the same package; both routes are equivalent.
@@ -268,8 +294,14 @@ so time-based eviction would empty historical steps.
268
294
  | Click a call row | Expand the full arguments, result and error in place |
269
295
  | `Esc` | Close the right page, not the panel |
270
296
  | Gear | Open settings: palette, motion, detail depth, node limit |
297
+ | `↻` | Read the current session's view again now; the button briefly shows "Refreshed" |
271
298
  | Copy control | Copy path / command / snapshot, with a receipt that restores after 1.5 s |
272
299
 
300
+ The view is polled: every 0.8 s while the page is visible, every 3 s while it is hidden, as a conditional
301
+ request (`If-None-Match`) that the host answers with `304` and no body when nothing changed. `↻` runs one
302
+ cycle of that same request, for the case where automatic polling is blocked; it neither changes the cadence
303
+ nor adds a second data path.
304
+
273
305
  Settings:
274
306
 
275
307
  | Item | Values | Effect |
@@ -293,8 +325,8 @@ steps, their status, the percentage source, the node list and the report artifac
293
325
 
294
326
  ## 8. Compatibility and constraints
295
327
 
296
- - **DeepSeek Harness** 0.2.x or newer (relies on session projections and `webServer` route registration).
297
- - **Node.js** ≥ 20。
328
+ - **DeepSeek Harness**: the `engines.dsh` range `>=0.2.1-rc.1 <0.3.0-0` (relies on session projections and `webServer` route registration); `0.3.0` and newer are not supported.
329
+ - **Node.js**: `>=20`.
298
330
  - The three endpoints mount on the host's existing port; **no new listener** is opened.
299
331
  - The composer is rendered by the host and is not part of this plugin. The plugin neither replaces nor
300
332
  overlays it; it only yields space on the session-tab page through a stylesheet rule.
@@ -309,7 +341,7 @@ steps, their status, the percentage source, the node list and the report artifac
309
341
  # after editing src/client/*.js, rebuild the browser half
310
342
  node tools/build-client.mjs
311
343
 
312
- # offline self-tests (10 suites, 1507 assertions)
344
+ # offline self-tests (10 suites)
313
345
  node tools/selftest-fold.mjs # fold semantics
314
346
  node tools/selftest-host.mjs # registration, schema, projection wiring
315
347
  node tools/selftest-orchestrator.mjs # plan file reading and merging
@@ -326,7 +358,7 @@ node tools/check-real-injects.mjs # fold historical logs in bulk
326
358
  node tools/audit-real.mjs # four-constraint audit
327
359
  ```
328
360
 
329
- Browser measurements (needs a Chromium; the scripts auto-detect Chrome / Edge; 308 assertions):
361
+ Browser measurements (needs a Chromium; the scripts auto-detect Chrome / Edge; light/dark theme contrast included):
330
362
 
331
363
  ```bash
332
364
  pwsh -File tools/measure-panel.ps1
@@ -348,7 +380,7 @@ pwsh -File tools/measure-main.ps1
348
380
 
349
381
  ## 10. Key constants
350
382
 
351
- Every threshold is defined once and asserted by the tests, so this table cannot drift from the code:
383
+ Every threshold is defined once and read by the self-tests, so this table and the code share one source:
352
384
 
353
385
  | Constant | Value | Location | Meaning |
354
386
  |---|---|---|---|
@@ -365,7 +397,23 @@ Every threshold is defined once and asserted by the tests, so this table cannot
365
397
 
366
398
  ---
367
399
 
368
- ## 11. Known issues
400
+ ## 11. Plan file and todo table precedence
401
+
402
+ The map's steps come from two sources, in a fixed order:
403
+
404
+ | Priority | Source | Effect |
405
+ |---|---|---|
406
+ | 1 | Todo table `todo/write` | This turn's step titles and statuses, the session's newest statement |
407
+ | 2 | Plan file `.pae/<sessionId>/orchestrator.json` | While `phase` is `planning` / `scheduled` / `executing` / `paused`, its step titles, `statuses` and `anchorSeqs` apply |
408
+ | - | The same file at `completed` / `aborted` | The plan is over: its steps and reports are still published for the detail page, but it no longer changes any row's title or status |
409
+
410
+ The plan file is written once and never deleted. Read without that distinction, a previous turn's plan
411
+ overrides the rows the current todo table has just created, and the panel disagrees with the conversation.
412
+ The view's plan section therefore carries a boolean `terminal`, and both halves (host and browser) obey it.
413
+
414
+ ---
415
+
416
+ ## 12. Known issues
369
417
 
370
418
  Both defects recorded in 0.3.0 are closed:
371
419
 
@@ -379,7 +427,7 @@ that is how activation works, not a defect.
379
427
 
380
428
  ---
381
429
 
382
- ## 12. Layout
430
+ ## 13. Layout
383
431
 
384
432
  ```
385
433
  dsh-workflow-map/
@@ -399,7 +447,7 @@ dsh-workflow-map/
399
447
 
400
448
  ---
401
449
 
402
- ## 13. FAQ
450
+ ## 14. FAQ
403
451
 
404
452
  **Can it show several sessions at once?**
405
453
  The panel shows the current session only. Switching sessions switches the content; nothing is merged or tiled.
@@ -417,7 +465,7 @@ baseline creeps up but is capped at 90 %. The second line ("estimated N minutes
417
465
  The todo table caps at 99 and the time baseline at 90; only a done/failed status pins 100 %. This keeps
418
466
  "nearly there" from being confused with "already there".
419
467
 
420
- **Does it upload my code?**
468
+ **Does it upload user code?**
421
469
  No. The plugin makes no network requests; it reads only the local session log and the `.pae` plan file.
422
470
 
423
471
  **Can I install only the UI and skip the host half?**
@@ -430,13 +478,13 @@ the panel to `none`.
430
478
 
431
479
  ---
432
480
 
433
- ## 14. License
481
+ ## 15. License
434
482
 
435
483
  MIT. Commercial use, modification and redistribution are permitted; keep the copyright notice.
436
484
 
437
485
  ---
438
486
 
439
- ## 15. Reporting an issue
487
+ ## 16. Reporting an issue
440
488
 
441
489
  The following helps locate a problem:
442
490
 
package/README.md CHANGED
@@ -4,20 +4,47 @@
4
4
 
5
5
  DeepSeek Harness 插件:把当前会话渲染为一张可交互的分步工作流地图。
6
6
 
7
- 三个入口共用同一份数据与同一套组件 —— 侧边栏面板项(`sidebar.panellist`)、主内容区(`main`)、
7
+ > **宿主适配要求(Host requirement)**
8
+ >
9
+ > - **DSH 内核**:`engines.dsh` = `>=0.2.1-rc.1 <0.3.0-0`,覆盖 `0.2.1` 全系列(`0.2.1-rc.1` 至 `0.2.1-rc.5`)与 `0.2.1` 正式版。
10
+ > - **Node.js**:`>=20`(`engines.node`)。
11
+ > - 宿主包以 `peerDependencies` 声明(均为可选 peer,区间与 `engines.dsh` 一致),不打包宿主;低于 `0.2.1-rc.1` 或 `0.3.0` 及以上的宿主不在声明范围内。
12
+ > - 区间写法:`>=0.2.1-rc.1 <0.3.0-0` 在 `0.2.1` 元组上带显式预发布标签,才能匹配 `0.2.1-rc.x`;起点若写 `>=0.1.0-rc.1`,`0.2.1` 的预发布版本会被静默排除。上界 `<0.3.0-0` 小于 `0.3.0` 的任何预发布版本,因此整条 `0.3` 线(含 `0.3.0-rc.1`)都在区间外。
13
+
14
+ 逐版本核验方式与结论:
15
+
16
+ | 宿主版本 | 核验方式 | 结论 |
17
+ |---|---|---|
18
+ | `0.2.1-rc.3` | 实机运行:插件加载、面板注入、视图路由 | 基线版本 |
19
+ | `0.2.1-rc.1` / `0.2.1-rc.2` / `0.2.1-rc.4` / `0.2.1-rc.5` | 跨版本契约核验:闭包元数据、逐文件 SHA-256、契约锚点窗口 | 九个契约点未变 |
20
+
21
+ 契约核验不等于实机运行:上表后四个版本只处理已发布的产物与契约锚点,未启动宿主。方法与证据见 [`docs/host-compatibility.md`](docs/host-compatibility.md)。
22
+
23
+ 三个入口共用同一份数据与同一套组件:侧边栏面板项(`sidebar.panellist`)、主内容区(`main`)、
8
24
  会话标签栏(`conversation.view`)。面板将会话事件折叠为分步序列,展示每步的状态、进度、
9
25
  工具调用与文件操作,并支持在会话运行中向其下一步注入提示词。
10
26
 
11
- ![工作流地图:单一主流程 + 两侧类型接口 + 右页详情](docs/screenshot-detail.png)
27
+ ![工作流地图主界面预览(深色主题):串行主流程 + 两侧类型接口 + 右页详情](docs/screenshots/01-main-dark.png)
12
28
 
13
- *左:侧边栏入口选中态;中:串行主流程(每步一张单元卡,两侧圆形接口为工具/文件类型,
14
- 运行中的单元高亮并带流动外框);右:点开分步后的右页(类型清单、分步详情),
15
- 底部为常驻注入条。*
29
+ *主界面(深色):左为侧边栏入口选中态,中为串行主流程(每步一张单元卡,两侧圆形接口为工具/文件
30
+ 类型,运行中的单元高亮并带流动外框),右为分步详情页,底部为常驻注入条。*
31
+
32
+ ### 界面截图(完整预览)
33
+
34
+ | | |
35
+ |---|---|
36
+ | ![主界面完整预览(浅色主题)](docs/screenshots/02-main-light.png) | ![运行中的步骤预览:流动渐变外框](docs/screenshots/03-running-step.png) |
37
+ | **主界面 · 浅色主题** —— 与深色同一布局,配色随宿主主题切换 | **运行中的单元** —— 流动渐变外框标出「此刻在调用」 |
38
+ | ![分步详情预览:右页的类型清单与调用记录](docs/screenshots/04-step-detail.png) | ![热注入界面预览:把提示词排入下一步队列](docs/screenshots/05-hot-inject.png) |
39
+ | **分步详情** —— 右页列出该步的类型清单、调用参数与结果摘要 | **热注入** —— 提示词排入下一步执行队列,被取用前可撤回 |
40
+ | ![设置面板预览:配色、动效、详情深度与导出](docs/screenshots/06-settings.png) | |
41
+
42
+ 以上六张为离屏渲染的界面预览,不含任何真实会话内容。
16
43
 
17
44
  ![license](https://img.shields.io/badge/license-MIT-blue)
18
45
  ![platform](https://img.shields.io/badge/platform-DeepSeek%20Harness-brightgreen)
19
46
  ![node](https://img.shields.io/badge/node-%E2%89%A520-informational)
20
- ![version](https://img.shields.io/badge/version-0.3.1-success)
47
+ ![version](https://img.shields.io/badge/version-0.3.5-success)
21
48
 
22
49
  ---
23
50
 
@@ -60,7 +87,6 @@ DeepSeek Harness 插件:把当前会话渲染为一张可交互的分步工作
60
87
 
61
88
  ### 2.1 热注入(steer / unsteer)
62
89
 
63
- 本节给出注入通道的写入语义、状态判定与失败语义;三者均以会话日志事件为依据,前端不作推测。
64
90
  **写入语义**:`POST /dsh-workflow/steer` 调用宿主的 `agent.steer(message)`,消息进入会话收件箱的
65
91
  `next-step` 队列,在**当前轮次结束后**由下一步取用。调用不触发 `cancel`,因此不中断正在执行的轮次。
66
92
 
@@ -81,27 +107,28 @@ HTTP 200 仅表示消息已进入收件箱,**不等价于**会话已读取;
81
107
  事件区分。撤回的持久记录是 splice 的**位置**而非消息 id,因此折叠侧维护一份收件箱台账
82
108
  (按宿主 `inboxProjectionDefinition` 的 splice 语义逐条重放)才能定位被移除的具体行。
83
109
 
84
- 失败语义:文档型错误一律返回 JSON,不向 HTTP 层抛出异常。常见状态码 —— `400` 缺字段 /
110
+ 失败语义:文档型错误一律返回 JSON,不向 HTTP 层抛出异常。常见状态码:`400` 缺字段 /
85
111
  body 非 JSON,`404` 会话不存在,`409` 会话未在运行或消息已被取用,`413` 文本超 2000 字或
86
112
  body 超 64 KiB,`503` 宿主未暴露相应能力。
87
113
 
88
114
  ### 2.2 进度估算
89
115
 
90
- 本节给出百分比的取值优先级、各来源的算法与上限,以及陈旧程度字段的定义。
91
-
92
116
  每步的百分比按以下**优先级**取值(`src/fold.mjs` 的 `percentFor`):
93
117
  | 优先级 | 来源 | 计算规则 | 上限 |
94
118
  |---|---|---|---|
95
119
  | 1 | 待办表实测 `todos` | `round(已完成项 / 总项 × 100)`;自报存在时也只取该实测值 | 99 |
96
- | 2 | 模型自报 `model` | 待办表无可计行时,取 `stepProgress/report` 百分比与自报高水位中的较 **大** 者 | 100 |
120
+ | 2 | 模型自报 `model` | 待办表无可计行时,取最近一次 `stepProgress/report` 的百分比 | 99 |
97
121
  | 3 | 时间基线 `baseline` | `elapsed / median(本会话已完步耗时) × 100` | 90 |
98
122
  | 4 | 计划步序 `baseline:steps` | 无历史耗时时,用 `当前步序 / 总步数 × 100` 冷启动 | 90 |
99
123
  | — | `none` | 四个来源均不可用 | 不显示数字 |
100
124
 
101
125
  约束:未结束的步不会显示 100%;已结束(完成或失败)的步固定为 100%。
102
- 选用规则由可验证性决定:待办表是逐行计数得到的实测值,模型自报无此行级依据。因此只要待办表有已完成行,
103
- 百分比就取该实测比值(`percentFor` 中两个分支值均为 `measured`),自报只能作为无实测行时的备选;
104
- 高水位记录 `step.maxPercent` 仍保留且不回退,只是不再参与显示值计算。
126
+
127
+ 选用规则依据可核验性:待办表是逐行计数得到的量值,模型自报没有对应的行级依据。因此只要待办表存在已完成行,
128
+ 百分比取该计数比值(`percentFor` 的两个分支均返回 `measured`),自报仅在无行可计数时作为备选;且取
129
+ **最近一次**自报,使后续报告能够改变显示值。高水位记录 `step.maxPercent` 保留且不回退,不参与显示值计算。
130
+ `foldTodos` 将已结束行改回 `in_progress`、或降级为 `pending` 时,清空 `percent` 与 `maxPercent` 并重置
131
+ `startedAt`:已结束记录的数字属于那一次结束,不属于重新打开它的行。
105
132
 
106
133
  进度条下方另有一行元信息:`N 分钟前估计`,即 `view.asOf − step.lastEstimateAt`。
107
134
  该值描述数字的**陈旧程度**,用于区分「正在更新」与「长时间未更新」。字段缺失时整行不渲染。
@@ -147,7 +174,7 @@ body 超 64 KiB,`503` 宿主未暴露相应能力。
147
174
 
148
175
  ## 3. 安装
149
176
 
150
- > **市场安装的前提**:该入口要求同时满足两个条件 ——(1)`dsh-workflow-map` 已发布到 npm(当前为 `0.3.1`);
177
+ > **市场安装的前提**:该入口要求同时满足两个条件。(1)`dsh-workflow-map` 已发布到 npm(当前为 `0.3.4`);
151
178
  > (2)`awesome-dsh-plugin` 的 PR 已合并,且市场索引(`dsh-plugin-catalog`,经 jsDelivr 分发)已刷新收录该条目。
152
179
  > 满足后在 **DSH 市场**(Plugins → Discover)搜索 `workflow` 即可安装,无需命令行;
153
180
  > 若尚未收录,使用下面的命令行方式(3.2)安装同一个包,两者效果一致。
@@ -251,8 +278,13 @@ curl -i "http://127.0.0.1:<GUI端口>/dsh-workflow/state?sessionId=<会话ID>"
251
278
  | 点调用行 | 就地展开参数全文、结果与错误 |
252
279
  | `Esc` | 关闭右页,不关闭面板 |
253
280
  | 齿轮 | 打开设置:配色、动效、详情深度、节点上限 |
281
+ | `↻` | 立即重新读取当前会话的视图;读取成功后按钮短暂显示「已刷新」 |
254
282
  | 复制控件 | 复制路径 / 命令 / 快照,1.5 秒后回执并复原 |
255
283
 
284
+ 视图由轮询获得:页面可见时每 0.8 秒一次,隐藏时每 3 秒一次,条件请求(`If-None-Match`),
285
+ 未变化时宿主返回 `304` 且无响应体。`↻` 只是触发同一请求的一个周期,用于自动轮询受阻时手动取一次,
286
+ 不改变轮询节奏,也不构成第二条数据通道。
287
+
256
288
  设置项:
257
289
 
258
290
  | 项 | 取值 | 作用 |
@@ -275,7 +307,7 @@ curl -i "http://127.0.0.1:<GUI端口>/dsh-workflow/state?sessionId=<会话ID>"
275
307
 
276
308
  ## 8. 兼容性与约束
277
309
 
278
- - **DeepSeek Harness** 0.2.x 及以上(依赖会话投影与 `webServer` 路由注册)。
310
+ - **DeepSeek Harness**:`engines.dsh` 声明的区间 `>=0.2.1-rc.1 <0.3.0-0`(依赖会话投影与 `webServer` 路由注册);`0.3.0` 及以后不支持。
279
311
  - **Node.js** ≥ 20。
280
312
  - 三个端点挂载在宿主既有端口上,**不新开监听**。
281
313
  - 输入框(composer)由宿主渲染,不属于本插件。插件不替代、不覆盖该组件;仅在会话标签栏入口
@@ -290,7 +322,7 @@ curl -i "http://127.0.0.1:<GUI端口>/dsh-workflow/state?sessionId=<会话ID>"
290
322
  # 修改 src/client/*.js 后必须重新生成浏览器半
291
323
  node tools/build-client.mjs
292
324
 
293
- # 离线自测(10 套,共 1507 条断言)
325
+ # 离线自测(10 套)
294
326
  node tools/selftest-fold.mjs # 折叠语义
295
327
  node tools/selftest-host.mjs # 注册、schema、投影接线
296
328
  node tools/selftest-orchestrator.mjs # 计划文件读取与合并
@@ -307,7 +339,7 @@ node tools/check-real-injects.mjs # 批量折叠历史日志
307
339
  node tools/audit-real.mjs # 四项约束审计
308
340
  ```
309
341
 
310
- 浏览器量测(需一份 Chromium,脚本自动探测 Chrome / Edge,共 308 条断言):
342
+ 浏览器量测(需一份 Chromium,脚本自动探测 Chrome / Edge;含浅色/深色主题对比检查):
311
343
 
312
344
  ```bash
313
345
  pwsh -File tools/measure-panel.ps1
@@ -329,7 +361,7 @@ pwsh -File tools/measure-main.ps1
329
361
 
330
362
  ## 10. 关键常量
331
363
 
332
- 所有阈值集中定义,并由测试直接断言,因此文档取值与实现取值不会脱节:
364
+ 所有阈值集中定义,并由自测直接引用,因此本表取值与实现取值同源:
333
365
 
334
366
  | 常量 | 值 | 位置 | 含义 |
335
367
  |---|---|---|---|
@@ -346,7 +378,22 @@ pwsh -File tools/measure-main.ps1
346
378
 
347
379
  ---
348
380
 
349
- ## 11. 已知问题
381
+ ## 11. 计划文件与待办表的优先级
382
+
383
+ 地图的分步有两个来源,优先级固定:
384
+
385
+ | 优先级 | 来源 | 作用 |
386
+ |---|---|---|
387
+ | 1 | 待办表 `todo/write` | 本轮分步的标题与状态,是当前会话的最新陈述 |
388
+ | 2 | 计划文件 `.pae/<sessionId>/orchestrator.json` | `phase` 为 `planning` / `scheduled` / `executing` / `paused` 时,其步骤标题、`statuses`、`anchorSeqs` 生效 |
389
+ | — | 同一文件的 `completed` / `aborted` | 计划已结束:步骤与报告仍发布给详情页,但不再修改任何行的标题与状态 |
390
+
391
+ 计划文件写入后长期保留,不会被删除。若不加区分地读取,上一轮计划的标题与状态会覆盖本轮待办表刚建出的行,
392
+ 面板与对话因此不一致。视图的计划段为此携带布尔 `terminal`,两侧(宿主与浏览器半)都以它为准。
393
+
394
+ ---
395
+
396
+ ## 12. 已知问题
350
397
 
351
398
  0.3.0 记录过的两个缺陷均已关闭:
352
399
 
@@ -359,7 +406,7 @@ pwsh -File tools/measure-main.ps1
359
406
 
360
407
  ---
361
408
 
362
- ## 12. 目录
409
+ ## 13. 目录
363
410
 
364
411
  ```
365
412
  dsh-workflow-map/
@@ -379,7 +426,7 @@ dsh-workflow-map/
379
426
 
380
427
  ---
381
428
 
382
- ## 13. 常见问题
429
+ ## 14. 常见问题
383
430
 
384
431
  **能同时看多个会话吗?**
385
432
  面板只展示当前会话。切换会话即切换内容;不合并、不并排。
@@ -396,7 +443,7 @@ dsh-workflow-map/
396
443
  待办表封顶 99、时间基线封顶 90;只有状态进入完成/失败才固定 100%。
397
444
  这是为了避免「看起来快好了」与「已经好了」混淆。
398
445
 
399
- **它会把我的代码上传吗?**
446
+ **会把用户代码上传吗?**
400
447
  不会。插件不发起任何网络请求;只读本地会话日志与 `.pae` 计划文件。
401
448
 
402
449
  **可以只换界面、不装宿主半吗?**
@@ -407,18 +454,18 @@ dsh-workflow-map/
407
454
 
408
455
  ---
409
456
 
410
- ## 14. 许可
457
+ ## 15. 许可
411
458
 
412
459
  MIT。允许商用、修改与再分发,需保留版权声明。
413
460
 
414
461
  ---
415
462
 
416
- ## 15. 问题反馈
463
+ ## 16. 问题反馈
417
464
 
418
465
  以下信息有助于定位问题:
419
466
 
420
467
  1. DSH 版本与插件版本(`package.json` 的 `version`);
421
- 2. `GET /dsh-workflow/state?sessionId=<id>` 的响应体 —— **其中包含任务标题与文件路径,
422
- 提交前请自行删减**;
468
+ 2. `GET /dsh-workflow/state?sessionId=<id>` 的响应体(**其中包含任务标题与文件路径,
469
+ 提交前请自行删减**);
423
470
  3. 出现问题的面板截图;
424
471
  4. 若与折叠有关,附上该会话日志中相关事件序列的片段。