@ahggg/dsh-side-chat 0.7.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +16 -13
  2. package/README.zh-CN.md +16 -13
  3. package/lib/client/apply.d.ts +0 -1
  4. package/lib/client/apply.js +48 -33
  5. package/lib/client/contracts.d.ts +3 -59
  6. package/lib/client/contracts.js +0 -1
  7. package/lib/client/dsh/ReadOnlyConversation.d.ts +10 -0
  8. package/lib/client/dsh/ReadOnlyConversation.js +91 -0
  9. package/lib/client/dsh/SideChatOverlay.d.ts +7 -0
  10. package/lib/client/{rc6/Rc6SideChatOverlay.js → dsh/SideChatOverlay.js} +26 -57
  11. package/lib/client/{rc6 → dsh}/context.d.ts +7 -3
  12. package/lib/client/dsh/context.js +1 -0
  13. package/lib/client/dsh/conversation-store.d.ts +22 -0
  14. package/lib/client/dsh/conversation-store.js +19 -0
  15. package/lib/client/dsh/primitive-labels.d.ts +2 -0
  16. package/lib/client/dsh/primitive-labels.js +4 -0
  17. package/lib/client/{rc6 → dsh}/remote-adapter.d.ts +2 -3
  18. package/lib/client/dsh/remote-adapter.js +22 -0
  19. package/lib/client/dsh/sessions-adapter.d.ts +56 -0
  20. package/lib/client/dsh/sessions-adapter.js +185 -0
  21. package/lib/client/index.d.ts +2 -3
  22. package/lib/client/index.js +0 -1
  23. package/lib/client/model-preference.d.ts +0 -1
  24. package/lib/client/model-preference.js +0 -1
  25. package/lib/client/panel/SelectionQuote.d.ts +0 -1
  26. package/lib/client/panel/SelectionQuote.js +8 -2
  27. package/lib/client/panel/SendIcon.d.ts +0 -1
  28. package/lib/client/panel/SendIcon.js +0 -1
  29. package/lib/client/panel/SideChatBody.d.ts +0 -1
  30. package/lib/client/panel/SideChatBody.js +0 -1
  31. package/lib/client/panel/SideChatErrorState.d.ts +0 -1
  32. package/lib/client/panel/SideChatErrorState.js +1 -2
  33. package/lib/client/panel/SideChatHeader.d.ts +0 -1
  34. package/lib/client/panel/SideChatHeader.js +0 -1
  35. package/lib/client/panel/SideChatModelSelect.d.ts +0 -1
  36. package/lib/client/panel/SideChatModelSelect.js +37 -19
  37. package/lib/client/panel/SideChatPanel.d.ts +0 -1
  38. package/lib/client/panel/SideChatPanel.js +12 -6
  39. package/lib/client/panel/StopIcon.d.ts +0 -1
  40. package/lib/client/panel/StopIcon.js +0 -1
  41. package/lib/client/panel/messages.d.ts +1 -2
  42. package/lib/client/panel/messages.js +8 -9
  43. package/lib/client/panel/use-auto-growing-textarea.d.ts +0 -1
  44. package/lib/client/panel/use-auto-growing-textarea.js +0 -1
  45. package/lib/client/parent-composer/ParentConversationAnnotations.d.ts +3 -3
  46. package/lib/client/parent-composer/ParentConversationAnnotations.js +71 -28
  47. package/lib/client/parent-composer/add-to-conversation.d.ts +14 -29
  48. package/lib/client/parent-composer/add-to-conversation.js +134 -457
  49. package/lib/client/parent-composer/annotation-persistence.d.ts +8 -9
  50. package/lib/client/parent-composer/annotation-persistence.js +106 -103
  51. package/lib/client/parent-composer/composer-reference.d.ts +19 -48
  52. package/lib/client/parent-composer/composer-reference.js +26 -46
  53. package/lib/client/parent-composer/referenced-conversation.d.ts +5 -8
  54. package/lib/client/parent-composer/referenced-conversation.js +27 -61
  55. package/lib/client/selection/ConversationAnnotationMarkers.d.ts +0 -1
  56. package/lib/client/selection/ConversationAnnotationMarkers.js +5 -2
  57. package/lib/client/selection/SelectionActions.d.ts +0 -1
  58. package/lib/client/selection/SelectionActions.js +2 -2
  59. package/lib/client/selection/conversation-dom.d.ts +5 -0
  60. package/lib/client/selection/conversation-dom.js +24 -0
  61. package/lib/client/selection/selection-controller.d.ts +1 -2
  62. package/lib/client/selection/selection-controller.js +7 -4
  63. package/lib/client/selection/selection-limits.d.ts +0 -1
  64. package/lib/client/selection/selection-limits.js +0 -1
  65. package/lib/client/selection/selection-normalizer.d.ts +0 -1
  66. package/lib/client/selection/selection-normalizer.js +1 -2
  67. package/lib/client/side-chat-controller.d.ts +28 -42
  68. package/lib/client/side-chat-controller.js +245 -338
  69. package/lib/client.js +1202 -8951
  70. package/lib/host/index.d.ts +1 -2
  71. package/lib/host/index.js +1 -2
  72. package/lib/host/read-only-chat-service.d.ts +30 -0
  73. package/lib/host/read-only-chat-service.js +314 -0
  74. package/lib/index.d.ts +16 -9
  75. package/lib/index.js +34 -14
  76. package/lib/remote.d.ts +23 -15
  77. package/lib/remote.js +2 -6
  78. package/lib/shared/constants.d.ts +0 -14
  79. package/lib/shared/constants.js +0 -14
  80. package/lib/shared/contracts.d.ts +52 -20
  81. package/lib/shared/contracts.js +2 -4
  82. package/lib/shared/error-codes.d.ts +1 -2
  83. package/lib/shared/error-codes.js +2 -3
  84. package/lib/shared/observable.d.ts +0 -1
  85. package/lib/shared/observable.js +0 -1
  86. package/lib/shared/{archived-wire.d.ts → side-chat-wire.d.ts} +109 -21
  87. package/lib/shared/side-chat-wire.js +34 -0
  88. package/lib/typert.d.ts +3 -69
  89. package/lib/typert.js +120 -51
  90. package/package.json +80 -38
  91. package/lib/client/apply.d.ts.map +0 -1
  92. package/lib/client/apply.js.map +0 -1
  93. package/lib/client/contracts.d.ts.map +0 -1
  94. package/lib/client/contracts.js.map +0 -1
  95. package/lib/client/index.d.ts.map +0 -1
  96. package/lib/client/index.js.map +0 -1
  97. package/lib/client/model-preference.d.ts.map +0 -1
  98. package/lib/client/model-preference.js.map +0 -1
  99. package/lib/client/panel/SelectionQuote.d.ts.map +0 -1
  100. package/lib/client/panel/SelectionQuote.js.map +0 -1
  101. package/lib/client/panel/SendIcon.d.ts.map +0 -1
  102. package/lib/client/panel/SendIcon.js.map +0 -1
  103. package/lib/client/panel/SideChatBody.d.ts.map +0 -1
  104. package/lib/client/panel/SideChatBody.js.map +0 -1
  105. package/lib/client/panel/SideChatErrorState.d.ts.map +0 -1
  106. package/lib/client/panel/SideChatErrorState.js.map +0 -1
  107. package/lib/client/panel/SideChatHeader.d.ts.map +0 -1
  108. package/lib/client/panel/SideChatHeader.js.map +0 -1
  109. package/lib/client/panel/SideChatModelSelect.d.ts.map +0 -1
  110. package/lib/client/panel/SideChatModelSelect.js.map +0 -1
  111. package/lib/client/panel/SideChatPanel.d.ts.map +0 -1
  112. package/lib/client/panel/SideChatPanel.js.map +0 -1
  113. package/lib/client/panel/StopIcon.d.ts.map +0 -1
  114. package/lib/client/panel/StopIcon.js.map +0 -1
  115. package/lib/client/panel/messages.d.ts.map +0 -1
  116. package/lib/client/panel/messages.js.map +0 -1
  117. package/lib/client/panel/use-auto-growing-textarea.d.ts.map +0 -1
  118. package/lib/client/panel/use-auto-growing-textarea.js.map +0 -1
  119. package/lib/client/parent-composer/ParentConversationAnnotations.d.ts.map +0 -1
  120. package/lib/client/parent-composer/ParentConversationAnnotations.js.map +0 -1
  121. package/lib/client/parent-composer/add-to-conversation.d.ts.map +0 -1
  122. package/lib/client/parent-composer/add-to-conversation.js.map +0 -1
  123. package/lib/client/parent-composer/annotation-persistence.d.ts.map +0 -1
  124. package/lib/client/parent-composer/annotation-persistence.js.map +0 -1
  125. package/lib/client/parent-composer/composer-reference.d.ts.map +0 -1
  126. package/lib/client/parent-composer/composer-reference.js.map +0 -1
  127. package/lib/client/parent-composer/referenced-conversation.d.ts.map +0 -1
  128. package/lib/client/parent-composer/referenced-conversation.js.map +0 -1
  129. package/lib/client/rc6/ArchivedConversation.d.ts +0 -15
  130. package/lib/client/rc6/ArchivedConversation.d.ts.map +0 -1
  131. package/lib/client/rc6/ArchivedConversation.js +0 -230
  132. package/lib/client/rc6/ArchivedConversation.js.map +0 -1
  133. package/lib/client/rc6/Rc6SideChatOverlay.d.ts +0 -8
  134. package/lib/client/rc6/Rc6SideChatOverlay.d.ts.map +0 -1
  135. package/lib/client/rc6/Rc6SideChatOverlay.js.map +0 -1
  136. package/lib/client/rc6/SideChatTool.d.ts +0 -23
  137. package/lib/client/rc6/SideChatTool.d.ts.map +0 -1
  138. package/lib/client/rc6/SideChatTool.js +0 -395
  139. package/lib/client/rc6/SideChatTool.js.map +0 -1
  140. package/lib/client/rc6/context.d.ts.map +0 -1
  141. package/lib/client/rc6/context.js +0 -2
  142. package/lib/client/rc6/context.js.map +0 -1
  143. package/lib/client/rc6/remote-adapter.d.ts.map +0 -1
  144. package/lib/client/rc6/remote-adapter.js +0 -33
  145. package/lib/client/rc6/remote-adapter.js.map +0 -1
  146. package/lib/client/rc6/sessions-adapter.d.ts +0 -61
  147. package/lib/client/rc6/sessions-adapter.d.ts.map +0 -1
  148. package/lib/client/rc6/sessions-adapter.js +0 -370
  149. package/lib/client/rc6/sessions-adapter.js.map +0 -1
  150. package/lib/client/selection/ConversationAnnotationMarkers.d.ts.map +0 -1
  151. package/lib/client/selection/ConversationAnnotationMarkers.js.map +0 -1
  152. package/lib/client/selection/SelectionActions.d.ts.map +0 -1
  153. package/lib/client/selection/SelectionActions.js.map +0 -1
  154. package/lib/client/selection/selection-controller.d.ts.map +0 -1
  155. package/lib/client/selection/selection-controller.js.map +0 -1
  156. package/lib/client/selection/selection-limits.d.ts.map +0 -1
  157. package/lib/client/selection/selection-limits.js.map +0 -1
  158. package/lib/client/selection/selection-normalizer.d.ts.map +0 -1
  159. package/lib/client/selection/selection-normalizer.js.map +0 -1
  160. package/lib/client/side-chat-controller.d.ts.map +0 -1
  161. package/lib/client/side-chat-controller.js.map +0 -1
  162. package/lib/client.js.map +0 -1
  163. package/lib/host/archived-fork-service.d.ts +0 -29
  164. package/lib/host/archived-fork-service.d.ts.map +0 -1
  165. package/lib/host/archived-fork-service.js +0 -264
  166. package/lib/host/archived-fork-service.js.map +0 -1
  167. package/lib/host/index.d.ts.map +0 -1
  168. package/lib/host/index.js.map +0 -1
  169. package/lib/index.d.ts.map +0 -1
  170. package/lib/index.js.map +0 -1
  171. package/lib/remote.d.ts.map +0 -1
  172. package/lib/remote.js.map +0 -1
  173. package/lib/shared/archived-wire.d.ts.map +0 -1
  174. package/lib/shared/archived-wire.js +0 -48
  175. package/lib/shared/archived-wire.js.map +0 -1
  176. package/lib/shared/constants.d.ts.map +0 -1
  177. package/lib/shared/constants.js.map +0 -1
  178. package/lib/shared/contracts.d.ts.map +0 -1
  179. package/lib/shared/contracts.js.map +0 -1
  180. package/lib/shared/error-codes.d.ts.map +0 -1
  181. package/lib/shared/error-codes.js.map +0 -1
  182. package/lib/shared/observable.d.ts.map +0 -1
  183. package/lib/shared/observable.js.map +0 -1
  184. package/lib/typert.d.ts.map +0 -1
  185. package/lib/typert.js.map +0 -1
