@jingyi0605/codingns4dsh 0.2.1-beta.5 → 0.2.1-beta.6

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 (301) hide show
  1. package/README.en.md +112 -161
  2. package/README.md +106 -263
  3. package/assets/provider-icons/doubao.png +0 -0
  4. package/data/build/dist/client/avatar/catalog-panel.d.ts +1 -1
  5. package/data/build/dist/client/avatar/catalog-panel.d.ts.map +1 -1
  6. package/data/build/dist/client/avatar/catalog-panel.js +13 -11
  7. package/data/build/dist/client/avatar/catalog-panel.js.map +1 -1
  8. package/data/build/dist/client/avatar/engine.d.ts.map +1 -1
  9. package/data/build/dist/client/avatar/engine.js +26 -7
  10. package/data/build/dist/client/avatar/engine.js.map +1 -1
  11. package/data/build/dist/client/avatar/manager.d.ts.map +1 -1
  12. package/data/build/dist/client/avatar/manager.js +5 -1
  13. package/data/build/dist/client/avatar/manager.js.map +1 -1
  14. package/data/build/dist/client/avatar/slot.d.ts.map +1 -1
  15. package/data/build/dist/client/avatar/slot.js +4 -1
  16. package/data/build/dist/client/avatar/slot.js.map +1 -1
  17. package/data/build/dist/client/bundle.js +49155 -99509
  18. package/data/build/dist/client/bundle.js.map +1 -1
  19. package/data/build/dist/client/cli-catalog.d.ts +2 -0
  20. package/data/build/dist/client/cli-catalog.d.ts.map +1 -1
  21. package/data/build/dist/client/cli-catalog.js +8 -0
  22. package/data/build/dist/client/cli-catalog.js.map +1 -1
  23. package/data/build/dist/client/cli-slots.js +2 -2
  24. package/data/build/dist/client/cli-slots.js.map +1 -1
  25. package/data/build/dist/client/client.assistant-configuration-view.js +4526 -0
  26. package/data/build/dist/client/client.assistant-configuration-view.js.map +1 -0
  27. package/data/build/dist/client/client.assistant-debug-workbench.js +764 -0
  28. package/data/build/dist/client/client.assistant-debug-workbench.js.map +1 -0
  29. package/data/build/dist/client/client.assistant-prompt-editor.js +76 -0
  30. package/data/build/dist/client/client.assistant-prompt-editor.js.map +1 -0
  31. package/data/build/dist/client/client.assistant-prompts.js +44 -0
  32. package/data/build/dist/client/client.assistant-prompts.js.map +1 -0
  33. package/data/build/dist/client/client.assistant-voice-sessions.js +144 -0
  34. package/data/build/dist/client/client.assistant-voice-sessions.js.map +1 -0
  35. package/data/build/dist/client/client.assistant-workbench.js +2929 -0
  36. package/data/build/dist/client/client.assistant-workbench.js.map +1 -0
  37. package/data/build/dist/client/client.delegate-command.js +1315 -0
  38. package/data/build/dist/client/client.delegate-command.js.map +1 -0
  39. package/data/build/dist/client/client.editor-engine.js +32486 -0
  40. package/data/build/dist/client/client.editor-engine.js.map +1 -0
  41. package/data/build/dist/client/client.manager.js +3454 -0
  42. package/data/build/dist/client/client.manager.js.map +1 -0
  43. package/data/build/dist/client/client.model-catalog-cache.js +543 -0
  44. package/data/build/dist/client/client.model-catalog-cache.js.map +1 -0
  45. package/data/build/dist/client/client.native-workspace-store.js +689 -0
  46. package/data/build/dist/client/client.native-workspace-store.js.map +1 -0
  47. package/data/build/dist/client/client.primitives-adapter.js +333 -0
  48. package/data/build/dist/client/client.primitives-adapter.js.map +1 -0
  49. package/data/build/dist/client/client.react-dom.js +7286 -0
  50. package/data/build/dist/client/client.react-dom.js.map +1 -0
  51. package/data/build/dist/client/client.rolldown-runtime.js +59 -0
  52. package/data/build/dist/client/client.theme.js +386 -0
  53. package/data/build/dist/client/client.theme.js.map +1 -0
  54. package/data/build/dist/client/client.transport.js +3323 -0
  55. package/data/build/dist/client/client.transport.js.map +1 -0
  56. package/data/build/dist/client/client.xterm-engine.js +13064 -0
  57. package/data/build/dist/client/client.xterm-engine.js.map +1 -0
  58. package/data/build/dist/client/editor-engine.d.ts +4 -0
  59. package/data/build/dist/client/editor-engine.d.ts.map +1 -0
  60. package/data/build/dist/client/editor-engine.js +50 -0
  61. package/data/build/dist/client/editor-engine.js.map +1 -0
  62. package/data/build/dist/client/engine-loader.d.ts +5 -0
  63. package/data/build/dist/client/engine-loader.d.ts.map +1 -0
  64. package/data/build/dist/client/engine-loader.js +35 -0
  65. package/data/build/dist/client/engine-loader.js.map +1 -0
  66. package/data/build/dist/client/features/assistant-configuration-loader.d.ts +14 -0
  67. package/data/build/dist/client/features/assistant-configuration-loader.d.ts.map +1 -0
  68. package/data/build/dist/client/features/assistant-configuration-loader.js +13 -0
  69. package/data/build/dist/client/features/assistant-configuration-loader.js.map +1 -0
  70. package/data/build/dist/client/features/assistant-configuration-session.d.ts.map +1 -1
  71. package/data/build/dist/client/features/assistant-configuration-session.js +13 -4
  72. package/data/build/dist/client/features/assistant-configuration-session.js.map +1 -1
  73. package/data/build/dist/client/features/assistant-configuration-view.d.ts +66 -0
  74. package/data/build/dist/client/features/assistant-configuration-view.d.ts.map +1 -0
  75. package/data/build/dist/client/features/assistant-configuration-view.js +133 -0
  76. package/data/build/dist/client/features/assistant-configuration-view.js.map +1 -0
  77. package/data/build/dist/client/features/assistant-debug-workbench.d.ts +4 -11
  78. package/data/build/dist/client/features/assistant-debug-workbench.d.ts.map +1 -1
  79. package/data/build/dist/client/features/assistant-debug-workbench.js +41 -68
  80. package/data/build/dist/client/features/assistant-debug-workbench.js.map +1 -1
  81. package/data/build/dist/client/features/assistant-display-store.d.ts +57 -0
  82. package/data/build/dist/client/features/assistant-display-store.d.ts.map +1 -0
  83. package/data/build/dist/client/features/assistant-display-store.js +189 -0
  84. package/data/build/dist/client/features/assistant-display-store.js.map +1 -0
  85. package/data/build/dist/client/features/assistant-prompt-editor.d.ts +12 -0
  86. package/data/build/dist/client/features/assistant-prompt-editor.d.ts.map +1 -0
  87. package/data/build/dist/client/features/assistant-prompt-editor.js +14 -0
  88. package/data/build/dist/client/features/assistant-prompt-editor.js.map +1 -0
  89. package/data/build/dist/client/features/assistant-view-loader.d.ts +17 -0
  90. package/data/build/dist/client/features/assistant-view-loader.d.ts.map +1 -0
  91. package/data/build/dist/client/features/assistant-view-loader.js +49 -0
  92. package/data/build/dist/client/features/assistant-view-loader.js.map +1 -0
  93. package/data/build/dist/client/features/assistant-workbench.d.ts +1 -60
  94. package/data/build/dist/client/features/assistant-workbench.d.ts.map +1 -1
  95. package/data/build/dist/client/features/assistant-workbench.js +41 -169
  96. package/data/build/dist/client/features/assistant-workbench.js.map +1 -1
  97. package/data/build/dist/client/features/cli-adapters.js +3 -7
  98. package/data/build/dist/client/features/cli-adapters.js.map +1 -1
  99. package/data/build/dist/client/features/global-voice-assistant.d.ts.map +1 -1
  100. package/data/build/dist/client/features/global-voice-assistant.js +21 -20
  101. package/data/build/dist/client/features/global-voice-assistant.js.map +1 -1
  102. package/data/build/dist/client/features/peer-host.d.ts.map +1 -1
  103. package/data/build/dist/client/features/peer-host.js +14 -25
  104. package/data/build/dist/client/features/peer-host.js.map +1 -1
  105. package/data/build/dist/client/file-management-dom.d.ts +3 -1
  106. package/data/build/dist/client/file-management-dom.d.ts.map +1 -1
  107. package/data/build/dist/client/file-management-dom.js +85 -79
  108. package/data/build/dist/client/file-management-dom.js.map +1 -1
  109. package/data/build/dist/client/locales/assistantAvatar.d.ts +1 -1
  110. package/data/build/dist/client/locales/assistantAvatar.js +2 -2
  111. package/data/build/dist/client/locales/assistantAvatar.js.map +1 -1
  112. package/data/build/dist/client/locales/fileWorkspace.d.ts.map +1 -1
  113. package/data/build/dist/client/locales/fileWorkspace.js +2 -0
  114. package/data/build/dist/client/locales/fileWorkspace.js.map +1 -1
  115. package/data/build/dist/client/peer-host-management-api.d.ts +2 -2
  116. package/data/build/dist/client/peer-host-management-api.d.ts.map +1 -1
  117. package/data/build/dist/client/peer-host-management-api.js +8 -5
  118. package/data/build/dist/client/peer-host-management-api.js.map +1 -1
  119. package/data/build/dist/client/provider-icon-assets.js +5 -39
  120. package/data/build/dist/client/provider-icon-assets.js.map +1 -1
  121. package/data/build/dist/client/provider-icon-bridge.d.ts +6 -0
  122. package/data/build/dist/client/provider-icon-bridge.d.ts.map +1 -0
  123. package/data/build/dist/client/provider-icon-bridge.js +65 -0
  124. package/data/build/dist/client/provider-icon-bridge.js.map +1 -0
  125. package/data/build/dist/client/provider-icons.d.ts +1 -0
  126. package/data/build/dist/client/provider-icons.d.ts.map +1 -1
  127. package/data/build/dist/client/provider-icons.js +2 -1
  128. package/data/build/dist/client/provider-icons.js.map +1 -1
  129. package/data/build/dist/client/remote-web-context.d.ts +2 -0
  130. package/data/build/dist/client/remote-web-context.d.ts.map +1 -1
  131. package/data/build/dist/client/remote-web-context.js +50 -2
  132. package/data/build/dist/client/remote-web-context.js.map +1 -1
  133. package/data/build/dist/client/serial-polling.d.ts +3 -1
  134. package/data/build/dist/client/serial-polling.d.ts.map +1 -1
  135. package/data/build/dist/client/serial-polling.js +10 -3
  136. package/data/build/dist/client/serial-polling.js.map +1 -1
  137. package/data/build/dist/client/settings-section.js +8 -2
  138. package/data/build/dist/client/settings-section.js.map +1 -1
  139. package/data/build/dist/client/terminal/xterm-engine.d.ts +4 -0
  140. package/data/build/dist/client/terminal/xterm-engine.d.ts.map +1 -0
  141. package/data/build/dist/client/terminal/xterm-engine.js +5 -0
  142. package/data/build/dist/client/terminal/xterm-engine.js.map +1 -0
  143. package/data/build/dist/client/terminal/xterm-view.d.ts.map +1 -1
  144. package/data/build/dist/client/terminal/xterm-view.js +33 -25
  145. package/data/build/dist/client/terminal/xterm-view.js.map +1 -1
  146. package/data/build/dist/host/avatar/temporary-previews.d.ts +3 -1
  147. package/data/build/dist/host/avatar/temporary-previews.d.ts.map +1 -1
  148. package/data/build/dist/host/avatar/temporary-previews.js +7 -2
  149. package/data/build/dist/host/avatar/temporary-previews.js.map +1 -1
  150. package/data/build/dist/host/cli-adapters/antigravity-driver.d.ts.map +1 -1
  151. package/data/build/dist/host/cli-adapters/antigravity-driver.js +2 -2
  152. package/data/build/dist/host/cli-adapters/antigravity-driver.js.map +1 -1
  153. package/data/build/dist/host/cli-adapters/binary-detection.d.ts +19 -0
  154. package/data/build/dist/host/cli-adapters/binary-detection.d.ts.map +1 -0
  155. package/data/build/dist/host/cli-adapters/binary-detection.js +71 -0
  156. package/data/build/dist/host/cli-adapters/binary-detection.js.map +1 -0
  157. package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
  158. package/data/build/dist/host/cli-adapters/claude-driver.d.ts.map +1 -1
  159. package/data/build/dist/host/cli-adapters/claude-driver.js +4 -0
  160. package/data/build/dist/host/cli-adapters/claude-driver.js.map +1 -1
  161. package/data/build/dist/host/cli-adapters/claude-model-options.d.ts.map +1 -1
  162. package/data/build/dist/host/cli-adapters/claude-model-options.js +2 -1
  163. package/data/build/dist/host/cli-adapters/claude-model-options.js.map +1 -1
  164. package/data/build/dist/host/cli-adapters/claude-process.d.ts +7 -0
  165. package/data/build/dist/host/cli-adapters/claude-process.d.ts.map +1 -0
  166. package/data/build/dist/host/cli-adapters/claude-process.js +59 -0
  167. package/data/build/dist/host/cli-adapters/claude-process.js.map +1 -0
  168. package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts +3 -0
  169. package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts.map +1 -1
  170. package/data/build/dist/host/cli-adapters/codebuddy-driver.js +18 -5
  171. package/data/build/dist/host/cli-adapters/codebuddy-driver.js.map +1 -1
  172. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +0 -1
  173. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
  174. package/data/build/dist/host/cli-adapters/command-code-driver.js +6 -29
  175. package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
  176. package/data/build/dist/host/cli-adapters/doubao-app.d.ts +27 -0
  177. package/data/build/dist/host/cli-adapters/doubao-app.d.ts.map +1 -0
  178. package/data/build/dist/host/cli-adapters/doubao-app.js +110 -0
  179. package/data/build/dist/host/cli-adapters/doubao-app.js.map +1 -0
  180. package/data/build/dist/host/cli-adapters/doubao-artifact-store.d.ts +16 -0
  181. package/data/build/dist/host/cli-adapters/doubao-artifact-store.d.ts.map +1 -0
  182. package/data/build/dist/host/cli-adapters/doubao-artifact-store.js +142 -0
  183. package/data/build/dist/host/cli-adapters/doubao-artifact-store.js.map +1 -0
  184. package/data/build/dist/host/cli-adapters/doubao-artifacts.d.ts +16 -0
  185. package/data/build/dist/host/cli-adapters/doubao-artifacts.d.ts.map +1 -0
  186. package/data/build/dist/host/cli-adapters/doubao-artifacts.js +34 -0
  187. package/data/build/dist/host/cli-adapters/doubao-artifacts.js.map +1 -0
  188. package/data/build/dist/host/cli-adapters/doubao-cdp.d.ts +49 -0
  189. package/data/build/dist/host/cli-adapters/doubao-cdp.d.ts.map +1 -0
  190. package/data/build/dist/host/cli-adapters/doubao-cdp.js +177 -0
  191. package/data/build/dist/host/cli-adapters/doubao-cdp.js.map +1 -0
  192. package/data/build/dist/host/cli-adapters/doubao-driver.d.ts +33 -0
  193. package/data/build/dist/host/cli-adapters/doubao-driver.d.ts.map +1 -0
  194. package/data/build/dist/host/cli-adapters/doubao-driver.js +291 -0
  195. package/data/build/dist/host/cli-adapters/doubao-driver.js.map +1 -0
  196. package/data/build/dist/host/cli-adapters/doubao-events.d.ts +21 -0
  197. package/data/build/dist/host/cli-adapters/doubao-events.d.ts.map +1 -0
  198. package/data/build/dist/host/cli-adapters/doubao-events.js +126 -0
  199. package/data/build/dist/host/cli-adapters/doubao-events.js.map +1 -0
  200. package/data/build/dist/host/cli-adapters/doubao-runtime.d.ts +6 -0
  201. package/data/build/dist/host/cli-adapters/doubao-runtime.d.ts.map +1 -0
  202. package/data/build/dist/host/cli-adapters/doubao-runtime.js +176 -0
  203. package/data/build/dist/host/cli-adapters/doubao-runtime.js.map +1 -0
  204. package/data/build/dist/host/cli-adapters/doubao-tools.d.ts +8 -0
  205. package/data/build/dist/host/cli-adapters/doubao-tools.d.ts.map +1 -0
  206. package/data/build/dist/host/cli-adapters/doubao-tools.js +139 -0
  207. package/data/build/dist/host/cli-adapters/doubao-tools.js.map +1 -0
  208. package/data/build/dist/host/cli-adapters/driver.d.ts +3 -2
  209. package/data/build/dist/host/cli-adapters/driver.d.ts.map +1 -1
  210. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  211. package/data/build/dist/host/cli-adapters/feature.js +2 -0
  212. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  213. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts.map +1 -1
  214. package/data/build/dist/host/cli-adapters/json-rpc-process.js +3 -1
  215. package/data/build/dist/host/cli-adapters/json-rpc-process.js.map +1 -1
  216. package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts.map +1 -1
  217. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +1 -0
  218. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js.map +1 -1
  219. package/data/build/dist/host/cli-adapters/model-catalog.d.ts +2 -0
  220. package/data/build/dist/host/cli-adapters/model-catalog.d.ts.map +1 -1
  221. package/data/build/dist/host/cli-adapters/model-catalog.js +10 -0
  222. package/data/build/dist/host/cli-adapters/model-catalog.js.map +1 -1
  223. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +1 -0
  224. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts.map +1 -1
  225. package/data/build/dist/host/cli-adapters/opencode-driver.js +9 -28
  226. package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
  227. package/data/build/dist/host/cli-adapters/process-utils.d.ts +4 -2
  228. package/data/build/dist/host/cli-adapters/process-utils.d.ts.map +1 -1
  229. package/data/build/dist/host/cli-adapters/process-utils.js +64 -11
  230. package/data/build/dist/host/cli-adapters/process-utils.js.map +1 -1
  231. package/data/build/dist/host/cli-adapters/qoder-driver.d.ts.map +1 -1
  232. package/data/build/dist/host/cli-adapters/qoder-driver.js +13 -33
  233. package/data/build/dist/host/cli-adapters/qoder-driver.js.map +1 -1
  234. package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
  235. package/data/build/dist/host/cli-adapters/registry.js +31 -4
  236. package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
  237. package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +2 -11
  238. package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts.map +1 -1
  239. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +1 -28
  240. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js.map +1 -1
  241. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -3
  242. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts.map +1 -1
  243. package/data/build/dist/host/cli-adapters/standard-stream-driver.js +12 -37
  244. package/data/build/dist/host/cli-adapters/standard-stream-driver.js.map +1 -1
  245. package/data/build/dist/host/cli-adapters/windows-command-environment.d.ts +12 -0
  246. package/data/build/dist/host/cli-adapters/windows-command-environment.d.ts.map +1 -0
  247. package/data/build/dist/host/cli-adapters/windows-command-environment.js +53 -0
  248. package/data/build/dist/host/cli-adapters/windows-command-environment.js.map +1 -0
  249. package/data/build/dist/host/features/assistant-avatar-runtime.d.ts.map +1 -1
  250. package/data/build/dist/host/features/assistant-avatar-runtime.js +23 -14
  251. package/data/build/dist/host/features/assistant-avatar-runtime.js.map +1 -1
  252. package/data/build/dist/host/features/assistant-background.d.ts +44 -0
  253. package/data/build/dist/host/features/assistant-background.d.ts.map +1 -0
  254. package/data/build/dist/host/features/assistant-background.js +116 -0
  255. package/data/build/dist/host/features/assistant-background.js.map +1 -0
  256. package/data/build/dist/host/features/assistant-index-updates.d.ts +5 -0
  257. package/data/build/dist/host/features/assistant-index-updates.d.ts.map +1 -1
  258. package/data/build/dist/host/features/assistant-index-updates.js +29 -6
  259. package/data/build/dist/host/features/assistant-index-updates.js.map +1 -1
  260. package/data/build/dist/host/features/assistant-source-cache.d.ts +2 -2
  261. package/data/build/dist/host/features/assistant-source-cache.d.ts.map +1 -1
  262. package/data/build/dist/host/features/assistant-source-cache.js +4 -2
  263. package/data/build/dist/host/features/assistant-source-cache.js.map +1 -1
  264. package/data/build/dist/host/features/global-voice-rpc.d.ts.map +1 -1
  265. package/data/build/dist/host/features/global-voice-rpc.js +269 -110
  266. package/data/build/dist/host/features/global-voice-rpc.js.map +1 -1
  267. package/data/build/dist/host/features/peer-host.d.ts.map +1 -1
  268. package/data/build/dist/host/features/peer-host.js +61 -21
  269. package/data/build/dist/host/features/peer-host.js.map +1 -1
  270. package/data/build/dist/host/features/types.d.ts +6 -4
  271. package/data/build/dist/host/features/types.d.ts.map +1 -1
  272. package/data/build/dist/host/index.d.ts.map +1 -1
  273. package/data/build/dist/host/index.js +29 -19
  274. package/data/build/dist/host/index.js.map +1 -1
  275. package/data/build/dist/host/provider-icon-assets.d.ts +5 -0
  276. package/data/build/dist/host/provider-icon-assets.d.ts.map +1 -0
  277. package/data/build/dist/host/provider-icon-assets.js +61 -0
  278. package/data/build/dist/host/provider-icon-assets.js.map +1 -0
  279. package/data/build/dist/host/rpc.js +1 -1
  280. package/data/build/dist/host/rpc.js.map +1 -1
  281. package/data/build/dist/host/session-migration-repair.d.ts +4 -0
  282. package/data/build/dist/host/session-migration-repair.d.ts.map +1 -1
  283. package/data/build/dist/host/session-migration-repair.js +75 -2
  284. package/data/build/dist/host/session-migration-repair.js.map +1 -1
  285. package/data/build/dist/shared/assistant-avatar-engine.d.ts +2 -0
  286. package/data/build/dist/shared/assistant-avatar-engine.d.ts.map +1 -1
  287. package/data/build/dist/shared/assistant-avatar-engine.js +11 -1
  288. package/data/build/dist/shared/assistant-avatar-engine.js.map +1 -1
  289. package/data/build/dist/shared/contracts/cli-adapter.d.ts +3 -0
  290. package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
  291. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  292. package/data/build/dist/shared/contracts/version.js +1 -1
  293. package/data/build/dist/shared/provider-icon-resources.d.ts +4 -0
  294. package/data/build/dist/shared/provider-icon-resources.d.ts.map +1 -0
  295. package/data/build/dist/shared/provider-icon-resources.js +24 -0
  296. package/data/build/dist/shared/provider-icon-resources.js.map +1 -0
  297. package/package.json +2 -2
  298. package/profile/README.md +2 -2
  299. package/profile/package.json +2 -2
  300. package/profile/version.json +1 -1
  301. package/version.json +1 -1
