dsh-retrace 0.4.25 → 0.4.27

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
@@ -77,21 +77,6 @@ Full steps in [📦 Installation](#-installation).
77
77
  | 🧭 | **Jump-to-conversation** | one click from a version to that point in the conversation (auto-loads history, anchor highlight) |
78
78
  | 🧹 | **Bounded storage** | snapshots keep the most recent N versions (default 50); throttled background sweep prunes truncated ones |
79
79
 
80
- **Close guard (don't lose work by accident)** — before you exit or reload, know what is still running:
81
-
82
- | | What | |
83
- |---|---|---|
84
- | 🛡️ | **Running-work detection** | every session is scanned for live work: agent running, queued inbox items, background jobs, unclosed turns |
85
- | 📋 | **Running banner** | sessions with live work show a persistent in-page banner (short session code + reasons), so you can see it before quitting |
86
- | ⚠️ | **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 |
87
- | 🔒 | **Page-close interception (Web)** | `beforeunload` interception: a strong confirm when work is running (details modal, `[仍关闭]` = confirm-and-go), a light confirm otherwise |
88
- | 🔎 | **Query surface** | `retrace.runningState` (host RPC) + `GET|POST /api/plugins/retrace/runningState` (HTTP) — same shape on both transports |
89
-
90
- > Desktop note: the Electron shell destroys the window on quit, so the page-level
91
- > `beforeunload` hook cannot fire there and the host exposes no plugin quit-veto seam —
92
- > Desktop is covered by the running banner plus the dispose notice; Web gets the full
93
- > interception.
94
-
95
80
  **Why it's different** (the interaction layer — the guarantees above are the storage layer):
96
81
 
97
82
  - 🎯 **Whole-round recall** — removes the input *and* its output (tool rows included), not just a single bubble.
@@ -129,8 +114,11 @@ The same result with plain file edits and `pnpm` — exactly the steps
129
114
  > steps below with the dependency line pointing at the folder:
130
115
  > `"dsh-retrace": "file:~/plugins/dsh-retrace"`.
131
116
 
132
- 1. Open the profile manifest (defaults: `~/.dsh/profiles/desktop` on DSH
133
- Desktop, `~/.dsh/profiles/web` for standalone Web) and add **both** the
117
+ 1. Open the profile manifest (defaults: `<plugin data home>/profiles/desktop`
118
+ on DSH Desktop, `<plugin data home>/profiles/web` for standalone Web — where
119
+ `<plugin data home>` is `$DSH_HOME` when set, otherwise the **active session
120
+ base**, e.g. a newer `DSH_HOME` directory; `~/.dsh/profiles` is only the
121
+ pre-migration fallback) and add **both** the
134
122
  dependency and the bundle-layer entry:
135
123
 
136
124
  ```json
@@ -156,7 +144,7 @@ The same result with plain file edits and `pnpm` — exactly the steps
156
144
  2. Install inside the profile directory:
157
145
 
158
146
  ```sh
159
- cd ~/.dsh/profiles/<name> && pnpm install
147
+ cd "$DSH_HOME/profiles/<name>" && pnpm install # or the active base you use
160
148
  ```
161
149
 
162
150
  3. Restart DSH Desktop / the `dsh` process (see above).
@@ -209,7 +197,7 @@ The dynamic host registers the same operations behind the package-private
209
197
  | **Start a fresh conversation after editing** | off | Hide earlier messages too, so the conversation looks like a fresh start (the whole surface is rewound before re-sending). Default off: only the edited round's context is replaced. |
210
198
  | **Hide shadowed messages per marker** | on | On (default): recall/edit/regenerate hide the replaced round per their markers. Off: every message stays visible; markers only show the notice and reference (review the full history). A single marker that would hide more than 40% of the conversation degrades to notice-only (history never silently vanishes). |
211
199
  | **Version & artifact snapshots** | on | On: every recall/edit records a version (messages and touched files) powering the timeline and artifact rollback. Off: only rewinds context — no version records, no artifact tracking (lightest). |
212
- | **Git integration** | on | On: use git to record and roll back when the workspace is a repository (never auto-commits, never touches your branches); non-repo workspaces can enable git from the timeline. Off: built-in snapshots under `~/.dsh` only — the plugin never touches the workspace git state; features are equivalent. |
200
+ | **Git integration** | on | On: use git to record and roll back when the workspace is a repository (never auto-commits, never touches your branches); non-repo workspaces can enable git from the timeline. Off: built-in snapshots under the plugin data home only — the plugin never touches the workspace git state; features are equivalent. |
213
201
  | **Version retention limit** | 50 | File snapshots are kept for the most recent N versions; older ones are pruned automatically (timeline records and the audit trail are always kept). |
214
202
 
215
203
  ---
@@ -257,6 +245,131 @@ The dynamic host registers the same operations behind the package-private
257
245
 
258
246
  ---
259
247
 
248
+ ## 🔺 Compatibility & upgrade notes
249
+
250
+ `dsh-retrace` is a **bundle plugin**: it plugs into whatever host surface it is
251
+ installed into. A host release that *removes* a package or a client service can
252
+ therefore break an older plugin build even though nothing in that build changed —
253
+ the symptom is usually a failed boot, not a wrong-looking feature.
254
+
255
+ This section exists so you can tell **host-side breakage** from **plugin-side bugs**.
256
+ Read it before filing an issue.
257
+
258
+ ### Host-side breaking changes that `0.4.26` adapts to — *not caused by this plugin*
259
+
260
+ 1. **`@deepseek-ai/dsh-session` dropped `decodeStorageRecord` from its public export surface (in `0.1.5-rc.1`; the function still exists internally but is no longer exported from the package root and is unreachable via the exports map).**
261
+ `dsh-retrace` itself never imported it, but its dependency `dsh-log-contract` did.
262
+ With no such export the loader aborts with
263
+ `plugin tree failed to load … does not provide an export named 'decodeStorageRecord'`
264
+ and **the whole plugin tree fails to load — not just this plugin**, so the app does
265
+ not start. `0.4.26` requires a `dsh-log-contract` build that decodes through its own
266
+ local compatibility layer instead of the removed host export.
267
+ → **Dependency note:** needs `dsh-log-contract >= 0.3.12`.
268
+ 2. **A client **service** disappeared: `conversationEvents`** — it used to be provided by
269
+ the legacy client runtime `@deepseek-ai/dsh-client-runtime`, which has been removed
270
+ (the string `conversationEvents` no longer occurs anywhere in the host). A plugin whose
271
+ client half still declares that service in `export const inject` never becomes ready:
272
+ its fiber stays **pending**, which the host reports as
273
+ `renderer boot failed (plugins: …): The client Loader did not provide an error message.`
274
+ — no error text at all, the window does not finish starting, and the only way in is to
275
+ disable the plugin.
276
+ `0.4.26` drops the service from `inject` and resolves it **defensively** in `apply`
277
+ (`uiConversation`, falling back to the legacy name), so it runs on hosts that provide
278
+ the new service *and* on older hosts that still provide the old one.
279
+ > Note: declaring a **package** that no longer exists in `dsh.client.inject` is *not*
280
+ > what breaks the boot — the client loader skips unknown entries silently. The
281
+ > breakage comes from the **service name** the plugin waits for.
282
+
283
+ > Both items above are **host-side removals**, documented here on purpose: if you hit
284
+ > either symptom right after a host upgrade, the first question is "does this plugin
285
+ > build predate the removal?", not "what did the plugin break?".
286
+
287
+ ### Plugin-side fixes in `0.4.26` (these are ours)
288
+
289
+ - **Data home and session base are now one source.** The plugin previously resolved its
290
+ own data directory through the host's home resolver (`$DSH_HOME` → `~/.dsh`), which
291
+ does not know about a migrated base (for example a newer `DSH_HOME` directory). With
292
+ `$DSH_HOME` unset,
293
+ sessions were read from one base while snapshots and the artifact store were written
294
+ to another. Snapshots, version stores and `verify-install` now follow the **active
295
+ session base**. When `$DSH_HOME` is set, behaviour is unchanged.
296
+ - **No user-visible string hard-codes `~/.dsh` any more** (the settings hint used to say
297
+ snapshots live under `~/.dsh`).
298
+ - Stale peer declarations with no remaining import site removed
299
+ (`@deepseek-ai/dsh-home-paths`, `@deepseek-ai/dsh-client-runtime`).
300
+
301
+ ### Host-side breaking changes that `0.4.27` adapts to — *not caused by this plugin*
302
+
303
+ 1. **`@deepseek-ai/dsh-session` removed the `Session.events` member** (in `0.1.5-rc.1`).
304
+ The class has no `events` field and no `events` getter at all any more; the supported
305
+ readers are `snapshotEvents(fromSeq, toSeqExclusive)` (frozen, sequence-indexed),
306
+ `eventAt(seq)`, `ownEvents()` and `isOwnSeq(seq)`. `0.4.27` reaches the log through a
307
+ compatibility accessor that prefers the new API and falls back to the old array, so it
308
+ runs on both host generations.
309
+ **Symptom before the fix:** recall and edit did nothing and surfaced the raw error
310
+ `TypeError: Cannot read properties of undefined (reading 'length')` — both operations
311
+ start by locating the target message id, and that lookup read the removed member.
312
+ 2. **The client-side session store has no `keys()`** (`ctx.sessions`). A plugin that
313
+ enumerates sessions with `keys()` silently sees **zero** of them: no crash, no error,
314
+ just safety warnings that never fire. `0.4.27` prefers the official `list()` and falls
315
+ back to `keys()`; it deliberately does **not** fall back to enumerating service fields,
316
+ because guessing produces a silent empty result as well.
317
+ 3. **The client session controller has no title accessor** — `getTitle` does not exist
318
+ anywhere in `@deepseek-ai/dsh-api-session-controller`, and its `getSnapshot()` carries
319
+ no `title`. See the plugin-side item below: this one used to *overwrite your titles*.
320
+
321
+ ### Plugin-side fixes in `0.4.27` (these are ours)
322
+
323
+ - **The edit / recall affordances never appeared at all.** The client half read chat
324
+ nodes from `snapshot.chat.nodes`, a path this host build does not have — nodes live in
325
+ the `useChat` store (`snapshot.nodes`). Every message-level component threw while
326
+ rendering and was swallowed by the error boundary, so the buttons were missing, while
327
+ the settings entry (which reads no nodes) rendered fine. Fixed: the client half now
328
+ takes `useChat` from the slot contract.
329
+ - **"Jump to message" in the version and fork views did nothing.** It resolved the target
330
+ anchor through `store.getSnapshot()?.chat?.nodes`, which is permanently `undefined`
331
+ here. It now resolves through the `useChat` snapshot injected by the view and pages
332
+ with the official `store.loadThrough(seq)`; when the jump cannot complete it reports a
333
+ **diagnosable reason** (renderer warning + host-log line) instead of failing silently.
334
+ - **Assigning a short code could overwrite your session title.** The client composed
335
+ `[CODE] <current title>` locally but had no way to read the current title, so the base
336
+ degraded to the session-id prefix (`[XXXXXX] 668f9166-648c-4c`). Title tagging now goes
337
+ through the host route only (`setBadgeTitle`), which reads the current title from the
338
+ session log. Manual renames are unaffected.
339
+ - **Host-side operation failures are logged again** (code + message + stack). They used to
340
+ return the message to the UI without a log line, which is why this whole class of bug
341
+ was hard to diagnose from outside.
342
+
343
+ ### Upgrading
344
+
345
+ ```bash
346
+ dsh plugin --profile desktop add dsh-retrace@0.4.27
347
+ # then restart DSH — plugins are not hot-reloaded
348
+ ```
349
+
350
+ **`0.4.27` needs no data migration.** The session format is unchanged (v3), no session is
351
+ re-written, and nothing has to be re-indexed: upgrade, restart, and the two symptoms above
352
+ are gone. If you are on a host that still provides the old members, the compatibility
353
+ accessors keep those paths working — this build does not drop older hosts.
354
+
355
+ If the app **fails to boot after an upgrade**, a single failing plugin can take the
356
+ whole tree down, so recover first and diagnose second:
357
+
358
+ 1. remove `dsh-retrace` from the profile's `dsh.profile.bundles` **and** its
359
+ `dependencies` entry, restart, and confirm you can get back in;
360
+ 2. read the host log —
361
+ macOS: `~/Library/Application Support/DSH Desktop/logs/host/dsh-<date>.error.log`;
362
+ 3. `plugin tree failed to load` is the **host** half; `renderer boot failed` is the
363
+ **client** half. Both name the offending plugin/package — start there.
364
+
365
+ ### Pinning
366
+
367
+ Pin an exact plugin version (`dsh-retrace@0.4.26`) and let `dsh-log-contract` resolve to
368
+ `>=0.3.12`. Do not rely on `^0.4` across a host upgrade: compatibility here is decided by
369
+ the **host surface**, not by semver alone.
370
+
371
+ ---
372
+
260
373
  ## ⚠️ Requirements & limitations
261
374
 
262
375
  - Only **user messages** can be edited; recall works on user and assistant
@@ -286,8 +399,24 @@ The dynamic host registers the same operations behind the package-private
286
399
  - **Real-time watchdog** — snapshots the log at the first sign of concurrent writes.
287
400
  - Companion **`dsh-log-contract`**: 30+ offline contract rules + in-place repair
288
401
  (`fix --neutralize` / `--clip-crossstep`) for sessions that would fail `/compact`.
402
+ **Close guard (don't lose work by accident)** — before you exit or reload, know what is still running:
403
+
404
+ | | What | |
405
+ |---|---|---|
406
+ | 🛡️ | **Running-work detection** | every session is scanned for live work: agent running, queued inbox items, background jobs, unclosed turns |
407
+ | 📋 | **Running banner** | sessions with live work show a persistent in-page banner (short session code + reasons), so you can see it before quitting |
408
+ | ⚠️ | **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 |
409
+ | 🔒 | **Page-close interception (Web)** | `beforeunload` interception: a strong confirm when work is running (details modal, `[仍关闭]` = confirm-and-go), a light confirm otherwise |
410
+ | 🔎 | **Query surface** | `retrace.runningState` (host RPC) + `GET|POST /api/plugins/retrace/runningState` (HTTP) — same shape on both transports |
411
+
412
+ > Desktop note: the Electron shell destroys the window on quit, so the page-level
413
+ > `beforeunload` hook cannot fire there and the host exposes no plugin quit-veto seam —
414
+ > Desktop is covered by the running banner plus the dispose notice; Web gets the full
415
+ > interception.
416
+
417
+ > Command surface: `retrace.runningState` (host RPC) + `GET|POST /api/plugins/retrace/runningState` (HTTP).
289
418
 
290
- **What's next** — see the [public roadmap](./docs/ROADMAP.md) for the agent
419
+ **What's next** — see the [public roadmap](https://github.com/yamingmou/dsh-retrace/blob/main/docs/ROADMAP.md) for the agent
291
420
  business-layer plan (runtime guard, interruption governance, ecosystem-facing
292
421
  interfaces). This README only describes what is already shipped.
293
422
 
@@ -328,7 +457,7 @@ npm pack --dry-run # verify the published file list
328
457
  > one and only swaps the transport (`host.call` vs the HTTP route) via
329
458
  > `__setMessageEditorWire`.
330
459
 
331
- PRs and issues are welcome — see [CONTRIBUTING](./CONTRIBUTING.md) (coming soon)
460
+ PRs and issues are welcome — a `CONTRIBUTING.md` is coming soon
332
461
  and the [issue tracker](https://github.com/yamingmou/dsh-retrace/issues).
333
462
 
334
463
  ---
@@ -338,7 +467,7 @@ and the [issue tracker](https://github.com/yamingmou/dsh-retrace/issues).
338
467
  Listed on the [dsh-plugin topic](https://github.com/topics/dsh-plugin).
339
468
 
340
469
  Part of the **Agent business layer (production-grade guarantees)** — see the
341
- [public roadmap](./docs/ROADMAP.md) for the framework-agnostic layer and how
470
+ [public roadmap](https://github.com/yamingmou/dsh-retrace/blob/main/docs/ROADMAP.md) for the framework-agnostic layer and how
342
471
  dsh-retrace is its DeepSeek Harness implementation. Companion components:
343
472
 
344
473
  - [**dsh-log-contract**](https://github.com/yamingmou/dsh-log-contract) — the
@@ -352,7 +481,7 @@ dsh-retrace is its DeepSeek Harness implementation. Companion components:
352
481
  > ```sh
353
482
  > dsh plugin --profile desktop add github:yamingmou/dsh-retrace
354
483
  > # or with pnpm directly into a profile:
355
- > cd ~/.dsh/profiles/desktop && pnpm add github:yamingmou/dsh-retrace
484
+ > cd "$DSH_HOME/profiles/desktop" && pnpm add github:yamingmou/dsh-retrace
356
485
  > ```
357
486
  >
358
487
  > Then restart DSH Desktop as usual. The `dsh-log-contract` dependency is
@@ -392,7 +521,7 @@ retrace lineage <session> # parent-chain lineage (A4)
392
521
  ```
393
522
 
394
523
  `<session>` is a full log path or a sessionId (auto-looked-up under
395
- `~/.dsh/sessions`). All read-only.
524
+ the active session base — `$DSH_HOME/sessions`, else a newer base, else `~/.dsh/sessions`). All read-only.
396
525
 
397
526
  **Session lineage in the fork map (A4, UI)**: the Fork map view header shows the
398
527
  current session's `parentSession` chain (session → parent → root, `←` direction).
package/README.zh.md CHANGED
@@ -45,7 +45,6 @@ dsh plugin --profile desktop add dshmarket # 只需一次
45
45
  重启后,悬停任意助手回复 → **↩ / ↻**;任意用户消息 → **✎**。详细步骤见
46
46
  [📦 安装](#-安装)。
47
47
 
48
- ---
49
48
  ---
50
49
 
51
50
  ## 🛡️ 生产级保证(0.4.x 全部已上线)
@@ -109,8 +108,9 @@ dsh plugin --profile <name> add dsh-retrace
109
108
  > 然后执行 `dsh plugin --profile desktop add ~/plugins/dsh-retrace`;或按下面步骤,
110
109
  > 把依赖行指向该文件夹:`"dsh-retrace": "file:~/plugins/dsh-retrace"`。
111
110
 
112
- 1. 打开 profile 清单(默认位置:DSH Desktop 为 `~/.dsh/profiles/desktop`,
113
- 独立 Web 为 `~/.dsh/profiles/web`),同时加入依赖**和** bundle 层条目:
111
+ 1. 打开 profile 清单(默认位置:DSH Desktop 为 `<插件数据家>/profiles/desktop`,
112
+ 独立 Web 为 `<插件数据家>/profiles/web` —— 插件数据家在设了 `$DSH_HOME` 时就是它,
113
+ 否则跟随**活动会话基座**;`~/.dsh/profiles` 只是迁移前的兜底),同时加入依赖**和** bundle 层条目:
114
114
 
115
115
  ```json
116
116
  {
@@ -134,7 +134,7 @@ dsh plugin --profile <name> add dsh-retrace
134
134
  2. 在 profile 目录里安装:
135
135
 
136
136
  ```sh
137
- cd ~/.dsh/profiles/<name> && pnpm install
137
+ cd "$DSH_HOME/profiles/<name>" && pnpm install # 或你实际使用的基座
138
138
  ```
139
139
 
140
140
  3. 重启 DSH Desktop / `dsh` 进程(见上文)。
@@ -180,7 +180,7 @@ Client 半区会依据包内 `dsh.client` 元数据被自动打包进 Web 客户
180
180
  | **编辑后从新对话开始** | 关 | 编辑后连此前的消息也一并隐藏,让对话看起来像从新消息重新开始(重发前回退整个表面)。默认关:只替换被编辑那一轮的上下文。 |
181
181
  | **按标记隐藏被编辑/撤回的消息** | 开 | 开(默认):撤回/编辑/重新生成按标记隐藏被替换的那一轮消息。关:所有消息保持可见,标记仅显示提示与对照(查看完整历史用)。单个 marker 要隐藏超过 40% 的对话时自动降级为不隐藏(历史永不静默消失)。 |
182
182
  | **版本与产物快照** | 开 | 开:每次撤回/编辑记录一个版本(消息与触碰文件),提供时间线与产物回退;关:仅回退上下文,不记录版本、不追踪产物(最省资源)。 |
183
- | **启用 git 集成** | 开 | 开:工作区是 git 仓库时用 git 记录与回退(不自动提交、不动你的分支),非仓库可在时间线里一键启用;关:一律用内置快照(存于 `~/.dsh`),不触碰工作区 git 状态,功能等价。 |
183
+ | **启用 git 集成** | 开 | 开:工作区是 git 仓库时用 git 记录与回退(不自动提交、不动你的分支),非仓库可在时间线里一键启用;关:一律用内置快照(存于插件数据家),不触碰工作区 git 状态,功能等价。 |
184
184
  | **版本保留上限** | 50 | 文件快照只保留最近 N 个版本,超出自动清理最旧的;时间线记录与审计痕迹始终保留。 |
185
185
 
186
186
  ---
@@ -221,6 +221,70 @@ Client 半区会依据包内 `dsh.client` 元数据被自动打包进 Web 客户
221
221
 
222
222
  ---
223
223
 
224
+ ## 🔺 兼容性与升级须知
225
+
226
+ `dsh-retrace` 是 **bundle 型插件**:它插进哪套宿主,就依赖那套宿主暴露的面。因此宿主
227
+ **移除**一个包或一个客户端服务时,**旧版插件即使一行没改也会坏** —— 而且症状通常是
228
+ 「起不来」,不是「某个功能看起来不对」。
229
+
230
+ 本节的目的:让你能分清 **宿主侧破坏性变更** 与 **插件侧缺陷**。提 issue 前请先看这节。
231
+
232
+ ### `0.4.26` 适配的宿主侧破坏性变更 —— *不是本插件造成的*
233
+
234
+ 1. **`@deepseek-ai/dsh-session` 把 `decodeStorageRecord` 从公开导出面拿掉了(`0.1.5-rc.1`;函数仍在内部模块里,但不再从包根导出、exports map 子路径也不可达)。**
235
+ 本插件自己从未 import 它,但它的依赖 `dsh-log-contract` import 了。宿主不再导出该符号时,
236
+ 加载器会以
237
+ `plugin tree failed to load … does not provide an export named 'decodeStorageRecord'`
238
+ 中止,而且**整棵插件树一起失败——不只是本插件**,于是 App 起不来。`0.4.26` 改为要求
239
+ 一个通过**自身兼容层**解码、不再依赖该已移除导出的 `dsh-log-contract`。
240
+ → **依赖说明:** 需要 `dsh-log-contract >= 0.3.12`。
241
+ 2. **一个客户端**服务**消失了:`conversationEvents`** —— 它原先由旧客户端运行时
242
+ `@deepseek-ai/dsh-client-runtime` 提供,而该运行时已被移除(`conversationEvents`
243
+ 这个串在宿主里**已 0 命中**)。插件的客户端半若仍在 `export const inject` 里声明它,
244
+ 就**永远不就绪**:fiber 停在 **pending**,宿主据此报
245
+ `renderer boot failed (plugins: …): The client Loader did not provide an error message.`
246
+ —— **一个字的错误信息都没有**,窗口起不来,唯一的进法是把插件禁用。
247
+ `0.4.26` 已把它从 `inject` 中删掉,并改在 `apply` 里**防御性解析**
248
+ (`uiConversation`,取不到则回退旧名),因此:**既能在提供新服务的宿主上跑,
249
+ 也仍兼容还提供旧服务的老宿主**。
250
+ > 注意:在 `dsh.client.inject` 里声明一个**已不存在的包**,**不会**导致启动失败 ——
251
+ > 客户端加载器对认不出的条目是**静默跳过**的。真正致命的是插件等待的那个**服务名**。
252
+
253
+ > 上面两条都是**宿主侧移除**,写在这里是有意的:如果你在**升级宿主之后**立刻遇到这两种症状,
254
+ > 第一个该问的是「这份插件构建是不是早于这次移除?」,而不是「插件改坏了什么?」。
255
+
256
+ ### `0.4.26` 里的插件侧修复(这些是我们自己的)
257
+
258
+ - **插件数据家与会话基座合并为同一来源。** 此前插件用宿主的 home 解析器
259
+ (`$DSH_HOME` → `~/.dsh`)决定自己的数据目录,而它不认识迁移后的基座(例如一个更新的 `DSH_HOME` 目录)。
260
+ 于是当 `$DSH_HOME` 未设时,**会话从一个基座读、快照与产物库写到另一个基座**。现在快照、
261
+ 版本库与 `verify-install` 都跟随**活动会话基座**;`$DSH_HOME` 已设时行为不变。
262
+ - **不再有任何用户可见文案写死 `~/.dsh`**(设置页原先提示快照存于 `~/.dsh`)。
263
+ - 删掉已无任何引用点的陈旧 peer 声明(`@deepseek-ai/dsh-home-paths`、
264
+ `@deepseek-ai/dsh-client-runtime`)。
265
+
266
+ ### 升级
267
+
268
+ ```bash
269
+ dsh plugin --profile desktop add dsh-retrace@0.4.26
270
+ # 然后重启 DSH —— 插件不会热重载
271
+ ```
272
+
273
+ 如果**升级后 App 起不来**:一个插件失败就能拖垮整棵树,所以**先恢复、再排查**:
274
+
275
+ 1. 把 `dsh-retrace` 从 profile 的 `dsh.profile.bundles` **和** `dependencies` 里删掉,重启,
276
+ 确认能先进得来;
277
+ 2. 读宿主日志 —— macOS:`~/Library/Application Support/DSH Desktop/logs/host/dsh-<日期>.error.log`;
278
+ 3. `plugin tree failed to load` 是**宿主侧**;`renderer boot failed` 是**客户端侧**。
279
+ 两者都会点名出问题的插件/包 —— 从那里查起。
280
+
281
+ ### 版本固定建议
282
+
283
+ 请固定到确切版本(`dsh-retrace@0.4.26`),并让 `dsh-log-contract` 解析到 `>=0.3.12`。
284
+ **不要在跨宿主升级时依赖 `^0.4` 这种范围**:这里的兼容性由**宿主的面**决定,光看 semver 不够。
285
+
286
+ ---
287
+
224
288
  ## ⚠️ 要求与限制
225
289
 
226
290
  - 只有**用户消息**可以编辑;撤回同时适用于用户与助手消息。工具结果会随区间一并
@@ -233,6 +297,15 @@ Client 半区会依据包内 `dsh.client` 元数据被自动打包进 Web 客户
233
297
 
234
298
  ---
235
299
 
300
+ ## 🗺️ 路线图
301
+
302
+ **当前已具备(0.4.x):**
303
+
304
+ - 撤回 / 编辑重发 / 重新生成——每次回退都过**三层写前校验**与安全编辑路径(自动停 agent、临时 step 包裹 marker),**不会损坏日志、不会破坏 /compact**。
305
+ - 单会话**版本时间线** + **产物回退**(git 优先 + 快照兜底、干跑预览、跳转对话)。
306
+ - 对话视图内的**分叉图** + **会话谱系**。
307
+ - **实时看门狗**——并发写入第一时间快照日志。
308
+ - 配套 **`dsh-log-contract`**:30+ 条离线契约规则 + 原地修复(`fix --neutralize` / `--clip-crossstep`),能处理会让 /compact 永久失败的会话。
236
309
  **关闭守卫(防误关丢进度)** —— 退出/重载前先看清还有什么在跑:
237
310
 
238
311
  | | 是什么 | |
@@ -246,19 +319,7 @@ Client 半区会依据包内 `dsh.client` 元数据被自动打包进 Web 客户
246
319
  > 桌面说明:Electron 宿主退出时销毁窗口,页面 `beforeunload` 不会触发,宿主也未暴露
247
320
  > 插件可用的退出否决点——桌面侧由运行中横幅 + dispose 提示覆盖;Web 端拦截完整生效。
248
321
 
249
- ---
250
-
251
- ## 🗺️ 路线图
252
-
253
- **当前已具备(0.4.x):**
254
-
255
- - 撤回 / 编辑重发 / 重新生成——每次回退都过**三层写前校验**与安全编辑路径(自动停 agent、临时 step 包裹 marker),**不会损坏日志、不会破坏 /compact**。
256
- - 单会话**版本时间线** + **产物回退**(git 优先 + 快照兜底、干跑预览、跳转对话)。
257
- - 对话视图内的**分叉图** + **会话谱系**。
258
- - **实时看门狗**——并发写入第一时间快照日志。
259
- - 配套 **`dsh-log-contract`**:30+ 条离线契约规则 + 原地修复(`fix --neutralize` / `--clip-crossstep`),能处理会让 /compact 永久失败的会话。
260
-
261
- **未来计划**——见 [公开路线图](./docs/ROADMAP.md)(agent 业务层规划:运行时守护、中断治理、生态开放接口)。本 README 只描述已上线的能力。
322
+ **未来计划**——见 [公开路线图](https://github.com/yamingmou/dsh-retrace/blob/main/docs/ROADMAP.md)(agent 业务层规划:运行时守护、中断治理、生态开放接口)。本 README 只描述已上线的能力。
262
323
 
263
324
  ---
264
325
 
@@ -296,7 +357,7 @@ npm pack --dry-run # 校验发布文件清单
296
357
  > 发布版共用同一份 client 源码,仅通过 `__setMessageEditorWire` 切换传输层
297
358
  > (`host.call` vs HTTP 路由)。
298
359
 
299
- 欢迎提交 PR 与 issue —— 见 [CONTRIBUTING](./CONTRIBUTING.md)(筹备中)与
360
+ 欢迎提交 PR 与 issue —— `CONTRIBUTING.md` 筹备中,先与
300
361
  [问题追踪](https://github.com/yamingmou/dsh-retrace/issues)。
301
362
 
302
363
  ---
@@ -305,7 +366,7 @@ npm pack --dry-run # 校验发布文件清单
305
366
 
306
367
  收录于 [dsh-plugin topic](https://github.com/topics/dsh-plugin)。
307
368
 
308
- **Agent 业务层(生产级保证)** 的一部分——见 [公开路线图](./docs/ROADMAP.md)
369
+ **Agent 业务层(生产级保证)** 的一部分——见 [公开路线图](https://github.com/yamingmou/dsh-retrace/blob/main/docs/ROADMAP.md)
309
370
  (框架无关的业务层定义,dsh-retrace 是它在 DeepSeek Harness 上的实现)。配套组件:
310
371
 
311
372
  - [**dsh-log-contract**](https://github.com/yamingmou/dsh-log-contract) —— 业务层的
@@ -317,7 +378,7 @@ npm pack --dry-run # 校验发布文件清单
317
378
  > ```sh
318
379
  > dsh plugin --profile desktop add github:yamingmou/dsh-retrace
319
380
  > # 或直接用 pnpm 装进 profile:
320
- > cd ~/.dsh/profiles/desktop && pnpm add github:yamingmou/dsh-retrace
381
+ > cd "$DSH_HOME/profiles/desktop" && pnpm add github:yamingmou/dsh-retrace
321
382
  > ```
322
383
  >
323
384
  > 然后照常重启 DSH Desktop。`dsh-log-contract` 依赖会自动带上。
@@ -354,7 +415,7 @@ retrace file-diff <session> <path> 0 5 # 两版本行级 diff(A3)
354
415
  retrace lineage <session> # 会话 parent 链谱系(A4)
355
416
  ```
356
417
 
357
- <session> 为完整日志路径或 sessionId(自动在 ~/.dsh/sessions 查找)。全部只读。
418
+ <session> 为完整日志路径或 sessionId(自动在**活动会话基座**查找:`$DSH_HOME/sessions`,否则更新的基座,最后才是 `~/.dsh/sessions`)。全部只读。
358
419
 
359
420
  **分叉图里的会话谱系(A4, UI)**:Fork map 视图头部展示当前会话的
360
421
  `parentSession` 接续链(当前会话 → 父 → 根,`←` 方向)。数据来自
package/bin/retrace.mjs CHANGED
@@ -17,7 +17,7 @@
17
17
  * retrace lineage <session> [--json]
18
18
  * 会话 parent 链谱系(A4,分叉图数据源)
19
19
  *
20
- * <session> 为完整文件路径或 sessionId(自动在 ~/.dsh/sessions 查找)。
20
+ * <session> 为完整文件路径或 sessionId(按 sessions 基座候选查找:$DSH_HOME/~/.dsh/~/dsh-v3)。
21
21
  */
22
22
  import fs from 'node:fs';
23
23
  import { loadSessionLog, extractToolOutputs, auditToolCalls } from 'dsh-log-contract';