@vibe-forge/client 0.8.4 → 0.10.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 (301) hide show
  1. package/AGENTS.md +75 -0
  2. package/dist/assets/abap-DLDM7-KI.js +1 -0
  3. package/dist/assets/apex-DNDY2TF8.js +1 -0
  4. package/dist/assets/{arc-DCuZPvAs.js → arc-CCXV7u3V.js} +1 -1
  5. package/dist/assets/azcli-Y6nb8tq_.js +1 -0
  6. package/dist/assets/bat-BwHxbl9M.js +1 -0
  7. package/dist/assets/bicep-CFznDFnq.js +2 -0
  8. package/dist/assets/{blockDiagram-c4efeb88-C39d7-Bu.js → blockDiagram-c4efeb88-Bm52FmvT.js} +1 -1
  9. package/dist/assets/{c4Diagram-c83219d4-jWPBJdeq.js → c4Diagram-c83219d4-C8tTEpcK.js} +1 -1
  10. package/dist/assets/cameligo-Bf6VGUru.js +1 -0
  11. package/dist/assets/channel-gq_WMRvv.js +1 -0
  12. package/dist/assets/{classDiagram-beda092f-Bo7Yvv2T.js → classDiagram-beda092f-CNAIBAH1.js} +1 -1
  13. package/dist/assets/{classDiagram-v2-2358418a-DfoCP9XM.js → classDiagram-v2-2358418a-BHeZAVdc.js} +1 -1
  14. package/dist/assets/clojure-Dnu-v4kV.js +1 -0
  15. package/dist/assets/clone-XxGY7A5N.js +1 -0
  16. package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  17. package/dist/assets/coffee-Bd8akH9Z.js +1 -0
  18. package/dist/assets/cpp-BbWJElDN.js +1 -0
  19. package/dist/assets/{createText-1719965b-8_Ez5rxh.js → createText-1719965b-BS2hLG8t.js} +1 -1
  20. package/dist/assets/csharp-Co3qMtFm.js +1 -0
  21. package/dist/assets/csp-D-4FJmMZ.js +1 -0
  22. package/dist/assets/css-DdJfP1eB.js +3 -0
  23. package/dist/assets/css.worker-BvV5MPou.js +93 -0
  24. package/dist/assets/cssMode-WHcTFAOU.js +1 -0
  25. package/dist/assets/cypher-cTPe9QuQ.js +1 -0
  26. package/dist/assets/dart-BOtBlQCF.js +1 -0
  27. package/dist/assets/dockerfile-BG73LgW2.js +1 -0
  28. package/dist/assets/ecl-BEgZUVRK.js +1 -0
  29. package/dist/assets/{edges-96097737-BEegO-R-.js → edges-96097737-C07f4iWA.js} +1 -1
  30. package/dist/assets/editor.worker-CKy7Pnvo.js +26 -0
  31. package/dist/assets/elixir-BkW5O-1t.js +1 -0
  32. package/dist/assets/{erDiagram-0228fc6a-derRgkLz.js → erDiagram-0228fc6a-BytsAWUs.js} +1 -1
  33. package/dist/assets/flow9-BeJ5waoc.js +1 -0
  34. package/dist/assets/{flowDb-c6c81e3f-ChtOxgsJ.js → flowDb-c6c81e3f-CQJkOpAs.js} +1 -1
  35. package/dist/assets/{flowDiagram-50d868cf-Bm4ufmmA.js → flowDiagram-50d868cf-CD5Tng2S.js} +1 -1
  36. package/dist/assets/flowDiagram-v2-4f6560a1-DIBOANLV.js +1 -0
  37. package/dist/assets/{flowchart-elk-definition-6af322e1-BRYLO-BL.js → flowchart-elk-definition-6af322e1-ylso-GWH.js} +1 -1
  38. package/dist/assets/freemarker2-U_9Jyyr3.js +3 -0
  39. package/dist/assets/fsharp-PahG7c26.js +1 -0
  40. package/dist/assets/{ganttDiagram-a2739b55-CbP6dzRO.js → ganttDiagram-a2739b55-Cg98bJx5.js} +1 -1
  41. package/dist/assets/{gitGraphDiagram-82fe8481-Du44v02s.js → gitGraphDiagram-82fe8481-7Yp4hz0N.js} +1 -1
  42. package/dist/assets/go-acbASCJo.js +1 -0
  43. package/dist/assets/{graph-0_VzJX6O.js → graph-Ig3nvzvL.js} +1 -1
  44. package/dist/assets/graphql-BxJiqAUM.js +1 -0
  45. package/dist/assets/handlebars-DQyCBwHe.js +1 -0
  46. package/dist/assets/hcl-DtV1sZF8.js +1 -0
  47. package/dist/assets/html-CNC2AT5k.js +1 -0
  48. package/dist/assets/html.worker-BLJhxQJQ.js +470 -0
  49. package/dist/assets/htmlMode-DlATk4xW.js +1 -0
  50. package/dist/assets/{index-5325376f-DLP7F7of.js → index-5325376f-C4zed9sb.js} +1 -1
  51. package/dist/assets/index-DRLsOoqb.css +32 -0
  52. package/dist/assets/index-Dbx0JG0p.js +1511 -0
  53. package/dist/assets/{infoDiagram-8eee0895-DY19rRl6.js → infoDiagram-8eee0895-C8oSBaFs.js} +1 -1
  54. package/dist/assets/ini-Kd9XrMLS.js +1 -0
  55. package/dist/assets/java-CXBNlu9o.js +1 -0
  56. package/dist/assets/javascript-9wv9uKW4.js +1 -0
  57. package/dist/assets/{journeyDiagram-c64418c1-4Asnwc86.js → journeyDiagram-c64418c1-D5kJldvy.js} +1 -1
  58. package/dist/assets/json.worker-usMZ-FED.js +58 -0
  59. package/dist/assets/jsonMode-45dv39mU.js +7 -0
  60. package/dist/assets/julia-cl7-CwDS.js +1 -0
  61. package/dist/assets/kotlin-s7OhZKlX.js +1 -0
  62. package/dist/assets/{layout-BILp7GjD.js → layout-DYNFLnIl.js} +1 -1
  63. package/dist/assets/less-9HpZscsL.js +2 -0
  64. package/dist/assets/lexon-OrD6JF1K.js +1 -0
  65. package/dist/assets/{line-D0Xqr8mi.js → line-1gvOYQYZ.js} +1 -1
  66. package/dist/assets/{linear-LpL8RZsq.js → linear-DHGm6Zdw.js} +1 -1
  67. package/dist/assets/liquid-BGoxrdXO.js +1 -0
  68. package/dist/assets/lspLanguageFeatures-CpCCXhrd.js +4 -0
  69. package/dist/assets/lua-Cyyb5UIc.js +1 -0
  70. package/dist/assets/m3-B8OfTtLu.js +1 -0
  71. package/dist/assets/markdown-BFxVWTOG.js +1 -0
  72. package/dist/assets/mdx-Dc2iMbEw.js +1 -0
  73. package/dist/assets/{mermaid.core-Bk0Y_0sz.js → mermaid.core-Cq2bBFF1.js} +6 -6
  74. package/dist/assets/{mindmap-definition-8da855dc-eCAgn5kY.js → mindmap-definition-8da855dc-y5l6GRVh.js} +1 -1
  75. package/dist/assets/mips-CiqrrVzr.js +1 -0
  76. package/dist/assets/msdax-DmeGPVcC.js +1 -0
  77. package/dist/assets/mysql-C_tMU-Nz.js +1 -0
  78. package/dist/assets/objective-c-BDtDVThU.js +1 -0
  79. package/dist/assets/pascal-vHIfCaH5.js +1 -0
  80. package/dist/assets/pascaligo-DtZ0uQbO.js +1 -0
  81. package/dist/assets/perl-Ub6l9XKa.js +1 -0
  82. package/dist/assets/pgsql-BlNEE0v7.js +1 -0
  83. package/dist/assets/php-BBUBE1dy.js +1 -0
  84. package/dist/assets/{pieDiagram-a8764435-BeGFFS1p.js → pieDiagram-a8764435-PNROcv9y.js} +1 -1
  85. package/dist/assets/pla-DSh2-awV.js +1 -0
  86. package/dist/assets/postiats-CocnycG-.js +1 -0
  87. package/dist/assets/powerquery-tScXyioY.js +1 -0
  88. package/dist/assets/powershell-COWaemsV.js +1 -0
  89. package/dist/assets/protobuf-Brw8urJB.js +2 -0
  90. package/dist/assets/pug-8SOpv6rk.js +1 -0
  91. package/dist/assets/python-DjYAge7h.js +1 -0
  92. package/dist/assets/qsharp-Bw9ernYp.js +1 -0
  93. package/dist/assets/{quadrantDiagram-1e28029f-tLqbYOHC.js → quadrantDiagram-1e28029f-CFJ3VPpp.js} +1 -1
  94. package/dist/assets/r-j7ic8hl3.js +1 -0
  95. package/dist/assets/razor-OIY8fx_i.js +1 -0
  96. package/dist/assets/redis-Bu5POkcn.js +1 -0
  97. package/dist/assets/redshift-Bs9aos_-.js +1 -0
  98. package/dist/assets/{requirementDiagram-08caed73-DCverr_g.js → requirementDiagram-08caed73-BpzDIINS.js} +1 -1
  99. package/dist/assets/restructuredtext-CqXO7rUv.js +1 -0
  100. package/dist/assets/ruby-zBfavPgS.js +1 -0
  101. package/dist/assets/rust-BzKRNQWT.js +1 -0
  102. package/dist/assets/{sankeyDiagram-a04cb91d-DidhF5PL.js → sankeyDiagram-a04cb91d-CH69-iIn.js} +1 -1
  103. package/dist/assets/sb-BBc9UKZt.js +1 -0
  104. package/dist/assets/scala-D9hQfWCl.js +1 -0
  105. package/dist/assets/scheme-BPhDTwHR.js +1 -0
  106. package/dist/assets/scss-CBJaRo0y.js +3 -0
  107. package/dist/assets/{sequenceDiagram-c5b8d532-CEKaiwTo.js → sequenceDiagram-c5b8d532-yBBEeVFU.js} +1 -1
  108. package/dist/assets/shell-DiJ1NA_G.js +1 -0
  109. package/dist/assets/solidity-Db0IVjzk.js +1 -0
  110. package/dist/assets/sophia-CnS9iZB_.js +1 -0
  111. package/dist/assets/sparql-CJmd_6j2.js +1 -0
  112. package/dist/assets/sql-ClhHkBeG.js +1 -0
  113. package/dist/assets/st-CHwy0fLd.js +1 -0
  114. package/dist/assets/{stateDiagram-1ecb1508-Cj9ZY7RH.js → stateDiagram-1ecb1508-BvF4sign.js} +1 -1
  115. package/dist/assets/{stateDiagram-v2-c2b004d7-Gsmps-dk.js → stateDiagram-v2-c2b004d7-BeyT7Ghx.js} +1 -1
  116. package/dist/assets/{styles-b4e223ce-D7E8Th0j.js → styles-b4e223ce-C58zxmK6.js} +1 -1
  117. package/dist/assets/{styles-ca3715f6-tTC26Jsm.js → styles-ca3715f6-DCE5sFi5.js} +1 -1
  118. package/dist/assets/{styles-d45a18b0-Bf6oqNdR.js → styles-d45a18b0-CG-C1aM8.js} +1 -1
  119. package/dist/assets/{svgDrawCommon-b86b1483-BxKi01m2.js → svgDrawCommon-b86b1483-F-K8GeDd.js} +1 -1
  120. package/dist/assets/swift-Bqt4WxQ4.js +3 -0
  121. package/dist/assets/systemverilog-Bs9z6M-B.js +1 -0
  122. package/dist/assets/tcl-Dm6ycUr_.js +1 -0
  123. package/dist/assets/{timeline-definition-faaaa080-D6PePEip.js → timeline-definition-faaaa080-DPv4uqVX.js} +1 -1
  124. package/dist/assets/ts.worker-DGHjMaqB.js +67731 -0
  125. package/dist/assets/tsMode-BtU8ZELV.js +11 -0
  126. package/dist/assets/twig-Csy3S7wG.js +1 -0
  127. package/dist/assets/typescript-CJHgISWo.js +1 -0
  128. package/dist/assets/typespec-Btyra-wh.js +1 -0
  129. package/dist/assets/vb-Db0cS2oM.js +1 -0
  130. package/dist/assets/wgsl-BTesnYfV.js +298 -0
  131. package/dist/assets/xml-C_TJw4Bi.js +1 -0
  132. package/dist/assets/{xychartDiagram-f5964ef8-BuP4qfXm.js → xychartDiagram-f5964ef8-BmXlhBzX.js} +1 -1
  133. package/dist/assets/yaml-BujeJOJ6.js +1 -0
  134. package/dist/favicon.svg +3 -3
  135. package/dist/index.html +2 -2
  136. package/package.json +14 -10
  137. package/public/favicon.svg +3 -3
  138. package/src/api/sessions.ts +36 -0
  139. package/src/api/workspace.ts +19 -0
  140. package/src/api.ts +4 -0
  141. package/src/components/NavRail.scss +9 -10
  142. package/src/components/ShortcutDisplay.scss +38 -0
  143. package/src/components/ShortcutDisplay.tsx +37 -0
  144. package/src/components/ShortcutTooltip.scss +36 -0
  145. package/src/components/ShortcutTooltip.tsx +84 -0
  146. package/src/components/Sidebar.scss +55 -13
  147. package/src/components/Sidebar.tsx +141 -52
  148. package/src/components/chat/AGENTS.md +163 -0
  149. package/src/components/chat/ChatHeader.scss +308 -49
  150. package/src/components/chat/ChatHeader.tsx +394 -80
  151. package/src/components/chat/ChatHistoryView.tsx +284 -69
  152. package/src/components/chat/ChatSettingsView.tsx +5 -3
  153. package/src/components/chat/ChatTimelineView.scss +3 -2
  154. package/src/components/chat/{sender/ThinkingStatus.tsx → ThinkingStatus.tsx} +1 -1
  155. package/src/components/chat/messages/MessageContextMenu.scss +145 -0
  156. package/src/components/chat/messages/MessageContextMenu.tsx +108 -0
  157. package/src/components/chat/messages/MessageContextMenuContent.tsx +87 -0
  158. package/src/components/chat/messages/MessageFooter.tsx +44 -24
  159. package/src/components/chat/messages/MessageItem.scss +147 -10
  160. package/src/components/chat/messages/MessageItem.tsx +378 -13
  161. package/src/components/chat/messages/build-message-context-menu-entries.ts +166 -0
  162. package/src/components/chat/messages/message-content-utils.ts +121 -0
  163. package/src/components/chat/messages/message-turns.ts +88 -0
  164. package/src/components/chat/messages/message-utils.ts +19 -1
  165. package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.scss +86 -0
  166. package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.tsx +54 -0
  167. package/src/components/chat/sender/@components/adapter-select/AdapterSelectDropdown.scss +42 -0
  168. package/src/components/chat/sender/@components/effort-select/EffortSelectControl.scss +68 -0
  169. package/src/components/chat/sender/@components/effort-select/EffortSelectControl.tsx +137 -0
  170. package/src/components/chat/sender/@components/effort-select/EffortSelectDropdown.scss +96 -0
  171. package/src/components/chat/sender/@components/model-select/ModelSelectControl.scss +82 -0
  172. package/src/components/chat/sender/@components/model-select/ModelSelectControl.tsx +171 -0
  173. package/src/components/chat/sender/@components/model-select/ModelSelectMenu.scss +95 -0
  174. package/src/components/chat/sender/@components/model-select/ModelSelectMenuLabels.scss +144 -0
  175. package/src/components/chat/sender/@components/model-select/ModelSelectOptionLabel.tsx +109 -0
  176. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.scss +106 -0
  177. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.tsx +156 -0
  178. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsOption.scss +34 -0
  179. package/src/components/chat/sender/@components/reference-actions/ReferencePermissionActionsPopover.tsx +111 -0
  180. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.scss +103 -0
  181. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.tsx +47 -0
  182. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +137 -0
  183. package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.scss +178 -0
  184. package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.tsx +145 -0
  185. package/src/components/chat/sender/@components/sender-monaco-editor/SenderMonacoEditor.scss +47 -0
  186. package/src/components/chat/sender/@components/sender-monaco-editor/SenderMonacoEditor.tsx +121 -0
  187. package/src/components/chat/sender/@components/sender-monaco-editor/monaco-runtime.ts +99 -0
  188. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-editor-handle.ts +48 -0
  189. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-editor.ts +209 -0
  190. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-theme.ts +24 -0
  191. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.scss +54 -0
  192. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.tsx +80 -0
  193. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectBase.scss +71 -0
  194. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectShared.scss +118 -0
  195. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.scss +99 -0
  196. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.tsx +87 -0
  197. package/src/components/chat/sender/@core/build-sender-controller-result.ts +119 -0
  198. package/src/components/chat/sender/@core/build-sender-toolbar.ts +122 -0
  199. package/src/components/chat/sender/@core/content-attachments.ts +76 -0
  200. package/src/components/chat/sender/@core/create-sender-toolbar-handlers.ts +115 -0
  201. package/src/components/chat/sender/@core/get-sender-interaction-state.ts +18 -0
  202. package/src/components/chat/sender/@core/get-sender-runtime-state.ts +14 -0
  203. package/src/components/chat/sender/@core/interaction-request.ts +5 -0
  204. package/src/components/chat/sender/@core/sender-toolbar-bindings.ts +155 -0
  205. package/src/components/chat/sender/@hooks/use-model-select-browser.tsx +189 -0
  206. package/src/components/chat/sender/@hooks/use-sender-attachments.ts +143 -0
  207. package/src/components/chat/sender/@hooks/use-sender-autofocus.ts +34 -0
  208. package/src/components/chat/sender/@hooks/use-sender-completion.ts +62 -0
  209. package/src/components/chat/sender/@hooks/use-sender-composer-state.ts +34 -0
  210. package/src/components/chat/sender/@hooks/use-sender-controller.ts +193 -0
  211. package/src/components/chat/sender/@hooks/use-sender-focus-restore.ts +72 -0
  212. package/src/components/chat/sender/@hooks/use-sender-history.ts +79 -0
  213. package/src/components/chat/sender/@hooks/use-sender-keydown.ts +113 -0
  214. package/src/components/chat/sender/@hooks/use-sender-reference-actions.ts +191 -0
  215. package/src/components/chat/sender/@hooks/use-sender-reference-focus-restore.ts +21 -0
  216. package/src/components/chat/sender/@hooks/use-sender-refs.ts +19 -0
  217. package/src/components/chat/sender/@hooks/use-sender-select-overlays.ts +83 -0
  218. package/src/components/chat/sender/@hooks/use-sender-shortcuts.ts +78 -0
  219. package/src/components/chat/sender/@hooks/use-sender-submit.ts +81 -0
  220. package/src/components/chat/sender/@types/sender-composer.ts +19 -0
  221. package/src/components/chat/sender/@types/sender-editor.ts +12 -0
  222. package/src/components/chat/sender/@types/sender-props.ts +50 -0
  223. package/src/components/chat/sender/@types/sender-toolbar-types.ts +83 -0
  224. package/src/components/chat/sender/@types/sender-types.ts +21 -0
  225. package/src/components/chat/sender/@utils/sender-completion.ts +164 -0
  226. package/src/components/chat/sender/@utils/sender-constants.ts +18 -0
  227. package/src/components/chat/sender/@utils/sender-utils.ts +45 -0
  228. package/src/components/chat/sender/Sender.scss +8 -605
  229. package/src/components/chat/sender/Sender.tsx +54 -880
  230. package/src/components/chat/session-metadata.ts +55 -0
  231. package/src/components/chat/terminal/@hooks/use-terminal-instance.ts +152 -0
  232. package/src/components/chat/terminal/@hooks/use-terminal-session.ts +196 -0
  233. package/src/components/chat/terminal/ChatTerminalView.scss +62 -0
  234. package/src/components/chat/terminal/ChatTerminalView.tsx +114 -0
  235. package/src/components/chat/tools/core/ToolGroup.scss +7 -0
  236. package/src/components/chat/tools/core/ToolGroup.tsx +97 -56
  237. package/src/components/config/ConfigSectionForm.tsx +8 -1
  238. package/src/components/config/ConfigShortcutInput.tsx +9 -2
  239. package/src/components/config/configSchema.ts +12 -2
  240. package/src/components/config/record-editors/ModelServicesRecordEditor.tsx +0 -14
  241. package/src/components/dock-panel/DockPanel.scss +152 -0
  242. package/src/components/dock-panel/DockPanel.tsx +195 -0
  243. package/src/components/layout/AppShell.scss +40 -2
  244. package/src/components/layout/AppShell.tsx +25 -10
  245. package/src/components/sidebar/SessionContextMenu.scss +143 -0
  246. package/src/components/sidebar/SessionContextMenu.tsx +196 -0
  247. package/src/components/sidebar/SessionContextMenuContent.tsx +89 -0
  248. package/src/components/sidebar/SessionItem.scss +150 -67
  249. package/src/components/sidebar/SessionItem.tsx +183 -134
  250. package/src/components/sidebar/SessionList.scss +47 -17
  251. package/src/components/sidebar/SessionList.tsx +31 -16
  252. package/src/components/sidebar/SidebarHeader.scss +329 -49
  253. package/src/components/sidebar/SidebarHeader.tsx +108 -86
  254. package/src/components/sidebar/SidebarHeaderBatchActions.tsx +81 -0
  255. package/src/components/sidebar/SidebarHeaderSearchActions.tsx +176 -0
  256. package/src/components/sidebar/SidebarHeaderSelectField.tsx +24 -0
  257. package/src/components/sidebar/filter-utils.ts +23 -0
  258. package/src/components/workspace/ContextFilePicker.scss +64 -0
  259. package/src/components/workspace/ContextFilePicker.tsx +171 -0
  260. package/src/connectionManager.ts +4 -2
  261. package/src/hooks/chat/interaction-state.ts +104 -0
  262. package/src/hooks/chat/model-selector-data-builders.ts +146 -0
  263. package/src/hooks/chat/model-selector-data-option-utils.ts +62 -0
  264. package/src/hooks/chat/model-selector-data-types.ts +27 -0
  265. package/src/hooks/chat/model-selector-data.ts +109 -0
  266. package/src/hooks/chat/model-selector-recommendations.ts +69 -0
  267. package/src/hooks/chat/model-selector.ts +9 -0
  268. package/src/hooks/chat/use-chat-interaction.ts +13 -8
  269. package/src/hooks/chat/use-chat-model-adapter-selection.tsx +167 -164
  270. package/src/hooks/chat/use-chat-models.tsx +46 -23
  271. package/src/hooks/chat/use-chat-session-actions.ts +69 -23
  272. package/src/hooks/chat/use-chat-session-messages.ts +165 -60
  273. package/src/hooks/chat/use-chat-session.ts +34 -9
  274. package/src/hooks/chat/use-chat-view.ts +26 -6
  275. package/src/hooks/chat/use-composer-control-shortcuts.ts +69 -0
  276. package/src/hooks/chat/use-terminal-dock-visibility.ts +39 -0
  277. package/src/hooks/use-roving-focus-list.ts +104 -0
  278. package/src/hooks/use-sidebar-navigation.ts +9 -4
  279. package/src/hooks/use-sidebar-query-state.ts +79 -0
  280. package/src/main.tsx +6 -1
  281. package/src/resources/locales/en.json +151 -6
  282. package/src/resources/locales/zh.json +151 -6
  283. package/src/routes/ChatRoute.scss +159 -4
  284. package/src/routes/ChatRoute.tsx +43 -9
  285. package/src/runtime-config.ts +21 -0
  286. package/src/store/index.ts +1 -3
  287. package/src/styles/global.scss +12 -2
  288. package/src/utils/chat-links.ts +21 -0
  289. package/src/utils/copy.ts +18 -0
  290. package/src/utils/shortcutUtils.ts +111 -1
  291. package/src/vite-env.d.ts +1 -0
  292. package/src/ws.ts +6 -5
  293. package/vite.config.ts +71 -7
  294. package/dist/assets/channel-bLjHfx-Q.js +0 -1
  295. package/dist/assets/clone-upfY39Je.js +0 -1
  296. package/dist/assets/flowDiagram-v2-4f6560a1-F66FzZTY.js +0 -1
  297. package/dist/assets/index-C1O04Df8.js +0 -557
  298. package/dist/assets/index-sE8VA1N7.css +0 -1
  299. package/src/components/chat/sender/CompletionMenu.scss +0 -70
  300. package/src/components/chat/sender/CompletionMenu.tsx +0 -58
  301. /package/src/components/chat/{sender/ThinkingStatus.scss → ThinkingStatus.scss} +0 -0
