@ahggg/dsh-side-chat 0.7.3 → 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.
Files changed (80) hide show
  1. package/README.md +52 -20
  2. package/README.zh-CN.md +52 -20
  3. package/lib/client/apply.js +48 -32
  4. package/lib/client/contracts.d.ts +3 -58
  5. package/lib/client/dsh/ReadOnlyConversation.d.ts +10 -0
  6. package/lib/client/dsh/ReadOnlyConversation.js +99 -0
  7. package/lib/client/dsh/SideChatOverlay.d.ts +7 -0
  8. package/lib/client/{rc6/Rc6SideChatOverlay.js → dsh/SideChatOverlay.js} +26 -56
  9. package/lib/client/{rc6 → dsh}/context.d.ts +4 -6
  10. package/lib/client/dsh/conversation-store.d.ts +22 -0
  11. package/lib/client/dsh/conversation-store.js +19 -0
  12. package/lib/client/dsh/primitive-labels.d.ts +2 -0
  13. package/lib/client/dsh/primitive-labels.js +4 -0
  14. package/lib/client/{rc6 → dsh}/remote-adapter.d.ts +2 -2
  15. package/lib/client/dsh/remote-adapter.js +22 -0
  16. package/lib/client/dsh/sessions-adapter.d.ts +56 -0
  17. package/lib/client/dsh/sessions-adapter.js +185 -0
  18. package/lib/client/index.d.ts +2 -2
  19. package/lib/client/panel/SelectionQuote.js +8 -1
  20. package/lib/client/panel/SideChatErrorState.js +1 -1
  21. package/lib/client/panel/SideChatModelSelect.d.ts +2 -1
  22. package/lib/client/panel/SideChatModelSelect.js +43 -24
  23. package/lib/client/panel/SideChatPanel.js +12 -5
  24. package/lib/client/panel/messages.d.ts +1 -1
  25. package/lib/client/panel/messages.js +8 -8
  26. package/lib/client/parent-composer/ParentConversationAnnotations.d.ts +3 -2
  27. package/lib/client/parent-composer/ParentConversationAnnotations.js +50 -30
  28. package/lib/client/parent-composer/add-to-conversation.d.ts +14 -28
  29. package/lib/client/parent-composer/add-to-conversation.js +131 -545
  30. package/lib/client/parent-composer/annotation-persistence.d.ts +8 -8
  31. package/lib/client/parent-composer/annotation-persistence.js +106 -102
  32. package/lib/client/parent-composer/composer-reference.d.ts +17 -55
  33. package/lib/client/parent-composer/composer-reference.js +20 -72
  34. package/lib/client/parent-composer/referenced-conversation.d.ts +5 -7
  35. package/lib/client/parent-composer/referenced-conversation.js +21 -73
  36. package/lib/client/selection/ConversationAnnotationMarkers.js +5 -1
  37. package/lib/client/selection/SelectionActions.js +2 -1
  38. package/lib/client/selection/conversation-dom.d.ts +5 -0
  39. package/lib/client/selection/conversation-dom.js +24 -0
  40. package/lib/client/selection/selection-controller.d.ts +1 -1
  41. package/lib/client/selection/selection-controller.js +7 -3
  42. package/lib/client/selection/selection-normalizer.js +1 -1
  43. package/lib/client/side-chat-controller.d.ts +28 -41
  44. package/lib/client/side-chat-controller.js +247 -337
  45. package/lib/client.js +84 -54
  46. package/lib/host/index.d.ts +1 -1
  47. package/lib/host/index.js +1 -1
  48. package/lib/host/read-only-chat-service.d.ts +30 -0
  49. package/lib/host/read-only-chat-service.js +315 -0
  50. package/lib/index.d.ts +16 -8
  51. package/lib/index.js +34 -13
  52. package/lib/remote.d.ts +23 -14
  53. package/lib/remote.js +2 -5
  54. package/lib/shared/constants.d.ts +0 -13
  55. package/lib/shared/constants.js +0 -13
  56. package/lib/shared/contracts.d.ts +52 -19
  57. package/lib/shared/contracts.js +2 -3
  58. package/lib/shared/error-codes.d.ts +1 -1
  59. package/lib/shared/error-codes.js +2 -2
  60. package/lib/shared/{archived-wire.d.ts → side-chat-wire.d.ts} +109 -20
  61. package/lib/shared/side-chat-wire.js +34 -0
  62. package/lib/typert.d.ts +3 -68
  63. package/lib/typert.js +60 -4
  64. package/package.json +76 -56
  65. package/lib/client/rc6/ArchivedConversation.d.ts +0 -14
  66. package/lib/client/rc6/ArchivedConversation.js +0 -231
  67. package/lib/client/rc6/Rc6SideChatOverlay.d.ts +0 -7
  68. package/lib/client/rc6/SideChatTool.d.ts +0 -22
  69. package/lib/client/rc6/SideChatTool.js +0 -404
  70. package/lib/client/rc6/primitive-labels.d.ts +0 -7
  71. package/lib/client/rc6/primitive-labels.js +0 -60
  72. package/lib/client/rc6/remote-adapter.js +0 -34
  73. package/lib/client/rc6/runtime-compat.d.ts +0 -120
  74. package/lib/client/rc6/runtime-compat.js +0 -187
  75. package/lib/client/rc6/sessions-adapter.d.ts +0 -64
  76. package/lib/client/rc6/sessions-adapter.js +0 -387
  77. package/lib/host/archived-fork-service.d.ts +0 -28
  78. package/lib/host/archived-fork-service.js +0 -309
  79. package/lib/shared/archived-wire.js +0 -47
  80. /package/lib/client/{rc6 → dsh}/context.js +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dsh-side-chat