package/README.md CHANGED
@@ -8,10 +8,10 @@ Ask a focused follow-up about selected text without leaving your current DeepSee
8
8
 
9
9
  ## Install
10
10
 
11
- Install DSH rc.7 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
+ Install DSH 0.2.0-rc.1 if it is not already available. This release is on npm's `next` tag, not `latest`. Refresh only this package's registry metadata before adding it, so pnpm cannot reuse an older `latest` value immediately after a release:
12
12
 
13
13
  ```powershell
14
- npm install --global @deepseek-ai/dsh@0.1.0-rc.7
14
+ npm install --global @deepseek-ai/dsh@0.2.0-rc.1
15
15
  pnpm cache delete "@ahggg/dsh-side-chat"
16
16
  dsh plugin --profile web add @ahggg/dsh-side-chat@latest
17
17
  ```
@@ -27,7 +27,7 @@ Open the URL printed by DSH. The plugin loads automatically in the Web client.
27
27
 
28
28
  ### Local linked development
29
29
 
30
- When `dsh plugin --profile web list` shows the plugin coming from a `link:` workspace, DSH loads the Git-ignored `lib/client.js`, not `src/`. Rebuild after switching branches or changing client source, then restart the `dsh web` process:
30
+ When `dsh plugin --profile web list` shows the plugin coming from a `link:` workspace, DSH loads the committed `lib/client.js`, not `src/`. Rebuild after switching branches or changing client source, then restart the `dsh web` process:
31
31
 
