dsh-rewind-plugin 0.15.0-beta.2 → 0.15.1
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 +1 -1
- package/lib/client.js +1 -1
- package/package.json +38 -40
- package/CONTRIBUTING.md +0 -73
- package/assets/screenshots/add-plugin.png +0 -0
- package/assets/screenshots/cleanup-setting.png +0 -0
- package/assets/screenshots/impact-list.png +0 -0
- package/assets/screenshots/mode-popover.png +0 -0
- package/assets/screenshots/rewind-button.png +0 -0
- package/assets/screenshots/rewind-candidates.png +0 -0
- package/docs/README.md +0 -33
- package/docs/architecture.md +0 -145
- package/docs/compat/audit.md +0 -170
- package/docs/compat/diagnostics.md +0 -83
- package/docs/compat/diagnostics.zh.md +0 -61
- package/docs/compat/tracking-boundary.md +0 -217
- package/docs/compat/tracking-boundary.zh.md +0 -131
- package/docs/contract/client-contract.md +0 -58
- package/docs/contract/client-contract.zh.md +0 -54
- package/docs/format.md +0 -271
- package/docs/harness-reference.md +0 -75
- package/docs/release/release.md +0 -136
- package/docs/release/release.zh.md +0 -124
- package/docs/rewind-fix.md +0 -89
- package/docs/rewind-fix.zh.md +0 -89
- package/docs/snapshot-auto-cleanup.md +0 -78
- package/docs/snapshot-auto-cleanup.zh.md +0 -48
package/README.md
CHANGED
|
@@ -187,7 +187,7 @@ node scripts/verify-host.mjs # 端到端验证构建产物
|
|
|
187
187
|
npm version patch && git push origin <branch> --tags
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
一次性 npm 侧配置与完整流程:见 [docs/release/release.
|
|
190
|
+
一次性 npm 侧配置与完整流程:见 [docs/release/release.md](docs/release/release.md)。
|
|
191
191
|
|
|
192
192
|
## 许可
|
|
193
193
|
|
package/lib/client.js
CHANGED
|
@@ -1320,7 +1320,7 @@ function createRewindBridge(deps) {
|
|
|
1320
1320
|
}
|
|
1321
1321
|
|
|
1322
1322
|
// src/client/build-info.ts
|
|
1323
|
-
var PLUGIN_VERSION = true ? "0.15.
|
|
1323
|
+
var PLUGIN_VERSION = true ? "0.15.1" : "dev";
|
|
1324
1324
|
var PLUGIN_PACKAGE = "dsh-rewind-plugin";
|
|
1325
1325
|
var BUILD_HASH = true ? "0582c3a6" : "dev";
|
|
1326
1326
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-rewind-plugin",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "In-window conversation rewind with workspace file restore · 同窗口内对话回退并可还原工作区文件",
|
|
5
5
|
"icon": "./assets/icon.svg",
|
|
6
6
|
"keywords": [
|
|
@@ -43,11 +43,9 @@
|
|
|
43
43
|
"README.md",
|
|
44
44
|
"README.en.md",
|
|
45
45
|
"SECURITY.md",
|
|
46
|
-
"CONTRIBUTING.md",
|
|
47
|
-
"docs",
|
|
48
|
-
"assets",
|
|
49
46
|
"LICENSE",
|
|
50
|
-
"locale/*.json"
|
|
47
|
+
"locale/*.json",
|
|
48
|
+
"assets/icon.svg"
|
|
51
49
|
],
|
|
52
50
|
"dsh": {
|
|
53
51
|
"bundle": {
|
|
@@ -145,41 +143,41 @@
|
|
|
145
143
|
"devDependencies": {
|
|
146
144
|
"@deepseek-ai/cordis": "4.0.3",
|
|
147
145
|
"@deepseek-ai/cosmokit": "1.8.4",
|
|
148
|
-
"@deepseek-ai/dsh-agent": "0.2.
|
|
149
|
-
"@deepseek-ai/dsh-api-session-controller": "0.2.
|
|
150
|
-
"@deepseek-ai/dsh-attachment": "0.2.
|
|
151
|
-
"@deepseek-ai/dsh-client-connection": "0.2.
|
|
152
|
-
"@deepseek-ai/dsh-client-locale": "0.2.
|
|
153
|
-
"@deepseek-ai/dsh-client-store": "0.2.
|
|
154
|
-
"@deepseek-ai/dsh-client-ui-commands": "0.2.
|
|
155
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.2.
|
|
156
|
-
"@deepseek-ai/dsh-client-ui-input-trigger": "0.2.
|
|
157
|
-
"@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.
|
|
158
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.2.
|
|
159
|
-
"@deepseek-ai/dsh-client-ui-session": "0.2.
|
|
160
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.2.
|
|
161
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.2.
|
|
162
|
-
"@deepseek-ai/dsh-command-compact": "0.2.
|
|
163
|
-
"@deepseek-ai/dsh-commands": "0.2.
|
|
164
|
-
"@deepseek-ai/dsh-compaction": "0.2.
|
|
165
|
-
"@deepseek-ai/dsh-compaction-basic": "0.2.
|
|
166
|
-
"@deepseek-ai/dsh-compaction-image-offload": "0.2.
|
|
167
|
-
"@deepseek-ai/dsh-fs": "0.2.
|
|
168
|
-
"@deepseek-ai/dsh-goal": "0.2.
|
|
169
|
-
"@deepseek-ai/dsh-home-paths": "0.2.
|
|
170
|
-
"@deepseek-ai/dsh-llm": "0.2.
|
|
171
|
-
"@deepseek-ai/dsh-plan-mode": "0.2.
|
|
172
|
-
"@deepseek-ai/dsh-sandbox": "0.2.
|
|
173
|
-
"@deepseek-ai/dsh-scope": "0.2.
|
|
174
|
-
"@deepseek-ai/dsh-session": "0.2.
|
|
175
|
-
"@deepseek-ai/dsh-session-projection": "0.2.
|
|
176
|
-
"@deepseek-ai/dsh-session-stats": "0.2.
|
|
177
|
-
"@deepseek-ai/dsh-session-title": "0.2.
|
|
178
|
-
"@deepseek-ai/dsh-settings": "0.2.
|
|
179
|
-
"@deepseek-ai/dsh-token-meter": "0.2.
|
|
180
|
-
"@deepseek-ai/dsh-tools": "0.2.
|
|
181
|
-
"@deepseek-ai/dsh-typert-protocol": "0.2.
|
|
182
|
-
"@deepseek-ai/dsh-user-questions": "0.2.
|
|
146
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.1",
|
|
147
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.1",
|
|
148
|
+
"@deepseek-ai/dsh-attachment": "0.2.1-alpha.1",
|
|
149
|
+
"@deepseek-ai/dsh-client-connection": "0.2.1-alpha.1",
|
|
150
|
+
"@deepseek-ai/dsh-client-locale": "0.2.1-alpha.1",
|
|
151
|
+
"@deepseek-ai/dsh-client-store": "0.2.1-alpha.1",
|
|
152
|
+
"@deepseek-ai/dsh-client-ui-commands": "0.2.1-alpha.1",
|
|
153
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.1-alpha.1",
|
|
154
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "0.2.1-alpha.1",
|
|
155
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.1-alpha.1",
|
|
156
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.1",
|
|
157
|
+
"@deepseek-ai/dsh-client-ui-session": "0.2.1-alpha.1",
|
|
158
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.1-alpha.1",
|
|
159
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.1",
|
|
160
|
+
"@deepseek-ai/dsh-command-compact": "0.2.1-alpha.1",
|
|
161
|
+
"@deepseek-ai/dsh-commands": "0.2.1-alpha.1",
|
|
162
|
+
"@deepseek-ai/dsh-compaction": "0.2.1-alpha.1",
|
|
163
|
+
"@deepseek-ai/dsh-compaction-basic": "0.2.1-alpha.1",
|
|
164
|
+
"@deepseek-ai/dsh-compaction-image-offload": "0.2.1-alpha.1",
|
|
165
|
+
"@deepseek-ai/dsh-fs": "0.2.1-alpha.1",
|
|
166
|
+
"@deepseek-ai/dsh-goal": "0.2.1-alpha.1",
|
|
167
|
+
"@deepseek-ai/dsh-home-paths": "0.2.1-alpha.1",
|
|
168
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.1",
|
|
169
|
+
"@deepseek-ai/dsh-plan-mode": "0.2.1-alpha.1",
|
|
170
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.1",
|
|
171
|
+
"@deepseek-ai/dsh-scope": "0.2.1-alpha.1",
|
|
172
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.1",
|
|
173
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.1",
|
|
174
|
+
"@deepseek-ai/dsh-session-stats": "0.2.1-alpha.1",
|
|
175
|
+
"@deepseek-ai/dsh-session-title": "0.2.1-alpha.1",
|
|
176
|
+
"@deepseek-ai/dsh-settings": "0.2.1-alpha.1",
|
|
177
|
+
"@deepseek-ai/dsh-token-meter": "0.2.1-alpha.1",
|
|
178
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.1",
|
|
179
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.1",
|
|
180
|
+
"@deepseek-ai/dsh-user-questions": "0.2.1-alpha.1",
|
|
183
181
|
"@deepseek-ai/schemastery": "3.18.4",
|
|
184
182
|
"@types/node": "^24.0.0",
|
|
185
183
|
"@types/react": "^18.3.31",
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# Contributing
|
|
2
|
-
|
|
3
|
-
Thanks for considering a contribution to dsh-rewind. This file is short on
|
|
4
|
-
purpose: the authoritative spec for how the repo works is `AGENTS.md` (read it
|
|
5
|
-
first), and the docs live under `docs/` with an index in `docs/README.md`.
|
|
6
|
-
|
|
7
|
-
## Project principles
|
|
8
|
-
|
|
9
|
-
- **Focused on purpose** — one thing: in-window rewind to any earlier user
|
|
10
|
-
message, never forking a session.
|
|
11
|
-
- **Security first** — session logs are append-only; file restores draw only from
|
|
12
|
-
the plugin's own backup directory. See `SECURITY.md`.
|
|
13
|
-
- **Minimal** — avoid over-abstraction; keep the plugin light and maintainable.
|
|
14
|
-
|
|
15
|
-
## Prerequisites
|
|
16
|
-
|
|
17
|
-
- Node `^22.19.0 || >=24.0.0` (see `engines` in `package.json`), npm.
|
|
18
|
-
|
|
19
|
-
## Setup and commands
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
npm install # devDeps from the npm registry
|
|
23
|
-
npm run build # esbuild → lib/ (host ESM + client closure + types)
|
|
24
|
-
npm run check # one-shot full gate: typecheck + test + build + verify:host + pack --dry-run
|
|
25
|
-
npm run typecheck # tsc --noEmit (host / client / client-test)
|
|
26
|
-
npm test # vitest: unit + compatibility suites
|
|
27
|
-
npm run verify:host # end-to-end host verification (full check suite)
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
`prepare` runs the build, so `npm pack` / `npm publish` always carry a fresh
|
|
31
|
-
`lib/` and `LICENSE`.
|
|
32
|
-
|
|
33
|
-
## Before you open a PR
|
|
34
|
-
|
|
35
|
-
- **Every change must pass** `npm run check`.
|
|
36
|
-
- Commit messages use conventional commits with **English** subjects
|
|
37
|
-
(`feat` / `fix` / `docs` / `test` / `refactor` / `chore` / …). Code comments
|
|
38
|
-
are written in English.
|
|
39
|
-
- Keep `rewind.ts` pure and `snapshot.ts` host-independent — if a change needs
|
|
40
|
-
I/O or harness types in the planning layer, that is a design smell.
|
|
41
|
-
|
|
42
|
-
## Documentation rules
|
|
43
|
-
|
|
44
|
-
- New/changed behavior that is durable (formats, contracts, compatibility
|
|
45
|
-
findings) must update the relevant doc in the same PR:
|
|
46
|
-
- **`docs/format.md`** — any change to the on-disk format (bump version,
|
|
47
|
-
migrate, or move the state root; no silent re-interpretation).
|
|
48
|
-
- **`docs/contract/client-contract.md`** — any change to the meaning of
|
|
49
|
-
`@<seq>` / `sourceEventSeq` / `data-dsh-rewind-hidden`; breaking a listed
|
|
50
|
-
stability tier is a minor/major version bump.
|
|
51
|
-
- **`docs/compat/audit.md`** — new compatibility findings (it is the single
|
|
52
|
-
source of truth; other docs link to it instead of restating).
|
|
53
|
-
- **`SECURITY.md`** — any change to the security model (trust boundary,
|
|
54
|
-
mutation gates, containment, crash handling).
|
|
55
|
-
- Bilingual docs use the `.md` / `.zh.md` file split; keep the two mirrors in
|
|
56
|
-
sync.
|
|
57
|
-
|
|
58
|
-
## Testing expectations
|
|
59
|
-
|
|
60
|
-
- Pure planning (`rewind.ts`, `hidden.ts`) → unit tests in `tests/`.
|
|
61
|
-
- Store behavior (`snapshot.ts`) → `tests/snapshot.test.ts` plus the focused
|
|
62
|
-
suites (`snapshot-bytes`, `snapshot-mode`, `downgrade-safety`,
|
|
63
|
-
`parent-guard`), and crash-safety scenarios in `tests/crash-safety.test.ts`
|
|
64
|
-
via the test-only `crash` seam (`RestoreRunOptions.crash`).
|
|
65
|
-
- Harness interaction → the compatibility suites
|
|
66
|
-
(`compat-invariants` / `compat-interop` / `compat-gaps`) and
|
|
67
|
-
`scripts/verify-host.mjs`.
|
|
68
|
-
|
|
69
|
-
## Releasing
|
|
70
|
-
|
|
71
|
-
Releases are CI-driven via GitHub Actions Trusted Publishing (OIDC, no stored
|
|
72
|
-
token): push a `v<version>` tag and CI publishes with Sigstore provenance. Full
|
|
73
|
-
details: `docs/release/release.md`.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/README.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Docs
|
|
2
|
-
|
|
3
|
-
Documentation lives under `docs/`, organized by concern. Everything here is
|
|
4
|
-
maintainer-facing unless the purpose column says otherwise. This file is the
|
|
5
|
-
index/navigation entry point.
|
|
6
|
-
|
|
7
|
-
## Index
|
|
8
|
-
|
|
9
|
-
| Path | Purpose | Audience |
|
|
10
|
-
| --- | --- | --- |
|
|
11
|
-
| `architecture.md` | Module layering, rewind/checkpoint pipelines, compatibility strategy, roadmap | maintainers |
|
|
12
|
-
| `format.md` | Durable on-disk format spec (checkpoint entries + restore journals) | maintainers |
|
|
13
|
-
| `harness-reference.md` | DeepSeek Harness interface reference + plugin source layout | maintainers |
|
|
14
|
-
| `snapshot-auto-cleanup.md` | Global snapshot auto-cleanup policy and command (`.zh` mirror) | users / maintainers |
|
|
15
|
-
| `rewind-fix.md` | Historical guide for the removed `/dsh-rewind-fix` (`.zh` mirror) | users / maintainers |
|
|
16
|
-
| `contract/client-contract.md` | Rewind visibility contract for third-party DOM plugins (`.zh` mirror) | integrators |
|
|
17
|
-
| `compat/audit.md` | Compatibility audit: verified surfaces, recorded findings, probe matrix | maintainers |
|
|
18
|
-
| `compat/tracking-boundary.md` | Which files a rewind restores: the tracking boundary (`.zh` mirror) | users / maintainers |
|
|
19
|
-
| `compat/diagnostics.md` | Browser diagnostics (anomaly alerts; verbose switch gates the startup identity line) (`.zh` mirror) | users / maintainers |
|
|
20
|
-
| `release/release.md` | Release workflow & DSH peer-version alignment (`.zh` mirror) | maintainers |
|
|
21
|
-
|
|
22
|
-
Repo-root docs outside `docs/`: `SECURITY.md` (security model) and
|
|
23
|
-
`CONTRIBUTING.md` (contribution guide).
|
|
24
|
-
|
|
25
|
-
## Conventions
|
|
26
|
-
|
|
27
|
-
- Bilingual docs use the `.md` / `.zh.md` file split (e.g. `contract/client-contract.md`
|
|
28
|
-
+ `contract/client-contract.zh.md`); the root pair is the exception — `README.md` is
|
|
29
|
-
Chinese, `README.en.md` English.
|
|
30
|
-
- `compat/audit.md` is the single source of truth for compatibility conclusions;
|
|
31
|
-
other docs link to it instead of restating them.
|
|
32
|
-
- Cross-links are relative so the whole `docs/` directory stays relocatable — and it
|
|
33
|
-
ships intact in the npm tarball via the `docs` entry in `files` (`package.json`).
|
package/docs/architecture.md
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# Architecture
|
|
2
|
-
|
|
3
|
-
How dsh-rewind is built: module layering, the rewind pipeline, the checkpoint
|
|
4
|
-
pipeline, the compatibility strategy, and the roadmap. The module map lives in
|
|
5
|
-
`AGENTS.md`; this document adds the wiring between the modules. The durable
|
|
6
|
-
on-disk format is specified separately in
|
|
7
|
-
`docs/format.md`, the security model in `SECURITY.md`.
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
One thing: rewind a conversation **in place** to any earlier user message —
|
|
12
|
-
never forking a session or switching windows — with an optional Claude-Code-
|
|
13
|
-
style workspace file restore (`both` mode). No network, no git operations, no
|
|
14
|
-
credentials access.
|
|
15
|
-
|
|
16
|
-
## Module layering
|
|
17
|
-
|
|
18
|
-
The module map is the one in `AGENTS.md` (`Layout at a glance`); this document
|
|
19
|
-
adds the wiring between the modules and does not repeat it.
|
|
20
|
-
|
|
21
|
-
Two dependency rules keep the design testable:
|
|
22
|
-
|
|
23
|
-
1. **`rewind.ts` is pure** — no I/O, no `Session` dependency; everything
|
|
24
|
-
derives from the event log + ordered surface, so the whole planning layer
|
|
25
|
-
is unit-testable without a host.
|
|
26
|
-
2. **`snapshot.ts` is host-independent** — it talks to the disk through plain
|
|
27
|
-
`node:fs` plus injected seams (`DiskProbe`, `DeleteFile`, a test-only
|
|
28
|
-
`crash` hook), so the store is testable without the harness.
|
|
29
|
-
|
|
30
|
-
The client never reads the DOM to derive rewind state; it consumes the host's
|
|
31
|
-
machine channels (see [Compatibility strategy](#compatibility-strategy)).
|
|
32
|
-
|
|
33
|
-
## Rewind pipeline
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
↶ button / /rewind @<seq> both
|
|
37
|
-
→ handleRewind: parseRewindTarget + planRewind (target must be a
|
|
38
|
-
user/message currently on the surface)
|
|
39
|
-
→ agent.cancel({ keepInbox: true }) if running; waitForAgentIdle
|
|
40
|
-
→ dropPendingSteering (next-step inbox only; queued messages untouched)
|
|
41
|
-
→ append the rewind marker = a user/message with surfaceOp
|
|
42
|
-
{ op: 'replace', startSeq, endSeq } over every surface node after the
|
|
43
|
-
target (+ sourceEventSeqs = shadowed seqs) [a single event]
|
|
44
|
-
→ if mode 'both': store.restoreAfter(targetSeq) + syncRestoreObservations
|
|
45
|
-
→ result text carries machine tokens (impact=<n>, restore:/delete: lines)
|
|
46
|
-
→ client: hides withdrawn rows (data-dsh-rewind-hidden), refills composer
|
|
47
|
-
with the target message's text
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Key invariants:
|
|
51
|
-
|
|
52
|
-
- **The log is append-only.** The marker is the *only* mutation: it cuts the
|
|
53
|
-
model-visible surface, never the raw history (search/export still see it).
|
|
54
|
-
- **The marker content is a constant `(empty message)` placeholder.** It carries
|
|
55
|
-
the dsh-rewind plugin source and is a `user/message`, the only surface type
|
|
56
|
-
that can cite the shadowed seqs (`sourceEventSeqs`) — `assistant/message` can
|
|
57
|
-
no longer carry them (v2). It derives to itself, so it stays as a present
|
|
58
|
-
user turn at the surface tail. The content is provider-independent and never
|
|
59
|
-
empty: the session log is immutable but the model serving a session may
|
|
60
|
-
change later, and a strict OpenAI-compatible gateway rejects an empty user
|
|
61
|
-
message (HTTP 400, Issue #21).
|
|
62
|
-
- **No ghost step frame is needed**: the token-meter step machine ignores
|
|
63
|
-
`user/message`, and the session invariant imposes no open-turn requirement
|
|
64
|
-
on it, so the marker is appended while idle, outside any turn, as one event.
|
|
65
|
-
- **Restore is reconciled against the live disk** (`planRestore`), so repeated
|
|
66
|
-
rewinds are idempotent and a rewind whose target state already matches is a
|
|
67
|
-
no-op. A path whose directory no longer resolves to its commit-time location
|
|
68
|
-
is skipped and reported, never written through (see `SECURITY.md`).
|
|
69
|
-
|
|
70
|
-
### Marker format history
|
|
71
|
-
|
|
72
|
-
The rewind marker is written as form C (a `user/message` with a
|
|
73
|
-
`surfaceOp.replace` over the shadowed range, `sourceEventSeqs`, and the
|
|
74
|
-
constant `(empty message)` content). Earlier
|
|
75
|
-
plugin versions wrote shapes a newer harness no longer accepts:
|
|
76
|
-
|
|
77
|
-
- **form A** — a bare `assistant/message(turn=N, step=0)` with no frame.
|
|
78
|
-
- **form B** — a ghost turn frame: `[step/start][assistant/message][step/end]`
|
|
79
|
-
inside a closed turn.
|
|
80
|
-
|
|
81
|
-
A/B became unreadable once v2 reserved surface `replace` to a node that cites
|
|
82
|
-
`sourceEventSeqs` (`assistant/message` can no longer carry them). The
|
|
83
|
-
`/dsh-rewind-fix` command rewrote form A/B in closed sessions to form C so a
|
|
84
|
-
newer harness accepts the log — it was a **temporary migration tool** (kept on
|
|
85
|
-
the `0.9.x` line) and is **removed in the `0.10.x` line**.
|
|
86
|
-
|
|
87
|
-
## Checkpoint pipeline (Claude Code before-backup model)
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
tools/execute captureBefore: for write / edit, stage a raw byte copy of
|
|
91
|
-
the file's BEFORE state into the store's .pending/
|
|
92
|
-
(node:fs copyFile, never through a string);
|
|
93
|
-
subagent sessions are NOT tracked and get no rewind
|
|
94
|
-
surface at all (Claude Code alignment — the Harness
|
|
95
|
-
refuses generic Session RPCs for a subagent-owned
|
|
96
|
-
identity, so /rewind cannot execute there).
|
|
97
|
-
tools/post-execute commitEntry: anchor = latest user/message seq; skip
|
|
98
|
-
failed calls; publish the staged bytes as the entry's
|
|
99
|
-
sidecar and write the metadata beside them — including
|
|
100
|
-
where the directory resolved (`realpath`), the location
|
|
101
|
-
pin a restore re-checks.
|
|
102
|
-
session/event user/message boundary: reconcileTracked re-reads every
|
|
103
|
-
(user/message) tracked file and records a new before-backup for any
|
|
104
|
-
whose disk state changed since last seen — external
|
|
105
|
-
edits/deletions enter the record this way.
|
|
106
|
-
prune keeps the newest 100 anchor groups per session, storing
|
|
107
|
-
identical before-content as in-place links that are
|
|
108
|
-
materialized before their group is dropped, never dropping
|
|
109
|
-
a group a non-terminal restore journal still references,
|
|
110
|
-
and recycles terminal restore journals.
|
|
111
|
-
pruneStale cross-session auto-cleanup (default off): whole
|
|
112
|
-
long-inactive session dirs past the cutoff are removed;
|
|
113
|
-
the active session is never targeted (a subagent's tool
|
|
114
|
-
result never triggers the sweep — it owns no dir and would
|
|
115
|
-
claim that exemption).
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## Compatibility strategy
|
|
119
|
-
|
|
120
|
-
- **Peer ranges as one tuple per DSH line**: npm's prerelease rules require the
|
|
121
|
-
peer range to share the host's `[major, minor, patch]` tuple, so a new DSH
|
|
122
|
-
tuple replaces the peer tuple (the single-line model) and `package.json` is
|
|
123
|
-
the only place the current tuple is written down; `scripts/check-dsh-version.mjs`
|
|
124
|
-
flags when a new tuple arrives. See `docs/release/release.md`.
|
|
125
|
-
- **Test-driven investigation**: `tests/compat-invariants.test.ts` /
|
|
126
|
-
`compat-interop.test.ts` / `compat-gaps.test.ts` probe harness behavior and pin findings in
|
|
127
|
-
`docs/compat/audit.md`; `scripts/verify-host.mjs` runs a real end-to-end
|
|
128
|
-
rewind + `/compact` chain (the full check suite).
|
|
129
|
-
- **Stable machine channels for third parties**: `dsh-rewind-plugin/client`
|
|
130
|
-
exports the pure hidden-span computation; withdrawn rows carry
|
|
131
|
-
`data-dsh-rewind-hidden`; both are semver-protected
|
|
132
|
-
(`docs/contract/client-contract.md`). DOM coupling is minimized to a small
|
|
133
|
-
set of marked rows plus a structurally-typed slot registration.
|
|
134
|
-
|
|
135
|
-
## Roadmap
|
|
136
|
-
|
|
137
|
-
Ideas under consideration, not commitments:
|
|
138
|
-
|
|
139
|
-
- **Multi-process identity/lock**: the current in-flight guard is
|
|
140
|
-
per-process; a cross-process exclusive lock (like the change-ledger
|
|
141
|
-
competitors) would cover multiple host processes on one worktree.
|
|
142
|
-
- **Locale expansion**: client/host copy is zh/en today; the copy layer
|
|
143
|
-
(`src/locales.ts`, `src/client/locales.ts`) is already keyed for more.
|
|
144
|
-
- **Composer re-send polish**: the target text is already refilled after a
|
|
145
|
-
rewind; a first-class "edit and re-send" affordance is a small extension.
|
package/docs/compat/audit.md
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
# Compatibility audit (compat-audit)
|
|
2
|
-
|
|
3
|
-
> Method: **the tests are the audit**. The probes in `tests/compat-invariants.test.ts`,
|
|
4
|
-
> `tests/compat-interop.test.ts`, `tests/compat-gaps.test.ts`,
|
|
5
|
-
> `tests/compat-tool-updates.test.ts` and
|
|
6
|
-
> `scripts/verify-host.mjs` drive the plugin's real execution paths through the DSH
|
|
7
|
-
> subsystems' **real consumer paths** (real `@deepseek-ai/*` packages) and assert
|
|
8
|
-
> compatibility invariants. A probe failure is a finding; it enters the
|
|
9
|
-
> fix/pin/record loop.
|
|
10
|
-
>
|
|
11
|
-
> Targeted version: npm `@deepseek-ai/*@0.2.0-rc.2` (the peers declare the matching range).
|
|
12
|
-
> Source reference: the upstream [github.com/deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
13
|
-
>
|
|
14
|
-
> Version alignment: one peer tuple per DSH line, declaring only what was verified — the
|
|
15
|
-
> model, the update steps, and the `check-dsh-version.mjs` signal live in
|
|
16
|
-
> `docs/release/release.md`. The floor moved when a release changed interfaces inside the
|
|
17
|
-
> tuple: `0.1.6-alpha.2` removed the client session-ownership and queue-mirror APIs, and
|
|
18
|
-
> `0.1.7-alpha.1` moved the session writer to format v4 (producer-owned message sources,
|
|
19
|
-
> tool-role results, `developer/message`) and replaced the settings namespace registry with
|
|
20
|
-
> each entry's volatile `Config`, so the peer floor and both plugin seams moved with them.
|
|
21
|
-
> The plugin targets a single DSH version line; compatibility with earlier lines is not kept.
|
|
22
|
-
|
|
23
|
-
### Single channel
|
|
24
|
-
|
|
25
|
-
The plugin targets one DSH channel. Each seam below reads the targeted
|
|
26
|
-
version's shape only (no `Session.events` / `<textarea>` / face-`chat`
|
|
27
|
-
legacy branch).
|
|
28
|
-
|
|
29
|
-
| Seam | Implementation at the targeted version |
|
|
30
|
-
| --- | --- |
|
|
31
|
-
| Host session log | `session.snapshotEvents()` |
|
|
32
|
-
| Client chat snapshot | `uiConversation` `chat` view (`chatSnapshotOf`) |
|
|
33
|
-
| Client composer refill | `conversation.input.setDraft` facade |
|
|
34
|
-
| Client settings card | entry form via `ctx.configForms.get(entryId)` (`plugins.bundle.config`) |
|
|
35
|
-
| Client seat-button DOM | structural locate of the copy-`<button>` container (`actionsContainerOf`) |
|
|
36
|
-
|
|
37
|
-
## Definition of "fully compatible" (invariants)
|
|
38
|
-
|
|
39
|
-
| Invariant | Meaning | Probe location |
|
|
40
|
-
|---|---|---|
|
|
41
|
-
| I1 log replayability | A rewound log passes token-meter replay and `Session.create` (the resume-preflight validation) without throwing | `compat-invariants` I1, `verify-host` 12/13 |
|
|
42
|
-
| I2 surface consistency | After a cut, the surface has no duplicate nodes, every node exists in the log, the withdrawn target never returns to the surface, and `deriveMessages()` is legal | `compat-invariants` I2 |
|
|
43
|
-
| I3 step/turn structure | Client turn-tail ordering, unique `step/start` (real turns), no ghost turns; the `user/message` rewind marker adds no step frame | `compat-invariants` I3, `helpers.assertTurnTailOrdering` |
|
|
44
|
-
| I4 fold-service safety | stats / title / goal / projection fold a marker-bearing log without throwing, with predictable values | `compat-invariants` I4 |
|
|
45
|
-
| I5 compact interop | A tool-call orphaned by a cancelled turn is pair-balanced once shadowed by a rewind; a rewind across a compaction checkpoint is explicitly refused; a rewind-then-compact transaction stays legal | `compat-interop` I5, `verify-host` 12/14 |
|
|
46
|
-
| I6 tool pipeline | before-snapshot capture/commit/restore is correct (existing `snapshot.test.ts` + `verify-host` 4–8) for the tracked tools `write` / `edit` — `str_replace_editor` is an optional DSH package that stopped being a default tool in DSH 0.1.3, so it is not tracked; cancellation timing never hangs | `verify-host` 4–9 |
|
|
47
|
-
| I7 client ordering | A log carrying tool turns and rewind markers (a single `user/message` replace) satisfies the client builder ordering | `compat-interop` I7 |
|
|
48
|
-
| I8 runtime safety | `rewind`/`compact` combinations never leave a dangling step/turn frame | `verify-host` 13 |
|
|
49
|
-
| I9 tool-update projection | `0.1.7-rc.2` folds mid-conversation tool additions from the whole log while a rewind cuts only the surface: after a withdrawal no `tool-addition`/`tool-removal` block reaches the provider and the current declarations stay complete | `compat-tool-updates` I9 |
|
|
50
|
-
|
|
51
|
-
## Verified-compatible surfaces (probes pass)
|
|
52
|
-
|
|
53
|
-
- **compaction transactions**: `toolPairingBalancedBefore/After` stays balanced after a marker cut; the real `/compact` command (`command-compact` + `compaction-basic`, stub summarizer) can land `compaction/start…end` on top of a rewind marker and stay replayable; `/compact` is a legal no-op on a small surface.
|
|
54
|
-
- **tool-update history (`0.1.7-rc.2`)**: `Session.toolHistory()` folds the full append-only log, so a withdrawn `tool-addition` stays recorded after a rewind; `projectToolUpdates` drops every update whose message left the request and falls back to the complete current declarations. Pin: `tests/compat-tool-updates.test.ts` (on-surface projection, then the post-rewind fallback).
|
|
55
|
-
- **session-stats**: the `user/message` marker adds no step (the step count stays at the real turns' steps), no phantom turn.
|
|
56
|
-
- **session-title / goal fold**: a marker does not disturb `foldSessionTitle` / `foldGoal`.
|
|
57
|
-
- **plan-mode**: the marker is a turn-less `user/message` (no phantom turn); a rewind never touches the log-only `plan/mode` state (plan mode stays active; the user leaves it with `/plan off`) and the log stays replayable (`compat-invariants` I1/I3 marker + `plan/mode` probe, `verify-host`).
|
|
58
|
-
- **agent-loop cancellation**: `finally` guarantees step/turn closure; the rewind force-stop path leaves no dangling frame.
|
|
59
|
-
- **settings card (`0.1.7-alpha.1`)**: the card declares `configForms` as a module-level inject (the web profile always composes `ui-settings`), stages through the harness's own `SettingsFormModel` + `SettingsForm`, and registers into `plugins.bundle.config` keyed by the bundle package name; it reads that shared per-entry form's snapshot and writes through its revision-fenced `mutate`, disposing the model's subscription with the fiber. The host half declares the bundle's page policy (`configure({ auto: false }, ctx.fiber)` inside a `ctx.inject(['settings'])` child, per the dsh-settings README), opting the entry out of a client-built schema page. Pins: `verify:host` (`the bundle declares its own config page`), `tests/client-settings-card.test.ts` (staged plan, refusal, subscription release), `tests/client-lifecycle.test.ts`.
|
|
60
|
-
- **client session ownership (`0.1.6-alpha.2`)**: `SessionListState.current` / `currentAddress` are gone; main-view ownership is read from the row's `retainedBy.mainView` label — what `ui-workspace` retains the open session with. Pin: `tests/client-refill.test.ts`.
|
|
61
|
-
- **client pending input (`0.1.6-alpha.2`)**: `SessionSnapshot.queue` and the host `queue-mirror` are gone; pending steering rows come from the session's own `inbox` projection, and only USER-sourced `next-step` rows are retractable (the removed mapping was `source.kind === 'user' ? 'steering' : 'context'`). The rendered bubble also stays the client's own submission echo until the durable node arrives (`ChatView.pendingInputs` merges the Host occurrence into that echo by `rpcId`; `QueueDock` mirrors only `next-turn`), so the occurrence lands with NO DOM mutation — the portal refresh therefore follows the `inbox` face's own `subscribe` beside the DOM observer. An attachment-bearing row is identified by its message-attachment block (`data-message-attachments`) and matched by block count, not by rendered text (the block renders a file name or an image's loading label, which no inbox row reproduces); a missing attribute degrades to "no button". Pins: `tests/pending.test.ts`, `tests/client-retract.test.ts`, `tests/client-portal-refresh.test.ts`, `tests/client-dom.test.ts`.
|
|
62
|
-
- **client settings slot (`0.1.6-alpha.2`)**: `settings.plugin.item` is gone; the configuration form registers into `plugins.bundle.config`, keyed by the bundle package name. Pin: `tests/client-contract.test.ts`.
|
|
63
|
-
- **`agent/created` lifecycle guard**: the session-format reconcile runs on the alpha line's `agent/created` (fire-and-forget); pin: `verify-host` 4e dispatches it.
|
|
64
|
-
- **marker vs `/compact`**: the checkpoint shape is unchanged on this line — a `user/message` replace (`surfaceOp {replace, startSeq, endSeq}` + `sourceEventSeqs`); `assistant/message` still cannot carry `sourceEventSeqs`.
|
|
65
|
-
- **`image/offload` projection**: the alpha line's first `SessionMessageProjection` changes derived content without touching surface node membership; candidate listing and target resolution tolerate it. Pin: `tests/image-offload-projection.test.ts`.
|
|
66
|
-
- **session-cwd**: the fs tools no longer canonicalize a parent-traversing cwd, so `src/session-cwd.ts` returns `header.cwd` verbatim. Pin: `tests/session-cwd.test.ts`.
|
|
67
|
-
- **startup admission (`0.1.7-rc.1`)**: the gate reads the manifest's own `@deepseek-ai/dsh-*` peers and requires each to satisfy the runtime with `includePrerelease`; it runs for a profile bundle and again for every row its patch inserts, and a denial skips the bundle or disables the row. This plugin's declarations all satisfy the targeted version above, so it is admitted with no exemption — a skipped bundle on a later line is a peer mismatch, not a load failure.
|
|
68
|
-
|
|
69
|
-
## Known behavior boundaries (deterministic differences, non-crash, documented)
|
|
70
|
-
|
|
71
|
-
- **session-stats / session-telemetry fold the full log**: post-rewind stats do **not** rewind — `turns`/`steps`/`llmMs` still include withdrawn content; the `user/message` marker is folded as a present user turn (it adds no step). This is the intended "fold the full log" semantics, pinned by probe.
|
|
72
|
-
- **token-meter usage anchor stays stable** (G3): the `user/message` marker carries no usage, but because it is not an `assistant/message`, the baseline anchor does not drop to a heuristic estimate — it stays `usage` across a rewind. Pinned by `compat-gaps` G3.
|
|
73
|
-
- **marker content is the constant `(empty message)` placeholder**: never empty, because the session log is immutable but the model serving a session may change later — a strict OpenAI-compatible gateway rejects an empty user message (HTTP 400, Issue #21). The marker is a small visible user turn in derived history. Pin: `verify-host` (`marker is a user/message with the dsh-rewind producer source and the (empty message) placeholder`).
|
|
74
|
-
- **marker shape (v4)**: the replace `surfaceOp` is `{ op: 'replace', startSeq, endSeq }` (renamed from `start`/`end` in session-format v3); the dsh-rewind source is the single producer-owned kind `{ kind: 'dsh-rewind' }`, declared by merging `MessageSourceMap` (v4 removed the catch-all `plugin` kind and rejects it on write). Two older shapes stay readable, because a stored marker is immutable: the released v3 wrapper `{ kind: 'plugin', plugin: 'dsh-rewind' }`, and the `plugin:dsh-rewind` form the v3→v4 conversion gives a producer its rename table does not know. The `user/message` + `sourceEventSeqs` shape and the `(empty message)` content are unchanged.
|
|
75
|
-
- **Withdrawn content stays searchable/exportable**: session-query full-text and `/export` read the raw log; a rewind cuts only the surface, so withdrawn messages remain (declared in the README).
|
|
76
|
-
- **Session title auto-regeneration**: the title derives from the surface, so an automatically-derived title may change after a rewind.
|
|
77
|
-
- **Files written but uncommitted in a cancelled turn**: a `both` rewind cannot restore them (tool side-effect timing; same as Claude Code).
|
|
78
|
-
- **Attachment files left after a message is shadowed**: attachment storage is not cleaned with the surface (`dsh-attachment-local` not installed, not automatically verified).
|
|
79
|
-
- **Rewind leaves plan mode untouched**: `/plan text` is two independent actions (enter plan mode + steer the message). Rewinding the message undoes only the message — the log-only `plan/mode` state stays active, and the user leaves plan mode with `/plan off`, which still commits after a rewind (the marker creates no open turn). Pin: `verify-host` plan checks (`plan rewind leaves plan mode active`, `/plan off after rewind turns plan mode off`), `tests/hidden.test.ts` `messageTextAt`.
|
|
80
|
-
- **Bundle card metadata is declared by the plugin, not by the manager (`0.1.7-alpha.1`)**: the manager still titles a bundle with `shortName(pkg.name)` and its raw `package.json` description when nothing else is declared, but it now reads plugin-owned resources first — `locale/<lang>.json` (`meta.title`/`meta.description`) through the package exports map, and an `icon` file relative to the manifest. This plugin declares both, so its card shows an icon and copy that follows the active language; only the fallback description remains mixed-language. Pin: `tests/package-layout.test.ts` (icon path/media/size and both dictionaries).
|
|
81
|
-
- **Synchronous Session history reads are deprecated** on this line (`snapshotEvents` / `eventAt` / `ownEvents`): existing calls may remain, new calls are prohibited. The plugin keeps its existing reads (turn anchor + candidate listing); the migration path is a `ctx.sessionProjections` projection unit or the async paged history read this line has not shipped yet — not a plugin-side index.
|
|
82
|
-
- **A peer version range is never enforced at runtime**, and a physical copy nearer than the host still wins (upstream profile-resolution lookup order: "a peer version range is not an additional resolver filter"; "a nearer physical candidate wins"). So the `@deepseek-ai/schemastery` peer floor *is* the availability guarantee for `.volatile()` (#40): a plugin directory or linked checkout left holding an older copy (e.g. the pre-3.18.2 devDependency) shadows the host's 3.18.4 and fails the top-level `Config` evaluation before the plugin loads. Floor pinned at `^3.18.4`.
|
|
83
|
-
|
|
84
|
-
## Upstream (harness) issues and the plugin's no-compensation stance
|
|
85
|
-
|
|
86
|
-
The plugin treats these as harness-side defects it does not compensate for. Each entry records
|
|
87
|
-
the harness issue, its current status, and the plugin's stance, so a future maintainer does not
|
|
88
|
-
"fix the wrong direction."
|
|
89
|
-
|
|
90
|
-
### RU-I18N (resolved on the 0.1.7 line): host command copy follows the user's language
|
|
91
|
-
|
|
92
|
-
> **Status**: the `locale` settings section is gone — the locale host half only calls
|
|
93
|
-
> `settings.configure({ auto: false })`, and the preference lives in the `locale` entry's own
|
|
94
|
-
> Config. The plugin reads that value through the settings service's descriptor read
|
|
95
|
-
> (`readHostLocale` in `src/index.ts` calls `settings.describe({ redactSecrets: true })`, the
|
|
96
|
-
> route the settings UI and the client's own locale runtime take) at mount — which is what the
|
|
97
|
-
> once-registered command descriptions carry — and again before each command renders, so the
|
|
98
|
-
> output follows a language switch without a remount. The read is best-effort: a missing
|
|
99
|
-
> method, a missing entry, or a throwing read stays on the neutral English default.
|
|
100
|
-
>
|
|
101
|
-
> **Client-side command-description i18n is still first-party-only**: DSH localizes host
|
|
102
|
-
> command descriptions through the client `locale` binding (`ui-commands`), but the
|
|
103
|
-
> description keys come from a **closed allowlist** (`HOST_FACES`: compact, export, feedback,
|
|
104
|
-
> goal, permission, plan). A command outside that set — every third-party plugin — is passed
|
|
105
|
-
> through verbatim, and no host command *output* is localized by the harness for anyone. This
|
|
106
|
-
> plugin's host dictionary is what carries both, in the language read above.
|
|
107
|
-
>
|
|
108
|
-
> **Plugin stance**: no compensation; the seam is the descriptor read above. The wrong direction
|
|
109
|
-
> is the earlier one — reading `settings.get('locale').preference` once inside an
|
|
110
|
-
> `ctx.inject(['settings'])` callback and assuming cordis ordered the locale section before it
|
|
111
|
-
> (it does not; the descriptors were baked in the English default, defect #28).
|
|
112
|
-
|
|
113
|
-
### R-OPENSTEP (rewind part resolved): an unclosed `step` in the log breaks token-meter replay
|
|
114
|
-
|
|
115
|
-
> **Status**: resolved upstream — DSH auto-closes crash-left step/turn/tool boundaries at load
|
|
116
|
-
> (`interruptedTurnClosers`, consumed by the agent-loop's `resumeWith` cold read). A leftover
|
|
117
|
-
> unclosed step can still come from a third-party plugin appending `step/start` through the public
|
|
118
|
-
> `Session.append`, or from a hand-edited log; when it does, replay rejects later step activity, so
|
|
119
|
-
> `/compact` fails and automatic compaction silently stays disabled. Rewind's part: the marker is a
|
|
120
|
-
> `user/message` and appends no `step/start`, so the rewind amplifier is closed.
|
|
121
|
-
>
|
|
122
|
-
> **Plugin stance**: no guard. A `hasOpenStep` + `planRewind` pre-refusal was implemented and
|
|
123
|
-
> misjudged on **real session logs** (normal rewinds refused, GUI verification broken), so it was
|
|
124
|
-
> reverted — do not re-attempt a plugin-side pre-refusal. The fix direction is upstream (token-meter
|
|
125
|
-
> recovery for unclosed steps), and the residual risk is accepted: that log is already abnormal.
|
|
126
|
-
|
|
127
|
-
### R-PROMPTCARD (resolved on the 0.1.7 line): the repeated `System prompt` card is filtered out of chat
|
|
128
|
-
|
|
129
|
-
> **Status**: `isVisibleChatNode`
|
|
130
|
-
> (`packages/client/ui-chat/src/client/contract/chat-visibility.ts`) drops `system-prompt`
|
|
131
|
-
> nodes — and the `permission` command — from the visible chat rows before the row is built,
|
|
132
|
-
> so a rewind on this line adds no prompt row at all; the durable events and the trajectory
|
|
133
|
-
> inspection keep them. #22, #31 and the (now removed) repeated `System prompt` card known issue
|
|
134
|
-
> no longer reproduce.
|
|
135
|
-
>
|
|
136
|
-
> **Plugin stance**: still no compensation. The DOM row-hiding seam (`hiddenSeqsOf`) covers
|
|
137
|
-
> withdrawn messages, where hiding is essential, not cosmetic.
|
|
138
|
-
|
|
139
|
-
### R-PANELKEYS: the panel keyboard stays the plugin's own
|
|
140
|
-
|
|
141
|
-
> **Situation**: on Web, Escape, Tab, the four arrows, and Enter without Alt are
|
|
142
|
-
> platform-reserved. The predicate is exported (`bindingIssue` from
|
|
143
|
-
> `@deepseek-ai/dsh-client-shortcuts/protocol`), but a reserved binding is still refused, so
|
|
144
|
-
> neither a plugin nor a user may bind them. `Menu`
|
|
145
|
-
> is the dropdown primitive, while the harness's command-option panel (`ui-commands`'
|
|
146
|
-
> popupSelect) renders `MenuSurface` plus its own controller with `role="listbox"`/
|
|
147
|
-
> `role="option"`. `ui-commands`' `settle()` also calls `focusComposer()` unconditionally
|
|
148
|
-
> after `await onSelect(...)`; its only early return requires replacing the popup binding,
|
|
149
|
-
> which needs the internal `segment` that business code cannot reach.
|
|
150
|
-
>
|
|
151
|
-
> **Decision**: the mode-selection and retract panels keep their own capture-phase
|
|
152
|
-
> Escape/↑/↓ handling — focus-independent, the shape the official command panel uses — and
|
|
153
|
-
> keep `role="dialog"`. The plugin neither renders `Menu` nor compensates the composer focus
|
|
154
|
-
> restore. Capture ownership is pinned by `tests/client-popover.test.ts`; the ghost-row walk
|
|
155
|
-
> exclusion is **not** pinned, and on the typed `/rewind` path Enter still needs a row to hold
|
|
156
|
-
> focus after the shell's focus restore.
|
|
157
|
-
|
|
158
|
-
## Uncovered boundaries (need an additional e2e layer; non-blocking)
|
|
159
|
-
|
|
160
|
-
- Real LLM streaming and auto title generation (L2 stubbed).
|
|
161
|
-
- Real SQLite index lifetime (`dsh-session-query-sqlite` not installed; native deps).
|
|
162
|
-
- Actual browser rendering replay (the client contract's logic layer is covered by `client-contract.test.ts`).
|
|
163
|
-
- Real JSONL persistence round-trip (`dsh-session-persistence-jsonl` depends on native `koffi`; the round-trip validates the harness's own zstd/JSON codec, the plugin is not party to it, not worth the cost, not installed).
|
|
164
|
-
- `session-reference`'s `SessionReferenceResolver.prepare` needs a full session-query service; its data base (current-surface projection) is already covered by the G1 probe (`foldSurface`).
|
|
165
|
-
- telemetry pipeline (`dsh-session-telemetry-otel`) and the attachment provider (`dsh-attachment-local`).
|
|
166
|
-
- Running workflow/jobs cancelled by a rewind: the tool contract requires observing `exec.signal` and settling (`packages/core/tools/src/index.ts`); a rewind triggers the harness's standard cancel, not plugin-specific — statically confirmed, real workflows untested.
|
|
167
|
-
|
|
168
|
-
Statuses and stances for the named findings live in the sections above; G1 (surface classification
|
|
169
|
-
via `foldSurface`), G2 (projection checkpoint) and G3 (the confirmed-non-defect usage anchor on
|
|
170
|
-
token-meter) all pass in `compat-gaps.test.ts`.
|