2
2
 
3
- Ask a focused follow-up about selected text without leaving your current DeepSeek Harness conversation.
3
+ Ask a focused follow-up about selected text without leaving your current conversation in **DeepSeek Harness Desktop or Web**.
4
4
 
5
5
  [简体中文](README.zh-CN.md)
6
6
 
@@ -8,12 +8,28 @@ Ask a focused follow-up about selected text without leaving your current DeepSee
8
8
 
9
9
  ## Install
10
10
 
11
- Install DSH 0.1.2-rc.1 if it is not already available. Refresh only this package's registry metadata before adding it, so pnpm cannot reuse an older `latest` value immediately after a release:
11
+ Use these exact versions together:
12
+
13
+ - **Side Chat:** `@ahggg/dsh-side-chat@1.0.0`
14
+ - **DeepSeek Harness runtime:** `0.2.0-rc.1`
15
+
16
+ ### DeepSeek Harness Desktop
17
+
18
+ Side Chat supports the Desktop app. In its plugin installation dialog, enter the complete package spec:
19
+
20
+ ```text
21
+ @ahggg/dsh-side-chat@1.0.0
22
+ ```
23
+
24
+ Install it, then restart DeepSeek Harness Desktop. Use the Desktop app's plugin manager for Desktop installations; the CLI commands below target the Web profile.
25
+
26
+ ### Web (`dsh web`)
27
+
28
+ Install the matching DSH CLI if needed, then install the pinned plugin version:
12
29
 
13
30
  ```powershell
14
- npm install --global @deepseek-ai/dsh@0.1.2-rc.1
15
- pnpm cache delete "@ahggg/dsh-side-chat"
16
- dsh plugin --profile web add @ahggg/dsh-side-chat@latest
31
+ npm install --global @deepseek-ai/dsh@0.2.0-rc.1
32
+ dsh plugin --profile web add @ahggg/dsh-side-chat@1.0.0 --save-exact
17
33
  ```
18
34
 
19
35
  Start DSH from the project you want the agent to work in:
@@ -43,48 +59,64 @@ On phones and tablets, long-press text in a completed message to select it. The
43
59
  3. Click `Add to chat` to add an optional comment before attaching the passage to the main composer, `More details` to send an explanation request immediately, or `Ask in side chat` to write a focused question.
44
60
  4. When writing your own message or question, press `Enter` to send it.
45
61
  5. After a Side Chat reply settles, click `Add to conversation` to attach that focused discussion to the main composer as one conversation reference.
46
- 6. Press `Esc` or click `×` when you are done.
62
+ 6. Click `×`, or press `Esc` while focused inside the Side Chat panel, when you are done. Escape in another composer or menu does not close Side Chat.
47
63
 
48
64
  Useful details:
49
65
 
50
66
  - `Shift+Enter` inserts a newline.
51
- - Use the model control beside Send to choose a provider/model and its available reasoning effort. The choice belongs to Side Chat, does not change the main conversation, and becomes the global default for the next Side Chat.
67
+ - Use the model control beside Send, then **Effort**, to choose the model's thinking level. You can change it while a reply runs: that reply keeps its original settings, while the accepted choice applies to the next reply and is remembered for future **Ask in side chat** and **More details** requests in this client. It does not change the main conversation.
52
68
  - After clicking `Add to chat`, press `Enter` or click `Save` to keep the annotation. Click outside the comment box or click `Cancel` to discard it.
53
69
  - `Add to chat` keeps any existing draft text and can collect multiple numbered passages, each with its own optional comment, in one annotation capsule.
54
70
  - `Add to conversation` captures the Side Chat's user/assistant history, preserves the main draft, and refreshes the existing reference instead of duplicating it when clicked again.
55
71
  - The input grows with its content and becomes scrollable at its maximum height.
56
- - While a reply is running, the send icon becomes a stop button.
72
+ - The composer has one action button: **Send** when idle, **Stop** while generating. It returns to Send after the reply finishes or stops, keeping any follow-up draft. Text entered while a send is pending stays in the composer.
57
73
  - Assistant replies use DSH's native Markdown rendering.
58
74
  - Hover over `N annotations` to preview every selected passage and its comment.
59
75
  - Before sending, hover over the annotation capsule and click `×` to remove it; after sending, the same capsule appears above the user message.
60
- - The main conversation stays visible and is never switched to the child Session.
76
+ - Your first Side Chat message displays the selected passage as one annotation and your question as plain text, not internal XML.
77
+ - The main conversation stays visible. No child Session is created.
61
78
 
62
79
  ## What happens to the conversation
63
80
 