32
32
  ```powershell
33
33
  pnpm install --frozen-lockfile
@@ -43,7 +43,7 @@ On phones and tablets, long-press text in a completed message to select it. The
43
43
  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
44
  4. When writing your own message or question, press `Enter` to send it.
45
45
  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.
46
+ 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
47
 
48
48
  Useful details:
49
49
 
@@ -53,27 +53,30 @@ Useful details:
53
53
  - `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
54
  - `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
55
  - 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.
56
+ - While a reply is running, use Stop to interrupt it; send a follow-up after it finishes or stops. Text entered while a send is pending stays in the composer.
57
57
  - Assistant replies use DSH's native Markdown rendering.
58
58
  - Hover over `N annotations` to preview every selected passage and its comment.
59
59
  - 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.
60
+ - Your first Side Chat message displays the selected passage as one annotation and your question as plain text, not internal XML.
61
+ - The main conversation stays visible. No child Session is created.
61
62
 
62
63
  ## What happens to the conversation
63
64
 
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.
65
+ `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
66
 
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.
67
+ 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
68
 
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.
69
+ 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.
70
+
71
+ 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
72
 
70
73
  ## Current limitations
71
74
 
72
75
  - 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.
76
+ - This is a text-only, read-only discussion, not a second Agent. Attachments, tools, Steer, and `/side` are not supported.
77
+ - Closed or expired discussions cannot be reopened. There is no “keep as normal Session” action.
78
+ - 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.
79
+ - Model requests time out after two minutes. Provider-specific context/output limits may be lower.
77
80
 