@@ -1,11 +1,12 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { createAssistantScope } from './assistant-scope.js';
2
+ import { createAssistantScope, getAssistantScopeState } from './assistant-scope.js';
3
3
  import { AssistantDispatcher } from './assistant-dispatch.js';
4
4
  import { AssistantVoiceTurnRouter } from './assistant-voice-turn.js';
5
5
  import { buildAssistantSessionIndex } from './assistant-session-index.js';
6
6
  import { sanitizeSpeechText, summarizeAssistantEntries } from './assistant-summary.js';
7
7
  import { parseAssistantIntent } from './assistant-intent.js';
8
- import { AssistantIndexUpdates, assistantSessionKey } from './assistant-index-updates.js';
8
+ import { AssistantIndexUpdates, assistantSessionKey, isAssistantIndexEvent } from './assistant-index-updates.js';
9
+ import { AssistantBackground } from './assistant-background.js';
9
10
  import { readAssistantPrompts } from '../../shared/assistant-prompts.js';
10
11
  import { createAssistantChatSystem } from './assistant-prompts.js';
11
12
  import { speakAssistantStructuredIndex } from './assistant-structured-index.js';
@@ -60,8 +61,13 @@ export function createGlobalVoiceRpcFeature(options = {}) {
60
61
  if (diagnostics !== undefined)
61
62
  context.resources.add(diagnostics.dispose);
62
63
  let resetting = false;
64
+ let disposed = false;
65
+ let statusRevision = 0;
66
+ let indexAbort = new AbortController();
63
67
  // 每次读取当前档案,资源准备与旧配置都不能隐式创建助理。
64
68
  const assistantProfile = () => readAssistantProfile(context.services.settings?.get().assistant ?? DEFAULT_ASSISTANT_SETTINGS);
69
+ const automaticEnabled = () => !disposed && !resetting && context.services.settings?.get().modules.globalVoiceAssistant === true
70
+ && assistantProfile().initialized && (context.services.settings?.get().assistant.managedWorkspaceIds.length ?? 0) > 0;
65
71
  let lifecycleRevision = 0;
66
72
  const tts = new AssistantTtsService(context.services, { isVoiceActive: () => coordinator.snapshot().active });
67
73
  context.resources.add(() => tts.dispose());
@@ -92,6 +98,7 @@ export function createGlobalVoiceRpcFeature(options = {}) {
92
98
  const router = new AssistantVoiceTurnRouter(dispatcher);
93
99
  const waitingState = new AssistantWaitingState();
94
100
  let lastSnapshot = null;
101
+ const indexedEntries = new Map();
95
102
  let lastArchivedSessionIds = [];
96
103
  let buildingIndex;
97
104
  let buildingIndexRevision = 0;
@@ -102,13 +109,20 @@ export function createGlobalVoiceRpcFeature(options = {}) {
102
109
  dispatcher,
103
110
  snapshot: (signal) => readManagementSnapshot(signal),
104
111
  read: async (entry, signal) => {
112
+ const revision = lifecycleRevision;
105
113
  const managed = assistantSettings().managedWorkspaceIds;
106
- const source = await readAssistantSource(context.services, managed, waitingState);
114
+ const source = await readAssistantSource(context.services, managed, waitingState, signal);
107
115
  signal.throwIfAborted();
116
+ if (resetting || revision !== lifecycleRevision)
117
+ throw new Error('助理管理范围已变化,请重新查询');
108
118
  const current = source.sessions.find((item) => item.hostId === entry.hostId && item.sessionId === entry.sessionId && item.workspaceId === entry.workspaceId);
109
119
  if (current === undefined || source.archivedSessionIds.includes(entry.sessionId))
110
120
  throw new Error('目标会话已退出助理管理范围');
111
- return await source.readSummary?.(current) ?? null;
121
+ const summary = await source.readSummary?.(current, signal) ?? null;
122
+ signal.throwIfAborted();
123
+ if (resetting || revision !== lifecycleRevision)
124
+ throw new Error('助理管理范围已变化,请重新查询');
125
+ return summary;
112
126
  },
113
127
  });
114
128
  const managementAgent = createAssistantAgentAdapter(context.services.dshContext, context.services.dshVersion ?? minimumSupportedDshVersion(), llm, managementTools);
@@ -131,14 +145,15 @@ export function createGlobalVoiceRpcFeature(options = {}) {
131
145
  return entry.activity ?? (entry.running ? 'running' : entry.completed || entry.error === true ? 'idle' : 'unknown');
132
146
  };
133
147
  const updates = new AssistantIndexUpdates(async () => {
134
- if (resetting || !assistantProfile().initialized)
148
+ if (!automaticEnabled())
135
149
  return;
136
150
  if (indexAnalysis.read()?.state === 'running' || buildingIndex !== undefined && buildingIndexRevision === indexRevision)
137
151
  return;
138
- const snapshot = await buildIndex();
139
- if (updates.hasReady(snapshot.entries) || indexAnalysis.read()?.generation !== snapshot.generation)
140
- startIndexAnalysis(snapshot);
152
+ const snapshot = await buildIndex('automatic');
153
+ if (automaticEnabled() && (updates.hasReady(snapshot.entries) || indexAnalysis.read()?.generation !== snapshot.generation))
154
+ startIndexAnalysis(snapshot, false, true);
141
155
  }, liveActivity);
156
+ updates.setEnabled(automaticEnabled());
142
157
  context.resources.add(() => textChat.dispose());
143
158
  context.resources.add(() => indexAnalysis.dispose());
144
159
  context.resources.add(() => updates.dispose());
@@ -161,8 +176,8 @@ export function createGlobalVoiceRpcFeature(options = {}) {
161
176
  };
162
177
  const cancelIndexAnalysis = () => indexAnalysis.cancelActive();
163
178
  // 正文索引立即返回,LLM 在后台生成;轮询现有调试接口即可读取进度与结果。
164
- const startIndexAnalysis = (snapshot, retryFailures = false) => {
165
- if (resetting || lastSnapshot !== snapshot)
179
+ const startIndexAnalysis = (snapshot, retryFailures = false, automatic = false) => {
180
+ if (disposed || resetting || lastSnapshot !== snapshot || automatic && !automaticEnabled())
166
181
  return withIndexAnalysis(snapshot);
167
182
  if (snapshot.entries.length === 0) {
168
183
  cancelIndexAnalysis();
@@ -170,9 +185,10 @@ export function createGlobalVoiceRpcFeature(options = {}) {
170
185
  }
171
186
  const revision = indexRevision;
172
187
  const prompts = readAssistantPrompts(context.services.settings?.get().assistant.prompts);
173
- indexAnalysis.start(snapshot, indexSelection, prompts.index, () => lastSnapshot === snapshot && sameManagedScope(snapshot, context.services.settings?.get().assistant.managedWorkspaceIds ?? []), (run) => {
188
+ indexAnalysis.start(snapshot, indexSelection, prompts.index, () => !disposed && (!automatic || automaticEnabled()) && lastSnapshot === snapshot && sameManagedScope(snapshot, context.services.settings?.get().assistant.managedWorkspaceIds ?? []), (run) => {
174
189
  if (resetting || revision !== indexRevision || lastSnapshot !== snapshot)
175
190
  return;
191
+ statusRevision++;
176
192
  journal.updateAnalysis(run);
177
193
  for (const task of run.tasks ?? []) {
178
194
  if (task.state === 'completed')
@@ -190,9 +206,14 @@ export function createGlobalVoiceRpcFeature(options = {}) {
190
206
  return updates.canIndex(entry);
191
207
  // 远端没有本地完成事件:每个真实模型请求前重新确认空闲和版本。
192
208
  const managed = context.services.settings?.get().assistant.managedWorkspaceIds ?? [];
193
- const source = await readAssistantSource(context.services, managed, waitingState);
209
+ const source = await readAssistantSource(context.services, managed, waitingState, signal);
194
210
  signal.throwIfAborted();
211
+ if (disposed || resetting || revision !== indexRevision || automatic && !automaticEnabled())
212
+ return false;
195
213
  const metadata = await buildAssistantSessionIndex({ sessions: source.sessions, scope: createAssistantScope(managed), archivedSessionIds: source.archivedSessionIds });
214
+ signal.throwIfAborted();
215
+ if (revision !== indexRevision)
216
+ return false;
196
217
  observeVersions(metadata.entries);
197
218
  const current = metadata.entries.find((item) => assistantSessionKey(item) === assistantSessionKey(entry));
198
219
  return current !== undefined && current.activity === 'idle' && current.updatedAt === entry.updatedAt && updates.canIndex(entry);
@@ -217,12 +238,15 @@ export function createGlobalVoiceRpcFeature(options = {}) {
217
238
  textChat.cancelActive();
218
239
  voiceChat.clear();
219
240
  updates.reset();
241
+ statusRevision++;
220
242
  };
221
243
  // 刷新可以清空缓存,但索引代次必须持续递增,不能每次回到 1。
222
244
  let indexGeneration = 0;
223
245
  let indexRevision = 0;
224
246
  const observeVersions = (entries) => {
225
247
  const changed = updates.observe(entries);
248
+ if (changed)
249
+ statusRevision++;
226
250
  if (indexAnalysis.read()?.state !== 'running')
227
251
  return changed;
228
252
  for (const entry of lastSnapshot?.entries ?? [])
@@ -230,21 +254,26 @@ export function createGlobalVoiceRpcFeature(options = {}) {
230
254
  indexAnalysis.deferSession(assistantSessionKey(entry));
231
255
  return changed;
232
256
  };
233
- const buildIndex = async (trigger = 'automatic') => {
234
- if (resetting)
235
- throw new Error('助理正在重置,请稍后操作');
257
+ const buildIndex = async (trigger = 'manual') => {
258
+ if (disposed || resetting || trigger === 'automatic' && !automaticEnabled())
259
+ throw new Error('助理后台索引已停止');
236
260
  const managed = context.services.settings?.get().assistant?.managedWorkspaceIds ?? [];
237
261
  const revision = indexRevision;
262
+ const signal = indexAbort.signal;
238
263
  if (buildingIndex !== undefined && buildingIndexRevision === revision)
239
264
  return buildingIndex;
240
- const source = await readAssistantSource(context.services, managed, waitingState);
265
+ if (buildingIndex !== undefined) {
266
+ await buildingIndex.catch(() => undefined);
267
+ signal.throwIfAborted();
268
+ }
269
+ const { source, metadata } = (await background.read(trigger === 'manual' || metadataDirty)).value;
270
+ signal.throwIfAborted();
241
271
  if (resetting || revision !== indexRevision)
242
272
  throw new Error('索引任务已撤销');
243
273
  if (JSON.stringify(managed) !== JSON.stringify(context.services.settings?.get().assistant.managedWorkspaceIds ?? []))
244
- return buildIndex(trigger);
274
+ throw new Error('索引范围已变化');
245
275
  if (buildingIndex !== undefined && buildingIndexRevision === revision)
246
276
  return buildingIndex;
247
- const metadata = await buildAssistantSessionIndex({ sessions: source.sessions, scope: createAssistantScope(managed), archivedSessionIds: source.archivedSessionIds });
248
277
  if (resetting || revision !== indexRevision)
249
278
  throw new Error('索引任务已撤销');
250
279
  observeVersions(metadata.entries);
@@ -256,6 +285,7 @@ export function createGlobalVoiceRpcFeature(options = {}) {
256
285
  if (trigger !== 'manual' && lastSnapshot !== null && sameIndexMembership(lastSnapshot, metadata) && updates.matches(lastSnapshot) && materialKeys.size === 0)
257
286
  return lastSnapshot;
258
287
  const recordId = journal.begin(trigger, managed);
288
+ statusRevision++;
259
289
  const failures = new Map();
260
290
  const task = (async () => {
261
291
  lastArchivedSessionIds = source.archivedSessionIds;
@@ -273,7 +303,9 @@ export function createGlobalVoiceRpcFeature(options = {}) {
273
303
  if (cached !== undefined && cached.version === session.sourceVersion)
274
304
  return cached.summary;
275
305
  try {
276
- const summary = await source.readSummary(session);
306
+ signal.throwIfAborted();
307
+ const summary = await source.readSummary(session, signal);
308
+ signal.throwIfAborted();
277
309
  if (revision === indexRevision && session.sourceVersion !== undefined && updates.canIndex(entry))
278
310
  materials.set(key, { version: session.sourceVersion, summary });
279
311
  return summary;
@@ -283,41 +315,147 @@ export function createGlobalVoiceRpcFeature(options = {}) {
283
315
  throw error;
284
316
  }
285
317
  } }),
286
- ...(source.readWaiting === undefined ? {} : { readWaiting: (session) => materialKeys.has(assistantSessionKey(session)) ? source.readWaiting(session) : session.waiting }),
318
+ ...(source.readWaiting === undefined ? {} : { readWaiting: (session) => materialKeys.has(assistantSessionKey(session)) ? source.readWaiting(session, signal) : session.waiting }),
287
319
  });
288
320
  // 构建期间若源数据已经变化,保留记录,但不能把结果误标为最新。
289
- if (revision !== indexRevision)
290
- return snapshot;
321
+ signal.throwIfAborted();
322
+ if (revision !== indexRevision || disposed)
323
+ throw new Error('索引任务已撤销');
291
324
  lastSnapshot = snapshot;
325
+ indexedEntries.clear();
326
+ for (const entry of snapshot.entries)
327
+ indexedEntries.set(assistantSessionKey(entry), entry);
328
+ statusRevision++;
292
329
  journal.complete(recordId, snapshot, source.warnings, failures);
293
330
  return snapshot;
294
- })().catch((error) => { if (revision === indexRevision)
295
- journal.fail(recordId, error); throw error; }).finally(() => { if (buildingIndex === task)
296
- buildingIndex = undefined; });
331
+ })().catch((error) => { journal.fail(recordId, error); throw error; }).finally(() => { if (buildingIndex === task) {
332
+ buildingIndex = undefined;
333
+ statusRevision++;
334
+ } });
297
335
  buildingIndexRevision = revision;
298
336
  buildingIndex = task;
299
337
  return task;
300
338
  };
301
- const invalidateIndex = () => { cancelIndexAnalysis(); textChat.cancelActive(); voiceChat.clear(); indexRevision += 1; lastSnapshot = null; updates.reset(); };
339
+ const cancelIndexWork = () => {
340
+ indexRevision++;
341
+ indexAbort.abort(new Error('索引任务已撤销'));
342
+ indexAbort = new AbortController();
343
+ cancelIndexAnalysis();
344
+ // 保留取消证据,但不把因模块停用而撤销的版本标成已尝试。
345
+ const run = indexAnalysis.read();
346
+ if (run !== undefined)
347
+ journal.updateAnalysis(run);
348
+ statusRevision++;
349
+ };
350
+ const invalidateIndex = () => { cancelIndexWork(); textChat.cancelActive(); voiceChat.clear(); lastSnapshot = null; updates.reset(); };
351
+ let metadataDirty = true;
352
+ let membershipDirty = true;
353
+ const localSessionKeys = new Map();
354
+ const waitingOverrides = new Map();
355
+ const refreshMembership = () => {
356
+ if (!membershipDirty)
357
+ return;
358
+ membershipDirty = false;
359
+ localSessionKeys.clear();
360
+ const managed = context.services.settings?.get().assistant.managedWorkspaceIds ?? [];
361
+ const archived = new Set(readStringArray(readOptionalService(context.services.dshContext, 'workspaceRegistry')?.archivedSessionIds));
362
+ for (const workspace of readWorkspaceRecords(context.services.dshContext)) {
363
+ if (!managed.some((selected) => assistantWorkspaceMatches(selected, localHostId, workspace.id, true)))
364
+ continue;
365
+ for (const id of workspace.sessionIds)
366
+ if (!archived.has(id))
367
+ localSessionKeys.set(id, assistantSessionKey({ hostId: localHostId, sessionId: id }));
368
+ }
369
+ };
370
+ let metadataEventRevision = 0;
371
+ const background = new AssistantBackground({
372
+ read: async (signal) => {
373
+ const eventRevision = metadataEventRevision;
374
+ const managed = [...context.services.settings?.get().assistant.managedWorkspaceIds ?? []];
375
+ let source = await readAssistantSource(context.services, managed, waitingState, signal);
376
+ signal.throwIfAborted();
377
+ if (source.failed) {
378
+ // 短暂断线不是删除成员。保留同一范围已知远端元数据,但撤销空闲证明,
379
+ // 从而保留材料与成功结果,同时禁止把离线来源当作最新事实送进模型。
380
+ const current = new Set(source.sessions.map(assistantSessionKey));
381
+ const missing = (background.snapshot()?.value.source.sessions ?? []).filter((entry) => entry.hostId !== localHostId && !current.has(assistantSessionKey(entry)));
382
+ source = { ...source, sessions: [...source.sessions, ...missing.map((entry) => ({ ...entry, running: false, completed: false, activity: 'unknown' }))] };
383
+ }
384
+ const metadata = await buildAssistantSessionIndex({ sessions: source.sessions, scope: createAssistantScope(managed), archivedSessionIds: source.archivedSessionIds });
385
+ signal.throwIfAborted();
386
+ return { source, metadata, eventRevision };
387
+ },
388
+ publish: ({ metadata, eventRevision }) => {
389
+ metadataDirty = metadataEventRevision !== eventRevision;
390
+ // 成员表随成功元数据快照更新;常规会话事件不再遍历工作区和会话列表。
391
+ localSessionKeys.clear();
392
+ for (const entry of metadata.entries)
393
+ if (entry.hostId === localHostId)
394
+ localSessionKeys.set(entry.sessionId, assistantSessionKey(entry));
395
+ membershipDirty = metadataDirty;
396
+ const key = readDefaultModelKey();
397
+ if (key !== defaultModelKey) {
398
+ defaultModelKey = key;
399
+ if (indexSelection.provider === undefined)
400
+ invalidateIndex();
401
+ }
402
+ observeVersions(metadata.entries);
403
+ if (updates.hasReady(metadata.entries) || lastSnapshot !== null && !sameIndexMembership(lastSnapshot, metadata))
404
+ updates.schedule(false);
405
+ },
406
+ cadence: ({ source, metadata }) => ({
407
+ remote: source.volatile === true || source.failed === true || metadata.entries.some((entry) => entry.hostId !== localHostId),
408
+ active: metadata.entries.some((entry) => entry.hostId !== localHostId && (entry.activity !== 'idle' || entry.waiting !== null)),
409
+ failed: source.failed === true,
410
+ }),
411
+ });
412
+ let backgroundEnabled = automaticEnabled();
413
+ const syncBackground = () => {
414
+ const enabled = automaticEnabled();
415
+ if (backgroundEnabled && !enabled)
416
+ cancelIndexWork();
417
+ backgroundEnabled = enabled;
418
+ updates.setEnabled(enabled);
419
+ background.setEnabled(enabled);
420
+ };
421
+ const markMetadataChanged = (members = false) => {
422
+ metadataDirty = true;
423
+ metadataEventRevision++;
424
+ statusRevision++;
425
+ membershipDirty ||= members;
426
+ background.request(5_000);
427
+ };
428
+ background.setEnabled(backgroundEnabled);
429
+ context.resources.add(() => { disposed = true; cancelIndexWork(); background.dispose(); updates.dispose(); });
302
430
  const eventDisposers = [];
303
431
  for (const eventName of ['workspace/archive', 'workspace/unarchive', 'workspace/changed']) {
304
- const disposer = context.services.events?.on(eventName, () => updates.schedule());
432
+ const disposer = context.services.events?.on(eventName, () => { markMetadataChanged(true); updates.schedule(); });
305
433
  if (typeof disposer === 'function')
306
434
  eventDisposers.push(disposer);
307
435
  }
308
436
  const sessionKey = (args) => {
437
+ if (!automaticEnabled() && lastSnapshot === null && background.snapshot() === undefined)
438
+ return null;
309
439
  const id = readIndexSessionId(args);
310
- const managed = context.services.settings?.get().assistant.managedWorkspaceIds ?? [];
311
- const workspace = readWorkspaceRecords(context.services.dshContext).find((item) => item.sessionIds.includes(id ?? '') && managed.some((selected) => assistantWorkspaceMatches(selected, localHostId, item.id, true)));
312
- const archived = readStringArray(readOptionalService(context.services.dshContext, 'workspaceRegistry')?.archivedSessionIds);
313
- return id === null || workspace === undefined || archived.includes(id) ? null : assistantSessionKey({ hostId: localHostId, sessionId: id });
440
+ if (id === null)
441
+ return null;
442
+ refreshMembership();
443
+ return localSessionKeys.get(id) ?? null;
314
444
  };
315
445
  const changedSession = (args) => { const key = sessionKey(args); if (key !== null) {
446
+ markMetadataChanged();
316
447
  updates.changed(key);
317
448
  indexAnalysis.deferSession(key);
318
449
  } };
319
450
  for (const eventName of ['session/update', 'session/title']) {
320
- const disposer = context.services.events?.on(eventName, (...args) => changedSession(args));
451
+ const disposer = context.services.events?.on(eventName, (...args) => {
452
+ if (eventName === 'session/title') {
453
+ const id = readIndexSessionId(args);
454
+ if (id !== null)
455
+ assistantSourceCache(context.services).invalidateTitle(id);
456
+ }
457
+ changedSession(args);
458
+ });
321
459
  if (typeof disposer === 'function')
322
460
  eventDisposers.push(disposer);
323
461
  }
@@ -327,10 +465,13 @@ export function createGlobalVoiceRpcFeature(options = {}) {
327
465
  const status = readIndexRunning(eventName, args);
328
466
  if (key === null || status === null)
329
467
  return;
468
+ markMetadataChanged();
330
469
  if (eventName === 'session/complete') {
331
470
  const id = readIndexSessionId(args);
332
- if (id !== null)
471
+ if (id !== null) {
333
472
  waitingState.resolve(id);
473
+ waitingOverrides.set(id, null);
474
+ }
334
475
  updates.changed(key);
335
476
  }
336
477
  updates.status(key, status);
@@ -350,8 +491,10 @@ export function createGlobalVoiceRpcFeature(options = {}) {
350
491
  for (const [eventName, kind] of waitingRequests) {
351
492
  const disposer = context.services.events?.on(eventName, (...args) => {
352
493
  const sessionId = readWaitingSessionId(args);
353
- if (sessionId !== null)
494
+ if (sessionId !== null) {
354
495
  waitingState.request({ sessionId, kind });
496
+ waitingOverrides.set(sessionId, kind);
497
+ }
355
498
  changedSession(args);
356
499
  });
357
500
  if (typeof disposer === 'function')
@@ -360,22 +503,27 @@ export function createGlobalVoiceRpcFeature(options = {}) {
360
503
  for (const eventName of ['approval/resolve', 'approval/resolved', 'user-questions/resolve', 'user-questions/resolved', 'user-question/resolve', 'user-question/answered']) {
361
504
  const disposer = context.services.events?.on(eventName, (...args) => {
362
505
  const sessionId = readWaitingSessionId(args);
363
- if (sessionId !== null)
506
+ if (sessionId !== null) {
364
507
  waitingState.resolve(sessionId);
508
+ waitingOverrides.set(sessionId, null);
509
+ }
365
510
  changedSession(args);
366
511
  });
367
512
  if (typeof disposer === 'function')
368
513
  eventDisposers.push(disposer);
369
514
  }
370
515
  const onSessionEvent = (session, event) => {
516
+ if (!isAssistantIndexEvent(event))
517
+ return;
371
518
  const record = asRecord(session);
372
519
  const sessionId = readText(record, ['id', 'sessionId']) ?? readText(asRecord(record?.header), ['id', 'sessionId']);
373
- if (sessionId !== null)
520
+ if (sessionId !== null && asRecord(event)?.type === 'session/title')
374
521
  assistantSourceCache(context.services).invalidateTitle(sessionId);
375
522
  const key = sessionKey([session]);
376
523
  if (key === null)
377
524
  return;
378
- const entry = lastSnapshot?.entries.find((item) => assistantSessionKey(item) === key);
525
+ markMetadataChanged();
526
+ const entry = indexedEntries.get(key);
379
527
  const before = entry === undefined ? undefined : updates.stamp(entry).sourceVersion;
380
528
  updates.event(key, event);
381
529
  if (entry !== undefined && before !== updates.stamp(entry).sourceVersion)
@@ -405,6 +553,7 @@ export function createGlobalVoiceRpcFeature(options = {}) {
405
553
  managedScopeKey = nextKey;
406
554
  currentPrompts = nextPrompts;
407
555
  configuredModelKey = nextModelKey;
556
+ syncBackground();
408
557
  return;
409
558
  }
410
559
  const scopeChanged = nextKey !== managedScopeKey;
@@ -428,8 +577,9 @@ export function createGlobalVoiceRpcFeature(options = {}) {
428
577
  voiceChat.clear();
429
578
  }
430
579
  if (scopeChanged) {
431
- cancelIndexAnalysis();
432
- indexRevision++;
580
+ cancelIndexWork();
581
+ background.invalidate();
582
+ markMetadataChanged(true);
433
583
  lastSnapshot = null;
434
584
  updates.schedule();
435
585
  }
@@ -438,37 +588,12 @@ export function createGlobalVoiceRpcFeature(options = {}) {
438
588
  managedScopeKey = nextKey;
439
589
  currentPrompts = nextPrompts;
440
590
  configuredModelKey = nextModelKey;
591
+ syncBackground();
592
+ if (scopeChanged)
593
+ background.request();
441
594
  });
442
595
  if (typeof disposeSettingsWatch === 'function')
443
596
  eventDisposers.push(disposeSettingsWatch);
444
- // 没有本地事件总线的远端也通过元数据版本发现变化;这里只读取列表,不读正文。
445
- let refreshingVersions = false;
446
- const refreshVersions = async () => {
447
- if (resetting || !assistantProfile().initialized)
448
- return;
449
- if (refreshingVersions)
450
- return;
451
- refreshingVersions = true;
452
- try {
453
- const key = readDefaultModelKey();
454
- if (key !== defaultModelKey) {
455
- defaultModelKey = key;
456
- if (indexSelection.provider === undefined)
457
- invalidateIndex();
458
- }
459
- const managed = context.services.settings?.get().assistant.managedWorkspaceIds ?? [];
460
- const source = await readAssistantSource(context.services, managed, waitingState);
461
- const metadata = await buildAssistantSessionIndex({ sessions: source.sessions, scope: createAssistantScope(managed), archivedSessionIds: source.archivedSessionIds });
462
- const changed = observeVersions(metadata.entries);
463
- if (updates.hasReady(metadata.entries) || lastSnapshot !== null && !sameIndexMembership(lastSnapshot, metadata))
464
- updates.schedule(changed);
465
- }
466
- finally {
467
- refreshingVersions = false;
468
- }
469
- };
470
- const versionTimer = setInterval(() => { void refreshVersions().catch(() => { }); }, 5000);
471
- context.resources.add(() => clearInterval(versionTimer));
472
597
  updates.schedule();
473
598
  context.resources.add(() => { for (const dispose of eventDisposers.splice(0))
474
599
  dispose(); });
@@ -496,6 +621,8 @@ export function createGlobalVoiceRpcFeature(options = {}) {
496
621
  }));
497
622
  // 两种入口共用完整结构化索引门禁,语音不能绕过待更新或失败的会话。
498
623
  const readChatContext = () => {
624
+ if (background.snapshot()?.value.source.failed)
625
+ throw new Error('远端索引状态暂不可确认,请等待连接恢复');
499
626
  const index = journal.snapshot().index;
500
627
  const managed = context.services.settings?.get().assistant.managedWorkspaceIds ?? [];
501
628
  if (index === undefined || lastSnapshot !== index || !sameManagedScope(index, managed) || !updates.matches(index))
@@ -532,10 +659,10 @@ export function createGlobalVoiceRpcFeature(options = {}) {
532
659
  requireInitialized();
533
660
  const revision = lifecycleRevision;
534
661
  const managed = assistantSettings().managedWorkspaceIds;
535
- const source = await readAssistantSource(context.services, managed, waitingState);
662
+ const source = await readAssistantSource(context.services, managed, waitingState, signal);
536
663
  signal.throwIfAborted();
537
664
  const index = await buildAssistantSessionIndex({ sessions: source.sessions.filter((entry) => !managementAgent.sessionIds.has(entry.sessionId)), scope: createAssistantScope(managed), archivedSessionIds: source.archivedSessionIds, generation: indexGeneration });
538
- const workspaces = [...readWorkspaceRecords(context.services.dshContext).map((item) => ({ workspaceId: item.id, name: item.name, path: item.path })), ...await readRemoteWorkspaces()];
665
+ const workspaces = source.workspaces;
539
666
  signal.throwIfAborted();
540
667
  if (resetting || revision !== lifecycleRevision || JSON.stringify(managed) !== JSON.stringify(assistantSettings().managedWorkspaceIds))
541
668
  throw new Error('助理管理范围已变化,请重新查询');
@@ -767,8 +894,9 @@ export function createGlobalVoiceRpcFeature(options = {}) {
767
894
  resetting = true;
768
895
  lifecycleRevision++;
769
896
  indexRevision++;
770
- updates.pause();
771
- cancelIndexAnalysis();
897
+ syncBackground();
898
+ background.invalidate();
899
+ cancelIndexWork();
772
900
  modelAbort.abort(new Error('助理已重置'));
773
901
  try {
774
902
  await stopAssistant();
@@ -779,6 +907,7 @@ export function createGlobalVoiceRpcFeature(options = {}) {
779
907
  indexAnalysis.clear();
780
908
  updates.clear();
781
909
  materials.clear();
910
+ waitingOverrides.clear();
782
911
  lastSnapshot = null;
783
912
  lastArchivedSessionIds = [];
784
913
  indexSelection = {};
@@ -789,8 +918,31 @@ export function createGlobalVoiceRpcFeature(options = {}) {
789
918
  finally {
790
919
  modelAbort = new AbortController();
791
920
  resetting = false;
921
+ syncBackground();
792
922
  }
793
923
  };
924
+ const readIndexView = () => {
925
+ const cached = background.snapshot();
926
+ const managed = context.services.settings?.get().assistant.managedWorkspaceIds ?? [];
927
+ const base = cached?.value.metadata ?? { generation: indexGeneration, scope: getAssistantScopeState(createAssistantScope(managed)), entries: [], unreadableCount: 0 };
928
+ const metadata = { ...base, entries: base.entries.map((entry) => ({ ...entry, ...(entry.hostId === localHostId && waitingOverrides.has(entry.sessionId) ? { waiting: waitingOverrides.get(entry.sessionId) } : {}) })) };
929
+ const history = journal.snapshot();
930
+ const matching = history.index !== undefined && sameManagedScope(history.index, managed) && sameIndexMembership(history.index, metadata);
931
+ const index = matching ? withIndexAnalysis(history.index, metadata.entries) : metadata;
932
+ return { cached, metadata, history, matching, index,
933
+ indexState: cached?.value.source.failed ? 'incomplete' : readDebugIndexState(index, { building: buildingIndex !== undefined && buildingIndexRevision === indexRevision, exists: history.index !== undefined, current: lastSnapshot === history.index && matching && updates.matches(index) }),
934
+ indexedAt: matching ? history.indexedAt : null, };
935
+ };
936
+ let cachedStatus;
937
+ const readStatus = () => {
938
+ const revision = statusRevision + background.version();
939
+ if (cachedStatus?.revision === revision)
940
+ return cachedStatus;
941
+ const view = readIndexView();
942
+ cachedStatus = { revision, capturedAt: view.cached?.capturedAt ?? null, indexState: view.indexState,
943
+ indexedAt: view.indexedAt, workspaces: view.cached?.value.source.workspaces ?? [] };
944
+ return cachedStatus;
945
+ };
794
946
  context.resources.add(context.services.rpc.register('assistant', async (action, payload) => {
795
947
  if (resetting && action !== 'lifecycle/read' && action !== 'voice/capabilities' && action !== 'tts/catalog')
796
948
  throw new Error('助理正在重置,请稍后操作');
@@ -802,6 +954,7 @@ export function createGlobalVoiceRpcFeature(options = {}) {
802
954
  return result;
803
955
  }
804
956
  switch (action) {
957
+ case 'status': return readStatus();
805
958
  case 'lifecycle/read': return await lifecycleSnapshot();
806
959
  case 'lifecycle/configure': return await configureAssistant(payload);
807
960
  case 'lifecycle/reset': return await resetAssistant();
@@ -864,32 +1017,22 @@ export function createGlobalVoiceRpcFeature(options = {}) {
864
1017
  }
865
1018
  case 'debug': {
866
1019
  // 查看面板只列元数据,不把“刷新视图”混同为“执行索引”。
867
- const managed = context.services.settings?.get().assistant.managedWorkspaceIds ?? [];
868
- const source = await readAssistantSource(context.services, managed, waitingState);
869
- const metadata = await buildAssistantSessionIndex({ sessions: source.sessions, scope: createAssistantScope(managed), archivedSessionIds: source.archivedSessionIds });
870
- observeVersions(metadata.entries);
871
- const history = journal.snapshot();
872
- // 归档、空白状态或工作区成员关系变化后,旧正文不能继续冒充当前结果。
873
- const matching = history.index !== undefined && sameManagedScope(history.index, managed) && sameIndexMembership(history.index, metadata);
874
- if (history.index !== undefined && !matching) {
875
- textChat.cancelActive();
876
- updates.schedule(false);
877
- }
878
- const index = matching ? withIndexAnalysis(history.index, metadata.entries) : metadata;
1020
+ await background.read(asRecord(payload)?.refresh === true);
1021
+ const { cached, metadata, index, indexState, indexedAt } = readIndexView();
879
1022
  const tasks = new Map((indexAnalysis.read()?.tasks ?? []).map((task) => [assistantSessionKey(task), task]));
880
1023
  const summary = summarizeAssistantEntries(index.entries, index.scope, index.generation, index.unreadableCount, 0);
881
1024
  const query = readOptionalService(context.services.dshContext, 'sessionQuery');
882
1025
  const snapshot = {
883
- capturedAt: Date.now(),
1026
+ capturedAt: cached.capturedAt,
884
1027
  index,
885
1028
  // 调试页不限制播报长度,复用摘要接口的范围状态和分组语义。
886
1029
  summary: index.analysis?.state === 'completed' && index.analysis.result !== undefined ? { ...summary, speechText: sanitizeSpeechText(speakAssistantStructuredIndex(index.analysis.result)) } : summary,
887
- workspaces: [...readWorkspaceRecords(context.services.dshContext).map((workspace) => ({ workspaceId: workspace.id, name: workspace.name, path: workspace.path })), ...await readRemoteWorkspaces()],
1030
+ workspaces: cached.value.source.workspaces,
888
1031
  modelId: readAssistantVoiceSettings(context.services).modelId ?? '',
889
- warnings: source.warnings,
1032
+ warnings: cached.value.source.warnings,
890
1033
  scopeSessions: metadata.entries.map((entry) => updates.stamp(entry, tasks.get(assistantSessionKey(entry)))),
891
- indexState: readDebugIndexState(index, { building: buildingIndex !== undefined && buildingIndexRevision === indexRevision, exists: history.index !== undefined, current: lastSnapshot === history.index && matching && updates.matches(index) }),
892
- indexedAt: matching ? history.indexedAt : null,
1034
+ indexState,
1035
+ indexedAt,
893
1036
  records: journal.snapshot().records,
894
1037
  services: {
895
1038
  workspaceList: typeof readOptionalService(context.services.dshContext, 'workspaceRegistry')?.list === 'function',
@@ -1210,23 +1353,25 @@ async function dispatchPrompt(services, request, signal = new AbortController().
1210
1353
  }
1211
1354
  throw new Error('DSH sessionController.prompt 不可用,无法派发任务');
1212
1355
  }
1213
- async function readAssistantSource(services, managedWorkspaceIds = [], waitingState) {
1356
+ async function readAssistantSource(services, managedWorkspaceIds = [], waitingState, signal) {
1357
+ signal?.throwIfAborted();
1214
1358
  const ctx = services.dshContext;
1215
1359
  const warnings = [];
1216
1360
  const query = readOptionalService(ctx, 'sessionQuery');
1217
1361
  const cache = assistantSourceCache(services);
1218
- const local = await cache.share('local-sessions', async () => {
1219
- const warnings = [];
1220
- return { sessions: await readLocalAssistantSessions(services, warnings), warnings };
1221
- });
1222
- warnings.push(...local.warnings);
1223
- const localSessions = local.sessions;
1362
+ const localSessions = await readLocalAssistantSessions(services, warnings, signal);
1363
+ signal?.throwIfAborted();
1364
+ let failed = false;
1224
1365
  const gatewayResult = services.assistantGateway === undefined || managedWorkspaceIds.length === 0
1225
1366
  ? undefined
1226
- : await cache.share(`gateway:${JSON.stringify([...managedWorkspaceIds].sort())}`, () => services.assistantGateway.list(managedWorkspaceIds)).catch((error) => {
1367
+ : await services.assistantGateway.list(managedWorkspaceIds, signal).catch((error) => {
1368
+ signal?.throwIfAborted();
1369
+ failed = true;
1227
1370
  warnings.push(`远端 Host 状态读取失败:${sanitizeSpeechText(error instanceof Error ? error.message : String(error)).slice(0, 500)}`);
1228
1371
  return undefined;
1229
1372
  });
1373
+ signal?.throwIfAborted();
1374
+ warnings.push(...gatewayResult?.warnings ?? []);
1230
1375
  const sessions = dedupeSessionRecords([
1231
1376
  ...localSessions,
1232
1377
  ...(gatewayResult?.sessions ?? []),
@@ -1315,7 +1460,7 @@ async function readAssistantSource(services, managedWorkspaceIds = [], waitingSt
1315
1460
  // 也不能把远端会话误当成本地会话产生一次额外 I/O。
1316
1461
  if (record.hostId !== hostId || !managed.has(record.workspaceId) || archivedSet.has(record.sessionId))
1317
1462
  return record;
1318
- const title = await cache.title(record.sessionId, JSON.stringify([record.updatedAt, record.title, record.titleEvents]), record.updatedAt !== null, () => readSessionTitle(readTitle, record.sessionId));
1463
+ const title = await cache.title(record.sessionId, JSON.stringify([record.title, record.titleEvents]), false, () => readSessionTitle(readTitle, record.sessionId, signal), signal);
1319
1464
  return title === null ? record : { ...record, title };
1320
1465
  }));
1321
1466
  // workspaceRegistry 可能只暴露归档 ID,而 sessionQuery 不再返回归档记录。
@@ -1341,37 +1486,46 @@ async function readAssistantSource(services, managedWorkspaceIds = [], waitingSt
1341
1486
  summary: null,
1342
1487
  }];
1343
1488
  }));
1489
+ signal?.throwIfAborted();
1490
+ const remoteWorkspaces = await services.assistantGateway?.workspaces?.(signal).catch((error) => { signal?.throwIfAborted(); return []; }) ?? [];
1491
+ signal?.throwIfAborted();
1344
1492
  return {
1345
1493
  sessions: [...hydratedRecords, ...archivedStubs],
1346
1494
  archivedSessionIds: archived,
1347
1495
  warnings,
1496
+ failed: failed || gatewayResult?.failed === true,
1497
+ workspaces: [...workspaces.map((workspace) => ({ workspaceId: workspace.id, name: workspace.name, path: workspace.path })), ...remoteWorkspaces],
1348
1498
  ...(gatewayResult?.volatile === true ? { volatile: true } : {}),
1349
1499
  ...((readSurface === undefined && gatewayResult?.readSummary === undefined) ? {} : {
1350
- readSummary: async (session) => {
1500
+ readSummary: async (session, readSignal) => {
1501
+ readSignal?.throwIfAborted();
1351
1502
  if (session.hostId === hostId && readSurface !== undefined)
1352
- return readSurfaceSummary(readSurface, session.sessionId);
1503
+ return readSurfaceSummary(readSurface, session.sessionId, readSignal);
1353
1504
  if (session.hostId !== hostId && gatewayResult?.readSummary !== undefined)
1354
- return gatewayResult.readSummary(session);
1505
+ return gatewayResult.readSummary(session, readSignal);
1355
1506
  return session.summary ?? null;
1356
1507
  },
1357
1508
  }),
1358
1509
  ...((gatewayResult?.readWaiting === undefined) ? {} : {
1359
- readWaiting: async (session) => {
1510
+ readWaiting: async (session, readSignal) => {
1511
+ readSignal?.throwIfAborted();
1360
1512
  if (session.hostId !== hostId)
1361
- return gatewayResult.readWaiting(session);
1513
+ return gatewayResult.readWaiting(session, readSignal);
1362
1514
  return session.waiting ?? null;
1363
1515
  },
1364
1516
  }),
1365
1517
  };
1366
1518
  }
1367
1519
  /** 原生列表提供 blank/origin 投影;原始日志目录只能作为旧版本的能力回退。 */
1368
- async function readLocalAssistantSessions(services, warnings) {
1520
+ async function readLocalAssistantSessions(services, warnings, signal) {
1521
+ signal?.throwIfAborted();
1369
1522
  const controller = readOptionalService(services.dshContext, 'sessionController') ?? services.nativeSessions?.controller;
1370
1523
  if (typeof controller?.list === 'function') {
1371
1524
  try {
1372
- return flattenSessionList(await controller.list({}));
1525
+ return flattenSessionList(await controller.list({}, signal));
1373
1526
  }
1374
1527
  catch {
1528
+ signal?.throwIfAborted();
1375
1529
  // 列表失败或列表为空都不能改用全量历史,否则会重新纳入被侧栏隐藏的会话。
1376
1530
  warnings.push('原生会话列表读取失败,暂不纳入本地会话;请刷新后重试。');
1377
1531
  return [];
@@ -1379,12 +1533,14 @@ async function readLocalAssistantSessions(services, warnings) {
1379
1533
  }
1380
1534
  const query = readOptionalService(services.dshContext, 'sessionQuery');
1381
1535
  if (typeof query?.listSessions === 'function')
1382
- return flattenSessionList(await query.listSessions());
1383
- return services.nativeSessions === undefined ? [] : flattenSessionList(await services.nativeSessions.listRemote());
1536
+ return flattenSessionList(await query.listSessions(signal));
1537
+ return services.nativeSessions === undefined ? [] : flattenSessionList(await services.nativeSessions.listRemote(signal));
1384
1538
  }
1385
- async function readSessionTitle(readTitle, sessionId) {
1539
+ async function readSessionTitle(readTitle, sessionId, signal) {
1386
1540
  try {
1387
- const value = await readTitle(sessionId);
1541
+ signal?.throwIfAborted();
1542
+ const value = await readTitle(sessionId, signal);
1543
+ signal?.throwIfAborted();
1388
1544
  if (typeof value === 'string' && value.trim() !== '')
1389
1545
  return value.trim();
1390
1546
  if (isRecord(value)) {
@@ -1403,12 +1559,15 @@ async function readSessionTitle(readTitle, sessionId) {
1403
1559
  }
1404
1560
  }
1405
1561
  catch {
1562
+ signal?.throwIfAborted();
1406
1563
  // 单个标题查询失败不阻断其余会话索引。
1407
1564
  }
1408
1565
  return null;
1409
1566
  }
1410
- async function readSurfaceSummary(readSurface, sessionId) {
1411
- const value = await readSurface(sessionId);
1567
+ async function readSurfaceSummary(readSurface, sessionId, signal) {
1568
+ signal?.throwIfAborted();
1569
+ const value = await readSurface(sessionId, signal);
1570
+ signal?.throwIfAborted();
1412
1571
  if (typeof value === 'string')
1413
1572
  return value.slice(0, 2000);
1414
1573
  if (!isRecord(value))