64
- The first send creates a real DSH Session fork at the selected message. The child inherits the complete event prefix, presets, and workspace. Its initial model choice uses the last accepted Side Chat choice when one is saved, otherwise it snapshots the parent configuration; later model or reasoning-effort changes apply to subsequent child steps without changing or following the parent conversation. Keeping the prefix unchanged is friendly to provider prompt caching, although a cache hit is never guaranteed.
81
+ `Ask in side chat` and `More details` use direct, streaming model calls through DSH's configured provider. On the first send, the plugin captures a **read-only text snapshot** of the parent conversation through the selected message's completed turn. Later parent prompts are excluded, and this context stays fixed for follow-ups. Opening the draft alone makes no model call.
65
82
 
66
- Closing Side Chat stops active work, archives the child Session, and releases its Agent. It does not delete the child's history from disk. The child and copied prefix therefore consume normal DSH Session storage.
83
+ Side Chat keeps its own temporary message history in memory. It does **not** fork, copy, create, or archive a DSH Session, run an Agent, or write a Session event log. It has **no tools, command execution, or file-editing capability**. Parent reasoning and image/file attachment bytes are omitted; historical tool text is only reference material. The initial model uses the saved Side Chat choice, or the selected parent turn's configuration. Changing it affects only subsequent Side Chat replies.
67
84
 
68
- The parent and child share the same workspace. File changes, commands, and other tool side effects made in Side Chat are real and are not reverted when the panel closes.
85
+ Closing the panel, reloading/disconnecting the client, or unloading the plugin cancels active work and discards the temporary discussion. Host records also expire after 30 minutes idle. To keep a discussion, use **Add to conversation**, then **send the parent draft** to store the reference in the parent history. Adding a reference alone does not send it. References already added to the parent draft are separate from the temporary panel and survive its closure.
86
+
87
+ The text snapshot and Side Chat history are sent to the model on each request, so normal provider token usage still applies; avoiding Session copies does not eliminate context tokens or guarantee cache reuse. Archived Sessions created by older plugin versions are not automatically deleted.
69
88
 
70
89
  ## Current limitations
71
90
 
72
91
  - A selection must stay inside one completed message.
73
- - Attachments and `/side` are not supported yet.
74
- - Closed Side Chats cannot be reopened from the panel.
75
- - There is no automatic history cleanup or “keep as normal chat” action.
76
- - An archived child may briefly appear in normal Session lists.
92
+ - This is a text-only, read-only discussion, not a second Agent. Attachments, tools, Steer, and `/side` are not supported.
93
+ - Closed or expired discussions cannot be reopened. There is no “keep as normal Session” action.
94
+ - Context is limited to 2,097,152 characters and rejected rather than silently truncated. Each question is limited to 65,536 characters; each reply (including reasoning) to 262,144 characters. A discussion permits at most 64 questions and 2,097,152 question/reply/reasoning characters. Output limits produce an explicit error and keep only the displayed partial reply.
95
+ - Model requests time out after two minutes. Provider-specific context/output limits may be lower.
77
96
 
78
97
  ## Upgrade or remove
79
98
 
80
- Refresh this package's registry metadata, update to the latest stable version, and restart DSH:
99
+ ### Desktop
100
+
101
+ In the Desktop app's plugin manager, update or reinstall using this exact package spec, then restart the app:
102
+
103
+ ```text
104
+ @ahggg/dsh-side-chat@1.0.0
105
+ ```
106
+
107
+ If Desktop still reports `@ahggg/dsh-side-chat@0.7.3`, it is loading the old plugin, which does not support DSH `0.2.0-rc.1`. A working `dsh web` installation does not mean Desktop has been updated. Update the Desktop installation rather than bypassing DSH's compatibility check.
108
+
109
+ To uninstall, remove Side Chat through the Desktop app's plugin manager.
110
+
111
+ ### Web
112
+
113
+ To upgrade an existing Web installation to `1.0.0`, re-add the exact version. `add --save-exact` replaces the installed package version and keeps the dependency pinned:
81
114
 
82
115
  ```powershell
83
- pnpm cache delete "@ahggg/dsh-side-chat"
84
- dsh plugin --profile web update @ahggg/dsh-side-chat --latest
116
+ dsh plugin --profile web add @ahggg/dsh-side-chat@1.0.0 --save-exact
85
117
  ```
86
118
 
87
- Remove the plugin with:
119
+ Restart `dsh web` and reload the browser after updating. Remove the Web-profile plugin with:
88
120
 
89
121
  ```powershell
90
122
  dsh plugin --profile web remove @ahggg/dsh-side-chat
package/README.zh-CN.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dsh-side-chat
2
2
 
3
- 在不离开当前 DeepSeek Harness 主会话的情况下,针对选中的文本发起一个独立的侧边对话。
3
+ 在不离开当前 DeepSeek Harness 主会话的情况下,针对选中的文本发起聚焦讨论。支持 **DeepSeek Harness 桌面端和 Web 端**。
4
4
 
5
5
  [English](README.md)
6
6
 
@@ -8,12 +8,28 @@
8
8
 
9
9
  ## 安装
10
10
 
11
- 如果尚未安装 DSH 0.1.2-rc.1,先安装它。添加插件前只刷新这个包的 registry metadata,避免刚发布新版本时 pnpm 仍复用旧的 `latest`:
11
+ 请配合使用以下确切版本:
12
+
13
+ - **Side Chat:** `@ahggg/dsh-side-chat@1.0.0`
14
+ - **DeepSeek Harness 运行时:** `0.2.0-rc.1`
15
+
16
+ ### DeepSeek Harness 桌面端
17
+
18
+ Side Chat 现已支持桌面应用。在桌面端的插件安装对话框中,输入完整的包名和版本:
19
+
20
+ ```text
21
+ @ahggg/dsh-side-chat@1.0.0
22
+ ```
23
+
24
+ 安装后重启 DeepSeek Harness 桌面应用。桌面端请使用应用内的插件管理功能;下方 CLI 命令仅针对 Web profile。
25
+
26
+ ### Web 端(`dsh web`)
27
+
28
+ 如有需要,先安装匹配版本的 DSH CLI,再安装并固定插件版本:
12
29
 
13
30
  ```powershell