78
81
  ## Upgrade or remove
79
82
 
package/README.zh-CN.md CHANGED
@@ -8,10 +8,10 @@
8
8
 
9
9
  ## 安装
10
10
 
11
- 如果尚未安装 DSH rc.7,先安装它。添加插件前只刷新这个包的 registry metadata,避免刚发布新版本时 pnpm 仍复用旧的 `latest`:
11
+ 如果尚未安装 DSH 0.2.0-rc.1,先安装它。该版本对应 npm 的 `next` 标签,而非 `latest`。添加插件前只刷新这个包的 registry metadata,避免刚发布新版本时 pnpm 仍复用旧的 `latest`:
12
12
 
13
13
  ```powershell
14
- npm install --global @deepseek-ai/dsh@0.1.0-rc.7
14
+ npm install --global @deepseek-ai/dsh@0.2.0-rc.1
15
15
  pnpm cache delete "@ahggg/dsh-side-chat"
16
16
  dsh plugin --profile web add @ahggg/dsh-side-chat@latest
17
17
  ```
@@ -27,7 +27,7 @@ dsh web --port 3080
27
27
 
28
28
  ### 本地链接调试
29
29
 
30
- 当 `dsh plugin --profile web list` 显示插件来自 `link:` 工作区时,DSH 加载的是被 Git 忽略的 `lib/client.js`,而不是 `src/`。切换分支或修改客户端源码后,需要重新构建并重启 `dsh web` 进程:
30
+ 当 `dsh plugin --profile web list` 显示插件来自 `link:` 工作区时,DSH 加载的是已提交的 `lib/client.js`,而不是 `src/`。切换分支或修改客户端源码后,需要重新构建并重启 `dsh web` 进程:
31
31
 
