dsh-retrace 0.4.30 → 0.4.32

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/README.md CHANGED
@@ -360,7 +360,7 @@ Read it before filing an issue.
360
360
  ### Upgrading
361
361
 
362
362
  ```bash
363
- dsh plugin --profile desktop add dsh-retrace@0.4.30
363
+ dsh plugin --profile desktop add dsh-retrace@0.4.32
364
364
  # then restart DSH — plugins are not hot-reloaded
365
365
  ```
366
366
 
@@ -385,7 +385,7 @@ whole tree down, so recover first and diagnose second:
385
385
 
386
386
  ### Pinning
387
387
 
388
- Pin an exact plugin version (`dsh-retrace@0.4.26`) and let `dsh-log-contract` resolve to
388
+ Pin an exact plugin version (`dsh-retrace@0.4.32`) and let `dsh-log-contract` resolve to
389
389
  `>=0.3.12`. Do not rely on `^0.4` across a host upgrade: compatibility here is decided by
390
390
  the **host surface**, not by semver alone.
391
391
 
@@ -439,34 +439,63 @@ the **host surface**, not by semver alone.
439
439
  | 🛡️ | **Running-work detection** | every session is scanned for live work: agent running, queued inbox items, background jobs, unclosed turns |
440
440
  | 📋 | **Running banner** | sessions with live work show a persistent in-page banner (short session code + reasons), so you can see it before quitting |
441
441
  | ⚠️ | **Exit prompt** | on plugin dispose (app exit / reload) a Chinese notice lists each running session and why it is considered busy — it only warns, it never cancels your running agent |
442
- | 🔒 | **Page-close interception (web browsers)** | `beforeunload` interception, armed only where the host reports a native confirm dialog: a strong confirm when work is running (details modal, `[仍关闭]` = confirm-and-go), a light confirm otherwise |
442
+ | 🔒 | **Page-close interception** | **Does not arm the host's native confirm dialog**; uses a **page-drawn confirm gate** instead: it draws the box and **verifies visibility synchronously** before blocking (`preventDefault`); if it cannot draw, is not visible, or the page is hidden ⇒ **lets the close through immediately**; the dialog waits for your choice (**Esc = cancel**); a **Web-Worker watchdog** (immune to background throttling) is only the *last* resort; turning the setting off returns to the official behaviour (**no restart**) |
443
443
  | 🔎 | **Query surface** | `retrace.runningState` (host RPC) + `GET|POST /api/plugins/retrace/runningState` (HTTP) — same shape on both transports; the all-sessions shape also carries the host-reported page surface (`surface` / `quitVeto`) |
444
444
 
445
- > Desktop note: quit entry points differ by version/platform, and the DSH Desktop Electron
446
- > shell we inspected has no `will-prevent-unload` handler (0 hits across the packaged 2.0.9
447
- > `app.asar`). Where the entry does reach the page — the external report's DSH Desktop 0.9.0 /
448
- > Windows — the page `beforeunload` veto is **swallowed silently**: no dialog, no feedback, and
449
- > the exit looks stuck (only a force-quit works). Where it does not — the 2.0.9 shell we
450
- > inspected routes the tray item through `requestQuit(0) → window.destroy() → app.exit(0)` — the
451
- > quit is unaffected either way. The page cannot tell which case it is in, so desktop **never**
452
- > arms the native gate; it relies on the running banner plus the dispose notice. The gate is
453
- > armed only where the host reports that the page really surfaces a native dialog
454
- > (`quitVeto: true`, i.e. browser pages).
445
+ > Desktop wording (corrected 2026-09-20): **desktop never arms the host's native confirm dialog** —
446
+ > quit entry points differ **by version/platform**, and the native dialog either does not exist on a
447
+ > desktop shell or is swallowed: the 2.0.9 shell we inspected has no `will-prevent-unload` handler
448
+ > (0 hits across the packaged `app.asar`) and routes the tray item through
449
+ > `requestQuit(0) → window.destroy() → app.exit(0)`; on the external report's DSH Desktop 0.9.0 /
450
+ > Windows the entry **does reach** the page `beforeunload` (0.9.0), but the veto is **swallowed
451
+ > silently** (no dialog, no feedback — the exit just looks stuck).
455
452
  >