14
- npm install --global @deepseek-ai/dsh@0.1.2-rc.1
15
- pnpm cache delete "@ahggg/dsh-side-chat"
16
- dsh plugin --profile web add @ahggg/dsh-side-chat@latest
31
+ npm install --global @deepseek-ai/dsh@0.2.0-rc.1
32
+ dsh plugin --profile web add @ahggg/dsh-side-chat@1.0.0 --save-exact
17
33
  ```
18
34
 
19
35
  从希望 Agent 操作的真实工程目录启动 DSH:
@@ -43,48 +59,64 @@ pnpm build
43
59
  3. 点击 `Add to chat` 可以先填写一条可选批注,再把引用加入主会话输入框;点击 `More details` 可以立即发送详细解释请求;点击 `Ask in side chat` 可以自己输入聚焦问题。
44
60
  4. 自己输入消息或问题时,按 `Enter` 发送。
45
61
  5. Side Chat 回复完成后,点击 `Add to conversation`,即可把这段聚焦讨论作为一个会话引用加入主会话输入框。
46
- 6. 完成后按 `Esc`,或者点击 `×` 关闭。
62
+ 6. 完成后点击 `×` 关闭,或者在 Side Chat 面板内聚焦时按 `Esc`。在其他输入框或菜单中按 Escape 不会关闭 Side Chat。
47
63
 
48
64
  常用操作:
49
65
 
50
66
  - `Shift+Enter` 换行。
51
- - 使用发送按钮旁的模型控件可以选择 provider/model 及其可用的推理等级;该选择只属于 Side Chat,不会修改主会话,并会成为下次打开 Side Chat 时使用的全局默认值。
67
+ - 点击发送按钮旁的模型控件,再选择 **Effort(推理等级)**,即可调整模型的思考等级。回复生成期间也可以修改:当前回复仍使用原设置,新选择会用于下一次回复,并记住为当前客户端之后 **Ask in side chat** 和 **More details** 请求的默认设置,不会修改主会话。
52
68
  - 点击 `Add to chat` 后,按 `Enter` 或点击 `Save` 保存 annotation;点击批注框外部或点击 `Cancel` 则直接取消。
53
69
  - `Add to chat` 会保留输入框中已有的草稿,并可把多段带序号的文本及各自的可选批注汇总到同一个 annotation 胶囊中。
54
70
  - `Add to conversation` 会捕获 Side Chat 中的用户/助手历史、保留主会话草稿;再次点击时会刷新已有引用,而不是重复添加。
55
71
  - 输入框会随内容自动增高,达到最大高度后在内部滚动。
56
- - 回复生成期间,发送图标会替换为停止按钮。
72
+ - 输入框只保留一个操作按钮:空闲时为 **Send(发送)**,生成期间切换为 **Stop(停止)**。回复结束或停止后恢复为发送按钮,并保留追问草稿。等待发送结果时新输入的文字也会保留。
57
73
  - Assistant 回复使用 DSH 原生 Markdown 渲染。
58
74
  - hover `N annotations` 可以预览每一段所选文本及对应批注。
59
75
  - 发送前可以 hover annotation 胶囊并点击 `×` 移除;发送后,同一个胶囊会显示在用户消息上方。
60
- - 主会话会一直保留在页面中,不会自动切换到 child Session。
76
+ - Side Chat 的首条消息会把选区显示为一个 annotation,问题正文保持纯文本,不显示内部 XML。
77
+ - 主会话会一直保留在页面中,插件不会创建 child Session。
61
78
 
62
79
  ## 会话和数据如何处理
63
80
 
64
- 第一次发送时,插件会在所选消息处创建一个真实的 DSH Session fork。child 会继承完整事件前缀、preset 和 workspace;初始模型优先使用最近一次被接受的 Side Chat 选择,没有已保存选择时才快照主会话配置。之后修改模型或推理等级只会作用于 child 的后续 step,既不会修改主会话,也不会继续跟随主会话切换。保持前缀不变有利于供应商的 prompt cache 复用,但不保证一定命中缓存。
81
+ `Ask in side chat` 和 `More details` 直接通过 DSH 配置的 provider 发起流式模型调用。第一次发送时,插件读取主会话截至所选消息所在轮次结束的**只读文本快照**,不包含后续主会话问题。此后追问一直使用这份固定上下文。仅打开草稿不会调用模型。
65
82
 
66
- 关闭 Side Chat 时,插件会停止正在运行的任务、归档 child Session,并释放其 Agent;不会删除 child 的磁盘历史。因此,child 和复制的完整前缀会占用正常的 DSH Session 存储空间。
83
+ Side Chat 只在内存中保留独立的临时讨论历史,**不会 fork、复制、创建或归档 DSH Session,也不会启动 Agent 或写入 Session 事件日志**。它**没有工具调用、命令执行和文件修改能力**。主会话的推理内容以及图片、文件附件的字节不会传入;过去的工具文本仅作为参考资料。初始模型优先使用已保存的 Side Chat 选择,否则使用所选父会话轮次的配置;之后切换模型只影响 Side Chat 的后续回复。
67
84
 
68
- 父会话与 child 共享同一个 workspace。Side Chat 中产生的文件修改、命令执行及其他工具副作用都是真实的,关闭面板不会撤销它们。
85
+ 关闭面板、刷新或断开客户端、卸载插件时,会取消正在运行的请求并丢弃临时讨论。Host 记录闲置 30 分钟后也会过期。如果希望保留讨论,请点击 **Add to conversation**,然后**发送主会话草稿**,将引用存入主会话历史。只添加引用不会自动发送;已经添加到主会话草稿的引用独立于临时面板,不会随面板关闭而删除。
86
+
87
+ 每次调用模型仍会发送文本快照和 Side Chat 历史,因此仍产生正常的 provider token 用量;不复制 Session 不代表不消耗上下文 token,也不保证缓存复用。旧版插件创建的归档 Session 不会自动删除。
69
88
 
70
89
  ## 当前限制
71
90
 
72
91
  - 选区必须位于同一条已完成消息内。
73
- - 暂不支持附件和 `/side`。
74
- - 关闭后不能从面板重新打开原 Side Chat。
75
- - 暂无自动历史清理或“保留为普通会话”操作。
76
- - 已归档的 child 可能短暂出现在普通会话列表中。
92
+ - 这是纯文本只读讨论,不是另一个 Agent;不支持附件、工具、Steer 和 `/side`。
93
+ - 关闭或过期后不能重新打开,也没有“保留为普通 Session”操作。
94
+ - 父上下文上限为 2,097,152 个字符,超过时明确拒绝而不会静默截断。每条问题上限为 65,536 个字符,每次回复(含推理)上限为 262,144 个字符;每次讨论最多 64 条问题,问题、回复和推理合计最多 2,097,152 个字符。达到输出上限会明确报错,仅保留已经显示的部分回复。
95
+ - 单次模型请求两分钟后超时;provider 自身的上下文或输出限制可能更低。
77
96
 
78
97
  ## 升级或卸载
79
98
 
80
- 刷新这个包的 registry metadata,升级到最新稳定版,然后重启 DSH:
99
+ ### 桌面端
100
+
101
+ 在桌面应用的插件管理功能中,使用以下确切包版本进行更新或重新安装,然后重启应用:
102
+
103
+ ```text
104
+ @ahggg/dsh-side-chat@1.0.0
105
+ ```
106
+
107
+ 如果桌面端仍提示 `@ahggg/dsh-side-chat@0.7.3`,说明它还在加载旧插件,该版本不支持 DSH `0.2.0-rc.1`。`dsh web` 可以正常使用,并不代表桌面端也已更新。请更新桌面端的插件安装,不要绕过 DSH 的兼容性检查。
108
+
109
+ 卸载时,在桌面应用的插件管理功能中移除 Side Chat 即可。
110
+
111
+ ### Web 端
112
+
113
+ 要将已有的 Web 安装升级到 `1.0.0`,请重新添加这个确切版本。`add --save-exact` 会替换已安装的包版本,并将依赖固定到该版本:
81
114
 
82
115
  ```powershell