32
32
  ```powershell
33
33
  pnpm install --frozen-lockfile
@@ -43,7 +43,7 @@ pnpm build
43
43
  3. 点击 `Add to chat` 可以先填写一条可选批注,再把引用加入主会话输入框;点击 `More details` 可以立即发送详细解释请求;点击 `Ask in side chat` 可以自己输入聚焦问题。
44
44
  4. 自己输入消息或问题时,按 `Enter` 发送。
45
45
  5. Side Chat 回复完成后,点击 `Add to conversation`,即可把这段聚焦讨论作为一个会话引用加入主会话输入框。
46
- 6. 完成后按 `Esc`,或者点击 `×` 关闭。
46
+ 6. 完成后点击 `×` 关闭,或者在 Side Chat 面板内聚焦时按 `Esc`。在其他输入框或菜单中按 Escape 不会关闭 Side Chat。
47
47
 
48
48
  常用操作:
49
49
 
@@ -53,27 +53,30 @@ pnpm build
53
53
  - `Add to chat` 会保留输入框中已有的草稿,并可把多段带序号的文本及各自的可选批注汇总到同一个 annotation 胶囊中。
54
54
  - `Add to conversation` 会捕获 Side Chat 中的用户/助手历史、保留主会话草稿;再次点击时会刷新已有引用,而不是重复添加。
55
55
  - 输入框会随内容自动增高,达到最大高度后在内部滚动。
56
- - 回复生成期间,发送图标会替换为停止按钮。
56
+ - 回复生成期间,可使用 Stop 中断;等回复结束或停止后再发送追问。等待发送结果时新输入的文字会保留在输入框中。
57
57
  - Assistant 回复使用 DSH 原生 Markdown 渲染。
58
58
  - hover `N annotations` 可以预览每一段所选文本及对应批注。
59
59
  - 发送前可以 hover annotation 胶囊并点击 `×` 移除;发送后,同一个胶囊会显示在用户消息上方。
60
- - 主会话会一直保留在页面中,不会自动切换到 child Session。
60
+ - Side Chat 的首条消息会把选区显示为一个 annotation,问题正文保持纯文本,不显示内部 XML。
61
+ - 主会话会一直保留在页面中,插件不会创建 child Session。
61
62
 
62
63
  ## 会话和数据如何处理
63
64
 
64
- 第一次发送时,插件会在所选消息处创建一个真实的 DSH Session fork。child 会继承完整事件前缀、preset 和 workspace;初始模型优先使用最近一次被接受的 Side Chat 选择,没有已保存选择时才快照主会话配置。之后修改模型或推理等级只会作用于 child 的后续 step,既不会修改主会话,也不会继续跟随主会话切换。保持前缀不变有利于供应商的 prompt cache 复用,但不保证一定命中缓存。
65
+ `Ask in side chat` 和 `More details` 直接通过 DSH 配置的 provider 发起流式模型调用。第一次发送时,插件读取主会话截至所选消息所在轮次结束的**只读文本快照**,不包含后续主会话问题。此后追问一直使用这份固定上下文。仅打开草稿不会调用模型。
65
66
 
66
- 关闭 Side Chat 时,插件会停止正在运行的任务、归档 child Session,并释放其 Agent;不会删除 child 的磁盘历史。因此,child 和复制的完整前缀会占用正常的 DSH Session 存储空间。
67
+ Side Chat 只在内存中保留独立的临时讨论历史,**不会 fork、复制、创建或归档 DSH Session,也不会启动 Agent 或写入 Session 事件日志**。它**没有工具调用、命令执行和文件修改能力**。主会话的推理内容以及图片、文件附件的字节不会传入;过去的工具文本仅作为参考资料。初始模型优先使用已保存的 Side Chat 选择,否则使用所选父会话轮次的配置;之后切换模型只影响 Side Chat 的后续回复。
67
68
 
68
- 父会话与 child 共享同一个 workspace。Side Chat 中产生的文件修改、命令执行及其他工具副作用都是真实的,关闭面板不会撤销它们。
69
+ 关闭面板、刷新或断开客户端、卸载插件时,会取消正在运行的请求并丢弃临时讨论。Host 记录闲置 30 分钟后也会过期。如果希望保留讨论,请点击 **Add to conversation**,然后**发送主会话草稿**,将引用存入主会话历史。只添加引用不会自动发送;已经添加到主会话草稿的引用独立于临时面板,不会随面板关闭而删除。
70
+
71
+ 每次调用模型仍会发送文本快照和 Side Chat 历史,因此仍产生正常的 provider token 用量;不复制 Session 不代表不消耗上下文 token,也不保证缓存复用。旧版插件创建的归档 Session 不会自动删除。
69
72
 
70
73
  ## 当前限制
71
74
 
72
75
  - 选区必须位于同一条已完成消息内。
73
- - 暂不支持附件和 `/side`。
74
- - 关闭后不能从面板重新打开原 Side Chat。
75
- - 暂无自动历史清理或“保留为普通会话”操作。
76
- - 已归档的 child 可能短暂出现在普通会话列表中。
76
+ - 这是纯文本只读讨论,不是另一个 Agent;不支持附件、工具、Steer 和 `/side`。
77
+ - 关闭或过期后不能重新打开,也没有“保留为普通 Session”操作。
78
+ - 父上下文上限为 2,097,152 个字符,超过时明确拒绝而不会静默截断。每条问题上限为 65,536 个字符,每次回复(含推理)上限为 262,144 个字符;每次讨论最多 64 条问题,问题、回复和推理合计最多 2,097,152 个字符。达到输出上限会明确报错,仅保留已经显示的部分回复。
79
+ - 单次模型请求两分钟后超时;provider 自身的上下文或输出限制可能更低。
77
80
 
78
81
  ## 升级或卸载
79
82
 
@@ -2,4 +2,3 @@ import type { Context } from '@deepseek-ai/cordis';
2
2
  export declare const name = "side-chat-client";
3
3
  export declare const inject: string[];
4
4
  export declare function apply(ctx: Context): Promise<void>;
5
- //# sourceMappingURL=apply.d.ts.map
@@ -4,41 +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
  }
44
- //# sourceMappingURL=apply.js.map
@@ -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';
@@ -75,4 +20,3 @@ export type SideChatActionResult<T = void> = {
75
20
  readonly ok: false;
76
21
  readonly error: SideChatWireError;
77
22
  };
78
- //# sourceMappingURL=contracts.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=contracts.js.map
@@ -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,91 @@
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 interactive = ['ready', 'running'].includes(state.phase);
35
+ useEffect(() => {
36
+ const element = scrollRef.current;
37
+ if (element !== null && follow.current)
38
+ element.scrollTop = element.scrollHeight;
39
+ }, [state.messages]);
40
+ const submit = async (event) => {
41
+ event.preventDefault();
42
+ if (draft.trim().length === 0 || !interactive || running || sendingRef.current || stoppingRef.current)
43
+ return;
44
+ const before = revision.current;
45
+ sendingRef.current = true;
46
+ setSending(true);
47
+ setActionError(undefined);
48
+ try {
49
+ const result = await controller.send(draft);
50
+ if (result.ok && before === revision.current)
51
+ setDraft('');
52
+ else if (!result.ok && controller.getSnapshot().error?.message !== result.error.message)
53
+ setActionError(result.error.message);
54
+ }
55
+ catch {
56
+ setActionError('The message could not be sent. Your draft has been kept.');
57
+ }
58
+ finally {
59
+ sendingRef.current = false;
60
+ setSending(false);
61
+ }
62
+ };
63
+ const stop = async () => {
64
+ if (stoppingRef.current)
65
+ return;
66
+ stoppingRef.current = true;
67
+ setStopping(true);
68
+ setActionError(undefined);
69
+ try {
70
+ const result = await controller.cancel();
71
+ if (!result.ok)
72
+ setActionError(result.error.message);
73
+ }
74
+ catch {
75
+ setActionError('The request could not be stopped. Try closing Side Chat.');
76
+ }
77
+ finally {
78
+ stoppingRef.current = false;
79
+ setStopping(false);
80
+ }
81
+ };
82
+ return _jsxs("div", { className: "dsh-side-chat-conversation", children: [_jsxs("div", { ref: scrollRef, className: "dsh-side-chat-transcript", "aria-live": "polite", onScroll: event => {
83
+ const element = event.currentTarget;
84
+ follow.current = element.scrollHeight - element.scrollTop - element.clientHeight < 48;
85
+ }, 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 => {
86
+ if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing && event.keyCode !== 229) {
87
+ event.preventDefault();
88
+ event.currentTarget.form?.requestSubmit();
89
+ }
90
+ } }), _jsxs("div", { className: "dsh-side-chat-composer-actions", children: [modelControl, running && _jsx("button", { type: "button", className: "dsh-side-chat-stop-button", "aria-label": "Stop generating", disabled: !interactive || stopping, onClick: () => { void stop(); }, children: _jsx(StopIcon, {}) }), _jsx("button", { type: "submit", className: "dsh-side-chat-send-button", "aria-label": "Send", disabled: !interactive || running || sending || stopping || draft.trim().length === 0, children: _jsx(SendIcon, {}) })] })] })] });
91
+ }
@@ -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;
@@ -7,8 +7,9 @@ import { SideChatPanel } from '../panel/SideChatPanel.js';
7
7
  import { ConversationAnnotationMarkers } from '../selection/ConversationAnnotationMarkers.js';
8
8
  import { captureDomConversationSelection } from '../selection/selection-controller.js';
9
9
  import { SelectionActions } from '../selection/SelectionActions.js';
10
- import { ArchivedConversation } from './ArchivedConversation.js';
11
- import { Rc6SideChatSessions, selectionDescriptor } from './sessions-adapter.js';
10
+ import { conversationChatRoot, focusConversationComposer } from '../selection/conversation-dom.js';
11
+ import { ReadOnlyConversation } from './ReadOnlyConversation.js';
12
+ import { DshSideChatSessions, selectionDescriptor } from './sessions-adapter.js';
12
13
  const MORE_DETAILS_PROMPT = 'Please explain the selected passage in more detail.';
13
14
  const TOUCH_SELECTION_SETTLE_MS = 300;
14
15
  const TOUCH_ACTIVATION_SUPPRESS_MS = 750;
@@ -24,28 +25,8 @@ function captureEvent(event) {
24
25
  '.dsh-side-chat-annotation-marker',
25
26
  ].join(', ')) !== null);
26
27
  }
27
- function focusParentComposer() {
28
- const input = document.querySelector([
29
- '[data-composer-seat] textarea',
30
- '[data-composer-seat] [role="textbox"]',
31
- '[data-composer-seat] [contenteditable="true"]',
32
- ].join(', '));
33
- if (input === null)
34
- return;
35
- input.focus();
36
- if (input instanceof HTMLTextAreaElement) {
37
- input.setSelectionRange(input.value.length, input.value.length);
38
- return;
39
- }
40
- const range = document.createRange();
41
- range.selectNodeContents(input);
42
- range.collapse(false);
43
- const selection = window.getSelection();
44
- selection?.removeAllRanges();
45
- selection?.addRange(range);
46
- }
47
- /** rc.6 compatibility surface: selection toolbar plus a non-current child conversation panel. */
48
- export function Rc6SideChatOverlay({ controller, sessions, }) {
28
+ /** Selection toolbar and plugin-owned read-only discussion panel. */
29
+ export function SideChatOverlay({ controller, sessions, }) {
49
30
  const state = useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);
50
31
  const currentSessionId = useSyncExternalStore(sessions.subscribeList, () => sessions.currentSessionId(), () => sessions.currentSessionId());
51
32
  const composerInput = useSyncExternalStore(sessions.subscribeConversationInput, sessions.currentConversationInputSnapshot, sessions.currentConversationInputSnapshot);
@@ -62,11 +43,11 @@ export function Rc6SideChatOverlay({ controller, sessions, }) {
62
43
  const generation = ++captureGeneration.current;
63
44
  const parentSessionId = sessions.currentSessionId();
64
45
  const face = parentSessionId === undefined ? undefined : sessions.face(parentSessionId);
65
- const conversationRoot = document.querySelector('[data-chat-flow]');
46
+ const conversationRoot = parentSessionId === undefined ? undefined : conversationChatRoot(parentSessionId);
66
47
  const browserSelection = window.getSelection();
67
48
  if (parentSessionId === undefined
68
49
  || face === undefined
69
- || conversationRoot === null
50
+ || conversationRoot === undefined
70
51
  || browserSelection === null
71
52
  || browserSelection.isCollapsed) {
72
53
  if (generation === captureGeneration.current)
@@ -86,7 +67,7 @@ export function Rc6SideChatOverlay({ controller, sessions, }) {
86
67
  },
87
68
  },
88
69
  });
89
- if (generation === captureGeneration.current) {
70
+ if (generation === captureGeneration.current && sessions.currentSessionId() === parentSessionId) {
90
71
  setActiveSelection({ value: captured, touch });
91
72
  }
92
73
  }
@@ -193,11 +174,13 @@ export function Rc6SideChatOverlay({ controller, sessions, }) {
193
174
  touchInteraction.current = false;
194
175
  cancelScheduledCapture();
195
176
  if (event.key === 'Escape') {
177
+ // Escape in a menu, annotation editor, or main composer belongs to
178
+ // that control. Closing discards the temporary discussion, so it must
179
+ // not fire from every keyup in the document.
196
180
  ++captureGeneration.current;
197
181
  annotationEditing.current = false;
198
182
  setActiveSelection(null);
199
183
  setEditingAnnotation(null);
200
- void controller.close();
201
184
  return;
202
185
  }
203
186
  if (captureEvent(event))
@@ -253,34 +236,25 @@ export function Rc6SideChatOverlay({ controller, sessions, }) {
253
236
  const askDisabledReason = state.phase === 'closed'
254
237
  ? undefined
255
238
  : 'Close the current Side Chat before starting another one.';
256
- const childFace = state.childSessionId === undefined ? undefined : sessions.face(state.childSessionId);
257
- const childCwd = state.childSessionId === undefined ? undefined : sessions.cwd(state.childSessionId);
258
- const inheritedThroughSeq = state.inheritedThroughSeq;
259
239
  const locale = navigator.language.toLowerCase().startsWith('zh') ? 'zh-CN' : 'en';
260
240
  const modelDirectory = state.parentSessionId === undefined
261
241
  ? undefined
262
242
  : sessions.modelDirectory?.(state.parentSessionId);
263
- const addToConversationDisabled = childFace === undefined
264
- || inheritedThroughSeq === undefined
265
- || state.parentSessionId === undefined
266
- || state.childSessionId === undefined
267
- || state.phase !== 'ready';
243
+ const addToConversationDisabled = state.parentSessionId === undefined
244
+ || state.chatId === undefined || state.phase !== 'ready'
245
+ || !state.messages.some(message => message.role === 'assistant' && message.text.trim().length > 0);
268
246
  const addToConversation = () => {
269
247
  const parentSessionId = state.parentSessionId;
270
- const childSessionId = state.childSessionId;
271
- const face = childSessionId === undefined ? undefined : sessions.face(childSessionId);
272
- if (parentSessionId === undefined
273
- || childSessionId === undefined
274
- || inheritedThroughSeq === undefined
275
- || face === undefined) {
248
+ const chatId = state.chatId;
249
+ if (parentSessionId === undefined || chatId === undefined || state.phase !== 'ready') {
276
250
  sessions.notify({ kind: 'warning', text: 'The Side Chat conversation is not ready to add yet.' });
277
251
  return;
278
252
  }
253
+ const parentTitle = sessions.title(parentSessionId);
279
254
  const reference = referencedSideChatConversation({
280
- conversationId: childSessionId,
281
- title: sessions.title(childSessionId) ?? (locale === 'zh-CN' ? '侧边对话' : 'Side Chat'),
282
- nodes: face.getSnapshot().nodes,
283
- inheritedThroughSeq,
255
+ conversationId: chatId,
256
+ title: [locale === 'zh-CN' ? '侧边对话' : 'Side Chat', parentTitle].filter(Boolean).join(' · '),
257
+ messages: state.messages,
284
258
  });
285
259
  if (reference.conversation.length === 0) {
286
260
  sessions.notify({ kind: 'warning', text: 'The Side Chat does not have any conversation history to add yet.' });
@@ -291,7 +265,7 @@ export function Rc6SideChatOverlay({ controller, sessions, }) {
291
265
  sessions.notify({ kind: 'warning', text: 'Could not add the Side Chat to the main conversation.' });
292
266
  return;
293
267
  }
294
- void sessions.openSession(parentSessionId).then(focusParentComposer, () => {
268
+ void sessions.openSession(parentSessionId).then(() => focusConversationComposer(parentSessionId), () => {
295
269
  sessions.notify({ kind: 'warning', text: 'The Side Chat was added, but its parent conversation could not be opened.' });
296
270
  });
297
271
  }
@@ -301,12 +275,12 @@ export function Rc6SideChatOverlay({ controller, sessions, }) {
301
275
  };
302
276
  const modelControl = modelDirectory === undefined
303
277
  ? undefined
304
- : (_jsx(SideChatModelSelect, { directory: modelDirectory, selection: state.modelSelection, locked: ['creating', 'opening', 'closing'].includes(state.phase) || state.error?.operation === 'close', validateInitialSelection: state.childSessionId === undefined, locale: locale, onInitialize: (selection, options) => {
278
+ : (_jsx(SideChatModelSelect, { directory: modelDirectory, selection: state.modelSelection, locked: ['creating', 'running', 'closing'].includes(state.phase) || state.error?.operation === 'close', validateInitialSelection: state.chatId === undefined, locale: locale, onInitialize: (selection, options) => {
305
279
  if (options.remember)
306
280
  void controller.selectModel(selection);
307
281
  else
308
282
  controller.initializeModel(selection);
309
- }, onSelect: (selection) => controller.selectModel(selection) }, `${state.parentSessionId}:${state.childSessionId ?? 'draft'}`));
283
+ }, onSelect: (selection) => controller.selectModel(selection) }, `${state.parentSessionId}:${state.chatId ?? 'draft'}`));
310
284
  return (_jsxs("div", { className: "dsh-side-chat-overlay", children: [_jsx(ConversationAnnotationMarkers, { annotations: annotations, ...editingAnnotation === null
311
285
  ? {}
312
286
  : { activeAnnotationIndex: editingAnnotation.annotationIndex }, onEdit: (annotation, restoredSelection) => {
@@ -318,7 +292,7 @@ export function Rc6SideChatOverlay({ controller, sessions, }) {
318
292
  } }), activeSelection !== null && (_jsx(SelectionActions, { selection: activeSelection.value, touchInteraction: activeSelection.touch, annotationNumber: sessions.nextConversationAnnotationNumber(), ...askDisabledReason === undefined ? {} : { askDisabledReason }, onAddToChat: (captured, comment) => {
319
293
  try {
320
294
  if (sessions.addSelectionToConversation(captured, comment))
321
- focusParentComposer();
295
+ focusConversationComposer(captured.parentSessionId);
322
296
  else
323
297
  sessions.notify({ kind: 'warning', text: 'Could not add the selection to the current chat.' });
324
298
  }
@@ -373,13 +347,8 @@ export function Rc6SideChatOverlay({ controller, sessions, }) {
373
347
  }, onMoreDetails: () => { }, onAskInSideChat: () => { }, onDismiss: () => {
374
348
  annotationEditing.current = false;
375
349
  setEditingAnnotation(null);
376
- } }, `annotation:${String(editingAnnotation.annotationIndex)}`)), state.phase !== 'closed' && (_jsx(SideChatPanel, { state: state, locale: locale, ...childFace === undefined || inheritedThroughSeq === undefined
377
- ? {}
378
- : {
379
- embeddedConversation: (_jsx(ArchivedConversation, { face: childFace, inheritedThroughSeq: inheritedThroughSeq, controller: controller, cwd: childCwd, ...state.selection === undefined ? {} : { selection: state.selection }, locale: locale, modelControl: modelControl })),
380
- }, modelControl: modelControl, onDraftChange: (draft) => { controller.setDraft(draft); }, onFirstSend: (question) => controller.sendFirst(question), onClose: () => controller.close(), onRetry: () => controller.retry(), onFocusParent: () => {
350
+ } }, `annotation:${String(editingAnnotation.annotationIndex)}`)), state.phase !== 'closed' && (_jsx(SideChatPanel, { state: state, locale: locale, embeddedConversation: _jsx(ReadOnlyConversation, { state: state, controller: controller, locale: locale, modelControl: modelControl }, state.chatId), modelControl: modelControl, onDraftChange: (draft) => { controller.setDraft(draft); }, onFirstSend: (question) => controller.sendFirst(question), onClose: () => controller.close(), onRetry: () => controller.retry(), onFocusParent: () => {
381
351
  if (state.parentSessionId !== undefined)
382
352
  void sessions.openSession(state.parentSessionId);
383
- }, onAddToConversation: addToConversation, addToConversationDisabled: addToConversationDisabled, onRemoveSelection: () => { controller.clearSelection(); } }))] }));
353
+ }, onAddToConversation: addToConversation, addToConversationDisabled: addToConversationDisabled, ...state.chatId === undefined ? { onRemoveSelection: () => { controller.clearSelection(); } } : {} }))] }));
384
354
  }
385
- //# sourceMappingURL=Rc6SideChatOverlay.js.map