@@ -0,0 +1,81 @@
1
+ import { Button, Checkbox, Popconfirm, Tooltip } from 'antd'
2
+ import { useTranslation } from 'react-i18next'
3
+
4
+ interface SidebarHeaderBatchActionsProps {
5
+ isAllSelected: boolean
6
+ selectedCount: number
7
+ totalCount: number
8
+ onBatchArchive: () => void
9
+ onBatchDelete: () => void
10
+ onBatchStar: () => void
11
+ onSelectAll: (selected: boolean) => void
12
+ }
13
+
14
+ export function SidebarHeaderBatchActions({
15
+ isAllSelected,
16
+ selectedCount,
17
+ totalCount,
18
+ onBatchArchive,
19
+ onBatchDelete,
20
+ onBatchStar,
21
+ onSelectAll
22
+ }: SidebarHeaderBatchActionsProps) {
23
+ const { t } = useTranslation()
24
+
25
+ return (
26
+ <div className='header-batch-actions'>
27
+ <Tooltip title={isAllSelected ? t('common.deselectAll') : t('common.selectAll')}>
28
+ <label className='batch-select-toggle'>
29
+ <Checkbox
30
+ checked={isAllSelected}
31
+ indeterminate={selectedCount > 0 && selectedCount < totalCount}
32
+ onChange={(e) => onSelectAll(e.target.checked)}
33
+ />
34
+ </label>
35
+ </Tooltip>
36
+ <Tooltip title={t('common.star')}>
37
+ <Button
38
+ className='sidebar-tool-btn is-icon-only'
39
+ type='text'
40
+ disabled={selectedCount === 0}
41
+ onClick={onBatchStar}
42
+ icon={<span className='material-symbols-rounded'>star</span>}
43
+ />
44
+ </Tooltip>
45
+ <Popconfirm
46
+ title={t('common.archiveConfirm', { count: selectedCount })}
47
+ onConfirm={onBatchArchive}
48
+ okText={t('common.confirm')}
49
+ cancelText={t('common.cancel')}
50
+ okButtonProps={{ danger: false }}
51
+ disabled={selectedCount === 0}
52
+ >
53
+ <Tooltip title={t('common.archive')}>
54
+ <Button
55
+ className='sidebar-tool-btn is-icon-only'
56
+ type='text'
57
+ disabled={selectedCount === 0}
58
+ icon={<span className='material-symbols-rounded'>archive</span>}
59
+ />
60
+ </Tooltip>
61
+ </Popconfirm>
62
+ <Popconfirm
63
+ title={t('common.deleteConfirm', { count: selectedCount })}
64
+ onConfirm={onBatchDelete}
65
+ okText={t('common.confirm')}
66
+ cancelText={t('common.cancel')}
67
+ okButtonProps={{ danger: true }}
68
+ disabled={selectedCount === 0}
69
+ >
70
+ <Tooltip title={t('common.delete')}>
71
+ <Button
72
+ className='sidebar-tool-btn is-icon-only is-danger'
73
+ type='text'
74
+ disabled={selectedCount === 0}
75
+ icon={<span className='material-symbols-rounded'>delete</span>}
76
+ />
77
+ </Tooltip>
78
+ </Popconfirm>
79
+ </div>
80
+ )
81
+ }
@@ -0,0 +1,176 @@
1
+ import { Button, Input, Tooltip } from 'antd'
2
+ import { useMemo } from 'react'
3
+ import { useTranslation } from 'react-i18next'
4
+
5
+ import type { SidebarSessionSortOrder } from '#~/hooks/use-sidebar-query-state'
6
+ import { SidebarHeaderBatchActions } from './SidebarHeaderBatchActions'
7
+ import { SidebarHeaderSelectField } from './SidebarHeaderSelectField'
8
+
9
+ interface SidebarHeaderSearchActionsProps {
10
+ adapterFilters: string[]
11
+ availableAdapters: string[]
12
+ availableTags: string[]
13
+ hasActiveSearchControls: boolean
14
+ isBatchMode: boolean
15
+ searchQuery: string
16
+ selectedCount: number
17
+ shouldShowSearchActions: boolean
18
+ sortOrder: SidebarSessionSortOrder
19
+ sortSelection?: SidebarSessionSortOrder
20
+ tagFilters: string[]
21
+ totalCount: number
22
+ onAdapterFilterChange: (filters: string[]) => void
23
+ onBatchArchive: () => void
24
+ onBatchDelete: () => void
25
+ onBatchStar: () => void
26
+ onSearchChange: (query: string) => void
27
+ onSortOrderChange: (sort?: SidebarSessionSortOrder) => void
28
+ onSelectAll: (selected: boolean) => void
29
+ onTagFilterChange: (tags: string[]) => void
30
+ onToggleBatchMode: () => void
31
+ onToggleSearchActions: () => void
32
+ }
33
+
34
+ export function SidebarHeaderSearchActions({
35
+ adapterFilters,
36
+ availableAdapters,
37
+ availableTags,
38
+ hasActiveSearchControls,
39
+ isBatchMode,
40
+ searchQuery,
41
+ selectedCount,
42
+ shouldShowSearchActions,
43
+ sortOrder,
44
+ sortSelection,
45
+ tagFilters,
46
+ totalCount,
47
+ onAdapterFilterChange,
48
+ onBatchArchive,
49
+ onBatchDelete,
50
+ onBatchStar,
51
+ onSearchChange,
52
+ onSortOrderChange,
53
+ onSelectAll,
54
+ onTagFilterChange,
55
+ onToggleBatchMode,
56
+ onToggleSearchActions
57
+ }: SidebarHeaderSearchActionsProps) {
58
+ const { t } = useTranslation()
59
+ const isAllSelected = totalCount > 0 && selectedCount === totalCount
60
+ const toOptions = useMemo(() => (values: string[]) => values.map((value) => ({ label: value, value })), [])
61
+ const filterSuffixIcon = <span className='material-symbols-rounded toolbar-filter-chevron'>expand_more</span>
62
+ const sortOptions = useMemo(
63
+ () => [
64
+ { label: t('automation.sortDesc'), value: 'desc' },
65
+ { label: t('automation.sortAsc'), value: 'asc' }
66
+ ],
67
+ [t]
68
+ )
69
+
70
+ return (
71
+ <>
72
+ <div className='header-search-row'>
73
+ <div className='search-input-wrap'>
74
+ <Input
75
+ className='search-input'
76
+ placeholder={t('common.search')}
77
+ value={searchQuery}
78
+ onChange={(e) => onSearchChange(e.target.value)}
79
+ prefix={<span className='material-symbols-rounded search-icon'>search</span>}
80
+ suffix={
81
+ <Tooltip title={t('common.searchActions')}>
82
+ <button
83
+ type='button'
84
+ className={`search-toggle-button ${shouldShowSearchActions ? 'is-open' : ''} ${
85
+ hasActiveSearchControls ? 'has-active-filters' : ''
86
+ }`}
87
+ aria-label={t('common.searchActions')}
88
+ onMouseDown={(event) => event.preventDefault()}
89
+ onClick={onToggleSearchActions}
90
+ >
91
+ <span className='material-symbols-rounded search-chevron'>expand_more</span>
92
+ </button>
93
+ </Tooltip>
94
+ }
95
+ allowClear
96
+ />
97
+ </div>
98
+ </div>
99
+ <div className={`header-search-actions ${shouldShowSearchActions ? 'is-open' : ''}`}>
100
+ <div className='header-search-actions-inner'>
101
+ <div className='header-toolbar-row'>
102
+ <div className='header-toolbar-leading'>
103
+ {isBatchMode
104
+ ? (
105
+ <Tooltip title={t('common.cancelBatch')}>
106
+ <Button
107
+ className='sidebar-tool-btn is-icon-only'
108
+ type='text'
109
+ onClick={onToggleBatchMode}
110
+ icon={<span className='material-symbols-rounded'>close</span>}
111
+ />
112
+ </Tooltip>
113
+ )
114
+ : (
115
+ <Tooltip title={t('common.batchMode')}>
116
+ <Button
117
+ className='sidebar-tool-btn is-icon-only'
118
+ type='text'
119
+ onClick={onToggleBatchMode}
120
+ icon={<span className='material-symbols-rounded'>checklist</span>}
121
+ />
122
+ </Tooltip>
123
+ )}
124
+ </div>
125
+ <div className='header-filter-stack'>
126
+ <SidebarHeaderSelectField
127
+ icon='sell'
128
+ mode='tags'
129
+ placeholder={t('common.allTags')}
130
+ options={toOptions(availableTags)}
131
+ value={tagFilters}
132
+ onChange={(value) => onTagFilterChange(value as string[])}
133
+ maxTagCount={1}
134
+ allowClear
135
+ suffixIcon={filterSuffixIcon}
136
+ tokenSeparators={[',']}
137
+ />
138
+ <SidebarHeaderSelectField
139
+ icon='extension'
140
+ mode='tags'
141
+ placeholder={t('common.allAdapters')}
142
+ options={toOptions(availableAdapters)}
143
+ value={adapterFilters}
144
+ onChange={(value) => onAdapterFilterChange(value as string[])}
145
+ maxTagCount={1}
146
+ allowClear
147
+ suffixIcon={filterSuffixIcon}
148
+ tokenSeparators={[',']}
149
+ />
150
+ <SidebarHeaderSelectField
151
+ icon='swap_vert'
152
+ placeholder={t('common.sort')}
153
+ options={sortOptions}
154
+ value={sortSelection}
155
+ onChange={(value) => onSortOrderChange(value as SidebarSessionSortOrder | undefined)}
156
+ allowClear
157
+ suffixIcon={filterSuffixIcon}
158
+ />
159
+ </div>
160
+ </div>
161
+ {isBatchMode && (
162
+ <SidebarHeaderBatchActions
163
+ isAllSelected={isAllSelected}
164
+ selectedCount={selectedCount}
165
+ totalCount={totalCount}
166
+ onBatchArchive={onBatchArchive}
167
+ onBatchDelete={onBatchDelete}
168
+ onBatchStar={onBatchStar}
169
+ onSelectAll={onSelectAll}
170
+ />
171
+ )}
172
+ </div>
173
+ </div>
174
+ </>
175
+ )
176
+ }
@@ -0,0 +1,24 @@
1
+ import { Select } from 'antd'
2
+ import type { SelectProps } from 'antd'
3
+
4
+ interface SidebarHeaderSelectFieldProps extends SelectProps {
5
+ icon: string
6
+ }
7
+
8
+ export function SidebarHeaderSelectField({
9
+ className,
10
+ icon,
11
+ ...selectProps
12
+ }: SidebarHeaderSelectFieldProps) {
13
+ return (
14
+ <div className='toolbar-filter-control'>
15
+ <span className='material-symbols-rounded toolbar-filter-icon'>{icon}</span>
16
+ <Select
17
+ className={className == null || className === ''
18
+ ? 'toolbar-filter-select'
19
+ : `toolbar-filter-select ${className}`}
20
+ {...selectProps}
21
+ />
22
+ </div>
23
+ )
24
+ }
@@ -0,0 +1,23 @@
1
+ const escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
2
+
3
+ const buildWildcardRegex = (pattern: string) => {
4
+ return new RegExp(`^${escapeRegex(pattern).replace(/\\\*/g, '.*')}$`, 'i')
5
+ }
6
+
7
+ const matchesPattern = (candidate: string, pattern: string) => {
8
+ const normalizedCandidate = candidate.trim()
9
+ const normalizedPattern = pattern.trim()
10
+
11
+ if (normalizedCandidate === '' || normalizedPattern === '') return false
12
+ if (normalizedPattern.includes('*')) {
13
+ return buildWildcardRegex(normalizedPattern).test(normalizedCandidate)
14
+ }
15
+
16
+ return normalizedCandidate.toLowerCase().includes(normalizedPattern.toLowerCase())
17
+ }
18
+
19
+ export const matchesAnyFilterPattern = (candidates: string[], patterns: string[]) => {
20
+ if (patterns.length === 0) return true
21
+
22
+ return patterns.some((pattern) => candidates.some((candidate) => matchesPattern(candidate, pattern)))
23
+ }
@@ -0,0 +1,64 @@
1
+ .context-file-picker {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 12px;
5
+
6
+ &__body {
7
+ min-height: 320px;
8
+ max-height: 460px;
9
+ overflow: auto;
10
+ }
11
+
12
+ &__loading {
13
+ min-height: 320px;
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ gap: 8px;
18
+ color: var(--sub-text-color, #6b7280);
19
+ font-size: 13px;
20
+ }
21
+
22
+ &__switcher {
23
+ font-size: 18px;
24
+ color: var(--sub-text-color, #6b7280);
25
+ }
26
+
27
+ &__footer {
28
+ display: flex;
29
+ justify-content: flex-end;
30
+ gap: 8px;
31
+ }
32
+
33
+ .ant-tree {
34
+ background: transparent;
35
+ color: var(--text-color);
36
+ }
37
+
38
+ .ant-tree-list-holder-inner {
39
+ gap: 2px;
40
+ }
41
+
42
+ .ant-tree-node-content-wrapper {
43
+ min-height: 34px;
44
+ border-radius: 8px;
45
+ display: inline-flex;
46
+ align-items: center;
47
+ }
48
+
49
+ .ant-tree-checkbox + .ant-tree-node-content-wrapper:hover,
50
+ .ant-tree-node-content-wrapper:hover {
51
+ background: var(--tag-hover-bg, #f3f4f6);
52
+ }
53
+ }
54
+
55
+ .context-file-picker--inline {
56
+ .context-file-picker__body {
57
+ min-height: 240px;
58
+ max-height: 280px;
59
+ }
60
+
61
+ .context-file-picker__loading {
62
+ min-height: 240px;
63
+ }
64
+ }
@@ -0,0 +1,171 @@
1
+ import './ContextFilePicker.scss'
2
+
3
+ import { App, Button, Empty, Modal, Spin, Tree } from 'antd'
4
+ import type { DataNode } from 'antd/es/tree'
5
+ import { useEffect, useState } from 'react'
6
+ import { useTranslation } from 'react-i18next'
7
+
8
+ import { listWorkspaceTree } from '#~/api/workspace'
9
+
10
+ export interface ContextPickerFile {
11
+ path: string
12
+ name?: string
13
+ }
14
+
15
+ interface ContextFileTreeNode extends DataNode {
16
+ key: string
17
+ path: string
18
+ title: string
19
+ children?: ContextFileTreeNode[]
20
+ isLeaf?: boolean
21
+ disableCheckbox?: boolean
22
+ }
23
+
24
+ const replaceNodeChildren = (
25
+ nodes: ContextFileTreeNode[],
26
+ targetPath: string,
27
+ children: ContextFileTreeNode[]
28
+ ): ContextFileTreeNode[] => {
29
+ return nodes.map((node) => {
30
+ if (node.path === targetPath) {
31
+ return {
32
+ ...node,
33
+ children
34
+ }
35
+ }
36
+ if (node.children == null) {
37
+ return node
38
+ }
39
+ return {
40
+ ...node,
41
+ children: replaceNodeChildren(node.children, targetPath, children)
42
+ }
43
+ })
44
+ }
45
+
46
+ const toTreeNodes = (entries: Awaited<ReturnType<typeof listWorkspaceTree>>['entries']): ContextFileTreeNode[] =>
47
+ entries.map(entry => ({
48
+ key: entry.path,
49
+ path: entry.path,
50
+ title: entry.name,
51
+ isLeaf: entry.type === 'file',
52
+ disableCheckbox: entry.type !== 'file'
53
+ }))
54
+
55
+ const toPendingFiles = (paths: string[]): ContextPickerFile[] =>
56
+ paths.map(path => ({
57
+ path,
58
+ name: path.split('/').pop() ?? path
59
+ }))
60
+
61
+ export function ContextFilePicker({
62
+ open,
63
+ selectedPaths,
64
+ variant = 'modal',
65
+ onCancel,
66
+ onConfirm
67
+ }: {
68
+ open: boolean
69
+ selectedPaths: string[]
70
+ variant?: 'inline' | 'modal'
71
+ onCancel: () => void
72
+ onConfirm: (files: ContextPickerFile[]) => void
73
+ }) {
74
+ const { t } = useTranslation()
75
+ const { message } = App.useApp()
76
+ const [checkedKeys, setCheckedKeys] = useState<string[]>(selectedPaths)
77
+ const [treeData, setTreeData] = useState<ContextFileTreeNode[]>([])
78
+ const [loadingRoot, setLoadingRoot] = useState(false)
79
+
80
+ useEffect(() => {
81
+ if (!open) {
82
+ return
83
+ }
84
+
85
+ setCheckedKeys(selectedPaths)
86
+ setLoadingRoot(true)
87
+ void listWorkspaceTree()
88
+ .then((result) => {
89
+ setTreeData(toTreeNodes(result.entries))
90
+ })
91
+ .catch(() => {
92
+ void message.error(t('chat.contextPickerLoadFailed'))
93
+ })
94
+ .finally(() => {
95
+ setLoadingRoot(false)
96
+ })
97
+ }, [message, open, selectedPaths, t])
98
+
99
+ const loadData = async (node: DataNode) => {
100
+ const path = String(node.key)
101
+ const result = await listWorkspaceTree(path)
102
+ setTreeData(prev => replaceNodeChildren(prev, path, toTreeNodes(result.entries)))
103
+ }
104
+
105
+ const body = (
106
+ <div className={`context-file-picker ${variant === 'inline' ? 'context-file-picker--inline' : ''}`.trim()}>
107
+ <div className='context-file-picker__body'>
108
+ {loadingRoot
109
+ ? (
110
+ <div className='context-file-picker__loading'>
111
+ <Spin size='small' />
112
+ <span>{t('chat.contextPickerLoading')}</span>
113
+ </div>
114
+ )
115
+ : treeData.length === 0
116
+ ? (
117
+ <Empty
118
+ image={Empty.PRESENTED_IMAGE_SIMPLE}
119
+ description={t('chat.contextPickerEmpty')}
120
+ />
121
+ )
122
+ : (
123
+ <Tree
124
+ blockNode
125
+ checkable
126
+ checkStrictly
127
+ treeData={treeData}
128
+ checkedKeys={checkedKeys}
129
+ loadData={loadData}
130
+ switcherIcon={
131
+ <span className='material-symbols-rounded context-file-picker__switcher'>chevron_right</span>
132
+ }
133
+ onCheck={(keys) => {
134
+ const nextKeys = Array.isArray(keys) ? keys : keys.checked
135
+ setCheckedKeys(nextKeys.map(String))
136
+ }}
137
+ />
138
+ )}
139
+ </div>
140
+ {variant === 'inline' && (
141
+ <div className='context-file-picker__footer'>
142
+ <Button size='small' onClick={onCancel}>
143
+ {t('common.cancel')}
144
+ </Button>
145
+ <Button type='primary' size='small' onClick={() => onConfirm(toPendingFiles(checkedKeys))}>
146
+ {t('chat.contextPickerConfirm')}
147
+ </Button>
148
+ </div>
149
+ )}
150
+ </div>
151
+ )
152
+
153
+ if (variant === 'inline') {
154
+ return body
155
+ }
156
+
157
+ return (
158
+ <Modal
159
+ open={open}
160
+ title={t('chat.contextPickerTitle')}
161
+ okText={t('chat.contextPickerConfirm')}
162
+ cancelText={t('common.cancel')}
163
+ width={640}
164
+ onCancel={onCancel}
165
+ onOk={() => onConfirm(toPendingFiles(checkedKeys))}
166
+ className='context-file-picker-modal'
167
+ >
168
+ {body}
169
+ </Modal>
170
+ )
171
+ }
@@ -146,9 +146,11 @@ class ConnectionManager {
146
146
  const ws = this.sockets.get(sessionId)
147
147
  if (ws && ws.readyState === WebSocket.OPEN) {
148
148
  ws.send(typeof data === 'string' ? data : JSON.stringify(data))
149
- } else {
150
- console.warn(`Cannot send message: Session ${sessionId} not connected or ready`)
149
+ return true
151
150
  }
151
+
152
+ console.warn(`Cannot send message: Session ${sessionId} not connected or ready`)
153
+ return false
152
154
  }
153
155
 
154
156
  private normalizeParams(params: Record<string, string>) {
@@ -0,0 +1,104 @@
1
+ import type { AskUserQuestionParams, Session, WSEvent } from '@vibe-forge/core'
2
+
3
+ export interface InteractionRequestState {
4
+ id: string
5
+ payload: AskUserQuestionParams
6
+ }
7
+
8
+ export interface ChatErrorBannerState {
9
+ kind: 'connection' | 'session'
10
+ message: string
11
+ }
12
+
13
+ export interface FatalSessionErrorState {
14
+ message: string
15
+ code?: string
16
+ }
17
+
18
+ export const getFatalSessionError = (event: WSEvent): FatalSessionErrorState | null => {
19
+ if (event?.type !== 'error') {
20
+ return null
21
+ }
22
+
23
+ if (event.data != null && typeof event.data === 'object' && 'fatal' in event.data && event.data.fatal === false) {
24
+ return null
25
+ }
26
+
27
+ if (event.data != null && typeof event.data === 'object' && 'message' in event.data) {
28
+ const message = event.data.message
29
+ if (typeof message === 'string' && message.trim() !== '') {
30
+ return {
31
+ message,
32
+ code: typeof event.data.code === 'string' && event.data.code.trim() !== ''
33
+ ? event.data.code
34
+ : undefined
35
+ }
36
+ }
37
+ }
38
+
39
+ if (typeof event.message === 'string' && event.message.trim() !== '') {
40
+ return { message: event.message }
41
+ }
42
+
43
+ return null
44
+ }
45
+
46
+ export const applyInteractionStateEvent = (
47
+ currentInteraction: InteractionRequestState | null,
48
+ data: WSEvent
49
+ ) => {
50
+ if (data.type === 'interaction_request') {
51
+ return { id: data.id, payload: data.payload }
52
+ }
53
+
54
+ if (data.type === 'interaction_response') {
55
+ return currentInteraction?.id === data.id || currentInteraction == null
56
+ ? null
57
+ : currentInteraction
58
+ }
59
+
60
+ if (data.type === 'session_updated') {
61
+ const session = data.session as Session | { id: string; isDeleted: boolean }
62
+ if ('isDeleted' in session) {
63
+ return null
64
+ }
65
+ if (session.status !== 'waiting_input') {
66
+ return null
67
+ }
68
+ }
69
+
70
+ if (data.type === 'error' && data.data != null && typeof data.data === 'object' && 'fatal' in data.data) {
71
+ return (data.data as { fatal?: boolean }).fatal !== false ? null : currentInteraction
72
+ }
73
+
74
+ return currentInteraction
75
+ }
76
+
77
+ export const restoreInteractionStateFromHistory = (
78
+ events: WSEvent[],
79
+ fallbackInteraction: InteractionRequestState | null,
80
+ sessionStatus?: Session['status']
81
+ ) => {
82
+ let currentInteraction: InteractionRequestState | null = null
83
+
84
+ for (const event of events) {
85
+ currentInteraction = applyInteractionStateEvent(currentInteraction, event)
86
+ }
87
+
88
+ if (currentInteraction != null) {
89
+ return currentInteraction
90
+ }
91
+
92
+ return sessionStatus === 'waiting_input' ? fallbackInteraction : null
93
+ }
94
+
95
+ export const findLatestFatalError = (events: WSEvent[]): FatalSessionErrorState | null => {
96
+ for (let index = events.length - 1; index >= 0; index -= 1) {
97
+ const resolved = getFatalSessionError(events[index]!)
98
+ if (resolved != null) {
99
+ return resolved
100
+ }
101
+ }
102
+
103
+ return null
104
+ }