83
- pnpm cache delete "@ahggg/dsh-side-chat"
84
- dsh plugin --profile web update @ahggg/dsh-side-chat --latest
116
+ dsh plugin --profile web add @ahggg/dsh-side-chat@1.0.0 --save-exact
85
117
  ```
86
118
 
87
- 卸载插件:
119
+ 更新后重启 `dsh web` 并刷新浏览器。卸载 Web profile 中的插件:
88
120
 
89
121
  ```powershell
90
122
  dsh plugin --profile web remove @ahggg/dsh-side-chat
@@ -4,40 +4,56 @@ import { selectionReferenceSource } from './parent-composer/add-to-conversation.
4
4
  import { mountParentConversationAnnotations } from './parent-composer/ParentConversationAnnotations.js';
5
5
  import { sideChatConversationReferenceSource } from './parent-composer/referenced-conversation.js';
6
6
  import { SideChatController } from './side-chat-controller.js';
7
- import { mountArchivedRemote } from './rc6/remote-adapter.js';
8
- import { Rc6SideChatOverlay } from './rc6/Rc6SideChatOverlay.js';
9
- import { Rc6SideChatSessions } from './rc6/sessions-adapter.js';
7
+ import { mountSideChatRemote } from './dsh/remote-adapter.js';
8
+ import { SideChatOverlay } from './dsh/SideChatOverlay.js';
9
+ import { DshSideChatSessions } from './dsh/sessions-adapter.js';
10
10
  export const name = 'side-chat-client';