456
- > **Both criteria must hold** (2026-09-18, second round): the host side is only a
457
- > *non-objection*, and the client keeps a **veto** — if this page's `navigator.userAgent`
458
- > contains `Electron`, or the page URL contains `dsh-desktop-` (the external report's shell
459
- > puts its desktop marker in the query string), the gate is **never** armed. The host-side
460
- > criteria were widened to four request-level facts: the `x-dsh-desktop-renderer` capability
461
- > header, a request `User-Agent` containing `Electron`, or a request URL **or `Referer`**
462
- > containing `dsh-desktop-` — **any one** of them classifies the page as a desktop page. (Our own
463
- > polling URL carries no query string, so the page marker is in practice read from `Referer`,
464
- > which a same-origin `fetch` sends by default.) That covers the
465
- > hosts that expose no desktop evidence at all (the reporter's: a pure-Node harness plus an
466
- > Electron renderer, where all three older criteria were false, and the old code filed
467
- > "no evidence at all" as a browser page ⇒ the gate was still armed ⇒ the exit still hung).
468
- > If Desktop cannot quit, turning off "Exit confirmation (close guard)" in settings recovers
469
- > immediately (no restart).
453
+ > What actually protects on desktop is the **page-drawn confirm gate** (finalised 2026-09-19):
454
+ > it **does call `preventDefault`** — but only after it has **drawn the box and verified it is
455
+ > visible, synchronously**; a `visibilityState` pre-check lets the close through immediately when the
456
+ > box cannot be drawn / is not visible / the page is hidden; the dialog waits for your choice
457
+ > (**Esc = cancel**); a **Web-Worker watchdog** (immune to background throttling) is only the
458
+ > **last** resort.
459
+ >
460
+ > **Both criteria must hold** (2026-09-18, second round): the host side is only a *non-objection*
461
+ > (`quitVeto: true`), and the client keeps a **veto** — if this page's `navigator.userAgent`
462
+ > contains `Electron`, or the page URL contains `dsh-desktop-` (the external report's shell puts its
463
+ > desktop marker in the query string), the native gate is **never** armed. The host-side criteria
464
+ > were widened to four request-level facts: the `x-dsh-desktop-renderer` capability header, a
465
+ > request `User-Agent` containing `Electron`, or a request URL **or `Referer`** containing
466
+ > `dsh-desktop-` — **any one** of them classifies the page as a desktop page. (Our own polling URL
467
+ > carries no query string, so the page marker is in practice read from `Referer`, which a
468
+ > same-origin `fetch` sends by default.) That covers the hosts that expose no desktop evidence at
469
+ > all (the reporter's: a pure-Node harness plus an Electron renderer, where all three older criteria
470
+ > were false, and the old code filed "no evidence at all" as a browser page ⇒ the gate was still
471
+ > armed ⇒ the exit still hung).
472
+ >
473
+ > **Known limitation (stated plainly)**: some desktop shells route quit **around the page entirely**
474
+ > (e.g. `requestQuit → destroy → app.exit`, or the X button merely hides the window) ⇒ on those
475
+ > shells a plugin **cannot** show a confirm dialog at quit time; it **needs a shell seam**
476
+ > (a `will-prevent-unload` handler, or a pre-quit prompt hook). If Desktop cannot quit, turning off
477
+ > "Exit confirmation (close guard)" in settings recovers immediately (no restart).
478
+
479
+ **Session badge & display name** (**planned / off by default**) — badges are for human
480
+ collaboration; **identity is still decided by the session id**. The transports
481
+ (`sessionBadge` / `setBadgeTitle` / `initBadgeTitles` / `badgeMap`, on both HTTP and harness),
482
+ the resolver and the write guards are **in place**, but the **automatic startup path that writes
483
+ titles is off by default** (after three distinct on-device failures it moves to the **scripting
484
+ layer T3**, which is not implemented yet) — set `__DSH_RETRACE_BADGE_BOOTSTRAP = true` to enable it:
485
+
486
+ - A session title renders as `[badge] original title` (shape placeholder: `[opxxxopxxx] original title`); the badge
487
+ shape is **`opxxxopxxx`** and comes from the **real table** `codes.json` (workspace + sequence +
488
+ parent-chain semantics — **not a hash**).
489
+ - Sessions with **no `session/title` event are left blank on purpose** (the title shows just
490
+ `[badge]`) — **no project-name fallback, no fabricated name**.
491
+ - If the table / resolver is unavailable it falls back to the **raw session id as a placeholder**,
492
+ and **never** to FNV.
493
+ - The sidebar row, the running banner and the checkpoint view share **one source**: the same badge
494
+ map the host hands down (host op `badgeMap`).
495
+
496
+ > **Planned (not shipped)**: the scripting-layer T3 is not implemented yet (the badge/name
497
+ > **automatic title-writing startup path** is blocked on it); the agent business-layer plan (runtime guard,
498
+ > interruption governance, ecosystem-facing interfaces) is a **plan**, not a shipped capability.
470
499
 
471
500
  > Command surface: `retrace.runningState` (host RPC) + `GET|POST /api/plugins/retrace/runningState` (HTTP).
472
501
 
package/README.zh.md CHANGED
@@ -271,7 +271,7 @@ Client 半区会依据包内 `dsh.client` 元数据被自动打包进 Web 客户
271
271
  ### 升级
