dsh-retrace 0.4.32 โ 0.4.131
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 +217 -217
- package/README.zh.md +185 -140
- package/bin/retrace.mjs +1 -1
- package/lib/adapter/contract.js +13 -12
- package/lib/adapter/dsh-writer.js +28 -11
- package/lib/adapter/dsh.js +188 -24
- package/lib/archaeology-cli.js +3 -3
- package/lib/artifact-store.js +1 -1
- package/lib/badge.js +8 -8
- package/lib/boot-pin.js +1 -1
- package/lib/boundary-derive.js +3 -3
- package/lib/boundary-now.js +1 -1
- package/lib/boundary-tree.js +1 -1
- package/lib/boundary-what.js +1 -1
- package/lib/client.bundle.js +1301 -101
- package/lib/client.js +1593 -175
- package/lib/close-guard-client.js +36 -20
- package/lib/close-guard.js +22 -22
- package/lib/dynamic-client.js +1301 -101
- package/lib/dynamic-host.js +1110 -75
- package/lib/forkmap.js +2 -2
- package/lib/git-adapter.js +1 -1
- package/lib/host-compat.js +2 -2
- package/lib/host-core.js +1044 -33
- package/lib/http.js +28 -18
- package/lib/identity/shortcode.js +35 -35
- package/lib/identity/sidebar-badge.js +244 -0
- package/lib/index.js +343 -48
- package/lib/interrupt-guard.js +2 -2
- package/lib/marker-carrier.js +24 -18
- package/lib/message-list.js +2 -2
- package/lib/migration-traces.js +4 -4
- package/lib/platform/session-paths.js +100 -49
- package/lib/prewrite-guard.js +173 -40
- package/lib/projection/forkmap.js +1 -1
- package/lib/projection/versions.js +1 -1
- package/lib/rollback.js +710 -10
- package/lib/summary-gate.js +2 -2
- package/lib/summary-store.js +1 -1
- package/lib/version-index.js +9 -4
- package/lib/versioning.js +4 -4
- package/lib/watchdog.js +12 -12
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -2,21 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
# ๐งญ dsh-retrace
|
|
4
4
|
|
|
5
|
-
**Recall ยท
|
|
5
|
+
**Recall ยท Resend (restart from here) ยท Regenerate**, plus **write-safe** in-conversation
|
|
6
6
|
versioning โ the **Agent business layer (production-grade guarantees)** for
|
|
7
7
|
DeepSeek Harness.
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/dsh-retrace)
|
|
10
10
|
[](https://www.npmjs.com/package/dsh-retrace)
|
|
11
|
-
[](https://
|
|
11
|
+
[](https://www.npmjs.com/package/dsh-retrace)
|
|
12
12
|
[](https://github.com/topics/dsh-plugin)
|
|
13
|
-
[](https://github.com/yamingmou/dsh-retrace/pulls)
|
|
14
13
|
|
|
15
14
|
**English** ยท [็ฎไฝไธญๆ](./README.zh.md)
|
|
16
15
|
|
|
17
16
|
</div>
|
|
18
17
|
|
|
19
|
-
**Recall /
|
|
18
|
+
**Recall / resend / regenerate** โ the three moves every conversation
|
|
20
19
|
deserves. But rewinding is not just "delete a message": DeepSeek Harness stores
|
|
21
20
|
conversations in an append-only event log, so a recall only rewinds the context
|
|
22
21
|
while changed **artifact files stay changed**. dsh-retrace versions the
|
|
@@ -27,8 +26,8 @@ it cannot dirty the log, and new markers create **no token-meter pairing debt**
|
|
|
27
26
|
> โ ๏ธ **Honest scope (matches the companion contract's own note)**: sessions that
|
|
28
27
|
> already contain **legacy single-segment markers** โ written by older versions โ
|
|
29
28
|
> are **known design debt**. Before `/compact`, run the companion `check` and
|
|
30
|
-
> clean them up (`fix --remove-markers`); otherwise the host's own
|
|
31
|
-
> blocks compaction. New rewinds do not add to that debt.
|
|
29
|
+
> clean them up (`fix --remove-markers`); otherwise the host's own pre-compaction
|
|
30
|
+
> self-check blocks compaction. New rewinds do not add to that debt.
|
|
32
31
|
|
|
33
32
|
> ๐ก๏ธ **Write safety** ยท ๐ **Deep offline checks** ยท ๐ **Detect โ repair โ guard** โ see below.
|
|
34
33
|
|
|
@@ -41,8 +40,8 @@ it cannot dirty the log, and new markers create **no token-meter pairing debt**
|
|
|
41
40
|
```sh
|
|
42
41
|
dsh plugin --profile desktop add dsh-retrace # DSH Desktop
|
|
43
42
|
# or Web: dsh plugin --profile web add dsh-retrace
|
|
44
|
-
# or
|
|
45
|
-
#
|
|
43
|
+
# or from an unpacked checkout / ZIP:
|
|
44
|
+
# dsh plugin --profile desktop add ~/plugins/dsh-retrace
|
|
46
45
|
```
|
|
47
46
|
|
|
48
47
|
**No command line?** Install the community plugin market once, then find
|
|
@@ -61,7 +60,7 @@ Full steps in [๐ฆ Installation](#-installation).
|
|
|
61
60
|
|
|
62
61
|
| | Capability | What it means |
|
|
63
62
|
|---|---|---|
|
|
64
|
-
| ๐ก๏ธ | **Write safety** | Every rewind passes a three-layer pre-write contract guard; running
|
|
63
|
+
| ๐ก๏ธ | **Write safety** | Every rewind passes a three-layer pre-write contract guard; a running agent is stopped automatically (official `cancel`/`whenIdle`, with a bounded wait); turn-interval markers are wrapped in a temporary step โ **new rewinds cannot dirty the log and add no token-meter pairing debt**; **legacy single-segment markers are known debt** (run the companion `check` + `fix --remove-markers` before `/compact`) |
|
|
65
64
|
| ๐ | **Deep offline checks** | Companion `dsh-log-contract` ships 30+ contract rules (token-meter pairing / cross-step references / physical order / inbox replay), validated against real corrupted-session fixtures โ it finds the class of problem that makes /compact permanently fail |
|
|
66
65
|
| ๐ | **Detect โ repair โ guard** | A watchdog snapshots the log at the first sign of concurrent writes; offline `fix` neutralizes problem markers and clips cross-step references in place; pre-write validation stops bad events before they land |
|
|
67
66
|
|
|
@@ -71,18 +70,33 @@ Full steps in [๐ฆ Installation](#-installation).
|
|
|
71
70
|
|
|
72
71
|
| Action | Where | What happens |
|
|
73
72
|
| --- | --- | --- |
|
|
74
|
-
| **โฉ Recall** | hover any assistant reply, or the row under any user message |
|
|
75
|
-
| **โ
|
|
73
|
+
| **โฉ Recall** | hover any assistant reply, or the row under any user message | Opens a **confirmation first** (destructive: this message and everything after it leaves the conversation). On confirm, if a turn is still running the plugin **pauses it first** through the official stop entry and waits (bounded); if the pause fails or times out the recall still continues, with a visible notice. The whole exchange round (the input **and** the agent's output, tool rows included) is then removed from both the model context and the conversation view; the input text is echoed into the composer so you can re-ask or re-edit immediately. A transient notice marks the rewind and disappears once you keep typing. |
|
|
74
|
+
| **โ Resend (restart from here)** | row under any user message | The primary meaning is **restart from this message**: this round and everything after it is rewound and the new text is re-sent, so the conversation continues on a new timeline branch. If that range is refused by the guard (too large to rewrite in place), the error offers a fallback action โ **Change this text only (keep what follows)**. A collapsed **"original input"** reference sits right under the new message โ click to expand, configurable off. |
|
|
76
75
|
| **โป Regenerate** | hover any assistant reply | The reply (and everything after it) is rewound and hidden, then the original prompt is re-sent so the agent answers again. |
|
|
77
76
|
|
|
78
|
-
|
|
77
|
+
### Undoing a recall
|
|
78
|
+
|
|
79
|
+
| Action | Scope | What happens |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| โบ **Restore display** | view only | Puts the rows hidden by that marker back into the conversation view. The log only grows, the model context is unchanged, and the recall trace stays. Available on the marker row. |
|
|
82
|
+
| โช **True restore** | log level | Neutralizes the two marker segments **in place** (sequence numbers are not renumbered), so the recalled range returns to the **model surface** and its edit / resend / recall actions work again. Requires an idle session that is not the one you are currently viewing; once it succeeds, switch away and reopen that session (or refresh the page) to see the restored content. Refusals are explained with a next step (session running / held by another process / already re-shadowed by a later marker / host does not provide the capability). |
|
|
83
|
+
|
|
84
|
+
**Checkpoints & rollback (live in 0.4.x)** โ every rewind is also recorded as a **checkpoint**:
|
|
79
85
|
|
|
80
86
|
| | What | |
|
|
81
87
|
|---|---|---|
|
|
82
|
-
| ๐ | **
|
|
83
|
-
| โฉ๏ธ | **Artifact rollback** | **context-only / artifacts-only / both** with dry-run preview; git-first + content-addressed snapshot fallback;
|
|
84
|
-
| ๐งญ | **Jump-to-conversation** | one click from a
|
|
85
|
-
| ๐งน | **Bounded storage** | snapshots keep the most recent N
|
|
88
|
+
| ๐ | **Checkpoints** | a **Checkpoints** tab in the conversation view: every change (type, time, message count, file-change badges), pushed live via `session/projection` (no polling), windowed for long histories; nested history is browsable outline-style |
|
|
89
|
+
| โฉ๏ธ | **Artifact rollback** | **context-only / artifacts-only / both** with dry-run preview; git-first + content-addressed snapshot fallback; a rollback is itself a new checkpoint (`restore`). Preview refuses while the agent is running (so "preview says yes, confirm says no" cannot happen), and preview/rollback failures are shown in the UI instead of failing silently |
|
|
90
|
+
| ๐งญ | **Jump-to-conversation** | one click from a checkpoint to that point in the conversation (auto-loads history, anchor highlight); when the point is out of the auto-load budget the reason is reported instead of nothing happening |
|
|
91
|
+
| ๐งน | **Bounded storage** | snapshots keep the most recent N checkpoints (default 50); throttled background sweep prunes truncated ones |
|
|
92
|
+
|
|
93
|
+
> **Rollback on a damaged log** is not silent: the host's strict replay is the
|
|
94
|
+
> authority, but a single malformed event (for example a `sourceEventSeqs` row
|
|
95
|
+
> written by a third party with range encoding) can poison the whole replay. In
|
|
96
|
+
> that case the rollback degrades to a mirror replay, logs both the degradation
|
|
97
|
+
> and the original error, and flags the result as `degraded` on the wire; if even
|
|
98
|
+
> the mirror cannot run you get a structured `replay-failed` error, not a generic
|
|
99
|
+
> `internal`.
|
|
86
100
|
|
|
87
101
|
**Why it's different** (the interaction layer โ the guarantees above are the storage layer):
|
|
88
102
|
|
|
@@ -124,14 +138,15 @@ The same result with plain file edits and `pnpm` โ exactly the steps
|
|
|
124
138
|
1. Open the profile manifest (defaults: `<plugin data home>/profiles/desktop`
|
|
125
139
|
on DSH Desktop, `<plugin data home>/profiles/web` for standalone Web โ where
|
|
126
140
|
`<plugin data home>` is `$DSH_HOME` when set, otherwise the **active session
|
|
127
|
-
base
|
|
128
|
-
|
|
141
|
+
base** (`$DSH_HOME` and any base configured through `$DSH_SESSION_BASES`,
|
|
142
|
+
falling back to `~/.dsh`); `~/.dsh/profiles` is only the pre-migration fallback)
|
|
143
|
+
and add **both** the
|
|
129
144
|
dependency and the bundle-layer entry:
|
|
130
145
|
|
|
131
146
|
```json
|
|
132
147
|
{
|
|
133
148
|
"dependencies": {
|
|
134
|
-
"dsh-retrace": "^0.4.
|
|
149
|
+
"dsh-retrace": "^0.4.131"
|
|
135
150
|
},
|
|
136
151
|
"dsh": {
|
|
137
152
|
"profile": {
|
|
@@ -159,9 +174,10 @@ The same result with plain file edits and `pnpm` โ exactly the steps
|
|
|
159
174
|
For local development, point the dependency at a checkout instead of the
|
|
160
175
|
registry: `"dsh-retrace": "file:/path/to/dsh-retrace"` โ or let
|
|
161
176
|
`dsh` do it: `dsh plugin --profile <name> add /path/to/dsh-retrace`.
|
|
162
|
-
For the latest
|
|
163
|
-
|
|
164
|
-
|
|
177
|
+
For the latest commit without a release, add a git dependency using the repository
|
|
178
|
+
URL from this package's `repository` field (pnpm syntax
|
|
179
|
+
`"dsh-retrace": "github:<owner>/dsh-retrace"`) in the same `dependencies` block,
|
|
180
|
+
then `pnpm install`.
|
|
165
181
|
|
|
166
182
|
### 3. npm package + composition (classic)
|
|
167
183
|
|
|
@@ -194,6 +210,66 @@ The dynamic host registers the same operations behind the package-private
|
|
|
194
210
|
`harness.handle` RPC (`retrace.recall` / `retrace.editAndResend` /
|
|
195
211
|
`retrace.regenerate`).
|
|
196
212
|
|
|
213
|
+
### 5. Requirements
|
|
214
|
+
|
|
215
|
+
- DeepSeek Harness providing the peer packages listed in `package.json`
|
|
216
|
+
(`@deepseek-ai/dsh-session`, `@deepseek-ai/dsh-client-*`, `cordis`, `react` โ the
|
|
217
|
+
current build targets the `0.1.7-rc.2` generation).
|
|
218
|
+
- `dsh-log-contract >= 0.3.12` (installed automatically as a dependency).
|
|
219
|
+
- The plugin declares the host capabilities it needs via `inject`
|
|
220
|
+
(`sessions`, `agents`, `webServer`, `fs`, `subprocess`, `sandboxPolicy`, `jobs`).
|
|
221
|
+
A missing capability degrades the corresponding feature (for example, no
|
|
222
|
+
`subprocess` โ rollback uses the built-in snapshots only); it does not block boot.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## ๐ Close guard (don't lose work by accident)
|
|
227
|
+
|
|
228
|
+
Before you exit or reload, know what is still running:
|
|
229
|
+
|
|
230
|
+
| | What | |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| ๐ก๏ธ | **Running-work detection** | every session is scanned for live work: agent running, queued inbox items, background jobs, unclosed turns |
|
|
233
|
+
| ๐ | **Running banner** | sessions with live work show a persistent in-page banner (short session code + reasons), so you can see it before quitting |
|
|
234
|
+
| โ ๏ธ | **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 |
|
|
235
|
+
| ๐ | **Page-close interception** | The desktop never arms the host's native confirm dialog; it 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 off "Exit confirmation (close guard)" recovers immediately (**no restart**) |
|
|
236
|
+
| ๐ | **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`) |
|
|
237
|
+
|
|
238
|
+
> **How the close confirm behaves (user-visible)**: whether the host pops its own
|
|
239
|
+
> **native** quit confirmation is decided **jointly by the host and the page**. On a
|
|
240
|
+
> desktop shell the plugin does **not** use the host's native dialog โ it draws its
|
|
241
|
+
> **own in-page confirmation** (Esc cancels). If the page cannot draw that box, the
|
|
242
|
+
> box is not visible, or the page itself is hidden, the close is let through
|
|
243
|
+
> immediately. Turning off "Exit confirmation (close guard)" reverts to the host's
|
|
244
|
+
> default behaviour (no restart needed).
|
|
245
|
+
>
|
|
246
|
+
> **Known limitation (stated plainly)**: some desktop shells route quit **around
|
|
247
|
+
> the page entirely** (e.g. the tray item destroys the window directly, or the X
|
|
248
|
+
> button merely hides the window) โ on those shells a plugin **cannot** show a
|
|
249
|
+
> confirm dialog at quit time; it **needs a shell seam** (a `will-prevent-unload`
|
|
250
|
+
> handler, or a pre-quit prompt hook). If the app cannot quit, turning off "Exit
|
|
251
|
+
> confirmation (close guard)" recovers immediately (no restart).
|
|
252
|
+
|
|
253
|
+
### Session badge & display name (**planned / off by default**)
|
|
254
|
+
|
|
255
|
+
Badges are for human collaboration; **identity is still decided by the session id**.
|
|
256
|
+
The transports (`sessionBadge` / `setBadgeTitle` / `initBadgeTitles` / `badgeMap`,
|
|
257
|
+
on both HTTP and harness), the resolver and the write guards are **in place**, but
|
|
258
|
+
the **automatic startup path that writes titles is off by default** (the client
|
|
259
|
+
gate is `globalThis.__DSH_RETRACE_BADGE_BOOTSTRAP = true`):
|
|
260
|
+
|
|
261
|
+
- A session title renders as `[badge] original title` โ the **stable identifier is
|
|
262
|
+
derived deterministically from the session id**, so a title change never changes it.
|
|
263
|
+
- Sessions with **no `session/title` event are left blank on purpose** (the title shows just
|
|
264
|
+
`[badge]`) โ **no project-name fallback, no fabricated name**.
|
|
265
|
+
- If the resolver is unavailable it falls back to the **raw session id as a placeholder**.
|
|
266
|
+
- The sidebar row, the running banner and the checkpoint view share **one source**: the same badge
|
|
267
|
+
map the host hands down (host op `badgeMap`).
|
|
268
|
+
|
|
269
|
+
> **Planned (not shipped)**: the agent business-layer plan (runtime guard,
|
|
270
|
+
> interruption governance, ecosystem-facing interfaces) is a **plan**, not a
|
|
271
|
+
> shipped capability.
|
|
272
|
+
|
|
197
273
|
---
|
|
198
274
|
|
|
199
275
|
## โ๏ธ Settings โ General
|
|
@@ -201,11 +277,18 @@ The dynamic host registers the same operations behind the package-private
|
|
|
201
277
|
| Setting | Default | Description |
|
|
202
278
|
| --- | --- | --- |
|
|
203
279
|
| **Show the original input after editing** | on | A collapsed "original input" reference under the re-sent message showing the **most recent** replaced text (reference only โ never sent to the model). |
|
|
204
|
-
| **
|
|
205
|
-
| **
|
|
206
|
-
| **
|
|
207
|
-
| **
|
|
208
|
-
| **
|
|
280
|
+
| **Checkpoints & artifact snapshots** | on | On: every recall/edit records a checkpoint (messages and touched files) powering the checkpoint list and artifact rollback. Off: only rewinds context โ no checkpoint records, no artifact tracking (lightest). |
|
|
281
|
+
| **Summarize old content with AI** | off | On: each recall/edit makes one small model call to summarize the discarded old content. Requires the host `llm` service (the official `@deepseek-ai/dsh-llm`); absent โ it degrades to **verbatim excerpts only**. Off: **zero LLM calls**; the verbatim excerpt (zero token cost) is **always recorded**. At most one small call per operation (input text capped at 500 characters, โค200 output tokens, 5 s timeout); model and credentials follow the session's own default selection, and the plugin adds **no configuration surface of its own**. |
|
|
282
|
+
| **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 checkpoint view. Off: built-in snapshots under the plugin data home only โ the plugin never touches the workspace git state; features are equivalent. |
|
|
283
|
+
| **Checkpoint retention limit** | 50 | File snapshots are kept for the most recent N checkpoints; older ones are pruned automatically (checkpoint records and the audit trail are always kept). |
|
|
284
|
+
| **Exit confirmation (close guard)** | off | On: confirm before the page closes when sessions have running or unfinished work (the plugin draws its own dialog; Esc cancels). Off by default because the gate can also intercept an in-app reload โ see [๐ Close guard](#-close-guard-dont-lose-work-by-accident) for the desktop details and the self-recovery note. |
|
|
285
|
+
|
|
286
|
+
The two toggles "hide shadowed messages per marker" and "start a fresh
|
|
287
|
+
conversation after editing" no longer exist: recall/resend/regenerate always hide
|
|
288
|
+
what they replaced (a single marker that would hide more than 40% of the
|
|
289
|
+
conversation degrades to notice-only, so history never silently vanishes), and
|
|
290
|
+
resend always uses the "restart from here" semantics with the fallback action
|
|
291
|
+
described above.
|
|
209
292
|
|
|
210
293
|
---
|
|
211
294
|
|
|
@@ -228,19 +311,20 @@ The dynamic host registers the same operations behind the package-private
|
|
|
228
311
|
`[message โฆ last surface node]`, and appends one replacement
|
|
229
312
|
`assistant/message` with an **empty** body โ a valid surface node that
|
|
230
313
|
derives to *no* model message, so the LLM context simply rewinds.
|
|
231
|
-
2. **
|
|
314
|
+
2. **Resend / regenerate** additionally call `agent.followup(...)` with the
|
|
232
315
|
(new) prompt text; the agent's next turn builds its request from the
|
|
233
316
|
rewound `session.deriveMessages()`.
|
|
234
317
|
3. **Client** (`lib/client.js`) registers:
|
|
235
318
|
- a `user-actions` conversation node under every user message
|
|
236
|
-
(
|
|
319
|
+
(a resend/recall row with an inline editor); recall echoes the text into the
|
|
237
320
|
composer,
|
|
238
321
|
- the `recall-marker` node renderer: a notice row that injects CSS hiding
|
|
239
322
|
every shadowed message row from the flow (view and model context stay in
|
|
240
|
-
sync), plus the optional original-input comparison block,
|
|
323
|
+
sync), plus the optional original-input comparison block, the
|
|
324
|
+
**Restore display** button and the **True restore** action,
|
|
241
325
|
- the `retrace` entry in the `conversation.chat.assistant-actions`
|
|
242
326
|
strip (recall / regenerate),
|
|
243
|
-
-
|
|
327
|
+
- the **Checkpoints** view and the preference toggles under Settings โ General.
|
|
244
328
|
|
|
245
329
|
> Two different layers are at play: the **durable transcript** (append-only; old
|
|
246
330
|
> events are never rewritten or deleted) and the **model-visible surface** (rewound
|
|
@@ -248,7 +332,8 @@ The dynamic host registers the same operations behind the package-private
|
|
|
248
332
|
> trail โ but they are **synchronized out of both the model context and the visible
|
|
249
333
|
> conversation**, and the view always reflects what the agent actually sees.
|
|
250
334
|
> Persistence, projections and the transcript remain consistent because the plugin
|
|
251
|
-
> only appends valid, typed session events.
|
|
335
|
+
> only appends valid, typed session events. (**True restore** is the one operation
|
|
336
|
+
> that does edit the log, and it does so in place, without renumbering sequences.)
|
|
252
337
|
|
|
253
338
|
---
|
|
254
339
|
|
|
@@ -262,114 +347,93 @@ the symptom is usually a failed boot, not a wrong-looking feature.
|
|
|
262
347
|
This section exists so you can tell **host-side breakage** from **plugin-side bugs**.
|
|
263
348
|
Read it before filing an issue.
|
|
264
349
|
|
|
265
|
-
### Host-side breaking changes
|
|
350
|
+
### Host-side breaking changes this plugin adapts to โ *not caused by this plugin*
|
|
266
351
|
|
|
267
|
-
1. **`@deepseek-ai/dsh-session`
|
|
352
|
+
1. **`@deepseek-ai/dsh-session` removed the `Session.events` member** (in `0.1.5-rc.1`).
|
|
353
|
+
The supported readers are `snapshotEvents(fromSeq, toSeqExclusive)` (frozen,
|
|
354
|
+
sequence-indexed), `eventAt(seq)`, `ownEvents()` and `isOwnSeq(seq)`. Before the
|
|
355
|
+
fix, recall and edit did nothing and surfaced
|
|
356
|
+
`TypeError: Cannot read properties of undefined (reading 'length')`. The plugin
|
|
357
|
+
now reaches the log through a compatibility accessor that prefers the new API and
|
|
358
|
+
falls back to the old array, so both host generations work.
|
|
359
|
+
2. **`@deepseek-ai/dsh-session` dropped `decodeStorageRecord` from its public export
|
|
360
|
+
surface** (in `0.1.5-rc.1`; the function still exists internally but is no longer
|
|
361
|
+
exported from the package root and is unreachable via the exports map).
|
|
268
362
|
`dsh-retrace` itself never imported it, but its dependency `dsh-log-contract` did.
|
|
269
363
|
With no such export the loader aborts with
|
|
270
364
|
`plugin tree failed to load โฆ does not provide an export named 'decodeStorageRecord'`
|
|
271
365
|
and **the whole plugin tree fails to load โ not just this plugin**, so the app does
|
|
272
|
-
not start.
|
|
273
|
-
local compatibility layer instead of the removed host export.
|
|
366
|
+
not start.
|
|
274
367
|
โ **Dependency note:** needs `dsh-log-contract >= 0.3.12`.
|
|
275
|
-
|
|
276
|
-
the legacy client runtime `@deepseek-ai/dsh-client-runtime`, which has been removed
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
its fiber stays **pending**, which the host reports as
|
|
368
|
+
3. **A client **service** disappeared: `conversationEvents`** โ it used to be provided by
|
|
369
|
+
the legacy client runtime `@deepseek-ai/dsh-client-runtime`, which has been removed.
|
|
370
|
+
A plugin whose client half still declares that service in `export const inject` never
|
|
371
|
+
becomes ready: its fiber stays **pending**, which the host reports as
|
|
280
372
|
`renderer boot failed (plugins: โฆ): The client Loader did not provide an error message.`
|
|
281
373
|
โ no error text at all, the window does not finish starting, and the only way in is to
|
|
282
|
-
disable the plugin.
|
|
283
|
-
|
|
284
|
-
(`uiConversation`, falling back to the legacy name), so it runs on hosts that provide
|
|
285
|
-
the new service *and* on older hosts that still provide the old one.
|
|
374
|
+
disable the plugin. The plugin drops the service from `inject` and resolves it
|
|
375
|
+
**defensively** in `apply` (`uiConversation`, falling back to the legacy name).
|
|
286
376
|
> Note: declaring a **package** that no longer exists in `dsh.client.inject` is *not*
|
|
287
377
|
> what breaks the boot โ the client loader skips unknown entries silently. The
|
|
288
378
|
> breakage comes from the **service name** the plugin waits for.
|
|
379
|
+
4. **The client-side session store has no `keys()`** (`ctx.sessions`). A plugin that
|
|
380
|
+
enumerates sessions with `keys()` silently sees **zero** of them: no crash, no error,
|
|
381
|
+
just safety warnings that never fire. The plugin prefers the official `list()` and
|
|
382
|
+
falls back to `keys()`; it deliberately does **not** fall back to enumerating service
|
|
383
|
+
fields, because guessing produces a silent empty result as well.
|
|
289
384
|
|
|
290
|
-
>
|
|
291
|
-
>
|
|
292
|
-
> build predate the removal?", not "what did the plugin break?".
|
|
385
|
+
> All four items are **host-side removals**, documented here on purpose: if you hit
|
|
386
|
+
> one of these symptoms right after a host upgrade, the first question is "does this
|
|
387
|
+
> plugin build predate the removal?", not "what did the plugin break?".
|
|
293
388
|
|
|
294
|
-
### Plugin-side fixes
|
|
389
|
+
### Plugin-side fixes (0.4.26 โ 0.4.28)
|
|
295
390
|
|
|
296
391
|
- **Data home and session base are now one source.** The plugin previously resolved its
|
|
297
392
|
own data directory through the host's home resolver (`$DSH_HOME` โ `~/.dsh`), which
|
|
298
|
-
does not know about
|
|
299
|
-
`$DSH_HOME` unset,
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
393
|
+
does not know about extra bases configured through `$DSH_SESSION_BASES`. With
|
|
394
|
+
`$DSH_HOME` unset, sessions were read from one base while snapshots and the artifact
|
|
395
|
+
store were written to another. Snapshots,
|
|
396
|
+
version stores and `verify-install` now follow the **active session base**. When
|
|
397
|
+
`$DSH_HOME` is set, behaviour is unchanged.
|
|
303
398
|
- **No user-visible string hard-codes `~/.dsh` any more** (the settings hint used to say
|
|
304
399
|
snapshots live under `~/.dsh`).
|
|
305
|
-
- Stale peer declarations with no remaining import site removed
|
|
306
|
-
(`@deepseek-ai/dsh-home-paths`, `@deepseek-ai/dsh-client-runtime`).
|
|
307
|
-
|
|
308
|
-
### Host-side breaking changes that `0.4.28` adapts to โ *not caused by this plugin*
|
|
309
|
-
|
|
310
|
-
1. **`@deepseek-ai/dsh-session` removed the `Session.events` member** (in `0.1.5-rc.1`).
|
|
311
|
-
The class has no `events` field and no `events` getter at all any more; the supported
|
|
312
|
-
readers are `snapshotEvents(fromSeq, toSeqExclusive)` (frozen, sequence-indexed),
|
|
313
|
-
`eventAt(seq)`, `ownEvents()` and `isOwnSeq(seq)`. `0.4.28` reaches the log through a
|
|
314
|
-
compatibility accessor that prefers the new API and falls back to the old array, so it
|
|
315
|
-
runs on both host generations.
|
|
316
|
-
**Symptom before the fix:** recall and edit did nothing and surfaced the raw error
|
|
317
|
-
`TypeError: Cannot read properties of undefined (reading 'length')` โ both operations
|
|
318
|
-
start by locating the target message id, and that lookup read the removed member.
|
|
319
|
-
2. **The client-side session store has no `keys()`** (`ctx.sessions`). A plugin that
|
|
320
|
-
enumerates sessions with `keys()` silently sees **zero** of them: no crash, no error,
|
|
321
|
-
just safety warnings that never fire. `0.4.28` prefers the official `list()` and falls
|
|
322
|
-
back to `keys()`; it deliberately does **not** fall back to enumerating service fields,
|
|
323
|
-
because guessing produces a silent empty result as well.
|
|
324
|
-
3. **The client session controller has no title accessor** โ `getTitle` does not exist
|
|
325
|
-
anywhere in `@deepseek-ai/dsh-api-session-controller`, and its `getSnapshot()` carries
|
|
326
|
-
no `title`. See the plugin-side item below: this one used to *overwrite your titles*.
|
|
327
|
-
|
|
328
|
-
### Plugin-side fixes in `0.4.28` (these are ours)
|
|
329
|
-
|
|
330
400
|
- **The edit / recall affordances never appeared at all.** The client half read chat
|
|
331
401
|
nodes from `snapshot.chat.nodes`, a path this host build does not have โ nodes live in
|
|
332
402
|
the `useChat` store (`snapshot.nodes`). Every message-level component threw while
|
|
333
403
|
rendering and was swallowed by the error boundary, so the buttons were missing, while
|
|
334
|
-
the settings entry (which reads no nodes) rendered fine.
|
|
335
|
-
|
|
336
|
-
- **"Jump to message" in the version and fork views did nothing.** It resolved the target
|
|
404
|
+
the settings entry (which reads no nodes) rendered fine.
|
|
405
|
+
- **"Jump to message" in the checkpoint view did nothing.** It resolved the target
|
|
337
406
|
anchor through `store.getSnapshot()?.chat?.nodes`, which is permanently `undefined`
|
|
338
407
|
here. It now resolves through the `useChat` snapshot injected by the view and pages
|
|
339
408
|
with the official `store.loadThrough(seq)`; when the jump cannot complete it reports a
|
|
340
409
|
**diagnosable reason** (renderer warning + host-log line) instead of failing silently.
|
|
341
|
-
- **Assigning a
|
|
342
|
-
`[CODE] <current title>` locally but had no way to read the current title, so the base
|
|
343
|
-
degraded to the session-id prefix (`[XXXXXX] <session-id-prefix>`). Title tagging now goes
|
|
410
|
+
- **Assigning a lineage ID could overwrite your session title.** Title tagging now goes
|
|
344
411
|
through the host route only (`setBadgeTitle`), which reads the current title from the
|
|
345
412
|
session log. Manual renames are unaffected.
|
|
346
|
-
- **Host-side operation failures are logged again** (code + message + stack). They used
|
|
347
|
-
return the message to the UI without a log line, which
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
It now reuses one per-snapshot hide plan: **8.3 ms** and **18.3 ms** respectively, with the
|
|
359
|
-
predicate verified equivalent against the old one.
|
|
413
|
+
- **Host-side operation failures are logged again** (code + message + stack). They used
|
|
414
|
+
to return the message to the UI without a log line, which made this class of bug hard
|
|
415
|
+
to diagnose from outside.
|
|
416
|
+
- **The checkpoint view now explains itself.** It used to show a title plus a row of
|
|
417
|
+
actions with **no sentence anywhere saying what a "checkpoint" is**, and rows printed
|
|
418
|
+
raw node types. It now carries an always-visible concept sentence, a type legend, and
|
|
419
|
+
a plain-language "why" line on every row.
|
|
420
|
+
- **Client hide-lookup no longer rescans per row.** `useSeqHidden` re-scanned the node map
|
|
421
|
+
for every row (measured **346 ms** at 2000 rows / 20 markers, **1568 ms** at 3000/30).
|
|
422
|
+
It now reuses one per-snapshot hide plan: **8.3 ms** and **18.3 ms** respectively, with
|
|
423
|
+
the predicate verified equivalent against the old one.
|
|
424
|
+
|
|
360
425
|
### Upgrading
|
|
361
426
|
|
|
362
427
|
```bash
|
|
363
|
-
dsh plugin --profile desktop add dsh-retrace@0.4.
|
|
428
|
+
dsh plugin --profile desktop add dsh-retrace@0.4.131
|
|
364
429
|
# then restart DSH โ plugins are not hot-reloaded
|
|
365
430
|
```
|
|
366
431
|
|
|
367
|
-
**`0.4.27`
|
|
368
|
-
|
|
369
|
-
`0.4.27` had, plus the two items below.
|
|
432
|
+
**`0.4.27` and `0.4.31` were withdrawn** (briefly published, then recalled and marked
|
|
433
|
+
deprecated on npm). Their replacements carry every fix they had.
|
|
370
434
|
|
|
371
|
-
|
|
372
|
-
re-written, and nothing has to be re-indexed: upgrade, restart, and the
|
|
435
|
+
**No data migration is required.** The session format is unchanged (v3), no session is
|
|
436
|
+
re-written, and nothing has to be re-indexed: upgrade, restart, and the symptoms above
|
|
373
437
|
are gone. If you are on a host that still provides the old members, the compatibility
|
|
374
438
|
accessors keep those paths working โ this build does not drop older hosts.
|
|
375
439
|
|
|
@@ -385,7 +449,7 @@ whole tree down, so recover first and diagnose second:
|
|
|
385
449
|
|
|
386
450
|
### Pinning
|
|
387
451
|
|
|
388
|
-
Pin an exact plugin version (`dsh-retrace@0.4.
|
|
452
|
+
Pin an exact plugin version (`dsh-retrace@0.4.131`) and let `dsh-log-contract` resolve to
|
|
389
453
|
`>=0.3.12`. Do not rely on `^0.4` across a host upgrade: compatibility here is decided by
|
|
390
454
|
the **host surface**, not by semver alone.
|
|
391
455
|
|
|
@@ -393,28 +457,23 @@ the **host surface**, not by semver alone.
|
|
|
393
457
|
|
|
394
458
|
## โ ๏ธ Requirements & limitations
|
|
395
459
|
|
|
396
|
-
- **
|
|
397
|
-
an `llm` service from the host (the official `@deepseek-ai/dsh-llm`, bundled
|
|
398
|
-
with DSH Desktop). The plugin takes it **dynamically** via `ctx.get('llm')`:
|
|
399
|
-
present โ summaries available, absent โ it degrades to **verbatim excerpts
|
|
400
|
-
only**. Install and startup are unaffected either way. Model and credentials
|
|
401
|
-
follow the session's own default selection
|
|
402
|
-
(`agentDefaultModel.currentSelection()`); the plugin adds **no configuration
|
|
403
|
-
surface of its own**. Summaries sit behind a **default-off** switch (at most one
|
|
404
|
-
small call per operation: โค6ร400 chars in, โค200 tokens out, 5 s timeout); when
|
|
405
|
-
it is off there are **zero LLM calls**, while the verbatim excerpt (zero token
|
|
406
|
-
cost) is **always recorded**.
|
|
407
|
-
- Only **user messages** can be edited; recall works on user and assistant
|
|
460
|
+
- Only **user messages** can be re-sent from; recall works on user and assistant
|
|
408
461
|
messages. Tool results are shadowed along with the recalled range but are not
|
|
409
462
|
themselves recall targets.
|
|
410
|
-
-
|
|
411
|
-
|
|
463
|
+
- A recall that targets a running turn **pauses it first** (official stop entry, bounded
|
|
464
|
+
wait). If the pause fails or times out, the recall continues and a notice tells you so.
|
|
465
|
+
True restore and rollback preview require an **idle** session; they are rejected
|
|
412
466
|
otherwise.
|
|
413
|
-
- Recall/
|
|
467
|
+
- Recall/resend operate on the **active model surface**: a message that was
|
|
414
468
|
already compacted away or previously recalled is rejected
|
|
415
|
-
(`target-shadowed`)
|
|
469
|
+
(`target-shadowed`), and a message whose write has not flushed yet is reported as
|
|
470
|
+
`message-pending` ("still being generated") rather than being misreported as gone.
|
|
416
471
|
- Regenerate re-sends only the **text** of the original prompt; prompts that
|
|
417
472
|
carried images fall back to the text-only content.
|
|
473
|
+
- True restore edits the session log **in place** (it neutralizes the two marker
|
|
474
|
+
segments; sequence numbers stay as they are). It refuses to run while the session is
|
|
475
|
+
the one you are viewing, while it is running, or while another process holds the file,
|
|
476
|
+
and you must reopen the session (or refresh) to see the result.
|
|
418
477
|
|
|
419
478
|
---
|
|
420
479
|
|
|
@@ -422,82 +481,22 @@ the **host surface**, not by semver alone.
|
|
|
422
481
|
|
|
423
482
|
**What's in today (0.4.x):**
|
|
424
483
|
|
|
425
|
-
- Recall /
|
|
484
|
+
- Recall / resend / regenerate, each written through a three-layer
|
|
426
485
|
**pre-write contract guard** and a safe-edit path (auto-stop the agent, temp-step
|
|
427
486
|
markers) โ new rewinds do not corrupt the log and add no `/compact` debt;
|
|
428
487
|
**legacy single-segment markers** remain known debt (see the honest note above).
|
|
429
|
-
-
|
|
488
|
+
- **Undo**: Restore display (view) and True restore (log level, in place).
|
|
489
|
+
- In-session **checkpoint timeline** + **artifact rollback** (git-first, snapshot
|
|
430
490
|
fallback, dry-run preview, jump-to-conversation).
|
|
431
|
-
- **
|
|
491
|
+
- **Close guard** (running-work banner, exit notice, page-drawn confirm gate).
|
|
432
492
|
- **Real-time watchdog** โ snapshots the log at the first sign of concurrent writes.
|
|
433
493
|
- Companion **`dsh-log-contract`**: 30+ offline contract rules + in-place repair
|
|
434
494
|
(`fix --neutralize` / `--clip-crossstep`) for sessions that would fail `/compact`.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
| โ ๏ธ | **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** | **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
|
-
| ๐ | **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
|
-
|
|
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).
|
|
452
|
-
>
|
|
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.
|
|
499
|
-
|
|
500
|
-
> Command surface: `retrace.runningState` (host RPC) + `GET|POST /api/plugins/retrace/runningState` (HTTP).
|
|
495
|
+
- Read-only host surfaces for integration: `retrace/versions` and `retrace/forkmap`
|
|
496
|
+
projections, `GET /api/plugins/retrace/{versions,forkmap,lineage,event,surface,doctor,snapshot}`,
|
|
497
|
+
`POST /api/plugins/retrace/{rollback/preview,rollback,git/init}`, plus the badge
|
|
498
|
+
ops (`sessionBadge` / `setBadgeTitle` / `initBadgeTitles` / `badgeMap`) and the
|
|
499
|
+
`retrace` CLI (see below).
|
|
501
500
|
|
|
502
501
|
**What's next** โ the agent business-layer plan (runtime guard, interruption
|
|
503
502
|
governance, ecosystem-facing interfaces) is **not published yet**: it is a plan,
|
|
@@ -526,10 +525,10 @@ cordis.patch.yml # dsh.bundle profile patch layer
|
|
|
526
525
|
```
|
|
527
526
|
|
|
528
527
|
```sh
|
|
529
|
-
pnpm install # install dev dependencies (vitest, esbuild)
|
|
528
|
+
pnpm install # install dev dependencies (vitest, esbuild); Node >= 22.13
|
|
530
529
|
pnpm check # syntax-check sources AND the generated dynamic entries
|
|
531
530
|
pnpm build # regenerate lib/dynamic-*.js + lib/client.bundle.js
|
|
532
|
-
pnpm test # run the
|
|
531
|
+
pnpm test # run the unit tests
|
|
533
532
|
npm pack --dry-run # verify the published file list
|
|
534
533
|
```
|
|
535
534
|
|
|
@@ -541,8 +540,8 @@ npm pack --dry-run # verify the published file list
|
|
|
541
540
|
> one and only swaps the transport (`host.call` vs the HTTP route) via
|
|
542
541
|
> `__setMessageEditorWire`.
|
|
543
542
|
|
|
544
|
-
PRs and issues are welcome โ a `CONTRIBUTING.md` is coming soon
|
|
545
|
-
|
|
543
|
+
PRs and issues are welcome โ a `CONTRIBUTING.md` is coming soon; use the issue
|
|
544
|
+
tracker linked from this package's `repository`/`bugs` metadata.
|
|
546
545
|
|
|
547
546
|
---
|
|
548
547
|
|
|
@@ -554,22 +553,22 @@ Part of the **Agent business layer (production-grade guarantees)** โ the
|
|
|
554
553
|
framework-agnostic layer that dsh-retrace implements on DeepSeek Harness.
|
|
555
554
|
Companion components:
|
|
556
555
|
|
|
557
|
-
- [**dsh-log-contract**](https://
|
|
556
|
+
- [**dsh-log-contract**](https://www.npmjs.com/package/dsh-log-contract) โ the
|
|
558
557
|
business layer's "doctor": 30+ offline contract rules + in-place repair
|
|
559
558
|
(`fix --neutralize` / `--clip-crossstep`). Installed automatically as a
|
|
560
559
|
dependency; also published standalone for direct use.
|
|
561
560
|
|
|
562
|
-
> **Install straight from
|
|
561
|
+
> **Install straight from git** (no npm registry needed โ handy when you
|
|
563
562
|
> hand this repo's link to an AI or want the latest commit):
|
|
564
563
|
>
|
|
565
564
|
> ```sh
|
|
566
|
-
> dsh plugin --profile desktop add github
|
|
565
|
+
> dsh plugin --profile desktop add github:<owner>/dsh-retrace
|
|
567
566
|
> # or with pnpm directly into a profile:
|
|
568
|
-
> cd "$DSH_HOME/profiles/desktop" && pnpm add github
|
|
567
|
+
> cd "$DSH_HOME/profiles/desktop" && pnpm add github:<owner>/dsh-retrace
|
|
569
568
|
> ```
|
|
570
569
|
>
|
|
571
|
-
>
|
|
572
|
-
> pulled in automatically.
|
|
570
|
+
> Substitute the owner from this package's `repository` field. Then restart DSH
|
|
571
|
+
> Desktop as usual โ the `dsh-log-contract` dependency is pulled in automatically.
|
|
573
572
|
|
|
574
573
|
A curated overview of the DeepSeek Harness plugin ecosystem lives at
|
|
575
574
|
[awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
@@ -581,13 +580,13 @@ A curated overview of the DeepSeek Harness plugin ecosystem lives at
|
|
|
581
580
|
|
|
582
581
|
Built by the [OfferKuai](https://www.offerkuai.com) team โ an AI job application
|
|
583
582
|
assistant on a mission that "users need results, not repeated conversations".
|
|
584
|
-
|
|
585
|
-
DeepSeek Harness community.
|
|
583
|
+
This plugin is released as open source for the DeepSeek Harness community.
|
|
586
584
|
|
|
587
585
|
## ๐ License
|
|
588
586
|
|
|
589
587
|
MIT
|
|
590
588
|
|
|
589
|
+
---
|
|
591
590
|
|
|
592
591
|
## ๐งญ Session archaeology (`retrace` CLI)
|
|
593
592
|
|
|
@@ -596,20 +595,21 @@ audit asset. The `retrace` CLI provides read-only archaeology (reusing
|
|
|
596
595
|
dsh-log-contract's contracts and extraction):
|
|
597
596
|
|
|
598
597
|
```sh
|
|
599
|
-
retrace index <session> # tool-call index
|
|
600
|
-
retrace query <session> --cmd "seed-scale" # search outputs by command regex
|
|
601
|
-
retrace extract <session> --pattern "seed-scale" --out ./found # export outputs
|
|
602
|
-
retrace file-history <session> <path> # write/edit history of a file
|
|
603
|
-
retrace file-diff <session> <path> 0 5 # line diff between two versions
|
|
604
|
-
retrace lineage <session> # parent-chain lineage
|
|
598
|
+
retrace index <session> # tool-call index
|
|
599
|
+
retrace query <session> --cmd "seed-scale" # search outputs by command regex
|
|
600
|
+
retrace extract <session> --pattern "seed-scale" --out ./found # export outputs
|
|
601
|
+
retrace file-history <session> <path> # write/edit history of a file
|
|
602
|
+
retrace file-diff <session> <path> 0 5 # line diff between two versions
|
|
603
|
+
retrace lineage <session> # parent-chain lineage
|
|
605
604
|
```
|
|
606
605
|
|
|
607
|
-
`<session>` is a full log path or a sessionId (auto-looked-up under
|
|
608
|
-
|
|
606
|
+
`<session>` is a full log path or a sessionId (auto-looked-up under the active
|
|
607
|
+
session base โ `$DSH_HOME/sessions`, else each `$DSH_SESSION_BASES` base, else
|
|
608
|
+
`~/.dsh/sessions`).
|
|
609
|
+
All read-only.
|
|
609
610
|
|
|
610
|
-
**Session lineage
|
|
611
|
-
current session's `parentSession` chain (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
serves as the fork-topology metadata source.
|
|
611
|
+
**Session lineage**: `GET /api/plugins/retrace/lineage?sessionId=` walks the
|
|
612
|
+
current session's `parentSession` chain (read-only, with cycle protection) โ the
|
|
613
|
+
same semantics as the CLI `retrace lineage` โ so "which session did this one
|
|
614
|
+
continue/fork from" is available to any client, and serves as the fork-topology
|
|
615
|
+
metadata source.
|