11
- export const inject = ['conversation', 'inputTriggers', 'modelDirectories', 'remote', 'sessions', 'slots'];
11
+ export const inject = ['conversation', 'inputTriggers', 'modelDirectories', 'remote', 'sessions', 'slots', 'uiConversation', 'uiWorkspace'];
12
12
  export async function apply(ctx) {
13
- const stylesheet = document.createElement('style');
14
- stylesheet.textContent = sideChatCss;
15
- stylesheet.dataset.plugin = 'dsh-side-chat';
16
- stylesheet.dataset['dshSideChat'] = 'styles';
17
- document.head.append(stylesheet);
18
- const clientCtx = ctx;
19
- const mounted = await mountArchivedRemote(clientCtx);
20
- const removeSelectionReferenceSource = clientCtx.inputTriggers.registerSource(selectionReferenceSource);
21
- const removeConversationReferenceSource = clientCtx.inputTriggers.registerSource(sideChatConversationReferenceSource);
22
- const sessions = new Rc6SideChatSessions(clientCtx);
23
- const removeParentAnnotations = mountParentConversationAnnotations(clientCtx, () => { sessions.removeConversationAnnotations(); });
24
- const controller = new SideChatController(mounted.remote, sessions);
25
- const removeOverlay = clientCtx.slots.inject('shell.overlay', () => clientCtx.slots.register({
26
- name: 'shell.overlay',
27
- id: 'dsh-side-chat',
28
- order: 90,
29
- }, () => createElement(Rc6SideChatOverlay, { controller, sessions })));
30
- ctx.effect(() => async () => {
31
- try {
32
- await controller.dispose();
33
- }
34
- finally {
35
- removeParentAnnotations();
36
- removeConversationReferenceSource();
37
- removeSelectionReferenceSource();
38
- removeOverlay();
13
+ const cleanups = [];
14
+ let disposed = false;
15
+ let cleanupOperation;
16
+ const cleanup = () => {
17
+ disposed = true;
18
+ cleanupOperation ??= (async () => {
19
+ for (const dispose of cleanups.splice(0).reverse()) {
20
+ try {
21
+ await dispose();
22
+ }
23
+ catch (error) {
24
+ console.warn('[dsh-side-chat] Cleanup failed', error);
25
+ }
26
+ }
27
+ })();
28
+ return cleanupOperation;
29
+ };
30
+ ctx.effect(() => cleanup, 'dsh-side-chat.clientLifecycle');
31
+ try {
32
+ const stylesheet = document.createElement('style');
33
+ stylesheet.textContent = sideChatCss;
34
+ stylesheet.dataset.plugin = 'dsh-side-chat';
35
+ stylesheet.dataset['dshSideChat'] = 'styles';
36
+ document.head.append(stylesheet);
37
+ cleanups.push(() => { stylesheet.remove(); });
38
+ const clientCtx = ctx;
39
+ const mounted = await mountSideChatRemote(clientCtx);
40
+ if (disposed) {
39
41
  await mounted.dispose();
40
- stylesheet.remove();
42
+ return;
41
43
  }
42
- }, 'dsh-side-chat.clientLifecycle');
44
+ cleanups.push(mounted.dispose);
45
+ cleanups.push(clientCtx.inputTriggers.registerSource(selectionReferenceSource));
46
+ cleanups.push(clientCtx.inputTriggers.registerSource(sideChatConversationReferenceSource));
47
+ const sessions = new DshSideChatSessions(clientCtx);
48
+ cleanups.push(mountParentConversationAnnotations(clientCtx, sessionId => { sessions.removeConversationAnnotations(sessionId); }));
49
+ const controller = new SideChatController(mounted.remote, sessions);
50
+ cleanups.push(() => controller.dispose());
51
+ cleanups.push(clientCtx.slots.inject('shell.overlay', () => clientCtx.slots.register({
52
+ name: 'shell.overlay', id: 'dsh-side-chat', order: 90,
53
+ }, () => createElement(SideChatOverlay, { controller, sessions }))));
54
+ }
55
+ catch (error) {
56
+ await cleanup();
57
+ throw error;
58
+ }
43
59
  }
@@ -1,67 +1,12 @@
1
- import type { ConversationSelection, SessionId, SideChatModelSelection, SideChatPromptPart, SideChatWireError } from '../shared/contracts.js';
2
- import type { HostObservable } from '../shared/observable.js';
3
- export interface SideChatSessionSnapshot {
4
- readonly status: 'idle' | 'running' | 'needs-input' | 'needs-approval' | 'failed' | 'interrupted';
5
- }
6
- export interface SideChatQuestionAnswer {
7
- readonly answers: readonly {
8
- readonly id: string;
9
- readonly selected: readonly string[];
10
- readonly custom?: string;
11
- }[];
12
- }
13
- export interface SideChatSessionBinding extends HostObservable<SideChatSessionSnapshot> {
14
- readonly sessionId: SessionId;
15
- prompt(content: readonly SideChatPromptPart[], mode: 'queue' | 'steer'): Promise<{
16
- readonly ok: true;
17
- } | {
18
- readonly ok: false;
19
- readonly error: SideChatWireError;
20
- }>;
21
- updateQueue(itemId: string, action: {
22
- readonly kind: 'edit';
23
- readonly content: readonly SideChatPromptPart[];
24
- } | {
25
- readonly kind: 'remove';
26
- } | {
27
- readonly kind: 'steer';
28
- }): Promise<{
29
- readonly ok: true;
30
- } | {
31
- readonly ok: false;
32
- readonly error: SideChatWireError;
33
- }>;
34
- cancel(): Promise<{
35
- readonly ok: true;
36
- } | {
37
- readonly ok: false;
38
- readonly error: SideChatWireError;
39
- }>;
40
- respondApproval(interactionId: string, decision: 'approve' | 'decline'): Promise<{
41
- readonly ok: true;
42
- } | {
43
- readonly ok: false;
44
- readonly error: SideChatWireError;
45
- }>;
46
- respondQuestion(interactionId: string, answer: SideChatQuestionAnswer | null): Promise<{
47
- readonly ok: true;
48
- } | {
49
- readonly ok: false;
50
- readonly error: SideChatWireError;
51
- }>;
52
- }
53
- export interface SideChatSessionLease {
54
- readonly sessionId: SessionId;
55
- readonly binding: SideChatSessionBinding;
56
- release(): void;
57
- }
1
+ import type { ConversationSelection, SessionId, SideChatModelSelection, SideChatWireError } from '../shared/contracts.js';
2
+ /** Parent-only DSH integration. Side discussions never retain a child Session. */
58
3
  export interface SideChatClientSessions {
59
4
  currentSessionId(): SessionId | undefined;
60
5
  lastCompletedSeq(parentSessionId: SessionId): number | undefined;
61
6
  selectionIsCurrent(selection: ConversationSelection): boolean;
62
7
  sideChatModelPreference(): SideChatModelSelection | undefined;
63
8
  rememberSideChatModelPreference(selection: SideChatModelSelection): void;
64
- retain(sessionId: SessionId): Promise<SideChatSessionLease>;
9
+ retainParent(sessionId: SessionId): () => void;
65
10
  openSession(sessionId: SessionId): Promise<void>;
66
11
  notify(message: {
67
12
  readonly kind: 'status' | 'warning';
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { SideChatState } from '../../shared/contracts.js';
3
+ import type { SideChatController } from '../side-chat-controller.js';
4
+ /** The UI renders structured plugin messages, never model-facing prompt serialization. */
5
+ export declare function ReadOnlyConversation({ state, controller, modelControl, locale }: {
6
+ readonly state: SideChatState;
7
+ readonly controller: SideChatController;
8
+ readonly modelControl?: ReactNode;
9
+ readonly locale?: 'en' | 'zh-CN';
10
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,99 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { DisclosureRow, IconThinkOutlineRegular, MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { SelectionQuote } from '../panel/SelectionQuote.js';
5
+ import { SIDE_CHAT_MESSAGES } from '../panel/messages.js';
6
+ import { SendIcon } from '../panel/SendIcon.js';
7
+ import { StopIcon } from '../panel/StopIcon.js';
8
+ import { useAutoGrowingTextarea } from '../panel/use-auto-growing-textarea.js';
9
+ import { MARKDOWN_LABELS } from './primitive-labels.js';
10
+ function Reasoning({ text, running }) {
11
+ const [expanded, setExpanded] = useState(false);
12
+ const lines = text.trimEnd().split('\n');
13
+ return _jsx("div", { className: "dsh-side-chat-reasoning", "data-variant": "think", "data-state": running ? 'running' : 'ok', children: _jsx(DisclosureRow, { rowClassName: "dsh-side-chat-reasoning-row", leadingClassName: "dsh-side-chat-reasoning-leading", titleClassName: "dsh-side-chat-reasoning-title", chevronClassName: "dsh-side-chat-reasoning-chevron", icon: _jsx(IconThinkOutlineRegular, { size: 14 }), title: "Think", open: expanded, expandable: true, expandOnRowClick: true, onToggle: () => { setExpanded(value => !value); }, collapsedContent: _jsx("span", { className: "dsh-side-chat-reasoning-summary", children: running ? lines.at(-1) : lines[0] }), children: _jsx("div", { className: "dsh-side-chat-reasoning-body", children: text }) }) });
14
+ }
15
+ function Message({ message, locale }) {
16
+ const bubble = _jsxs("article", { className: "dsh-side-chat-message", "data-role": message.role, children: [_jsx("span", { className: "dsh-side-chat-message-role", children: message.role === 'user' ? 'You' : 'Assistant' }), message.role === 'user'
17
+ ? _jsx("div", { className: "dsh-side-chat-message-text", children: message.text })
18
+ : _jsxs(_Fragment, { children: [message.reasoning && _jsx(Reasoning, { text: message.reasoning, running: message.status === 'streaming' }), _jsx(MarkdownText, { text: message.text, streaming: message.status === 'streaming', labels: MARKDOWN_LABELS }), message.status === 'streaming' && message.text === '' && !message.reasoning && _jsx("span", { children: "Thinking\u2026" }), message.status === 'stopped' && _jsx("span", { className: "dsh-side-chat-message-note", children: "Stopped" }), message.status === 'error' && _jsx("span", { className: "dsh-side-chat-message-note", children: "Reply failed" })] })] });
19
+ return message.selectedText === undefined ? bubble : _jsxs("div", { className: "dsh-side-chat-annotated-user-message", children: [_jsx(SelectionQuote, { selections: [{ text: message.selectedText }], messages: SIDE_CHAT_MESSAGES[locale] }), bubble] });
20
+ }
21
+ /** The UI renders structured plugin messages, never model-facing prompt serialization. */
22
+ export function ReadOnlyConversation({ state, controller, modelControl, locale = 'en' }) {
23
+ const [draft, setDraft] = useState('');
24
+ const [sending, setSending] = useState(false);
25
+ const [stopping, setStopping] = useState(false);
26
+ const [actionError, setActionError] = useState();
27
+ const sendingRef = useRef(false);
28
+ const stoppingRef = useRef(false);
29
+ const revision = useRef(0);
30
+ const follow = useRef(true);
31
+ const scrollRef = useRef(null);
32
+ const inputRef = useAutoGrowingTextarea(draft);
33
+ const running = state.phase === 'running';
34
+ const showStop = running || stopping;
35
+ const interactive = ['ready', 'running'].includes(state.phase);
36
+ useEffect(() => {
37
+ const element = scrollRef.current;
38
+ if (element !== null && follow.current)
39
+ element.scrollTop = element.scrollHeight;
40
+ }, [state.messages]);
41
+ const submit = async (event) => {
42
+ event.preventDefault();
43
+ if (draft.trim().length === 0 || !interactive || running || sendingRef.current || stoppingRef.current)
44
+ return;
45
+ const before = revision.current;
46
+ sendingRef.current = true;
47
+ setSending(true);
48
+ setActionError(undefined);
49
+ try {
50
+ const result = await controller.send(draft);
51
+ if (result.ok && before === revision.current)
52
+ setDraft('');
53
+ else if (!result.ok && controller.getSnapshot().error?.message !== result.error.message)
54
+ setActionError(result.error.message);
55
+ }
56
+ catch {
57
+ setActionError('The message could not be sent. Your draft has been kept.');
58
+ }
59
+ finally {
60
+ sendingRef.current = false;
61
+ setSending(false);
62
+ }
63
+ };
64
+ const stop = async () => {
65
+ if (stoppingRef.current)
66
+ return;
67
+ stoppingRef.current = true;
68
+ setStopping(true);
69
+ setActionError(undefined);
70
+ try {
71
+ const result = await controller.cancel();
72
+ if (!result.ok)
73
+ setActionError(result.error.message);
74
+ }
75
+ catch {
76
+ setActionError('The request could not be stopped. Try closing Side Chat.');
77
+ }
78
+ finally {
79
+ stoppingRef.current = false;
80
+ setStopping(false);
81
+ }
82
+ };
83
+ return _jsxs("div", { className: "dsh-side-chat-conversation", children: [_jsxs("div", { ref: scrollRef, className: "dsh-side-chat-transcript", "aria-live": "polite", onScroll: event => {
84
+ const element = event.currentTarget;
85
+ follow.current = element.scrollHeight - element.scrollTop - element.clientHeight < 48;
86
+ }, children: [state.messages.map(message => _jsx(Message, { message: message, locale: locale }, message.id)), actionError !== undefined && _jsx("div", { className: "dsh-side-chat-turn-notice", role: "alert", children: actionError })] }), _jsxs("form", { className: "dsh-side-chat-composer", "data-composer-card": "", onSubmit: event => { void submit(event); }, children: [_jsx("textarea", { ref: inputRef, rows: 1, value: draft, disabled: !interactive, placeholder: "Reply in Side Chat", onChange: event => { ++revision.current; setDraft(event.target.value); }, onKeyDown: event => {
87
+ if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing && event.keyCode !== 229) {
88
+ event.preventDefault();
89
+ event.currentTarget.form?.requestSubmit();
90
+ }
91
+ } }), _jsxs("div", { className: "dsh-side-chat-composer-actions", children: [modelControl, _jsx("button", { type: showStop ? 'button' : 'submit', className: showStop ? 'dsh-side-chat-stop-button' : 'dsh-side-chat-send-button', "aria-label": showStop ? 'Stop generating' : 'Send', "aria-busy": stopping || sending || undefined, disabled: !interactive || (showStop ? stopping : sending || draft.trim().length === 0), onClick: event => {
92
+ if (!showStop)
93
+ return;
94
+ // Do not submit a retained draft if cancellation changes the button
95
+ // back to Send before this click's default action completes.
96
+ event.preventDefault();
97
+ void stop();
98
+ }, children: showStop ? _jsx(StopIcon, {}) : _jsx(SendIcon, {}) })] })] })] });
99
+ }
@@ -0,0 +1,7 @@
1
+ import type { SideChatController } from '../side-chat-controller.js';
2
+ import { DshSideChatSessions } from './sessions-adapter.js';
3
+ /** Selection toolbar and plugin-owned read-only discussion panel. */
4
+ export declare function SideChatOverlay({ controller, sessions, }: {
5
+ readonly controller: SideChatController;
6
+ readonly sessions: DshSideChatSessions;
7
+ }): import("react/jsx-runtime").JSX.Element;