272
272
 
273
273
  ```bash
274
- dsh plugin --profile desktop add dsh-retrace@0.4.26
274
+ dsh plugin --profile desktop add dsh-retrace@0.4.32
275
275
  # 然后重启 DSH —— 插件不会热重载
276
276
  ```
277
277
 
@@ -285,7 +285,7 @@ dsh plugin --profile desktop add dsh-retrace@0.4.26
285
285
 
286
286
  ### 版本固定建议
287
287
 
288
- 请固定到确切版本(`dsh-retrace@0.4.26`),并让 `dsh-log-contract` 解析到 `>=0.3.12`。
288
+ 请固定到确切版本(`dsh-retrace@0.4.32`),并让 `dsh-log-contract` 解析到 `>=0.3.12`。
289
289
  **不要在跨宿主升级时依赖 `^0.4` 这种范围**:这里的兼容性由**宿主的面**决定,光看 semver 不够。
290
290
 
291
291
  ---
@@ -326,25 +326,47 @@ dsh plugin --profile desktop add dsh-retrace@0.4.26
326
326
  | 🛡️ | **运行中检测** | 逐会话扫描运行中工作:agent 正在跑 / inbox 排队 / 后台 jobs / 未闭合轮 |
327
327
  | 📋 | **运行中横幅** | 有运行中工作的会话显示页面常驻横幅(会话短码 + 原因),退出前可见 |
328
328
  | ⚠️ | **退出提示** | 插件 dispose(应用退出/重载)时中文提示列出每个运行中会话与原因——只提示,绝不代你取消 agent |
329
- | 🔒 | **页面关闭拦截(普通浏览器)** | `beforeunload` 拦截,只在宿主回报会弹原生确认框的页面上武装:有运行中任务强确认(明细模态,`[仍关闭]` 即确认离开),无任务轻确认 |
329
+ | 🔒 | **页面关闭拦截** | **不武装宿主原生确认框**;改用**页面自绘确认门**:先画框并**同步校验可见**才拦(`preventDefault`),画不出 / 不可见 / 页面不可见 ⇒ **当场放行**;确认框一直等你选择(Esc = 取消);**看门狗(Web Worker 计时器,不受后台节流)**只作最后一道;关掉设置项即退回官方形态(免重启) |
330
330
  | 🔎 | **查询面** | `retrace.runningState`(host RPC)+ `GET|POST /api/plugins/retrace/runningState`(HTTP),两入口同形状;全会话形状另带宿主判定的承载面(`surface` / `quitVeto`) |
331
331
 
332
- > 桌面说明:**退出入口随版本/平台而变**,而我们检查的 Electron 壳没有处理 `will-prevent-unload`
333
- > (装好的 2.0.9 `app.asar` 全文检索 0 命中)。**会走到该入口的那类版本**(外部报告所在的
334
- > DSH Desktop 0.9.0 / Windows)上,页面 `beforeunload` 否决被**静默吞掉**:不弹界面、不给
335
- > 反馈,表现为退出卡住(只能强退);**不走该入口的版本**(我们检查的 2.0.9:托盘项走
336
- > `requestQuit(0) → window.destroy() → app.exit(0)`)上,退出本来就不受影响。页面里分不出
337
- > 自己属于哪一类,因此**桌面端一律不武装**原生门,保护由运行中横幅 + dispose 提示承担;
338
- > 只有宿主回报"这个页面会弹原生确认框"(`quitVeto: true`,即普通浏览器页)时才武装。
332
+ > **桌面端口径(2026-09-20 更正)**:**桌面端一律不武装宿主原生确认框** —— **退出入口随版本/平台而变**,
333
+ > 而原生确认框在桌面壳上要么不存在、要么被静默吞掉:我们检查的 2.0.9 壳没有处理
334
+ > `will-prevent-unload`(打包 `app.asar` 全文检索 0 命中),托盘项走
335
+ > `requestQuit(0) → window.destroy() → app.exit(0)`;外部报告那套 DSH Desktop 0.9.0 / Windows
336
+ > 上虽然**会走到页面** `beforeunload`,但页面否决被**静默吞掉**(不弹界面、不给反馈,表现为退出卡住)。
339
337
  >
