dsh-plugin-message-edit 1.0.0 → 1.0.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.en.md +11 -2
- package/README.md +7 -2
- package/docs/ARCHITECTURE.md +164 -156
- package/docs/DEVELOPMENT.md +135 -75
- package/lib/client.js +1666 -1642
- package/lib/index.js +829 -830
- package/lib/session-record.js +37 -0
- package/package.json +66 -51
- package/plugin.client.js +1666 -1642
package/README.en.md
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README.md)
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-plugin-message-edit)
|
|
6
|
+
[](https://github.com/SpookySandwich/dsh-plugin-message-edit/actions/workflows/ci.yml)
|
|
5
7
|
[](LICENSE)
|
|
6
|
-
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
7
9
|
[](https://github.com/SpookySandwich/dsh-plugin-message-edit/stargazers)
|
|
8
10
|
|
|
9
11
|
Edit a message you already sent and the conversation **rewinds and branches** from that point, the way ChatGPT, Claude and DeepSeek all do it. The old version is not overwritten — a `‹ 2/4 ›` counter appears under the bubble, and a Versions tab draws the whole tree.
|
|
@@ -70,9 +72,16 @@ For technical details and developer guides, see:
|
|
|
70
72
|
|
|
71
73
|
## Compatibility
|
|
72
74
|
|
|
75
|
+
Version `1.0.1` has been verified in an isolated DSH `0.1.2-rc.1` Web environment:
|
|
76
|
+
plugin loading, images, edit/retry, nested branches, and restored sessions. Model
|
|
77
|
+
responses use a local test adapter; remote model services were not exercised.
|
|
78
|
+
|
|
79
|
+
The compatibility layer retains the older `events` / `seedLength` interfaces,
|
|
80
|
+
covered by automated tests. Later DSH releases need separate verification.
|
|
81
|
+
Restart DSH after updating the plugin.
|
|
82
|
+
|
|
73
83
|
Coexists with [dsh-plugin-smooth-stream](https://github.com/SpookySandwich/dsh-plugin-smooth-stream) and [dsh-plugin-rollout-scout](https://github.com/SpookySandwich/dsh-plugin-rollout-scout).
|
|
74
84
|
|
|
75
85
|
## License
|
|
76
86
|
|
|
77
87
|
MIT © SpookySandwich. Portions of the host half derive from dsh-message-edit (MIT © Moeblack).
|
|
78
|
-
|
package/README.md
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.en.md) | 简体中文
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-plugin-message-edit)
|
|
6
|
+
[](https://github.com/SpookySandwich/dsh-plugin-message-edit/actions/workflows/ci.yml)
|
|
5
7
|
[](LICENSE)
|
|
6
|
-
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
7
9
|
[](https://github.com/SpookySandwich/dsh-plugin-message-edit/stargazers)
|
|
8
10
|
|
|
9
11
|
编辑一条已经发出的消息,对话会从那一刻 **真正回溯并分叉**——和 ChatGPT、Claude、DeepSeek 的做法一致。旧版本不会被覆盖:气泡下方出现 `‹ 2/4 ›` 计数,「版本」标签页则画出整棵树。
|
|
@@ -70,9 +72,12 @@ DSH 的会话是仅追加的事件日志,本身不支持会话内分支,因
|
|
|
70
72
|
|
|
71
73
|
## 兼容性
|
|
72
74
|
|
|
75
|
+
`1.0.1` 已在 DSH `0.1.2-rc.1` 的隔离 Web 环境验证:插件加载、图片显示、编辑与重试、深层分支,以及持久会话的恢复读取。模型回复使用本地测试实现,未调用远程模型服务。
|
|
76
|
+
|
|
77
|
+
兼容层保留了旧版 `events` / `seedLength` 接口支持,并由自动测试覆盖。DSH 仍在快速迭代,尚未验证的后续版本不在此保证范围内。更新插件后请重启 DSH。
|
|
78
|
+
|
|
73
79
|
可与 [dsh-plugin-smooth-stream](https://github.com/SpookySandwich/dsh-plugin-smooth-stream)、[dsh-plugin-rollout-scout](https://github.com/SpookySandwich/dsh-plugin-rollout-scout) 共存。
|
|
74
80
|
|
|
75
81
|
## 许可
|
|
76
82
|
|
|
77
83
|
MIT © SpookySandwich。宿主端部分逻辑源自 dsh-message-edit(MIT © Moeblack)。
|
|
78
|
-
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,156 +1,164 @@
|
|
|
1
|
-
# Architecture Overview
|
|
2
|
-
|
|
3
|
-
`dsh-plugin-message-edit` provides ChatGPT/Claude-style conversation branching and message editing for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness).
|
|
4
|
-
|
|
5
|
-
Because DSH session event logs are append-only without native in-session branching, this plugin splits responsibilities across a **Node.js Host Service** and a **Browser/Web Client**.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. System Components
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
13
|
-
│ DSH Desktop / Web │
|
|
14
|
-
│ │
|
|
15
|
-
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
|
16
|
-
│ │ Client Half │ HTTP │ Host Half │ │
|
|
17
|
-
│ │ (plugin.client.js) │<───────>│ (lib/index.js) │ │
|
|
18
|
-
│ └──────────┬───────────┘ └──────────┬───────────┘ │
|
|
19
|
-
│ │ │ │
|
|
20
|
-
│ Shadow User Message Cordis Services: │
|
|
21
|
-
│ Versions Tab (Graph) - sessions │
|
|
22
|
-
│ Settings UI - agents │
|
|
23
|
-
│ - webServer │
|
|
24
|
-
│ - sessionPersistence │
|
|
25
|
-
└─────────────────────────────────────────────────────────────┘
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### 1.1 Host Half (`lib/index.js`)
|
|
29
|
-
- Runs in the Node.js backend process via Cordis lifecycle injection.
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
- **
|
|
154
|
-
- **
|
|
155
|
-
|
|
156
|
-
|
|
1
|
+
# Architecture Overview
|
|
2
|
+
|
|
3
|
+
`dsh-plugin-message-edit` provides ChatGPT/Claude-style conversation branching and message editing for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness).
|
|
4
|
+
|
|
5
|
+
Because DSH session event logs are append-only without native in-session branching, this plugin splits responsibilities across a **Node.js Host Service** and a **Browser/Web Client**.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. System Components
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
13
|
+
│ DSH Desktop / Web │
|
|
14
|
+
│ │
|
|
15
|
+
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
|
16
|
+
│ │ Client Half │ HTTP │ Host Half │ │
|
|
17
|
+
│ │ (plugin.client.js) │<───────>│ (lib/index.js) │ │
|
|
18
|
+
│ └──────────┬───────────┘ └──────────┬───────────┘ │
|
|
19
|
+
│ │ │ │
|
|
20
|
+
│ Shadow User Message Cordis Services: │
|
|
21
|
+
│ Versions Tab (Graph) - sessions │
|
|
22
|
+
│ Settings UI - agents │
|
|
23
|
+
│ - webServer │
|
|
24
|
+
│ - sessionPersistence │
|
|
25
|
+
└─────────────────────────────────────────────────────────────┘
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 1.1 Host Half (`lib/index.js`)
|
|
29
|
+
- Runs in the Node.js backend process via Cordis lifecycle injection.
|
|
30
|
+
- Normalizes DSH sessions through `lib/session-record.js`: current live sessions
|
|
31
|
+
expose `snapshotEvents()`, query snapshots carry their header in `session`,
|
|
32
|
+
and older records expose `events` / `header`. Invalid logs fail explicitly.
|
|
33
|
+
- Current DSH branches use `meta.isSeeded` plus `inheritedEventCount`; older
|
|
34
|
+
hosts use `meta.seedLength`. The normalized inherited cut prevents a nested
|
|
35
|
+
branch from mistaking an inherited version marker for its own marker.
|
|
36
|
+
- Registers the `/message-tree` HTTP route on `ctx.webServer`.
|
|
37
|
+
- Owns branch creation transactions (`POST /message-tree`):
|
|
38
|
+
1. Truncates parent events up to the target turn.
|
|
39
|
+
2. Seeds a new DSH session with the prefix events.
|
|
40
|
+
3. Appends a durable `message-tree/version` marker with `ignorable: true`.
|
|
41
|
+
4. Submits the edited prompt into the new session.
|
|
42
|
+
- Owns graph queries (`GET /message-tree?sessionId=...`):
|
|
43
|
+
- Traverses the session family DAG.
|
|
44
|
+
- Recovers deleted/ghost ancestors from surviving descendants' event logs.
|
|
45
|
+
- Extracts turn event boundaries for turn-level rendering.
|
|
46
|
+
|
|
47
|
+
### 1.2 Client Half (`plugin.client.js`)
|
|
48
|
+
- Runs in the browser / renderer process.
|
|
49
|
+
- Injects a shadowed `user` message renderer at priority `-1` to add the edit/copy/retry toolbar and `‹ n/m ›` version ring without modifying agent responses, tool calls, or reasoning blocks.
|
|
50
|
+
- Adds the **Versions** tab (`VIEW_ORDER: 16`) providing an interactive pan/zoom graph with spring physics.
|
|
51
|
+
- Adds settings options in **Settings → Message Edit** with live layout switching (ChatGPT, DeepSeek, Claude styles).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 2. Durable Storage Model
|
|
56
|
+
|
|
57
|
+
DSH sessions are immutable append-only logs. When branching:
|
|
58
|
+
|
|
59
|
+
1. **Seed Inheritance**: A new session is initialized whose log begins with an exact clone of the parent's event log up to the start of the edited turn (`seedLength`).
|
|
60
|
+
2. **Durable Marker**: The host appends a custom event:
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"type": "message-tree/version",
|
|
64
|
+
"data": {
|
|
65
|
+
"schemaVersion": 1,
|
|
66
|
+
"effect": {
|
|
67
|
+
"operation": "edit",
|
|
68
|
+
"targetTurn": 1,
|
|
69
|
+
"targetEventSeq": 5,
|
|
70
|
+
"before": "Original message text",
|
|
71
|
+
"after": "Edited message text"
|
|
72
|
+
},
|
|
73
|
+
"inverse": {
|
|
74
|
+
"kind": "restore-version",
|
|
75
|
+
"sessionId": "parent-session-id"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
3. **`ignorable` Flag**: Custom plugin event types fall outside DSH's core schema. The event envelope must set `ignorable: true`; otherwise, DSH's built-in event reader will reject the entire session log.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 3. HTTP API
|
|
85
|
+
|
|
86
|
+
### `GET /message-tree?sessionId={id}`
|
|
87
|
+
Returns the entire conversation family surrounding the requested session.
|
|
88
|
+
|
|
89
|
+
**Response Schema:**
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"sessionId": "current-session-id",
|
|
93
|
+
"versions": [
|
|
94
|
+
{
|
|
95
|
+
"sessionId": "session-a",
|
|
96
|
+
"createdAt": 1724334000000,
|
|
97
|
+
"depth": 0,
|
|
98
|
+
"current": false,
|
|
99
|
+
"onCurrentPath": true,
|
|
100
|
+
"turns": [
|
|
101
|
+
{ "turn": 1, "text": "Hello", "time": 1724334001000 },
|
|
102
|
+
{ "turn": 2, "text": "Tell me more", "time": 1724334005000 }
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"sessionId": "session-b",
|
|
107
|
+
"parentSessionId": "session-a",
|
|
108
|
+
"createdAt": 1724334020000,
|
|
109
|
+
"depth": 1,
|
|
110
|
+
"current": true,
|
|
111
|
+
"onCurrentPath": true,
|
|
112
|
+
"operation": "edit",
|
|
113
|
+
"targetTurn": 1,
|
|
114
|
+
"before": "Hello",
|
|
115
|
+
"after": "Hello world",
|
|
116
|
+
"turns": [
|
|
117
|
+
{ "turn": 1, "text": "Hello world", "time": 1724334021000 },
|
|
118
|
+
{ "turn": 2, "text": "What is next?", "time": 1724334025000 }
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `POST /message-tree`
|
|
126
|
+
Performs branch creation or reactivation.
|
|
127
|
+
|
|
128
|
+
- **`edit`**: Rewinds to before the specified user turn, creates a new branched session, appends a durable `message-tree/version` marker, and submits the replacement prompt.
|
|
129
|
+
- **`retry`**: Rewinds to before the target turn, creates a child session, and replays the original user prompt.
|
|
130
|
+
- **`activate`**: Unarchives an archived version session via the host registry queue so the client can navigate to it.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 4. Performance & In-Memory Caching
|
|
135
|
+
|
|
136
|
+
1. **Host-Side Parsed Session Cache (`sessionParsedCache`)**:
|
|
137
|
+
- Parses turn boundaries (`extractTurns`) and version headers once per immutable event sequence.
|
|
138
|
+
- Bounded to 500 entries per plugin context. Live keys use session identity
|
|
139
|
+
and event count; unchanged modern logs do not need a new snapshot.
|
|
140
|
+
- Cold logs are read before comparing their event count. Creation timestamps
|
|
141
|
+
cannot invalidate append-only history and must not be used as revisions.
|
|
142
|
+
|
|
143
|
+
2. **Client-Side Family SWR Store (`treeStore`)**:
|
|
144
|
+
- Maps every non-deleted branch in a tree to the shared family structure upon fetch.
|
|
145
|
+
- Switching between sibling branches (`‹ n/m ›` or Versions view) is 100% synchronous (0ms lag, zero indicator flicker).
|
|
146
|
+
- Uses monotonic request timestamps to prevent race-condition overwrites from out-of-order responses.
|
|
147
|
+
- Optimistically seeds newly created edit/retry branches before navigation.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 5. Security and Error Resilience
|
|
152
|
+
|
|
153
|
+
- **Ignorable Event Envelope**: `ignorable: true` ensures foreign event markers do not crash the core DSH log parser.
|
|
154
|
+
- **Fail-Safe Mutation Recovery**: Transaction reversals (`child.dispose()`) on failures prevent dangling session artifacts.
|
|
155
|
+
- **Memory Bounded Stores**: LRU bounds (500 sessions) prevent unbounded memory growth in long-running processes.
|
|
156
|
+
|
|
157
|
+
## 6. Naming & Namespaces
|
|
158
|
+
|
|
159
|
+
- **NPM Package**: `dsh-plugin-message-edit`
|
|
160
|
+
- **Cordis Service Name**: `message-tree`
|
|
161
|
+
- **HTTP Path**: `/message-tree`
|
|
162
|
+
- **Durable Event Type**: `message-tree/version`
|
|
163
|
+
|
|
164
|
+
> The package uses `dsh-plugin-message-edit` for discovery, but retains `message-tree` in routes, cordis IDs, and event types to prevent collisions with prior third-party plugins (such as `dsh-message-edit`) and ensure seamless side-by-side operation.
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -1,75 +1,135 @@
|
|
|
1
|
-
# Development & Testing Guide
|
|
2
|
-
|
|
3
|
-
This guide covers building, testing, packaging, and installing `dsh-plugin-message-edit`.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 1. Repository Structure
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
dsh-plugin-message-edit/
|
|
11
|
-
├── lib/
|
|
12
|
-
│ ├── index.js # Host-side Cordis plugin (routes, session log processing)
|
|
13
|
-
│ ├── tree-logic.js # Pure tree algorithms (shared with client and tests)
|
|
14
|
-
│ └── client.js # Generated client bundle (wrapped from plugin.client.js)
|
|
15
|
-
├── plugin.client.js # Source client-side UI and React components
|
|
16
|
-
├── scripts/
|
|
17
|
-
│ └── build-client.mjs # Build script wrapping plugin.client.js into lib/client.js
|
|
18
|
-
├── test/
|
|
19
|
-
│ └── tree.test.mjs # Automated test suite (36+ unit tests)
|
|
20
|
-
├── cordis.patch.yml # Service dependencies and injection metadata
|
|
21
|
-
├── docs/ # Technical architecture and data model documentation
|
|
22
|
-
└── package.json
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## 2. Build Pipeline
|
|
28
|
-
|
|
29
|
-
The client component [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js) is written in browser-compatible JavaScript. Before distribution or testing, it is wrapped with a Cordis module preamble into [`lib/client.js`](file:///D:/dsh-plugin-message-edit/lib/client.js).
|
|
30
|
-
|
|
31
|
-
### Build Client
|
|
32
|
-
```bash
|
|
33
|
-
npm run build
|
|
34
|
-
```
|
|
35
|
-
Executes `node scripts/build-client.mjs` to regenerate `lib/client.js`.
|
|
36
|
-
|
|
37
|
-
### Check Build Integrity
|
|
38
|
-
```bash
|
|
39
|
-
node scripts/build-client.mjs --check
|
|
40
|
-
```
|
|
41
|
-
Exits with code 1 if `lib/client.js` is out of date relative to `plugin.client.js`.
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## 3. Testing
|
|
46
|
-
|
|
47
|
-
The project includes an automated test suite verifying tree construction, sibling fan-out, ghost recovery, active path calculation, and ring index calculation.
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npm test
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
1
|
+
# Development & Testing Guide
|
|
2
|
+
|
|
3
|
+
This guide covers building, testing, packaging, and installing `dsh-plugin-message-edit`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Repository Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
dsh-plugin-message-edit/
|
|
11
|
+
├── lib/
|
|
12
|
+
│ ├── index.js # Host-side Cordis plugin (routes, session log processing)
|
|
13
|
+
│ ├── tree-logic.js # Pure tree algorithms (shared with client and tests)
|
|
14
|
+
│ └── client.js # Generated client bundle (wrapped from plugin.client.js)
|
|
15
|
+
├── plugin.client.js # Source client-side UI and React components
|
|
16
|
+
├── scripts/
|
|
17
|
+
│ └── build-client.mjs # Build script wrapping plugin.client.js into lib/client.js
|
|
18
|
+
├── test/
|
|
19
|
+
│ └── tree.test.mjs # Automated test suite (36+ unit tests)
|
|
20
|
+
├── cordis.patch.yml # Service dependencies and injection metadata
|
|
21
|
+
├── docs/ # Technical architecture and data model documentation
|
|
22
|
+
└── package.json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Build Pipeline
|
|
28
|
+
|
|
29
|
+
The client component [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js) is written in browser-compatible JavaScript. Before distribution or testing, it is wrapped with a Cordis module preamble into [`lib/client.js`](file:///D:/dsh-plugin-message-edit/lib/client.js).
|
|
30
|
+
|
|
31
|
+
### Build Client
|
|
32
|
+
```bash
|
|
33
|
+
npm run build
|
|
34
|
+
```
|
|
35
|
+
Executes `node scripts/build-client.mjs` to regenerate `lib/client.js`.
|
|
36
|
+
|
|
37
|
+
### Check Build Integrity
|
|
38
|
+
```bash
|
|
39
|
+
node scripts/build-client.mjs --check
|
|
40
|
+
```
|
|
41
|
+
Exits with code 1 if `lib/client.js` is out of date relative to `plugin.client.js`.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 3. Testing
|
|
46
|
+
|
|
47
|
+
The project includes an automated test suite verifying tree construction, sibling fan-out, ghost recovery, active path calculation, and ring index calculation.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm test
|
|
51
|
+
```
|
|
52
|
+
Automatically builds the client first, checks that it matches the source, then
|
|
53
|
+
runs the Node test runner. Install development dependencies with `npm ci` on a
|
|
54
|
+
fresh checkout before running tests (Node 22.19+ or Node 24).
|
|
55
|
+
|
|
56
|
+
- `test/tree.test.mjs`: 36 assertions covering branch and version-tree behavior.
|
|
57
|
+
- `test/client-images.test.mjs`: loads the generated client through its module
|
|
58
|
+
loader, mounts its registered user-message component using React and jsdom,
|
|
59
|
+
and checks image delegation, multiple/image-only messages, old-host fallback,
|
|
60
|
+
text-only messages, and entering/cancelling an edit. Host services and the image
|
|
61
|
+
gallery are test doubles; these are not full DSH integration tests.
|
|
62
|
+
- `test/client-registration.test.mjs`: checks module dependencies and visible
|
|
63
|
+
failures when services or registrations are unavailable.
|
|
64
|
+
- `test/session-record.test.mjs` and `test/host-compatibility.test.mjs`: cover
|
|
65
|
+
legacy/current session shapes, live/resumed edit requests, retained images,
|
|
66
|
+
retry ancestry, nested version markers, and cache invalidation.
|
|
67
|
+
|
|
68
|
+
GitHub Actions runs these checks for pull requests and branch pushes, on
|
|
69
|
+
Linux (Node 22 and 24) and Windows (Node 22). It also runs:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm run check:package
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This creates a real npm archive in a temporary directory, verifies that runtime
|
|
76
|
+
entry points are present and development-only directories are absent, checks
|
|
77
|
+
the host entry's syntax, and removes the temporary archive. `npm pack` and
|
|
78
|
+
`npm publish` now build the client automatically through `prepack`, preventing
|
|
79
|
+
an old or missing generated client from being shipped.
|
|
80
|
+
|
|
81
|
+
The workflow needs to be pushed to GitHub to run there. Making its checks
|
|
82
|
+
mandatory before merging is a separate repository ruleset/branch-protection
|
|
83
|
+
setting; adding the workflow alone does not block the Merge button.
|
|
84
|
+
|
|
85
|
+
Before accepting an image-rendering change, also check it in a running DSH:
|
|
86
|
+
send text with one/multiple images and an image-only message, open an image in
|
|
87
|
+
the native viewer, enter/cancel an edit, then verify the original attachments
|
|
88
|
+
survive an edit submission. CI's gallery double cannot verify native image
|
|
89
|
+
loading, lightbox behavior, or compatibility with DSH's module injection.
|
|
90
|
+
|
|
91
|
+
To add new tests, edit [`test/tree.test.mjs`](file:///D:/dsh-plugin-message-edit/test/tree.test.mjs).
|
|
92
|
+
|
|
93
|
+
### Optional real DSH acceptance
|
|
94
|
+
|
|
95
|
+
`test/fixtures/dsh-acceptance.mjs` is an offline model adapter and live/cold
|
|
96
|
+
session fixture for an installed official DSH `0.1.2-rc.1` runtime. Mount it
|
|
97
|
+
only in a new temporary home whose name contains `message-edit-dsh-qa-`.
|
|
98
|
+
Set `DSH_HOME` to that home and `DSH_QA_MODULES` to the official runtime's
|
|
99
|
+
`node_modules` directory. Use a separate Web profile with the base/Web bundles,
|
|
100
|
+
a built copy of this plugin, and a loader entry for the fixture. Never point
|
|
101
|
+
this fixture at an existing user's DSH home.
|
|
102
|
+
|
|
103
|
+
Once the isolated server prints its URL, run:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
node scripts/verify-dsh-acceptance.mjs http://127.0.0.1:61587
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The verifier exercises real HTTP edit/retry operations, local model execution,
|
|
110
|
+
image retention, unchanged source messages, and nested branch markers. Restart
|
|
111
|
+
the same isolated server and repeat to exercise persisted branches. The fixture
|
|
112
|
+
adds `/qa/state` and `/qa/followup` endpoints solely for this disposable test.
|
|
113
|
+
Browser acceptance additionally checks the settings entry, version switcher,
|
|
114
|
+
thumbnails, and native original-image viewer. No remote API key is needed.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 4. Local Installation into DSH Desktop
|
|
119
|
+
|
|
120
|
+
### Step 1: Build and Package
|
|
121
|
+
```bash
|
|
122
|
+
npm run build
|
|
123
|
+
npm pack
|
|
124
|
+
```
|
|
125
|
+
This produces a tarball: `dsh-plugin-message-edit-0.1.0.tgz`.
|
|
126
|
+
|
|
127
|
+
### Step 2: Install into DSH Profile
|
|
128
|
+
To install into the DSH Desktop profile:
|
|
129
|
+
```bash
|
|
130
|
+
dsh plugin --profile desktop add file:/path/to/dsh-plugin-message-edit-0.1.0.tgz
|
|
131
|
+
```
|
|
132
|
+
Or sync files directly into `~/.dsh/profiles/desktop/node_modules/dsh-plugin-message-edit/`.
|
|
133
|
+
|
|
134
|
+
### Step 3: Restart DSH Desktop
|
|
135
|
+
Restart DSH Desktop to reload the host-side plugin in the server process and mount the updated client interface.
|