dsh-side-chat-plus 0.4.0-alpha.2 → 0.4.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 CHANGED
@@ -1,335 +1,329 @@
1
- # dsh-side-chat — 侧边聊天 (Side chat)
2
-
3
- An **enhanced version of a Codex-style side chat** for
4
- [DSH](https://www.deepseek.com): a dedicated, agentic chat in a right-side
5
- panel, scoped to the conversation it was started from and aware of its
6
- workspace. Select part of a conversation and ask about it in the side chat; the
7
- side chat inherits the main conversation's toolset, model, and permission
8
- preset, and its AI replies can be **brought back to the main conversation**
9
- (directly or as a summary, into the composer draft or as a collapsed context
10
- row).
11
-
12
- On top of the Codex-style base, it adds one extra capability: when the main
13
- agent asks you a **question dialog**, you can side-chat about the question and
14
- its options **without interrupting the flow** — let the AI help you think it
15
- through first, then bring the answer back and answer the dialog.
16
-
17
- > 中文文档见 [README.zh.md](./README.zh.md).
18
-
19
- ## What it does
20
-
21
- - **Select text → ask in a side chat.** Select any part of a message and a
22
- floating button *"Ask in side chat"* appears. The selected text is carried
23
- into the side chat automatically.
24
- - **Per-conversation isolation.** Each side chat is a hidden ordinary DSH
25
- session (`meta.parentSession` links it to the conversation that started it,
26
- and the session is archived so it never appears in the main session list).
27
- Every conversation gets its own side chat.
28
- - **Inherits main-conversation context.** The side chat is aware of the
29
- conversation it was started from and its working directory, and inherits the
30
- main conversation's toolset, model, thinking effort, and permission preset by
31
- default — so it can act on the same workspace as the main conversation.
32
- - **Model / effort / permission are adjustable.** A two-level model menu
33
- (provider → model → effort) and a permission menu are copied from the main
34
- conversation, so each side chat can be tuned independently.
35
- - **"Look up workspace / parent when needed" switch** (default off). When on,
36
- the side chat may read files from the workspace and the parent conversation
37
- when it needs more information.
38
- - **Normal conversation capabilities.** Markdown replies, thinking/reasoning
39
- display, image attachments (paste / drag-and-drop), send/stop controls, and
40
- thinking-duration display — all reuse the same UI primitives as the main
41
- conversation.
42
- - **Bring AI replies back to the main conversation.** Every assistant reply in
43
- the side chat can be brought into the current main conversation: select part
44
- of it with the mouse, or insert the whole reply in one click. Either way you
45
- can choose **"Insert directly"** (verbatim) or **"Summarize & insert"** (the
46
- side chat's inherited model summarizes it first). Where it lands is
47
- configurable: **into the composer draft**, or **as a collapsed context row**
48
- (injected as context — not into the composer, never sent).
49
- - **Ask about the current question dialog — without interrupting the flow.**
50
- When the main conversation shows a question dialog (the agent asking you
51
- something), the side panel automatically lists the question and each option
52
- (no manual text selection needed). Each question has "Bring all" and each
53
- option has "Bring", both offering to **continue an existing side chat** or
54
- **start a new one**. The list can be **collapsed / expanded**, and items can
55
- be **deleted individually or all at once** (deleted items stay gone).
56
- - **Deletable side chats.** Each entry in the side-chat list can be deleted
57
- individually, or all of them at once via "Delete all".
58
- - **Resolution-aware, resizable, collapsible panel.** Drag to resize
59
- (280–720 px); the width is automatically capped to keep the panel within
60
- ~40% of the window and to leave the main chat usable, re-adapts when the
61
- window is resized (different monitor, zoom), and is remembered across
62
- reloads. Collapse and expand; no close button.
63
- - **Language-aware.** The plugin follows DSH's language setting (Chinese /
64
- English).
65
-
66
- ## Codex-style side chat, enhanced
67
-
68
- This plugin is an **enhanced version of a Codex-style side chat**: a second,
69
- agentic chat that works beside the main conversation — plus an extra capability
70
- on top of the Codex-style base: side-chatting about the current **question
71
- dialog** without interrupting the flow.
72
-
73
- **The Codex-style base.** The side chat is a real DSH agent session, not a
74
- scratchpad:
75
-
76
- - It **inherits the main conversation's setup** — toolset, permission preset,
77
- model / thinking effort, and working directory — so it can read and work with
78
- the same workspace as the main conversation, subject to the permission preset
79
- you choose for it.
80
- - The **"Look up workspace / parent when needed"** switch (default off) lets it
81
- read workspace files and the parent conversation's records when the task needs
82
- more context.
83
- - **Model / thinking effort / permission preset are adjustable per chat**, so a
84
- hard task can be handed to a stronger model without touching the main
85
- conversation.
86
- - The final reply (or a summary of it) can be **brought back** into the main
87
- conversation — into the composer draft or as a collapsed context row — the
88
- Codex-style "hand the result over" without copy-pasting.
89
-
90
- **The enhancement: question-dialog side chat, no flow interruption.** When the
91
- main agent asks you something, the flow normally stops until you answer. This
92
- plugin lets you keep moving: the side panel automatically lists the question and
93
- every option; bring the whole question or any single option into a side chat
94
- (new or existing), let the AI analyze it for you, then bring the answer back and
95
- answer the dialog. The main conversation simply waits — nothing is interrupted,
96
- and you never have to answer before you're ready.
97
-
98
- ## Requirements
99
-
100
- - [Node.js](https://nodejs.org) ≥ 20
101
- - [pnpm](https://pnpm.io)
102
- - DSH ≥ `0.1.0-rc.6` (the harness `engines.dsh` constraint); verified against
103
- `0.1.5-alpha.2` and `0.1.6-alpha.1`. Where those lines differ (`catalog()` vs
104
- `selectFor()` on permission presets, the added `maxImageDimension` image
105
- limit, the `IconSendOutline16` icon removal) the plugin feature-detects and
106
- serves both.
107
- - `pnpm build` ends with `check-seed-exports`, which compares the client
108
- bundle's frozen-module-table imports against a real DSH install. Set
109
- `DSH_INSTALL` (or pass `--shell`) when the install is not discoverable via
110
- `dsh` on `PATH`; without a target the check skips instead of failing.
111
-
112
- ## Build
113
-
114
- ```bash
115
- pnpm install
116
- pnpm build
117
- ```
118
-
119
- `pnpm build` clears `lib/`, runs `tsc -p tsconfig.build.json` for type
120
- declarations, then bundles the host (`lib/index.js`) and client
121
- (`lib/client.js` + `lib/client-registry.js`) with tsdown.
122
-
123
- ## CI and publishing
124
-
125
- [GitHub Actions](.github/workflows) covers two jobs:
126
-
127
- - **[`ci.yml`](.github/workflows/ci.yml)** runs on every push to `main` and on
128
- pull requests: it installs dependencies, typechecks, builds, runs the test
129
- script, and packs the tarball (uploaded as an artifact) for Node 20 and 22.
130
- - **[`publish.yml`](.github/workflows/publish.yml)** fires when a `v*` tag is
131
- pushed: it builds and publishes the package to **npm** as
132
- [`dsh-side-chat-plus`](https://www.npmjs.com/package/dsh-side-chat-plus),
133
- authenticated with the `NPM_TOKEN` repository secret.
134
-
135
- The package name is `dsh-side-chat-plus` (the plain `dsh-side-chat` name on npm
136
- belongs to a different maintainer, so the release is published under the
137
- `-plus` name).
138
-
139
- > **Registry dependencies.** The harness packages this plugin targets (the DSH
140
- > `0.1.2` API line) are published to npm as prereleases (`0.1.2-rc.1`) and, in a
141
- > few cases, declare internal dependencies with a plain `>=0.1.2` range that
142
- > npm/pnpm refuses to match against a prerelease. The developer `pnpm-workspace.yaml`
143
- > therefore points the `@deepseek-ai/dsh-*` packages at a local harness checkout
144
- > (absolute `D:/code/...` paths that exist only on the maintainer's machine). CI
145
- > swaps that file for one whose overrides pin every `@deepseek-ai/dsh-*`
146
- > dependency to `0.1.2-rc.1`, so installs resolve from the registry. On another
147
- > machine, point the overrides at your own harness clone or use the CI workspace
148
- > file as a template.
149
-
150
- ## Deploy
151
-
152
- DSH web loads external plugins from the active profile. This package is a
153
- **bundle**: its `package.json` declares `dsh.bundle.patch` →
154
- [`cordis.patch.yml`](./cordis.patch.yml), whose `insert` row mounts the plugin.
155
- That declaration is what lets `dsh plugin add` install the package *and*
156
- activate it in one step.
157
-
158
- > **Recommended: install from npm.** The package is published to
159
- > [npm](https://www.npmjs.com/package/dsh-side-chat-plus) as
160
- > `dsh-side-chat-plus`, so `dsh plugin add` needs no source checkout — the
161
- > tarball ships the prebuilt `lib/` and is activated in one go.
162
-
163
- ### Install from npm
164
-
165
- The built package is published to npm as [`dsh-side-chat-plus`](https://www.npmjs.com/package/dsh-side-chat-plus). Installed from the registry, the tarball ships the prebuilt `lib/` (plus `cordis.patch.yml` and `dsh.plugin.json`), so no source build is needed:
166
-
167
- ```bash
168
- npx -p @deepseek-ai/dsh dsh plugin --profile web add dsh-side-chat-plus
169
- ```
170
-
171
- `dsh plugin` reconciles the bundle into the profile's `dsh.profile.bundles`
172
- layer list; the prebuilt `lib/` means nothing extra runs at install time.
173
- Version falls out of the tagged release (see [CI and publishing](#ci-and-publishing)).
174
-
175
- Restart `dsh web`, then hard-refresh the page (Ctrl/Cmd+Shift+R).
176
-
177
- ### Install from GitHub
178
-
179
- ```bash
180
- npx -p @deepseek-ai/dsh dsh plugin --profile web add github:heartmove/dsh-side-chat-plus
181
- ```
182
-
183
- `dsh plugin` forwards to pnpm inside `~/.dsh/profiles/web/`, then reconciles the
184
- bundle into the profile's `dsh.profile.bundles` layer list. A git install
185
- fetches sources, so pnpm runs the package's `prepare` script (`tsdown`) to build
186
- `lib/` from `src/` after checkout.
187
-
188
- pnpm ≥ 10 refuses to run a git dependency's `prepare` script until it is
189
- allowlisted, so the first `add` fails with an "Ignored build scripts" hint. Copy
190
- the exact package key pnpm printed into the profile's `pnpm-workspace.yaml`
191
- (`~/.dsh/profiles/web/pnpm-workspace.yaml`):
192
-
193
- ```yaml
194
- allowBuilds:
195
- dsh-side-chat-plus: true
196
- ```
197
-
198
- then re-run the `add`. That allowance means "run this package's code on my
199
- machine at install time" — only allow packages whose source you trust, and pin a
200
- commit (`github:heartmove/dsh-side-chat-plus#<sha>`) so a later push cannot
201
- silently change what runs.
202
-
203
- Restart `dsh web`, then hard-refresh the page (Ctrl/Cmd+Shift+R).
204
-
205
- ### Install from a local checkout
206
-
207
- From the directory that contains this checkout:
208
-
209
- ```bash
210
- npx -p @deepseek-ai/dsh dsh plugin --profile web add ./dsh-side-chat-plus
211
- ```
212
-
213
- pnpm links the checkout and `dsh` activates the bundle the same way.
214
-
215
- ### Manual link
216
-
217
- To manage the profile by hand, link the package and list it as a bundle in
218
- `~/.dsh/profiles/web/package.json` (the bundle's own `cordis.patch.yml` supplies
219
- the loader row, so no `insert` entry is needed):
220
-
221
- ```json
222
- {
223
- "dependencies": {
224
- "dsh-side-chat-plus": "link:D:\\path\\to\\dsh-side-chat-plus"
225
- },
226
- "dsh": {
227
- "profile": {
228
- "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-side-chat-plus"]
229
- }
230
- }
231
- }
232
- ```
233
-
234
- (On POSIX systems use `link:/path/to/dsh-side-chat-plus`.) Then run `pnpm install`
235
- in the profile directory and restart `dsh web`.
236
-
237
- ## Usage
238
-
239
- 1. Select part of any message in the main conversation.
240
- 2. A floating **"Ask in side chat"** button appears — click it.
241
- - If a side chat already exists for this conversation, you'll also see
242
- **"Continue active side chat"**.
243
- 3. The right-side panel opens (or expands) with the selected text staged in the
244
- composer.
245
- 4. Adjust **model / effort** and **permission**, and toggle **"Look up workspace
246
- / parent when needed"** as desired.
247
- 5. Send. The reply streams back with markdown rendering and, where applicable,
248
- a "Think" row for the model's reasoning.
249
- 6. Drag the panel's left edge to resize, or use the collapse/expand control.
250
-
251
- ### Sending behavior
252
-
253
- By default (`sendImmediately` on), selecting text **sends it immediately** and
254
- appends your configured **default prompt**. Turn `sendImmediately` off in
255
- settings to stage the selection as an attachment instead, so you can review and
256
- edit before sending.
257
-
258
- ### Bring replies back to the main conversation
259
-
260
- Assistant replies in the side chat can be brought into the current main
261
- conversation (**never sent**):
262
-
263
- 1. **Bring a selection.** Select part of an assistant reply in the side chat,
264
- then choose **"Insert directly"** (verbatim) or **"Summarize & insert"** (the
265
- side chat's inherited model summarizes it first) from the floating menu.
266
- 2. **Bring the whole reply.** Each assistant reply has **"Insert directly"** and
267
- **"Summarize & insert"** buttons under its text, for inserting the full reply
268
- (or its summary) in one click.
269
- 3. Per the **bring-back target** setting, the content is either **appended to
270
- the main composer draft** (edit before sending) or **injected as a collapsed
271
- context row** (source-tagged, not into the composer; the model sees it next
272
- turn).
273
-
274
- ![Bring-back demo](docs/bring-back.gif)
275
-
276
- ### Ask about the current question dialog
277
-
278
- When the main conversation shows a question dialog, the side panel automatically
279
- lists the question and its options:
280
-
281
- 1. While the panel is closed, a **floating entry** appears beside the dialog's
282
- header — click it to open the panel.
283
- 2. Each question has "Bring all" and each option has "Bring", both offering to
284
- **continue an existing side chat** or **start a new one**.
285
- 3. The list can be **collapsed / expanded**, and items can be **deleted
286
- individually or all at once** (deleted items stay gone).
287
-
288
- The main conversation keeps waiting on the dialog — nothing is interrupted; do
289
- the research in the side chat first, then bring the answer back and answer the
290
- dialog.
291
-
292
- ![Question dialog demo](docs/question-dialog.gif)
293
-
294
- ### Delete side chats
295
-
296
- Each entry in the side-chat list has a "×" delete button; "Delete all" at the
297
- top-right removes every side chat of the current conversation.
298
-
299
- ## Settings
300
-
301
- Open DSH **Settings → 侧边聊天 (Side chat)** to configure:
302
-
303
- | Setting | Default | Description |
304
- | --- | --- | --- |
305
- | `lookupDefault` | off | Whether the "look up workspace / parent" switch is on by default for new side chats. |
306
- | `sendImmediately` | on | Whether selecting text sends it immediately, or stages it as an attachment. |
307
- | `defaultPrompt` | *(empty)* | Extra prompt appended when the selection is sent immediately. |
308
- | `bringMode` | `draft` | Where brought-back content lands: `draft` into the composer, or `context` as a collapsed context row. |
309
- | `panelHome` | `sidebar-right` | Where the panel lives: `sidebar-right` docks it into the new built-in right sidebar as a "Side chat" tab (same rail as document previews, default); `floating` is the classic drag-resizable right-edge panel. In floating mode the side chat yields automatically while the built-in sidebar is open (docked, fullscreen or floated), so the two never cover each other. |
310
-
311
- Preferences are stored in the DSH settings namespace `dsh-side-chat`.
312
-
313
- ## Project layout
314
-
315
- ```
316
- src/
317
- index.ts host plugin (routes, session/agent lifecycle, transcript folding)
318
- wire.ts request/response helpers
319
- trust-fence.ts loopback / trusted-API request guard
320
- settings-shared.ts preference vocabulary shared by host and client
321
- context-types.ts Cordis Context type augmentation
322
- client/
323
- index.tsx client plugin (panel, composer, settings section, floating buttons)
324
- api.ts client↔host API types
325
- locales.ts zh/en dictionaries
326
- client.module.css panel/composer/settings styles
327
- layout.css #root margin-right driven by panel width
328
- cordis.patch.yml bundle patch layer (inserts the loader row; dsh.bundle.patch)
329
- dsh.plugin.json external plugin manifest
330
- tsdown.config.ts bundle config (client externals + CSS inlining)
331
- ```
332
-
333
- ## License
334
-
335
- [MIT](./LICENSE)
1
+ # dsh-side-chat — 侧边聊天 (Side chat)
2
+
3
+ Targets DSH **0.1.7-alpha.1**. See [compatibility changes](CHANGELOG.md).
4
+
5
+ An **enhanced version of a Codex-style side chat** for
6
+ [DSH](https://www.deepseek.com): a dedicated, agentic chat in a right-side
7
+ panel, scoped to the conversation it was started from and aware of its
8
+ workspace. Select part of a conversation and ask about it in the side chat; the
9
+ side chat inherits the main conversation's toolset, model, and permission
10
+ preset, and its AI replies can be **brought back to the main conversation**
11
+ (directly or as a summary, into the composer draft or as a collapsed context
12
+ row).
13
+
14
+ On top of the Codex-style base, it adds one extra capability: when the main
15
+ agent asks you a **question dialog**, you can side-chat about the question and
16
+ its options **without interrupting the flow** — let the AI help you think it
17
+ through first, then bring the answer back and answer the dialog.
18
+
19
+ > 中文文档见 [README.zh.md](./README.zh.md).
20
+
21
+ ## What it does
22
+
23
+ - **Select text → ask in a side chat.** Select any part of a message and a
24
+ floating button *"Ask in side chat"* appears. The selected text is carried
25
+ into the side chat automatically.
26
+ - **Per-conversation isolation.** Each side chat is a hidden ordinary DSH
27
+ session (`meta.parentSession` links it to the conversation that started it,
28
+ and the session is archived so it stays out of the main session list).
29
+ DSH 0.1.7 refuses a model step to an archived session, so the archive is
30
+ lifted for exactly as long as a side-chat turn is running and restored when
31
+ it settles. A host-created session is never accounted to a Workspace, so even
32
+ in that window it can only surface under the sidebar's *Ungrouped* bucket,
33
+ never under the workspace it was started from.
34
+ Every conversation gets its own side chat.
35
+ - **Inherits main-conversation context.** The side chat is aware of the
36
+ conversation it was started from and its working directory, and inherits the
37
+ main conversation's toolset, model, thinking effort, and permission preset by
38
+ default — so it can act on the same workspace as the main conversation.
39
+ - **Model / effort / permission are adjustable.** A two-level model menu
40
+ (provider → model → effort) and a permission menu are copied from the main
41
+ conversation, so each side chat can be tuned independently.
42
+ - **"Look up workspace / parent when needed" switch** (default off). When on,
43
+ the side chat may read files from the workspace and the parent conversation
44
+ when it needs more information.
45
+ - **Normal conversation capabilities.** Markdown replies, thinking/reasoning
46
+ display, image attachments (paste / drag-and-drop), send/stop controls, and
47
+ thinking-duration display — all reuse the same UI primitives as the main
48
+ conversation.
49
+ - **Bring AI replies back to the main conversation.** Every assistant reply in
50
+ the side chat can be brought into the current main conversation: select part
51
+ of it with the mouse, or insert the whole reply in one click. Either way you
52
+ can choose **"Insert directly"** (verbatim) or **"Summarize & insert"** (the
53
+ side chat's inherited model summarizes it first). Where it lands is
54
+ configurable: **into the composer draft**, or **as a collapsed context row**
55
+ (injected as context — not into the composer, never sent).
56
+ - **Ask about the current question dialog — without interrupting the flow.**
57
+ When the main conversation shows a question dialog (the agent asking you
58
+ something), the side panel automatically lists the question and each option
59
+ (no manual text selection needed). Each question has "Bring all" and each
60
+ option has "Bring", both offering to **continue an existing side chat** or
61
+ **start a new one**. The list can be **collapsed / expanded**, and items can
62
+ be **deleted individually or all at once** (deleted items stay gone).
63
+ - **Deletable side chats.** Each entry in the side-chat list can be deleted
64
+ individually, or all of them at once via "Delete all".
65
+ - **Resolution-aware, resizable, collapsible panel.** Drag to resize
66
+ (280–720 px); the width is automatically capped to keep the panel within
67
+ ~40% of the window and to leave the main chat usable, re-adapts when the
68
+ window is resized (different monitor, zoom), and is remembered across
69
+ reloads. Collapse and expand; no close button.
70
+ - **Language-aware.** The plugin follows DSH's language setting (Chinese /
71
+ English).
72
+
73
+ ## Codex-style side chat, enhanced
74
+
75
+ This plugin is an **enhanced version of a Codex-style side chat**: a second,
76
+ agentic chat that works beside the main conversation — plus an extra capability
77
+ on top of the Codex-style base: side-chatting about the current **question
78
+ dialog** without interrupting the flow.
79
+
80
+ **The Codex-style base.** The side chat is a real DSH agent session, not a
81
+ scratchpad:
82
+
83
+ - It **inherits the main conversation's setup** — toolset, permission preset,
84
+ model / thinking effort, and working directory — so it can read and work with
85
+ the same workspace as the main conversation, subject to the permission preset
86
+ you choose for it.
87
+ - The **"Look up workspace / parent when needed"** switch (default off) lets it
88
+ read workspace files and the parent conversation's records when the task needs
89
+ more context.
90
+ - **Model / thinking effort / permission preset are adjustable per chat**, so a
91
+ hard task can be handed to a stronger model without touching the main
92
+ conversation.
93
+ - The final reply (or a summary of it) can be **brought back** into the main
94
+ conversation — into the composer draft or as a collapsed context row — the
95
+ Codex-style "hand the result over" without copy-pasting.
96
+
97
+ **The enhancement: question-dialog side chat, no flow interruption.** When the
98
+ main agent asks you something, the flow normally stops until you answer. This
99
+ plugin lets you keep moving: the side panel automatically lists the question and
100
+ every option; bring the whole question or any single option into a side chat
101
+ (new or existing), let the AI analyze it for you, then bring the answer back and
102
+ answer the dialog. The main conversation simply waits — nothing is interrupted,
103
+ and you never have to answer before you're ready.
104
+
105
+ ## Requirements
106
+
107
+ - [Node.js](https://nodejs.org) ≥ 20
108
+ - [pnpm](https://pnpm.io)
109
+ - DSH `0.1.7-alpha.1` (`^0.1.7-0`).
110
+ - `pnpm build` ends with `check-seed-exports`, which compares the client
111
+ bundle's frozen-module-table imports against a real DSH install. Set
112
+ `DSH_INSTALL` (or pass `--shell`) when the install is not discoverable via
113
+ `dsh` on `PATH`; without a target the check skips instead of failing.
114
+
115
+ ## Build
116
+
117
+ ```bash
118
+ pnpm install
119
+ pnpm build
120
+ ```
121
+
122
+ `pnpm build` clears `lib/`, runs `tsc -p tsconfig.build.json` for type
123
+ declarations, then bundles the host (`lib/index.js`) and client
124
+ (`lib/client.js` + `lib/client-registry.js`) with tsdown.
125
+
126
+ ## CI and publishing
127
+
128
+ [GitHub Actions](.github/workflows) covers two jobs:
129
+
130
+ - **[`ci.yml`](.github/workflows/ci.yml)** runs on every push to `main` and on
131
+ pull requests: it installs dependencies, typechecks, builds, runs the test
132
+ script, and packs the tarball (uploaded as an artifact) for Node 20 and 22.
133
+ - **[`publish.yml`](.github/workflows/publish.yml)** fires when a `v*` tag is
134
+ pushed: it builds and publishes the package to **npm** as
135
+ [`dsh-side-chat-plus`](https://www.npmjs.com/package/dsh-side-chat-plus),
136
+ authenticated with the `NPM_TOKEN` repository secret.
137
+
138
+ The package name is `dsh-side-chat-plus` (the plain `dsh-side-chat` name on npm
139
+ belongs to a different maintainer, so the release is published under the
140
+ `-plus` name).
141
+
142
+ > **Registry dependencies.** Local and CI builds use exact DSH `0.1.7-alpha.1` development dependencies and the checked-in lockfile. No local harness checkout or CI-only overrides are required.
143
+
144
+ ## Deploy
145
+
146
+ DSH web loads external plugins from the active profile. This package is a
147
+ **bundle**: its `package.json` declares `dsh.bundle.patch` →
148
+ [`cordis.patch.yml`](./cordis.patch.yml), whose `insert` row mounts the plugin.
149
+ That declaration is what lets `dsh plugin add` install the package *and*
150
+ activate it in one step.
151
+
152
+ > **Recommended: install from npm.** The package is published to
153
+ > [npm](https://www.npmjs.com/package/dsh-side-chat-plus) as
154
+ > `dsh-side-chat-plus`, so `dsh plugin add` needs no source checkout — the
155
+ > tarball ships the prebuilt `lib/` and is activated in one go.
156
+
157
+ ### Install from npm
158
+
159
+ The built package is published to npm as [`dsh-side-chat-plus`](https://www.npmjs.com/package/dsh-side-chat-plus). Installed from the registry, the tarball ships the prebuilt `lib/` (plus `cordis.patch.yml` and `dsh.plugin.json`), so no source build is needed:
160
+
161
+ ```bash
162
+ npx -p @deepseek-ai/dsh dsh plugin --profile web add dsh-side-chat-plus
163
+ ```
164
+
165
+ `dsh plugin` reconciles the bundle into the profile's `dsh.profile.bundles`
166
+ layer list; the prebuilt `lib/` means nothing extra runs at install time.
167
+ Version falls out of the tagged release (see [CI and publishing](#ci-and-publishing)).
168
+
169
+ Restart `dsh web`, then hard-refresh the page (Ctrl/Cmd+Shift+R).
170
+
171
+ ### Install from GitHub
172
+
173
+ ```bash
174
+ npx -p @deepseek-ai/dsh dsh plugin --profile web add github:heartmove/dsh-side-chat-plus
175
+ ```
176
+
177
+ `dsh plugin` forwards to pnpm inside `~/.dsh/profiles/web/`, then reconciles the
178
+ bundle into the profile's `dsh.profile.bundles` layer list. A git install
179
+ fetches sources, so pnpm runs the package's `prepare` script (`tsdown`) to build
180
+ `lib/` from `src/` after checkout.
181
+
182
+ pnpm ≥ 10 refuses to run a git dependency's `prepare` script until it is
183
+ allowlisted, so the first `add` fails with an "Ignored build scripts" hint. Copy
184
+ the exact package key pnpm printed into the profile's `pnpm-workspace.yaml`
185
+ (`~/.dsh/profiles/web/pnpm-workspace.yaml`):
186
+
187
+ ```yaml
188
+ allowBuilds:
189
+ dsh-side-chat-plus: true
190
+ ```
191
+
192
+ then re-run the `add`. That allowance means "run this package's code on my
193
+ machine at install time" — only allow packages whose source you trust, and pin a
194
+ commit (`github:heartmove/dsh-side-chat-plus#<sha>`) so a later push cannot
195
+ silently change what runs.
196
+
197
+ Restart `dsh web`, then hard-refresh the page (Ctrl/Cmd+Shift+R).
198
+
199
+ ### Install from a local checkout
200
+
201
+ From the directory that contains this checkout:
202
+
203
+ ```bash
204
+ npx -p @deepseek-ai/dsh dsh plugin --profile web add ./dsh-side-chat-plus
205
+ ```
206
+
207
+ pnpm links the checkout and `dsh` activates the bundle the same way.
208
+
209
+ ### Manual link
210
+
211
+ To manage the profile by hand, link the package and list it as a bundle in
212
+ `~/.dsh/profiles/web/package.json` (the bundle's own `cordis.patch.yml` supplies
213
+ the loader row, so no `insert` entry is needed):
214
+
215
+ ```json
216
+ {
217
+ "dependencies": {
218
+ "dsh-side-chat-plus": "link:D:\\path\\to\\dsh-side-chat-plus"
219
+ },
220
+ "dsh": {
221
+ "profile": {
222
+ "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-side-chat-plus"]
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ (On POSIX systems use `link:/path/to/dsh-side-chat-plus`.) Then run `pnpm install`
229
+ in the profile directory and restart `dsh web`.
230
+
231
+ ## Usage
232
+
233
+ 1. Select part of any message in the main conversation.
234
+ 2. A floating **"Ask in side chat"** button appears — click it.
235
+ - If a side chat already exists for this conversation, you'll also see
236
+ **"Continue active side chat"**.
237
+ 3. The right-side panel opens (or expands) with the selected text staged in the
238
+ composer.
239
+ 4. Adjust **model / effort** and **permission**, and toggle **"Look up workspace
240
+ / parent when needed"** as desired.
241
+ 5. Send. The reply streams back with markdown rendering and, where applicable,
242
+ a "Think" row for the model's reasoning.
243
+ 6. Drag the panel's left edge to resize, or use the collapse/expand control.
244
+
245
+ ### Sending behavior
246
+
247
+ By default (`sendImmediately` on), selecting text **sends it immediately** and
248
+ appends your configured **default prompt**. Turn `sendImmediately` off in
249
+ settings to stage the selection as an attachment instead, so you can review and
250
+ edit before sending.
251
+
252
+ ### Bring replies back to the main conversation
253
+
254
+ Assistant replies in the side chat can be brought into the current main
255
+ conversation (**never sent**):
256
+
257
+ 1. **Bring a selection.** Select part of an assistant reply in the side chat,
258
+ then choose **"Insert directly"** (verbatim) or **"Summarize & insert"** (the
259
+ side chat's inherited model summarizes it first) from the floating menu.
260
+ 2. **Bring the whole reply.** Each assistant reply has **"Insert directly"** and
261
+ **"Summarize & insert"** buttons under its text, for inserting the full reply
262
+ (or its summary) in one click.
263
+ 3. Per the **bring-back target** setting, the content is either **appended to
264
+ the main composer draft** (edit before sending) or **injected as a collapsed
265
+ context row** (source-tagged, not into the composer; the model sees it next
266
+ turn).
267
+
268
+ ![Bring-back demo](docs/bring-back.gif)
269
+
270
+ ### Ask about the current question dialog
271
+
272
+ When the main conversation shows a question dialog, the side panel automatically
273
+ lists the question and its options:
274
+
275
+ 1. While the panel is closed, a **floating entry** appears beside the dialog's
276
+ header — click it to open the panel.
277
+ 2. Each question has "Bring all" and each option has "Bring", both offering to
278
+ **continue an existing side chat** or **start a new one**.
279
+ 3. The list can be **collapsed / expanded**, and items can be **deleted
280
+ individually or all at once** (deleted items stay gone).
281
+
282
+ The main conversation keeps waiting on the dialog — nothing is interrupted; do
283
+ the research in the side chat first, then bring the answer back and answer the
284
+ dialog.
285
+
286
+ ![Question dialog demo](docs/question-dialog.gif)
287
+
288
+ ### Delete side chats
289
+
290
+ Each entry in the side-chat list has a "×" delete button; "Delete all" at the
291
+ top-right removes every side chat of the current conversation.
292
+
293
+ ## Settings
294
+
295
+ Open DSH **Settings → 侧边聊天 (Side chat)** to configure:
296
+
297
+ | Setting | Default | Description |
298
+ | --- | --- | --- |
299
+ | `lookupDefault` | off | Whether the "look up workspace / parent" switch is on by default for new side chats. |
300
+ | `sendImmediately` | on | Whether selecting text sends it immediately, or stages it as an attachment. |
301
+ | `defaultPrompt` | *(empty)* | Extra prompt appended when the selection is sent immediately. |
302
+ | `bringMode` | `draft` | Where brought-back content lands: `draft` into the composer, or `context` as a collapsed context row. |
303
+ | `panelHome` | `sidebar-right` | Where the panel lives: `sidebar-right` docks it into the new built-in right sidebar as a "Side chat" tab (same rail as document previews, default); `floating` is the classic drag-resizable right-edge panel. In floating mode the side chat yields automatically while the built-in sidebar is open (docked, fullscreen or floated), so the two never cover each other. |
304
+
305
+ Preferences are stored in the DSH settings namespace `dsh-side-chat`.
306
+
307
+ ## Project layout
308
+
309
+ ```
310
+ src/
311
+ index.ts host plugin (routes, session/agent lifecycle, transcript folding)
312
+ wire.ts request/response helpers
313
+ trust-fence.ts loopback / trusted-API request guard
314
+ settings-shared.ts preference vocabulary shared by host and client
315
+ context-types.ts Cordis Context type augmentation
316
+ client/
317
+ index.tsx client plugin (panel, composer, settings section, floating buttons)
318
+ api.ts client↔host API types
319
+ locales.ts zh/en dictionaries
320
+ client.module.css panel/composer/settings styles
321
+ layout.css #root margin-right driven by panel width
322
+ cordis.patch.yml bundle patch layer (inserts the loader row; dsh.bundle.patch)
323
+ dsh.plugin.json external plugin manifest
324
+ tsdown.config.ts bundle config (client externals + CSS inlining)
325
+ ```
326
+
327
+ ## License
328
+
329
+ [MIT](./LICENSE)