@dsh-xhl/dsh-live-inspector 1.0.0

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 ADDED
@@ -0,0 +1,163 @@
1
+ # @dsh-xhl/dsh-live-inspector
2
+
3
+ DeepSeek Harness (DSH) Web plugin that provides a lightweight, real-time **Git Tree & Live Changes Inspector** in the right sidebar (`sidebar.right` tab), with **turn-scoped and hunk-level undo**.
4
+
5
+ ## Features
6
+
7
+ - **Single Tab Architecture (Zero RAM Bloat)**: Instead of opening heavy editor/viewer tabs for every accessed file, it keeps everything inside a single, high-performance **Git Tree** tab (`~10 KB` memory footprint).
8
+ - **Live File Status Badges**:
9
+ - `[M]` (Amber): Modified files (`edit`, `str_replace_editor`).
10
+ - `[A]` (Emerald): Created / added files (`write`, `write_file`).
11
+ - `[R]` (Sky Blue): Read / inspected files (`read`).
12
+ - `[D]` (Red): Deleted in this session by an undo.
13
+ - **Real-Time Active Indicator**: Displays `⚡ Agent active on: <path>` with a pulsing live status on the exact file currently being touched.
14
+ - **Interactive Controls**:
15
+ - Filter by `All`, `Changes (M/A)`, or `Reads (R)`.
16
+ - Instant text filter search.
17
+ - Optional `View` button to open a single file preview only when explicitly desired.
18
+ - `Clear` button to reset the session file list.
19
+
20
+ ## Undo
21
+
22
+ Undo is scoped to a **turn**. There is no file-level or session-level undo: undoing turn N
23
+ restores the state each file entered turn N with, so earlier turns always survive.
24
+
25
+ ### Only the newest turn is undoable
26
+
27
+ **Undo controls appear only while the newest turn is selected.** On any older turn they are
28
+ absent — not greyed out — because an older turn has already been reviewed, and a visible dead
29
+ button would imply an action the panel is going to refuse.
30
+
31
+ Today's turn is the default, so the panel opens ready to undo what the agent just did.
32
+
33
+ The selected turn scopes the **whole panel**:
34
+
35
+ - the **file list** shows only the files that turn changed (`All (n)` counts that scope),
36
+ - the **diff inspector** follows, re-selecting a file when the turn changes,
37
+ - the **`⟲ Undo Turn N (n)`** button undoes that turn across every file it changed.
38
+
39
+ `All turns` is available in the selector for reading history across the whole session; it is a
40
+ view rather than an undo scope, so no undo controls appear while it is selected.
41
+
42
+ - Each file row shows its turns (`Turn 3`, or `Turn 1–4`) and carries `↶ Undo Turn N`.
43
+ - A file's detail header carries `↶ Undo change`, which reverts just the selected hunk.
44
+ - Older turns' change counts are labelled `reviewed`, and hunks outside the selected turn are
45
+ dimmed and not individually revertible.
46
+
47
+ ### The turn snapshot
48
+
49
+ A file's text is captured immediately before that turn's first mutation of it, so the snapshot
50
+ is exactly what the file inherited — including every earlier turn's work. Writing it back is
51
+ what makes a turn-scoped undo unable to reach past its own turn. A `read` before the first
52
+ mutation counts as untouched too, which is what catches a write-after-read.
53
+
54
+ ### Line / diff-level undo
55
+
56
+ Reverts **one contiguous change** — one tool call's hunk — leaving the file's other changes
57
+ in place.
58
+
59
+ - Every hunk the diff view shows is listed as a `#n` chip above the diff, each with its
60
+ own `↶ Undo change` button.
61
+ - A hunk is undone by replacing its recorded post-change text with its recorded original
62
+ text, so unrelated edits in the same file survive.
63
+
64
+ ### Deleting and restoring
65
+
66
+ Deleting is the **only destructive action**, so it is the only button rendered as a solid red
67
+ fill. Every restorative action — hunk undo, turn undo, `⤴ Restore` — is amber, because it
68
+ writes content back rather than removing it.
69
+
70
+ Two kinds of deletion are covered:
71
+
72
+ - **A file the agent created and you delete** with `🗑 Delete file`. The row becomes a
73
+ strike-through `[D]` and `⤴ Restore` writes it back from the recorded content.
74
+ - **A file deleted by a shell command** (`rm`, `del`, …). The file tools have no delete verb,
75
+ so the panel cannot see the operation directly. After any shell call it re-probes the paths
76
+ it already knows about, and a path that vanished becomes a `[D]` row you can restore from
77
+ the newest recorded content — or from the turn snapshot if the file was only read.
78
+
79
+ A deleted file with no recorded content cannot be restored, and its `⤴ Restore` says so rather
80
+ than failing on click.
81
+
82
+ ### Safety rules
83
+
84
+ Undo refuses rather than guesses:
85
+
86
+ - **Drift**: if the file changed since the edit, the recorded text no longer matches and the
87
+ undo is refused.
88
+ - **Ambiguity**: if the recorded text now occurs more than once, the undo is refused.
89
+ - **No artifact**: a change with no exact recorded before/after pair (replayed history, or a
90
+ tool other than `edit`/`write`) is never offered as individually revertible. The panel says
91
+ so instead of showing a button that would corrupt the file.
92
+ - **Older turn**: undo is not offered at all outside the newest turn.
93
+ - **Mid-write**: a file the agent is currently writing is not undoable until the step ends.
94
+ - **Oversized**: files above 512 KB get no text snapshot. They are still recognized by digest,
95
+ but a restore is refused.
96
+ - **Roots**: paths that resolve to a filesystem root, a drive root, or a UNC share root are
97
+ rejected outright.
98
+
99
+ Destructive actions need a second click (the button switches to a `⚠ Confirm …` state).
100
+
101
+ ## Architecture
102
+
103
+ - `lib/index.js` — Host half. Owns real filesystem access and publishes a **Typert Remote
104
+ namespace** named `inspectorFs` with four methods: `readText`, `writeText`, `deleteFile`,
105
+ and `statPath` (the last detects deletions performed outside the file tools). Paths resolve
106
+ through `ctx.fs.processPathFromHostPath` when the backend provides a mapping, and by joining
107
+ the session working directory otherwise.
108
+ - `lib/client.js` — Browser half. Records file activity, diff artifacts, and per-turn
109
+ snapshots from session events, then renders the panel and undo controls.
110
+
111
+ ### Why a Remote namespace, not `host.call`
112
+
113
+ `host.call(method, args)` is an API for **dynamic packages** — the ones an agent authors as a
114
+ code string. There, `host` is injected as a closure parameter by the runner.
115
+
116
+ An installed bundle's Client module runs under `window.__ModuleLoader__`, which resolves only a
117
+ fixed seed list (`react`, `react-dom`, `@deepseek-ai/cordis`, `@deepseek-ai/dsh-client-store`,
118
+ the `dsh-client-ui-*` primitives) and traps `require` itself. **`host` does not exist there**, so
119
+ `host.call` throws `host is not defined`. A bundle reaches its Host half through a Typert Remote
120
+ namespace consumed as `ctx.inject(['remote.<namespace>'])`.
121
+
122
+ This package has no build step, so `lib/index.js` writes the protocol's Remote-method descriptor
123
+ onto the service prototype by hand instead of using the build-time `@Remote` decorator. The
124
+ Gateway's source-mode (`src-json`) fallback accepts it: parameter names come from the function
125
+ signature and arguments travel as plain JSON, so no generated schema is required.
126
+
127
+ Method failures **reject** across the boundary. The Browser half converts a rejection into the
128
+ `{ ok: false, error }` shape it reasons about, so a refused path surfaces as one toast rather
129
+ than an unhandled throw.
130
+
131
+ Per-turn snapshots are keyed by file and turn. A snapshot is read immediately before that
132
+ turn's first mutation of the file, so it captures exactly what the file inherited — including
133
+ every earlier turn's work. That is what makes a turn-scoped undo unable to reach past its own
134
+ turn.
135
+
136
+ The pre-edit baseline read is authorized by this plugin's own `approval/request` listener,
137
+ which allows `fs-read` **only**; every other request is left untouched for the normal
138
+ approval chain.
139
+
140
+ ## Installation
141
+
142
+ Published to npm as `@dsh-xhl/dsh-live-inspector`:
143
+
144
+ ```bash
145
+ dsh plugin --profile web add @dsh-xhl/dsh-live-inspector
146
+ ```
147
+
148
+ From a checkout, `dsh plugin --profile web add <path-to-this-directory>` installs it as a
149
+ local link, which is what the `test` profile does.
150
+
151
+ ### Manifest notes
152
+
153
+ - `dsh.bundle.patch` points at `cordis.patch.yml`, whose row inserts the plugin under the
154
+ id `dsh-live-inspector` with `name: '@dsh-xhl/dsh-live-inspector'`.
155
+ - `dsh.client` declares `platform: web` and the two sidebar Client packages this UI orders
156
+ itself against.
157
+ - `lib/client.js` registers into `window.__ModuleLoader__` under an `id` that **must equal the
158
+ package name**. The loader derives the expected module id from the manifest and reports
159
+ `loaded without registering "<id>"` when the two disagree, so renaming the package without
160
+ updating that id silently drops the panel.
161
+ - `files` ships only `lib/` plus the patch and README. The former root-level `client.js` and
162
+ `index.js` copies were removed; `exports` has pointed at `lib/` for some time, and keeping
163
+ two near-identical copies on disk invited editing the wrong one.
@@ -0,0 +1,3 @@
1
+ - insert:
2
+ - id: dsh-live-inspector
3
+ name: '@dsh-xhl/dsh-live-inspector'