340
- > **两道判据都成立才武装**(2026-09-18 第二轮):宿主那条只是"不反对",客户端还有**一票否决** ——
341
- > 本页 `navigator.userAgent` 含 `Electron`,或页面 URL 含 `dsh-desktop-`(外部报告那套壳的查询参数
342
- > 前缀)⇒ 一律不武装。宿主侧的判据也补成四条请求级证据:能力头 `x-dsh-desktop-renderer` /
343
- > 请求 UA 含 Electron / 请求 URL **或 `Referer`** 含 `dsh-desktop-`(我们自己的轮询 URL 没有 query,
344
- > 所以页面标记实际是从 `Referer` 读到的),**任一成立**即判桌面页面。这样"壳什么桌面痕迹
345
- > 都不给"的宿主(报告人那台:纯 Node 跑 harness + Electron 渲染页,三条旧判据全不成立,
346
- > 旧实现把"完全没有证据"归成了浏览器页 ⇒ 照旧武装 ⇒ 仍退不掉)也不会再把退出卡住。
347
- > 桌面端退不掉时,把设置里的「退出确认(关闭守卫)」关掉即可立即恢复(不用重启)。
338
+ > **桌面端真正生效的是页面自绘确认门**(2026-09-19 收尾):它**确实会调用 `preventDefault`** ——
339
+ > 但只在"**先画出来 + 同步可见性校验通过**"之后才拦;`visibilityState` 前置检查,框画不出来 /
340
+ > 不可见 / 页面不可见 ⇒ **当场放行**;确认框一直等你选择(**Esc = 取消**);
341
+ > **看门狗用 Web Worker 计时器**(不受后台节流)只作**最后一道**分流。
342
+ >
343
+ > 武装**宿主原生门**的判据:**两道判据都成立才武装**(2026-09-18 第二轮)—— 宿主那条只是"不反对"
344
+ > (`quitVeto: true`),客户端还有**一票否决**:本页 `navigator.userAgent` 含 `Electron`,
345
+ > 或页面 URL 含 `dsh-desktop-`(外部报告那套壳把桌面标记放在查询串里)⇒ 一律不武装。
346
+ > 宿主侧判据补成**四条请求级证据**:能力头 `x-dsh-desktop-renderer` / 请求 UA 含 `Electron` /
347
+ > 请求 URL **或 `Referer`** 含 `dsh-desktop-`(我们自己的轮询 URL 没有 query,所以页面标记
348
+ > 实际是从 `Referer` 读到的),**任一成立**即判桌面页面。这样"壳什么桌面痕迹都不给"的宿主
349
+ > (报告人那台:纯 Node 跑 harness + Electron 渲染页,三条旧判据全不成立,旧实现把
350
+ > "**完全没有证据**"归成了浏览器页 ⇒ 照旧武装 ⇒ 仍退不掉)也不会再把退出卡住。
351
+ >
352
+ > **已知限制(照实写)**:某些桌面壳的退出口径**根本不经过页面**(如
353
+ > `requestQuit → destroy → app.exit`,或点 X 只是隐藏窗口)⇒ 那类壳上"退出时弹确认"
354
+ > **插件侧做不到**,需要**壳提供 seam**(`will-prevent-unload` 处理,或退出前的询问钩子)。
355
+ > **桌面端退不掉时**,把设置里的「退出确认(关闭守卫)」关掉即退回官方形态(**不用重启**)。
356
+
357
+ **会话短码与显示名**(**计划 / 默认关闭**)—— 人机协作用短码,**身份判定仍以 session id 为准**。
358
+ 通道(`sessionBadge` / `setBadgeTitle` / `initBadgeTitles` / `badgeMap`,HTTP + harness 两入口)与
359
+ 解析器、写护栏**均已就位**;但**启动自动写标题那条路默认关闭**(真机上三次不同失败后按
360
+ 「文件/脚本优先」改走**脚本层 T3**,T3 尚未实现)—— 需要时置 `__DSH_RETRACE_BADGE_BOOTSTRAP = true` 打开:
361
+
362
+ - 会话标题显示为 `[短码] 原标题`(形态占位:`[opxxxopxxx] 原标题`),短码形态 **`opxxxopxxx`**,
363
+ 由**真表** `codes.json` 解析(工作区 + 序号 + 父链语义,**不是 hash**)。
364
+ - **没有 `session/title` 事件的会话如实留空**(标题只显示 `[短码]`)—— **不回落项目名、不伪造名字**。
365
+ - 拿不到短码表 / 解析器不可用时退回**原始 session id 占位**,**绝不回落 FNV**。
366
+ - 侧栏会话行、运行中横幅、读档点视图的短码**同源**:都取宿主下发的**同一份**映射(宿主 op `badgeMap`)。
367
+
368
+ > **计划(未上线)**:脚本层 T3 尚未实现(短码/名字的**启动自动写标题**即卡在这里);业务层规划
369
+ > (运行时守护、中断治理、生态开放接口)见下节,属**计划**而非已上线能力。
348
370
 
349
371
  **未来计划**——agent 业务层规划(运行时守护、中断治理、生态开放接口)**尚未发布**,此节是**计划**而非已上线能力。本 README 描述的是**开发线(main)**,可能领先于 npm 上最新发布版。
350
372