@ottocode/web-sdk 0.1.226 → 0.1.228

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 (406) hide show
  1. package/dist/components/chat/FileMentionPopup.d.ts.map +1 -1
  2. package/dist/components/index.js +25158 -80
  3. package/dist/components/index.js.map +180 -1
  4. package/dist/components/messages/AssistantMessageGroup.d.ts.map +1 -1
  5. package/dist/components/messages/CompactActivityGroup.d.ts +10 -0
  6. package/dist/components/messages/CompactActivityGroup.d.ts.map +1 -0
  7. package/dist/components/messages/MessagePartItem.d.ts.map +1 -1
  8. package/dist/components/messages/MessageThread.d.ts +1 -0
  9. package/dist/components/messages/MessageThread.d.ts.map +1 -1
  10. package/dist/components/messages/MessageThreadContainer.d.ts.map +1 -1
  11. package/dist/components/messages/compactActivity.d.ts +34 -0
  12. package/dist/components/messages/compactActivity.d.ts.map +1 -0
  13. package/dist/components/messages/renderers/EditRenderer.d.ts +1 -1
  14. package/dist/components/messages/renderers/EditRenderer.d.ts.map +1 -1
  15. package/dist/components/messages/renderers/index.d.ts.map +1 -1
  16. package/dist/components/messages/renderers/types.d.ts +3 -1
  17. package/dist/components/messages/renderers/types.d.ts.map +1 -1
  18. package/dist/components/settings/SettingsSidebar.d.ts.map +1 -1
  19. package/dist/components/settings/SetuTopupModal.d.ts.map +1 -1
  20. package/dist/hooks/index.js +4821 -29
  21. package/dist/hooks/index.js.map +67 -1
  22. package/dist/hooks/useAuthStatus.d.ts.map +1 -1
  23. package/dist/hooks/useMCP.d.ts +9 -0
  24. package/dist/hooks/useMCP.d.ts.map +1 -1
  25. package/dist/hooks/usePreferences.d.ts +1 -0
  26. package/dist/hooks/usePreferences.d.ts.map +1 -1
  27. package/dist/hooks/useSessionStream.d.ts.map +1 -1
  28. package/dist/index.js +25928 -7
  29. package/dist/index.js.map +185 -1
  30. package/dist/lib/api-client/approval.d.ts.map +1 -1
  31. package/dist/lib/api-client/utils.d.ts.map +1 -1
  32. package/dist/lib/index.js +1016 -5
  33. package/dist/lib/index.js.map +22 -1
  34. package/dist/stores/index.js +693 -22
  35. package/dist/stores/index.js.map +30 -1
  36. package/dist/stores/toastStore.d.ts +1 -0
  37. package/dist/stores/toastStore.d.ts.map +1 -1
  38. package/dist/types/api.js +2 -2
  39. package/dist/types/api.js.map +9 -1
  40. package/package.json +4 -4
  41. package/dist/assets/provider-logos.js +0 -43
  42. package/dist/assets/provider-logos.js.map +0 -1
  43. package/dist/components/branch/BranchModal.js +0 -45
  44. package/dist/components/branch/BranchModal.js.map +0 -1
  45. package/dist/components/chat/ChatInput.js +0 -267
  46. package/dist/components/chat/ChatInput.js.map +0 -1
  47. package/dist/components/chat/ChatInputContainer.js +0 -295
  48. package/dist/components/chat/ChatInputContainer.js.map +0 -1
  49. package/dist/components/chat/ChatInputKeyHandler.js +0 -75
  50. package/dist/components/chat/ChatInputKeyHandler.js.map +0 -1
  51. package/dist/components/chat/CommandSuggestionsPopup.js +0 -45
  52. package/dist/components/chat/CommandSuggestionsPopup.js.map +0 -1
  53. package/dist/components/chat/ConfigModal.js +0 -46
  54. package/dist/components/chat/ConfigModal.js.map +0 -1
  55. package/dist/components/chat/ConfigSelector.js +0 -47
  56. package/dist/components/chat/ConfigSelector.js.map +0 -1
  57. package/dist/components/chat/FileMentionPopup.js +0 -138
  58. package/dist/components/chat/FileMentionPopup.js.map +0 -1
  59. package/dist/components/chat/ShortcutsModal.js +0 -275
  60. package/dist/components/chat/ShortcutsModal.js.map +0 -1
  61. package/dist/components/chat/StopButton.js +0 -25
  62. package/dist/components/chat/StopButton.js.map +0 -1
  63. package/dist/components/chat/UnifiedAgentSelector.js +0 -107
  64. package/dist/components/chat/UnifiedAgentSelector.js.map +0 -1
  65. package/dist/components/chat/UnifiedModelSelector.js +0 -209
  66. package/dist/components/chat/UnifiedModelSelector.js.map +0 -1
  67. package/dist/components/common/ProviderLogo.js +0 -30
  68. package/dist/components/common/ProviderLogo.js.map +0 -1
  69. package/dist/components/common/StatusIndicator.js +0 -54
  70. package/dist/components/common/StatusIndicator.js.map +0 -1
  71. package/dist/components/common/UsageModal.js +0 -55
  72. package/dist/components/common/UsageModal.js.map +0 -1
  73. package/dist/components/common/UsageRing.js +0 -22
  74. package/dist/components/common/UsageRing.js.map +0 -1
  75. package/dist/components/file-browser/FileBrowserSidebar.js +0 -50
  76. package/dist/components/file-browser/FileBrowserSidebar.js.map +0 -1
  77. package/dist/components/file-browser/FileBrowserSidebarToggle.js +0 -12
  78. package/dist/components/file-browser/FileBrowserSidebarToggle.js.map +0 -1
  79. package/dist/components/file-browser/FileViewerPanel.js +0 -85
  80. package/dist/components/file-browser/FileViewerPanel.js.map +0 -1
  81. package/dist/components/file-browser/QuickFilePicker.js +0 -118
  82. package/dist/components/file-browser/QuickFilePicker.js.map +0 -1
  83. package/dist/components/git/GitCommitModal.js +0 -70
  84. package/dist/components/git/GitCommitModal.js.map +0 -1
  85. package/dist/components/git/GitDiffPanel.js +0 -60
  86. package/dist/components/git/GitDiffPanel.js.map +0 -1
  87. package/dist/components/git/GitDiffViewer.js +0 -224
  88. package/dist/components/git/GitDiffViewer.js.map +0 -1
  89. package/dist/components/git/GitFileItem.js +0 -145
  90. package/dist/components/git/GitFileItem.js.map +0 -1
  91. package/dist/components/git/GitFileList.js +0 -108
  92. package/dist/components/git/GitFileList.js.map +0 -1
  93. package/dist/components/git/GitSidebar.js +0 -156
  94. package/dist/components/git/GitSidebar.js.map +0 -1
  95. package/dist/components/git/GitSidebarToggle.js +0 -18
  96. package/dist/components/git/GitSidebarToggle.js.map +0 -1
  97. package/dist/components/mcp/AddMCPServerModal.js +0 -166
  98. package/dist/components/mcp/AddMCPServerModal.js.map +0 -1
  99. package/dist/components/mcp/MCPSidebar.js +0 -192
  100. package/dist/components/mcp/MCPSidebar.js.map +0 -1
  101. package/dist/components/mcp/MCPSidebarToggle.js +0 -14
  102. package/dist/components/mcp/MCPSidebarToggle.js.map +0 -1
  103. package/dist/components/mcp/index.js +0 -4
  104. package/dist/components/mcp/index.js.map +0 -1
  105. package/dist/components/messages/AssistantMessageGroup.js +0 -176
  106. package/dist/components/messages/AssistantMessageGroup.js.map +0 -1
  107. package/dist/components/messages/MessagePartItem.js +0 -362
  108. package/dist/components/messages/MessagePartItem.js.map +0 -1
  109. package/dist/components/messages/MessageThread.js +0 -239
  110. package/dist/components/messages/MessageThread.js.map +0 -1
  111. package/dist/components/messages/MessageThreadContainer.js +0 -22
  112. package/dist/components/messages/MessageThreadContainer.js.map +0 -1
  113. package/dist/components/messages/ToolApprovalCard.js +0 -143
  114. package/dist/components/messages/ToolApprovalCard.js.map +0 -1
  115. package/dist/components/messages/TopupApprovalCard.js +0 -52
  116. package/dist/components/messages/TopupApprovalCard.js.map +0 -1
  117. package/dist/components/messages/UserMessageGroup.js +0 -132
  118. package/dist/components/messages/UserMessageGroup.js.map +0 -1
  119. package/dist/components/messages/renderers/ApplyPatchRenderer.js +0 -55
  120. package/dist/components/messages/renderers/ApplyPatchRenderer.js.map +0 -1
  121. package/dist/components/messages/renderers/BashRenderer.js +0 -55
  122. package/dist/components/messages/renderers/BashRenderer.js.map +0 -1
  123. package/dist/components/messages/renderers/DatabaseToolRenderer.js +0 -151
  124. package/dist/components/messages/renderers/DatabaseToolRenderer.js.map +0 -1
  125. package/dist/components/messages/renderers/DebugRenderer.js +0 -5
  126. package/dist/components/messages/renderers/DebugRenderer.js.map +0 -1
  127. package/dist/components/messages/renderers/DiffView.js +0 -206
  128. package/dist/components/messages/renderers/DiffView.js.map +0 -1
  129. package/dist/components/messages/renderers/EditRenderer.js +0 -25
  130. package/dist/components/messages/renderers/EditRenderer.js.map +0 -1
  131. package/dist/components/messages/renderers/ErrorRenderer.js +0 -162
  132. package/dist/components/messages/renderers/ErrorRenderer.js.map +0 -1
  133. package/dist/components/messages/renderers/FinishRenderer.js +0 -7
  134. package/dist/components/messages/renderers/FinishRenderer.js.map +0 -1
  135. package/dist/components/messages/renderers/GenericRenderer.js +0 -50
  136. package/dist/components/messages/renderers/GenericRenderer.js.map +0 -1
  137. package/dist/components/messages/renderers/GitCommitRenderer.js +0 -21
  138. package/dist/components/messages/renderers/GitCommitRenderer.js.map +0 -1
  139. package/dist/components/messages/renderers/GitDiffRenderer.js +0 -34
  140. package/dist/components/messages/renderers/GitDiffRenderer.js.map +0 -1
  141. package/dist/components/messages/renderers/GitStatusRenderer.js +0 -60
  142. package/dist/components/messages/renderers/GitStatusRenderer.js.map +0 -1
  143. package/dist/components/messages/renderers/ListRenderer.js +0 -28
  144. package/dist/components/messages/renderers/ListRenderer.js.map +0 -1
  145. package/dist/components/messages/renderers/LoadMcpToolsRenderer.js +0 -20
  146. package/dist/components/messages/renderers/LoadMcpToolsRenderer.js.map +0 -1
  147. package/dist/components/messages/renderers/McpToolRenderer.js +0 -68
  148. package/dist/components/messages/renderers/McpToolRenderer.js.map +0 -1
  149. package/dist/components/messages/renderers/ProgressUpdateRenderer.js +0 -9
  150. package/dist/components/messages/renderers/ProgressUpdateRenderer.js.map +0 -1
  151. package/dist/components/messages/renderers/ReadRenderer.js +0 -87
  152. package/dist/components/messages/renderers/ReadRenderer.js.map +0 -1
  153. package/dist/components/messages/renderers/ReasoningRenderer.js +0 -34
  154. package/dist/components/messages/renderers/ReasoningRenderer.js.map +0 -1
  155. package/dist/components/messages/renderers/SearchRenderer.js +0 -52
  156. package/dist/components/messages/renderers/SearchRenderer.js.map +0 -1
  157. package/dist/components/messages/renderers/SkillRenderer.js +0 -36
  158. package/dist/components/messages/renderers/SkillRenderer.js.map +0 -1
  159. package/dist/components/messages/renderers/TerminalRenderer.js +0 -84
  160. package/dist/components/messages/renderers/TerminalRenderer.js.map +0 -1
  161. package/dist/components/messages/renderers/TodosRenderer.js +0 -15
  162. package/dist/components/messages/renderers/TodosRenderer.js.map +0 -1
  163. package/dist/components/messages/renderers/ToolErrorDisplay.js +0 -10
  164. package/dist/components/messages/renderers/ToolErrorDisplay.js.map +0 -1
  165. package/dist/components/messages/renderers/TreeRenderer.js +0 -33
  166. package/dist/components/messages/renderers/TreeRenderer.js.map +0 -1
  167. package/dist/components/messages/renderers/WebSearchRenderer.js +0 -29
  168. package/dist/components/messages/renderers/WebSearchRenderer.js.map +0 -1
  169. package/dist/components/messages/renderers/WriteRenderer.js +0 -28
  170. package/dist/components/messages/renderers/WriteRenderer.js.map +0 -1
  171. package/dist/components/messages/renderers/index.js +0 -129
  172. package/dist/components/messages/renderers/index.js.map +0 -1
  173. package/dist/components/messages/renderers/shared/CopyButton.js +0 -27
  174. package/dist/components/messages/renderers/shared/CopyButton.js.map +0 -1
  175. package/dist/components/messages/renderers/shared/ImagePreview.js +0 -14
  176. package/dist/components/messages/renderers/shared/ImagePreview.js.map +0 -1
  177. package/dist/components/messages/renderers/shared/ToolContentBox.js +0 -9
  178. package/dist/components/messages/renderers/shared/ToolContentBox.js.map +0 -1
  179. package/dist/components/messages/renderers/shared/ToolHeader.js +0 -69
  180. package/dist/components/messages/renderers/shared/ToolHeader.js.map +0 -1
  181. package/dist/components/messages/renderers/shared/index.js +0 -5
  182. package/dist/components/messages/renderers/shared/index.js.map +0 -1
  183. package/dist/components/messages/renderers/types.js +0 -2
  184. package/dist/components/messages/renderers/types.js.map +0 -1
  185. package/dist/components/messages/renderers/utils.js +0 -60
  186. package/dist/components/messages/renderers/utils.js.map +0 -1
  187. package/dist/components/onboarding/OnboardingModal.js +0 -22
  188. package/dist/components/onboarding/OnboardingModal.js.map +0 -1
  189. package/dist/components/onboarding/index.js +0 -4
  190. package/dist/components/onboarding/index.js.map +0 -1
  191. package/dist/components/onboarding/steps/DefaultsStep.js +0 -104
  192. package/dist/components/onboarding/steps/DefaultsStep.js.map +0 -1
  193. package/dist/components/onboarding/steps/ProviderSetupStep.js +0 -398
  194. package/dist/components/onboarding/steps/ProviderSetupStep.js.map +0 -1
  195. package/dist/components/research/ResearchSidebar.js +0 -288
  196. package/dist/components/research/ResearchSidebar.js.map +0 -1
  197. package/dist/components/research/ResearchSidebarToggle.js +0 -15
  198. package/dist/components/research/ResearchSidebarToggle.js.map +0 -1
  199. package/dist/components/research/index.js +0 -3
  200. package/dist/components/research/index.js.map +0 -1
  201. package/dist/components/session-files/SessionFilesDiffPanel.js +0 -345
  202. package/dist/components/session-files/SessionFilesDiffPanel.js.map +0 -1
  203. package/dist/components/session-files/SessionFilesSidebar.js +0 -87
  204. package/dist/components/session-files/SessionFilesSidebar.js.map +0 -1
  205. package/dist/components/session-files/SessionFilesSidebarToggle.js +0 -15
  206. package/dist/components/session-files/SessionFilesSidebarToggle.js.map +0 -1
  207. package/dist/components/session-files/index.js +0 -4
  208. package/dist/components/session-files/index.js.map +0 -1
  209. package/dist/components/sessions/EditableTitle.js +0 -40
  210. package/dist/components/sessions/EditableTitle.js.map +0 -1
  211. package/dist/components/sessions/LeanHeader.js +0 -70
  212. package/dist/components/sessions/LeanHeader.js.map +0 -1
  213. package/dist/components/sessions/SessionHeader.js +0 -75
  214. package/dist/components/sessions/SessionHeader.js.map +0 -1
  215. package/dist/components/sessions/SessionItem.js +0 -28
  216. package/dist/components/sessions/SessionItem.js.map +0 -1
  217. package/dist/components/sessions/SessionListContainer.js +0 -86
  218. package/dist/components/sessions/SessionListContainer.js.map +0 -1
  219. package/dist/components/settings/SettingsSidebar.js +0 -212
  220. package/dist/components/settings/SettingsSidebar.js.map +0 -1
  221. package/dist/components/settings/SettingsSidebarToggle.js +0 -12
  222. package/dist/components/settings/SettingsSidebarToggle.js.map +0 -1
  223. package/dist/components/settings/SetuTopupModal.js +0 -306
  224. package/dist/components/settings/SetuTopupModal.js.map +0 -1
  225. package/dist/components/skills/SkillViewerPanel.js +0 -89
  226. package/dist/components/skills/SkillViewerPanel.js.map +0 -1
  227. package/dist/components/skills/SkillsSidebar.js +0 -59
  228. package/dist/components/skills/SkillsSidebar.js.map +0 -1
  229. package/dist/components/skills/SkillsSidebarToggle.js +0 -12
  230. package/dist/components/skills/SkillsSidebarToggle.js.map +0 -1
  231. package/dist/components/skills/index.js +0 -4
  232. package/dist/components/skills/index.js.map +0 -1
  233. package/dist/components/terminals/TerminalPanelToggle.js +0 -13
  234. package/dist/components/terminals/TerminalPanelToggle.js.map +0 -1
  235. package/dist/components/terminals/TerminalTabBar.js +0 -22
  236. package/dist/components/terminals/TerminalTabBar.js.map +0 -1
  237. package/dist/components/terminals/TerminalViewer.js +0 -371
  238. package/dist/components/terminals/TerminalViewer.js.map +0 -1
  239. package/dist/components/terminals/TerminalsPanel.js +0 -134
  240. package/dist/components/terminals/TerminalsPanel.js.map +0 -1
  241. package/dist/components/terminals/index.js +0 -11
  242. package/dist/components/terminals/index.js.map +0 -1
  243. package/dist/components/tunnel/TunnelSidebar.js +0 -55
  244. package/dist/components/tunnel/TunnelSidebar.js.map +0 -1
  245. package/dist/components/tunnel/TunnelSidebarToggle.js +0 -14
  246. package/dist/components/tunnel/TunnelSidebarToggle.js.map +0 -1
  247. package/dist/components/tunnel/index.js +0 -3
  248. package/dist/components/tunnel/index.js.map +0 -1
  249. package/dist/components/ui/Button.js +0 -19
  250. package/dist/components/ui/Button.js.map +0 -1
  251. package/dist/components/ui/Card.js +0 -7
  252. package/dist/components/ui/Card.js.map +0 -1
  253. package/dist/components/ui/ConfirmationDialog.js +0 -72
  254. package/dist/components/ui/ConfirmationDialog.js.map +0 -1
  255. package/dist/components/ui/Input.js +0 -7
  256. package/dist/components/ui/Input.js.map +0 -1
  257. package/dist/components/ui/Modal.js +0 -51
  258. package/dist/components/ui/Modal.js.map +0 -1
  259. package/dist/components/ui/ResizeHandle.js +0 -39
  260. package/dist/components/ui/ResizeHandle.js.map +0 -1
  261. package/dist/components/ui/Textarea.js +0 -7
  262. package/dist/components/ui/Textarea.js.map +0 -1
  263. package/dist/components/ui/Toaster.js +0 -41
  264. package/dist/components/ui/Toaster.js.map +0 -1
  265. package/dist/components/ui/ToolApprovalDialog.js +0 -57
  266. package/dist/components/ui/ToolApprovalDialog.js.map +0 -1
  267. package/dist/hooks/useAuthStatus.js +0 -287
  268. package/dist/hooks/useAuthStatus.js.map +0 -1
  269. package/dist/hooks/useBranch.js +0 -44
  270. package/dist/hooks/useBranch.js.map +0 -1
  271. package/dist/hooks/useCommandSuggestions.js +0 -86
  272. package/dist/hooks/useCommandSuggestions.js.map +0 -1
  273. package/dist/hooks/useConfig.js +0 -32
  274. package/dist/hooks/useConfig.js.map +0 -1
  275. package/dist/hooks/useFileBrowser.js +0 -30
  276. package/dist/hooks/useFileBrowser.js.map +0 -1
  277. package/dist/hooks/useFileMention.js +0 -56
  278. package/dist/hooks/useFileMention.js.map +0 -1
  279. package/dist/hooks/useFileUpload.js +0 -317
  280. package/dist/hooks/useFileUpload.js.map +0 -1
  281. package/dist/hooks/useFiles.js +0 -15
  282. package/dist/hooks/useFiles.js.map +0 -1
  283. package/dist/hooks/useGit.js +0 -149
  284. package/dist/hooks/useGit.js.map +0 -1
  285. package/dist/hooks/useImageUpload.js +0 -197
  286. package/dist/hooks/useImageUpload.js.map +0 -1
  287. package/dist/hooks/useKeyboardShortcuts.js +0 -249
  288. package/dist/hooks/useKeyboardShortcuts.js.map +0 -1
  289. package/dist/hooks/useMCP.js +0 -202
  290. package/dist/hooks/useMCP.js.map +0 -1
  291. package/dist/hooks/useMessages.js +0 -26
  292. package/dist/hooks/useMessages.js.map +0 -1
  293. package/dist/hooks/usePreferences.js +0 -61
  294. package/dist/hooks/usePreferences.js.map +0 -1
  295. package/dist/hooks/useProviderUsage.js +0 -44
  296. package/dist/hooks/useProviderUsage.js.map +0 -1
  297. package/dist/hooks/useQueueState.js +0 -31
  298. package/dist/hooks/useQueueState.js.map +0 -1
  299. package/dist/hooks/useResearch.js +0 -139
  300. package/dist/hooks/useResearch.js.map +0 -1
  301. package/dist/hooks/useSessionFiles.js +0 -15
  302. package/dist/hooks/useSessionFiles.js.map +0 -1
  303. package/dist/hooks/useSessionStream.js +0 -572
  304. package/dist/hooks/useSessionStream.js.map +0 -1
  305. package/dist/hooks/useSessions.js +0 -83
  306. package/dist/hooks/useSessions.js.map +0 -1
  307. package/dist/hooks/useSetuBalance.js +0 -71
  308. package/dist/hooks/useSetuBalance.js.map +0 -1
  309. package/dist/hooks/useSetuPayments.js +0 -171
  310. package/dist/hooks/useSetuPayments.js.map +0 -1
  311. package/dist/hooks/useShareStatus.js +0 -12
  312. package/dist/hooks/useShareStatus.js.map +0 -1
  313. package/dist/hooks/useSkills.js +0 -54
  314. package/dist/hooks/useSkills.js.map +0 -1
  315. package/dist/hooks/useTerminals.js +0 -67
  316. package/dist/hooks/useTerminals.js.map +0 -1
  317. package/dist/hooks/useTheme.js +0 -57
  318. package/dist/hooks/useTheme.js.map +0 -1
  319. package/dist/hooks/useToolApprovalShortcuts.js +0 -88
  320. package/dist/hooks/useToolApprovalShortcuts.js.map +0 -1
  321. package/dist/hooks/useTopupCallback.js +0 -74
  322. package/dist/hooks/useTopupCallback.js.map +0 -1
  323. package/dist/hooks/useTunnel.js +0 -158
  324. package/dist/hooks/useTunnel.js.map +0 -1
  325. package/dist/hooks/useVimMode.js +0 -298
  326. package/dist/hooks/useVimMode.js.map +0 -1
  327. package/dist/hooks/useWorkingDirectory.js +0 -34
  328. package/dist/hooks/useWorkingDirectory.js.map +0 -1
  329. package/dist/lib/api-client/approval.js +0 -25
  330. package/dist/lib/api-client/approval.js.map +0 -1
  331. package/dist/lib/api-client/auth.js +0 -139
  332. package/dist/lib/api-client/auth.js.map +0 -1
  333. package/dist/lib/api-client/branches.js +0 -45
  334. package/dist/lib/api-client/branches.js.map +0 -1
  335. package/dist/lib/api-client/config.js +0 -38
  336. package/dist/lib/api-client/config.js.map +0 -1
  337. package/dist/lib/api-client/files.js +0 -35
  338. package/dist/lib/api-client/files.js.map +0 -1
  339. package/dist/lib/api-client/git.js +0 -154
  340. package/dist/lib/api-client/git.js.map +0 -1
  341. package/dist/lib/api-client/index.js +0 -92
  342. package/dist/lib/api-client/index.js.map +0 -1
  343. package/dist/lib/api-client/sessions.js +0 -106
  344. package/dist/lib/api-client/sessions.js.map +0 -1
  345. package/dist/lib/api-client/setu.js +0 -144
  346. package/dist/lib/api-client/setu.js.map +0 -1
  347. package/dist/lib/api-client/skills.js +0 -31
  348. package/dist/lib/api-client/skills.js.map +0 -1
  349. package/dist/lib/api-client/utils.js +0 -65
  350. package/dist/lib/api-client/utils.js.map +0 -1
  351. package/dist/lib/commands.js +0 -124
  352. package/dist/lib/commands.js.map +0 -1
  353. package/dist/lib/config.js +0 -28
  354. package/dist/lib/config.js.map +0 -1
  355. package/dist/lib/nerd-font.js +0 -55
  356. package/dist/lib/nerd-font.js.map +0 -1
  357. package/dist/lib/open-url.js +0 -9
  358. package/dist/lib/open-url.js.map +0 -1
  359. package/dist/lib/parseResearchContext.js +0 -30
  360. package/dist/lib/parseResearchContext.js.map +0 -1
  361. package/dist/lib/sse-client.js +0 -113
  362. package/dist/lib/sse-client.js.map +0 -1
  363. package/dist/stores/confirmationStore.js +0 -27
  364. package/dist/stores/confirmationStore.js.map +0 -1
  365. package/dist/stores/fileBrowserStore.js +0 -63
  366. package/dist/stores/fileBrowserStore.js.map +0 -1
  367. package/dist/stores/filePickerStore.js +0 -8
  368. package/dist/stores/filePickerStore.js.map +0 -1
  369. package/dist/stores/focusStore.js +0 -12
  370. package/dist/stores/focusStore.js.map +0 -1
  371. package/dist/stores/gitStore.js +0 -63
  372. package/dist/stores/gitStore.js.map +0 -1
  373. package/dist/stores/mcpStore.js +0 -63
  374. package/dist/stores/mcpStore.js.map +0 -1
  375. package/dist/stores/onboardingStore.js +0 -38
  376. package/dist/stores/onboardingStore.js.map +0 -1
  377. package/dist/stores/panelWidthStore.js +0 -12
  378. package/dist/stores/panelWidthStore.js.map +0 -1
  379. package/dist/stores/pendingResearchStore.js +0 -38
  380. package/dist/stores/pendingResearchStore.js.map +0 -1
  381. package/dist/stores/queueStore.js +0 -11
  382. package/dist/stores/queueStore.js.map +0 -1
  383. package/dist/stores/researchStore.js +0 -54
  384. package/dist/stores/researchStore.js.map +0 -1
  385. package/dist/stores/sessionFilesStore.js +0 -62
  386. package/dist/stores/sessionFilesStore.js.map +0 -1
  387. package/dist/stores/settingsStore.js +0 -29
  388. package/dist/stores/settingsStore.js.map +0 -1
  389. package/dist/stores/setuStore.js +0 -29
  390. package/dist/stores/setuStore.js.map +0 -1
  391. package/dist/stores/sidebarStore.js +0 -10
  392. package/dist/stores/sidebarStore.js.map +0 -1
  393. package/dist/stores/skillsStore.js +0 -46
  394. package/dist/stores/skillsStore.js.map +0 -1
  395. package/dist/stores/terminalStore.js +0 -32
  396. package/dist/stores/terminalStore.js.map +0 -1
  397. package/dist/stores/toastStore.js +0 -46
  398. package/dist/stores/toastStore.js.map +0 -1
  399. package/dist/stores/toolApprovalStore.js +0 -16
  400. package/dist/stores/toolApprovalStore.js.map +0 -1
  401. package/dist/stores/topupApprovalStore.js +0 -11
  402. package/dist/stores/topupApprovalStore.js.map +0 -1
  403. package/dist/stores/tunnelStore.js +0 -55
  404. package/dist/stores/tunnelStore.js.map +0 -1
  405. package/dist/stores/usageStore.js +0 -14
  406. package/dist/stores/usageStore.js.map +0 -1
@@ -1,29 +1,4821 @@
1
- // Export all hooks
2
- export * from './useConfig';
3
- export * from './usePreferences';
4
- export * from './useFiles';
5
- export * from './useGit';
6
- export * from './useMessages';
7
- export * from './useSessions';
8
- export * from './useSessionStream';
9
- export * from './useTheme';
10
- export * from './useWorkingDirectory';
11
- export * from './useKeyboardShortcuts';
12
- export * from './useImageUpload';
13
- export * from './useFileUpload';
14
- export * from './useSessionFiles';
15
- export * from './useQueueState';
16
- export * from './useBranch';
17
- export * from './useResearch';
18
- export * from './useSetuPayments';
19
- export * from './useSetuBalance';
20
- export * from './useShareStatus';
21
- export * from './useToolApprovalShortcuts';
22
- export * from './useTopupCallback';
23
- export * from './useAuthStatus';
24
- export * from './useTunnel';
25
- export * from './useProviderUsage';
26
- export * from './useFileBrowser';
27
- export * from './useMCP';
28
- export * from './useSkills';
29
- //# sourceMappingURL=index.js.map
1
+ // src/hooks/useConfig.ts
2
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
3
+
4
+ // src/lib/api-client/sessions.ts
5
+ import {
6
+ createSession as apiCreateSession,
7
+ listSessions as apiListSessions,
8
+ listMessages as apiListMessages,
9
+ createMessage as apiCreateMessage,
10
+ abortSession as apiAbortSession,
11
+ deleteSession as apiDeleteSession,
12
+ updateSession as apiUpdateSession,
13
+ getSessionQueue as apiGetSessionQueue,
14
+ removeFromQueue as apiRemoveFromQueue,
15
+ retryMessage as apiRetryMessage
16
+ } from "@ottocode/api";
17
+
18
+ // src/lib/api-client/utils.ts
19
+ import { client } from "@ottocode/api";
20
+
21
+ // src/lib/config.ts
22
+ function computeApiBaseUrl() {
23
+ const envUrl = import.meta.env?.VITE_API_BASE_URL;
24
+ if (envUrl) {
25
+ return envUrl;
26
+ }
27
+ if (typeof window !== "undefined") {
28
+ const win = window;
29
+ if (win.OTTO_SERVER_URL) {
30
+ return win.OTTO_SERVER_URL;
31
+ }
32
+ if (win.__OTTO_API_URL__) {
33
+ return win.__OTTO_API_URL__;
34
+ }
35
+ }
36
+ return "http://localhost:9100";
37
+ }
38
+ function getRuntimeApiBaseUrl() {
39
+ return computeApiBaseUrl();
40
+ }
41
+ var API_BASE_URL = computeApiBaseUrl();
42
+ var config = {
43
+ apiBaseUrl: API_BASE_URL
44
+ };
45
+
46
+ // src/lib/api-client/utils.ts
47
+ function getClientAdapter() {
48
+ if (typeof window !== "undefined" && typeof window.fetch === "function") {
49
+ return "fetch";
50
+ }
51
+ return;
52
+ }
53
+ function extractErrorMessage(error) {
54
+ if (!error)
55
+ return "Unknown error";
56
+ if (typeof error === "string")
57
+ return error;
58
+ if (error && typeof error === "object") {
59
+ const errObj = error;
60
+ if (errObj.error && typeof errObj.error === "object") {
61
+ const innerError = errObj.error;
62
+ if (typeof innerError.message === "string")
63
+ return innerError.message;
64
+ }
65
+ if (typeof errObj.error === "string")
66
+ return errObj.error;
67
+ if (typeof errObj.message === "string")
68
+ return errObj.message;
69
+ try {
70
+ return JSON.stringify(error);
71
+ } catch {
72
+ return "Error occurred (unable to parse)";
73
+ }
74
+ }
75
+ return "Unknown error";
76
+ }
77
+ function configureApiClient() {
78
+ const win = window;
79
+ const baseURL = win.OTTO_SERVER_URL || API_BASE_URL;
80
+ client.setConfig({
81
+ baseURL,
82
+ adapter: getClientAdapter()
83
+ });
84
+ }
85
+ configureApiClient();
86
+ function getBaseUrl() {
87
+ const win = window;
88
+ if (win.OTTO_SERVER_URL)
89
+ return win.OTTO_SERVER_URL;
90
+ return API_BASE_URL;
91
+ }
92
+ function convertSession(apiSession) {
93
+ return {
94
+ ...apiSession,
95
+ title: apiSession.title ?? null,
96
+ createdAt: typeof apiSession.createdAt === "string" ? new Date(apiSession.createdAt).getTime() : apiSession.createdAt,
97
+ lastActiveAt: typeof apiSession.lastActiveAt === "string" ? new Date(apiSession.lastActiveAt).getTime() : apiSession.lastActiveAt
98
+ };
99
+ }
100
+ function convertMessage(apiMessage) {
101
+ return {
102
+ ...apiMessage,
103
+ createdAt: typeof apiMessage.createdAt === "string" ? new Date(apiMessage.createdAt).getTime() : apiMessage.createdAt,
104
+ completedAt: apiMessage.completedAt ? typeof apiMessage.completedAt === "string" ? new Date(apiMessage.completedAt).getTime() : apiMessage.completedAt : null
105
+ };
106
+ }
107
+
108
+ // src/lib/api-client/sessions.ts
109
+ var sessionsMixin = {
110
+ async getSessions() {
111
+ const page = await this.getSessionsPage({ limit: 200 });
112
+ return page.items;
113
+ },
114
+ async getSessionsPage(params = {}) {
115
+ const { limit = 50, offset = 0 } = params;
116
+ const response = await apiListSessions({
117
+ query: { limit, offset }
118
+ });
119
+ if (response.error)
120
+ throw new Error(extractErrorMessage(response.error));
121
+ const data = response.data;
122
+ return {
123
+ items: (data?.items ?? []).map((s) => convertSession(s)),
124
+ hasMore: data?.hasMore ?? false,
125
+ nextOffset: data?.nextOffset ?? null
126
+ };
127
+ },
128
+ async createSession(data) {
129
+ const response = await apiCreateSession({
130
+ body: data
131
+ });
132
+ if (response.error)
133
+ throw new Error(extractErrorMessage(response.error));
134
+ if (!response.data)
135
+ throw new Error("No data returned from create session");
136
+ return convertSession(response.data);
137
+ },
138
+ async updateSession(sessionId, data) {
139
+ const response = await apiUpdateSession({
140
+ path: { sessionId },
141
+ body: data
142
+ });
143
+ if (response.error)
144
+ throw new Error(extractErrorMessage(response.error));
145
+ return convertSession(response.data);
146
+ },
147
+ async deleteSession(sessionId) {
148
+ const response = await apiDeleteSession({ path: { sessionId } });
149
+ if (response.error)
150
+ throw new Error(extractErrorMessage(response.error));
151
+ return response.data;
152
+ },
153
+ async abortSession(sessionId) {
154
+ const response = await apiAbortSession({ path: { sessionId } });
155
+ if (response.error)
156
+ throw new Error(extractErrorMessage(response.error));
157
+ return response.data;
158
+ },
159
+ async abortMessage(sessionId, _messageId) {
160
+ const response = await apiAbortSession({
161
+ path: { sessionId }
162
+ });
163
+ if (response.error)
164
+ throw new Error("Failed to abort message");
165
+ return response.data;
166
+ },
167
+ async getQueueState(sessionId) {
168
+ const response = await apiGetSessionQueue({ path: { sessionId } });
169
+ if (response.error)
170
+ throw new Error("Failed to get queue state");
171
+ return response.data;
172
+ },
173
+ async removeFromQueue(sessionId, messageId) {
174
+ const response = await apiRemoveFromQueue({
175
+ path: { sessionId, messageId }
176
+ });
177
+ if (response.error)
178
+ throw new Error("Failed to remove from queue");
179
+ return response.data;
180
+ },
181
+ async getMessages(sessionId) {
182
+ const response = await apiListMessages({ path: { id: sessionId } });
183
+ if (response.error)
184
+ throw new Error(extractErrorMessage(response.error));
185
+ return (response.data || []).map(convertMessage);
186
+ },
187
+ async sendMessage(sessionId, data) {
188
+ const response = await apiCreateMessage({
189
+ path: { id: sessionId },
190
+ body: data
191
+ });
192
+ if (response.error)
193
+ throw new Error(extractErrorMessage(response.error));
194
+ return response.data;
195
+ },
196
+ getStreamUrl(sessionId) {
197
+ return `${getBaseUrl()}/v1/sessions/${sessionId}/stream`;
198
+ },
199
+ async retryMessage(sessionId, messageId) {
200
+ const response = await apiRetryMessage({
201
+ path: { sessionId, messageId }
202
+ });
203
+ if (response.error)
204
+ throw new Error(extractErrorMessage(response.error));
205
+ return response.data;
206
+ }
207
+ };
208
+
209
+ // src/lib/api-client/git.ts
210
+ import {
211
+ getGitStatus as apiGetGitStatus,
212
+ getGitDiff as apiGetGitDiff,
213
+ getGitBranch as apiGetGitBranch,
214
+ stageFiles as apiStageFiles,
215
+ unstageFiles as apiUnstageFiles,
216
+ restoreFiles as apiRestoreFiles,
217
+ deleteFiles as apiDeleteFiles,
218
+ commitChanges as apiCommitChanges,
219
+ generateCommitMessage as apiGenerateCommitMessage,
220
+ pushCommits as apiPushCommits,
221
+ pullChanges as apiPullChanges,
222
+ initGitRepo as apiInitGitRepo,
223
+ getGitRemotes as apiGetGitRemotes,
224
+ addGitRemote as apiAddGitRemote,
225
+ removeGitRemote as apiRemoveGitRemote
226
+ } from "@ottocode/api";
227
+ var gitMixin = {
228
+ async initGitRepo() {
229
+ const response = await apiInitGitRepo();
230
+ if (response.error)
231
+ throw new Error(extractErrorMessage(response.error));
232
+ return response.data?.data;
233
+ },
234
+ async getGitStatus() {
235
+ const response = await apiGetGitStatus();
236
+ if (response.error)
237
+ throw new Error(extractErrorMessage(response.error));
238
+ return response.data?.data;
239
+ },
240
+ async getGitDiff(file, staged = false) {
241
+ const response = await apiGetGitDiff({
242
+ query: { file, staged: staged ? "true" : "false" }
243
+ });
244
+ if (response.error)
245
+ throw new Error(extractErrorMessage(response.error));
246
+ return response.data?.data;
247
+ },
248
+ async getGitDiffFullFile(file, staged = false) {
249
+ const response = await apiGetGitDiff({
250
+ query: {
251
+ file,
252
+ staged: staged ? "true" : "false",
253
+ fullFile: "true"
254
+ }
255
+ });
256
+ if (response.error)
257
+ throw new Error(extractErrorMessage(response.error));
258
+ return response.data?.data;
259
+ },
260
+ async generateCommitMessage(sessionId) {
261
+ const response = await apiGenerateCommitMessage({
262
+ body: sessionId ? { sessionId } : {}
263
+ });
264
+ if (response.error)
265
+ throw new Error(extractErrorMessage(response.error));
266
+ return response.data?.data;
267
+ },
268
+ async stageFiles(files) {
269
+ const response = await apiStageFiles({
270
+ body: { files }
271
+ });
272
+ if (response.error)
273
+ throw new Error(extractErrorMessage(response.error));
274
+ return response.data?.data;
275
+ },
276
+ async unstageFiles(files) {
277
+ const response = await apiUnstageFiles({
278
+ body: { files }
279
+ });
280
+ if (response.error)
281
+ throw new Error(extractErrorMessage(response.error));
282
+ return response.data?.data;
283
+ },
284
+ async restoreFiles(files) {
285
+ const response = await apiRestoreFiles({
286
+ body: { files }
287
+ });
288
+ if (response.error)
289
+ throw new Error(extractErrorMessage(response.error));
290
+ return response.data?.data;
291
+ },
292
+ async deleteFiles(files) {
293
+ const response = await apiDeleteFiles({
294
+ body: { files }
295
+ });
296
+ if (response.error)
297
+ throw new Error(extractErrorMessage(response.error));
298
+ return response.data?.data;
299
+ },
300
+ async commitChanges(message) {
301
+ const response = await apiCommitChanges({
302
+ body: { message }
303
+ });
304
+ if (response.error)
305
+ throw new Error(extractErrorMessage(response.error));
306
+ return response.data?.data;
307
+ },
308
+ async getGitBranch() {
309
+ const response = await apiGetGitBranch();
310
+ if (response.error)
311
+ throw new Error(extractErrorMessage(response.error));
312
+ return response.data?.data;
313
+ },
314
+ async pushCommits() {
315
+ const response = await apiPushCommits({
316
+ body: {}
317
+ });
318
+ if (response.error)
319
+ throw new Error(extractErrorMessage(response.error));
320
+ return response.data?.data;
321
+ },
322
+ async pullChanges() {
323
+ const response = await apiPullChanges({
324
+ body: {}
325
+ });
326
+ if (response.error)
327
+ throw new Error(extractErrorMessage(response.error));
328
+ return response.data?.data;
329
+ },
330
+ async getRemotes() {
331
+ const response = await apiGetGitRemotes();
332
+ if (response.error)
333
+ throw new Error(extractErrorMessage(response.error));
334
+ return response.data?.data?.remotes;
335
+ },
336
+ async addRemote(name, url) {
337
+ const response = await apiAddGitRemote({
338
+ body: { name, url }
339
+ });
340
+ if (response.error)
341
+ throw new Error(extractErrorMessage(response.error));
342
+ return response.data?.data;
343
+ },
344
+ async removeRemote(name) {
345
+ const response = await apiRemoveGitRemote({
346
+ body: { name }
347
+ });
348
+ if (response.error)
349
+ throw new Error(extractErrorMessage(response.error));
350
+ return response.data?.data;
351
+ }
352
+ };
353
+
354
+ // src/lib/api-client/config.ts
355
+ import {
356
+ getConfig as apiGetConfig,
357
+ getProviderModels as apiGetProviderModels,
358
+ getAllModels as apiGetAllModels,
359
+ updateDefaults as apiUpdateDefaults
360
+ } from "@ottocode/api";
361
+ var configMixin = {
362
+ async getConfig() {
363
+ const response = await apiGetConfig();
364
+ if (response.error)
365
+ throw new Error(extractErrorMessage(response.error));
366
+ return response.data;
367
+ },
368
+ async getModels(providerId) {
369
+ const response = await apiGetProviderModels({
370
+ path: { provider: providerId }
371
+ });
372
+ if (response.error)
373
+ throw new Error(extractErrorMessage(response.error));
374
+ return response.data;
375
+ },
376
+ async getAllModels() {
377
+ const response = await apiGetAllModels();
378
+ if (response.error)
379
+ throw new Error(extractErrorMessage(response.error));
380
+ return response.data;
381
+ },
382
+ async updateDefaults(data) {
383
+ const response = await apiUpdateDefaults({
384
+ body: data
385
+ });
386
+ if (response.error)
387
+ throw new Error(extractErrorMessage(response.error));
388
+ return response.data;
389
+ }
390
+ };
391
+
392
+ // src/lib/api-client/files.ts
393
+ import {
394
+ listFiles as apiListFiles,
395
+ getFileTree as apiGetFileTree,
396
+ readFile as apiReadFile,
397
+ getSessionFiles as apiGetSessionFiles
398
+ } from "@ottocode/api";
399
+ var filesMixin = {
400
+ async listFiles() {
401
+ const response = await apiListFiles();
402
+ if (response.error)
403
+ throw new Error(extractErrorMessage(response.error));
404
+ return response.data;
405
+ },
406
+ async getFileTree(dirPath = ".") {
407
+ const response = await apiGetFileTree({
408
+ query: { path: dirPath }
409
+ });
410
+ if (response.error)
411
+ throw new Error(extractErrorMessage(response.error));
412
+ return response.data;
413
+ },
414
+ async readFileContent(filePath) {
415
+ const response = await apiReadFile({
416
+ query: { path: filePath }
417
+ });
418
+ if (response.error)
419
+ throw new Error(extractErrorMessage(response.error));
420
+ return response.data;
421
+ },
422
+ async getSessionFiles(sessionId) {
423
+ const response = await apiGetSessionFiles({ path: { sessionId } });
424
+ if (response.error)
425
+ throw new Error(extractErrorMessage(response.error));
426
+ return response.data;
427
+ }
428
+ };
429
+
430
+ // src/lib/api-client/branches.ts
431
+ import {
432
+ createBranch as apiCreateBranch,
433
+ listBranches as apiListBranches,
434
+ getParentSession as apiGetParentSession,
435
+ getShareStatus as apiGetShareStatus,
436
+ shareSession as apiShareSession,
437
+ syncShare as apiSyncShare
438
+ } from "@ottocode/api";
439
+ var branchesMixin = {
440
+ async createBranch(sessionId, data) {
441
+ const response = await apiCreateBranch({
442
+ path: { sessionId },
443
+ body: data
444
+ });
445
+ if (response.error)
446
+ throw new Error(extractErrorMessage(response.error));
447
+ return response.data;
448
+ },
449
+ async listBranches(sessionId) {
450
+ const response = await apiListBranches({ path: { sessionId } });
451
+ if (response.error)
452
+ throw new Error(extractErrorMessage(response.error));
453
+ return response.data;
454
+ },
455
+ async getParentSession(sessionId) {
456
+ const response = await apiGetParentSession({ path: { sessionId } });
457
+ if (response.error)
458
+ throw new Error(extractErrorMessage(response.error));
459
+ return response.data;
460
+ },
461
+ async getShareStatus(sessionId) {
462
+ const response = await apiGetShareStatus({ path: { sessionId } });
463
+ if (response.error)
464
+ return { shared: false };
465
+ return response.data;
466
+ },
467
+ async shareSession(sessionId) {
468
+ const response = await apiShareSession({ path: { sessionId } });
469
+ if (response.error)
470
+ throw new Error(extractErrorMessage(response.error));
471
+ return response.data;
472
+ },
473
+ async syncSession(sessionId) {
474
+ const response = await apiSyncShare({ path: { sessionId } });
475
+ if (response.error)
476
+ throw new Error(extractErrorMessage(response.error));
477
+ return response.data;
478
+ }
479
+ };
480
+
481
+ // src/lib/api-client/approval.ts
482
+ import {
483
+ resolveApproval as apiResolveApproval,
484
+ getPendingApprovals as apiGetPendingApprovals
485
+ } from "@ottocode/api";
486
+ var approvalMixin = {
487
+ async approveToolCall(sessionId, callId, approved) {
488
+ const response = await apiResolveApproval({
489
+ path: { id: sessionId },
490
+ body: { callId, approved, sessionId }
491
+ });
492
+ if (response.error)
493
+ throw new Error("Failed to send tool approval");
494
+ return response.data;
495
+ },
496
+ async getPendingApprovals(sessionId) {
497
+ const response = await apiGetPendingApprovals({
498
+ path: { id: sessionId }
499
+ });
500
+ if (response.error)
501
+ return { ok: false, pending: [] };
502
+ return response.data;
503
+ }
504
+ };
505
+
506
+ // src/lib/api-client/setu.ts
507
+ import {
508
+ getSetuBalance as apiGetSetuBalance,
509
+ getSetuWallet as apiGetSetuWallet,
510
+ getSetuUsdcBalance as apiGetSetuUsdcBalance,
511
+ createPolarCheckout as apiCreatePolarCheckout,
512
+ getPolarTopupEstimate as apiGetPolarTopupEstimate,
513
+ getPolarTopupStatus as apiGetPolarTopupStatus,
514
+ getRazorpayTopupEstimate as apiGetRazorpayTopupEstimate,
515
+ createRazorpayOrder as apiCreateRazorpayOrder,
516
+ verifyRazorpayPayment as apiVerifyRazorpayPayment,
517
+ selectTopupMethod as apiSelectTopupMethod,
518
+ cancelTopup as apiCancelTopup,
519
+ getPendingTopup as apiGetPendingTopup
520
+ } from "@ottocode/api";
521
+ var setuMixin = {
522
+ async getSetuBalance() {
523
+ try {
524
+ const response = await apiGetSetuBalance();
525
+ if (response.error)
526
+ return null;
527
+ return response.data;
528
+ } catch {
529
+ return null;
530
+ }
531
+ },
532
+ async getSetuWallet() {
533
+ try {
534
+ const response = await apiGetSetuWallet();
535
+ if (response.error)
536
+ return { configured: false };
537
+ return response.data;
538
+ } catch {
539
+ return { configured: false };
540
+ }
541
+ },
542
+ async getSetuUsdcBalance(network = "mainnet") {
543
+ try {
544
+ const response = await apiGetSetuUsdcBalance({
545
+ query: { network }
546
+ });
547
+ if (response.error)
548
+ return null;
549
+ return response.data;
550
+ } catch {
551
+ return null;
552
+ }
553
+ },
554
+ async getPolarTopupEstimate(amount) {
555
+ try {
556
+ const response = await apiGetPolarTopupEstimate({
557
+ query: { amount }
558
+ });
559
+ if (response.error)
560
+ return null;
561
+ return response.data;
562
+ } catch {
563
+ return null;
564
+ }
565
+ },
566
+ async createPolarCheckout(amount, successUrl) {
567
+ const response = await apiCreatePolarCheckout({
568
+ body: { amount, successUrl }
569
+ });
570
+ if (response.error)
571
+ throw new Error(extractErrorMessage(response.error));
572
+ return response.data;
573
+ },
574
+ async selectTopupMethod(sessionId, method) {
575
+ const response = await apiSelectTopupMethod({
576
+ body: { sessionId, method }
577
+ });
578
+ if (response.error)
579
+ throw new Error(extractErrorMessage(response.error));
580
+ return response.data;
581
+ },
582
+ async cancelTopup(sessionId, reason) {
583
+ const response = await apiCancelTopup({
584
+ body: { sessionId, reason }
585
+ });
586
+ if (response.error)
587
+ throw new Error(extractErrorMessage(response.error));
588
+ return response.data;
589
+ },
590
+ async getPendingTopup(sessionId) {
591
+ try {
592
+ const response = await apiGetPendingTopup({
593
+ query: { sessionId }
594
+ });
595
+ if (response.error)
596
+ return { hasPending: false };
597
+ return response.data;
598
+ } catch {
599
+ return { hasPending: false };
600
+ }
601
+ },
602
+ async getPolarTopupStatus(checkoutId) {
603
+ try {
604
+ const response = await apiGetPolarTopupStatus({
605
+ query: { checkoutId }
606
+ });
607
+ if (response.error)
608
+ return null;
609
+ return response.data;
610
+ } catch {
611
+ return null;
612
+ }
613
+ },
614
+ async getRazorpayTopupEstimate(amount) {
615
+ try {
616
+ const response = await apiGetRazorpayTopupEstimate({
617
+ query: { amount }
618
+ });
619
+ if (response.error)
620
+ return null;
621
+ return response.data;
622
+ } catch {
623
+ return null;
624
+ }
625
+ },
626
+ async createRazorpayOrder(amount) {
627
+ const response = await apiCreateRazorpayOrder({
628
+ body: { amount }
629
+ });
630
+ if (response.error)
631
+ throw new Error(extractErrorMessage(response.error));
632
+ return response.data;
633
+ },
634
+ async verifyRazorpayPayment(params) {
635
+ const response = await apiVerifyRazorpayPayment({
636
+ body: params
637
+ });
638
+ if (response.error)
639
+ throw new Error(extractErrorMessage(response.error));
640
+ return response.data;
641
+ }
642
+ };
643
+
644
+ // src/lib/api-client/auth.ts
645
+ import {
646
+ getAuthStatus as apiGetAuthStatus,
647
+ setupSetuWallet as apiSetupSetuWallet,
648
+ importSetuWallet as apiImportSetuWallet,
649
+ exportSetuWallet as apiExportSetuWallet,
650
+ addProviderApiKey as apiAddProviderApiKey,
651
+ removeProvider as apiRemoveProvider,
652
+ completeOnboarding as apiCompleteOnboarding,
653
+ getOAuthUrl as apiGetOAuthUrl,
654
+ exchangeOAuthCode as apiExchangeOAuthCode,
655
+ startCopilotDeviceFlow as apiStartCopilotDeviceFlow,
656
+ pollCopilotDeviceFlow as apiPollCopilotDeviceFlow,
657
+ getCopilotAuthMethods as apiGetCopilotAuthMethods,
658
+ saveCopilotToken as apiSaveCopilotToken,
659
+ importCopilotTokenFromGh as apiImportCopilotTokenFromGh,
660
+ getCopilotDiagnostics as apiGetCopilotDiagnostics,
661
+ getProviderUsage as apiGetProviderUsage
662
+ } from "@ottocode/api";
663
+ var authMixin = {
664
+ async getAuthStatus() {
665
+ const response = await apiGetAuthStatus();
666
+ if (response.error)
667
+ throw new Error(extractErrorMessage(response.error));
668
+ return response.data;
669
+ },
670
+ async setupSetuWallet() {
671
+ const response = await apiSetupSetuWallet();
672
+ if (response.error)
673
+ throw new Error(extractErrorMessage(response.error));
674
+ return response.data;
675
+ },
676
+ async importSetuWallet(privateKey) {
677
+ const response = await apiImportSetuWallet({
678
+ body: { privateKey }
679
+ });
680
+ if (response.error)
681
+ throw new Error(extractErrorMessage(response.error));
682
+ return response.data;
683
+ },
684
+ async exportSetuWallet() {
685
+ const response = await apiExportSetuWallet();
686
+ if (response.error)
687
+ throw new Error(extractErrorMessage(response.error));
688
+ return response.data;
689
+ },
690
+ async addProvider(provider, apiKey) {
691
+ const response = await apiAddProviderApiKey({
692
+ path: { provider },
693
+ body: { apiKey }
694
+ });
695
+ if (response.error)
696
+ throw new Error(extractErrorMessage(response.error));
697
+ return response.data;
698
+ },
699
+ async removeProvider(provider) {
700
+ const response = await apiRemoveProvider({ path: { provider } });
701
+ if (response.error)
702
+ throw new Error(extractErrorMessage(response.error));
703
+ return response.data;
704
+ },
705
+ async completeOnboarding() {
706
+ const response = await apiCompleteOnboarding();
707
+ if (response.error)
708
+ throw new Error(extractErrorMessage(response.error));
709
+ return response.data;
710
+ },
711
+ getOAuthStartUrl(provider, mode) {
712
+ const baseUrl = `${getBaseUrl()}/v1/auth/${provider}/oauth/start`;
713
+ if (mode)
714
+ return `${baseUrl}?mode=${mode}`;
715
+ return baseUrl;
716
+ },
717
+ async getOAuthUrl(provider, mode) {
718
+ const response = await apiGetOAuthUrl({
719
+ path: { provider },
720
+ body: { mode }
721
+ });
722
+ if (response.error)
723
+ throw new Error(extractErrorMessage(response.error));
724
+ return response.data;
725
+ },
726
+ async exchangeOAuthCode(provider, code, sessionId) {
727
+ const response = await apiExchangeOAuthCode({
728
+ path: { provider },
729
+ body: { code, sessionId }
730
+ });
731
+ if (response.error)
732
+ throw new Error(extractErrorMessage(response.error));
733
+ return response.data;
734
+ },
735
+ async startCopilotDeviceFlow() {
736
+ const response = await apiStartCopilotDeviceFlow();
737
+ if (response.error)
738
+ throw new Error(extractErrorMessage(response.error));
739
+ return response.data;
740
+ },
741
+ async pollCopilotDeviceFlow(sessionId) {
742
+ const response = await apiPollCopilotDeviceFlow({
743
+ body: { sessionId }
744
+ });
745
+ if (response.error)
746
+ throw new Error(extractErrorMessage(response.error));
747
+ return response.data;
748
+ },
749
+ async getCopilotAuthMethods() {
750
+ const response = await apiGetCopilotAuthMethods();
751
+ if (response.error)
752
+ throw new Error(extractErrorMessage(response.error));
753
+ return response.data;
754
+ },
755
+ async saveCopilotToken(token) {
756
+ const response = await apiSaveCopilotToken({ body: { token } });
757
+ if (response.error)
758
+ throw new Error(extractErrorMessage(response.error));
759
+ return response.data;
760
+ },
761
+ async importCopilotTokenFromGh() {
762
+ const response = await apiImportCopilotTokenFromGh();
763
+ if (response.error)
764
+ throw new Error(extractErrorMessage(response.error));
765
+ return response.data;
766
+ },
767
+ async getCopilotDiagnostics() {
768
+ const response = await apiGetCopilotDiagnostics();
769
+ if (response.error)
770
+ throw new Error(extractErrorMessage(response.error));
771
+ return response.data;
772
+ },
773
+ async getProviderUsage(provider) {
774
+ const response = await apiGetProviderUsage({
775
+ path: { provider }
776
+ });
777
+ if (response.error)
778
+ throw new Error(extractErrorMessage(response.error));
779
+ return response.data;
780
+ }
781
+ };
782
+
783
+ // src/lib/api-client/skills.ts
784
+ import {
785
+ listSkills as apiListSkills,
786
+ getSkill as apiGetSkill,
787
+ listSkillFiles as apiListSkillFiles,
788
+ getSkillFile as apiGetSkillFile
789
+ } from "@ottocode/api";
790
+ var skillsMixin = {
791
+ async listSkills() {
792
+ const response = await apiListSkills();
793
+ if (response.error)
794
+ throw new Error(extractErrorMessage(response.error));
795
+ return response.data;
796
+ },
797
+ async getSkill(name) {
798
+ const response = await apiGetSkill({ path: { name } });
799
+ if (response.error)
800
+ throw new Error(extractErrorMessage(response.error));
801
+ return response.data;
802
+ },
803
+ async getSkillFiles(name) {
804
+ const response = await apiListSkillFiles({ path: { name } });
805
+ if (response.error)
806
+ throw new Error(extractErrorMessage(response.error));
807
+ return response.data;
808
+ },
809
+ async getSkillFileContent(name, filePath) {
810
+ const response = await apiGetSkillFile({
811
+ path: { name, filePath }
812
+ });
813
+ if (response.error)
814
+ throw new Error(extractErrorMessage(response.error));
815
+ return response.data;
816
+ }
817
+ };
818
+
819
+ // src/lib/api-client/index.ts
820
+ class ApiClient {
821
+ getSessions = sessionsMixin.getSessions;
822
+ getSessionsPage = sessionsMixin.getSessionsPage;
823
+ createSession = sessionsMixin.createSession;
824
+ updateSession = sessionsMixin.updateSession;
825
+ deleteSession = sessionsMixin.deleteSession;
826
+ abortSession = sessionsMixin.abortSession;
827
+ abortMessage = sessionsMixin.abortMessage;
828
+ getQueueState = sessionsMixin.getQueueState;
829
+ removeFromQueue = sessionsMixin.removeFromQueue;
830
+ getMessages = sessionsMixin.getMessages;
831
+ sendMessage = sessionsMixin.sendMessage;
832
+ getStreamUrl = sessionsMixin.getStreamUrl;
833
+ retryMessage = sessionsMixin.retryMessage;
834
+ initGitRepo = gitMixin.initGitRepo;
835
+ getGitStatus = gitMixin.getGitStatus;
836
+ getGitDiff = gitMixin.getGitDiff;
837
+ getGitDiffFullFile = gitMixin.getGitDiffFullFile;
838
+ generateCommitMessage = gitMixin.generateCommitMessage;
839
+ stageFiles = gitMixin.stageFiles;
840
+ unstageFiles = gitMixin.unstageFiles;
841
+ restoreFiles = gitMixin.restoreFiles;
842
+ deleteFiles = gitMixin.deleteFiles;
843
+ commitChanges = gitMixin.commitChanges;
844
+ getGitBranch = gitMixin.getGitBranch;
845
+ pushCommits = gitMixin.pushCommits;
846
+ pullChanges = gitMixin.pullChanges;
847
+ getRemotes = gitMixin.getRemotes;
848
+ addRemote = gitMixin.addRemote;
849
+ removeRemote = gitMixin.removeRemote;
850
+ getConfig = configMixin.getConfig;
851
+ getModels = configMixin.getModels;
852
+ getAllModels = configMixin.getAllModels;
853
+ updateDefaults = configMixin.updateDefaults;
854
+ listFiles = filesMixin.listFiles;
855
+ getFileTree = filesMixin.getFileTree;
856
+ readFileContent = filesMixin.readFileContent;
857
+ getSessionFiles = filesMixin.getSessionFiles;
858
+ createBranch = branchesMixin.createBranch;
859
+ listBranches = branchesMixin.listBranches;
860
+ getParentSession = branchesMixin.getParentSession;
861
+ getShareStatus = branchesMixin.getShareStatus;
862
+ shareSession = branchesMixin.shareSession;
863
+ syncSession = branchesMixin.syncSession;
864
+ approveToolCall = approvalMixin.approveToolCall;
865
+ getPendingApprovals = approvalMixin.getPendingApprovals;
866
+ getSetuBalance = setuMixin.getSetuBalance;
867
+ getSetuWallet = setuMixin.getSetuWallet;
868
+ getSetuUsdcBalance = setuMixin.getSetuUsdcBalance;
869
+ getPolarTopupEstimate = setuMixin.getPolarTopupEstimate;
870
+ createPolarCheckout = setuMixin.createPolarCheckout;
871
+ selectTopupMethod = setuMixin.selectTopupMethod;
872
+ cancelTopup = setuMixin.cancelTopup;
873
+ getPendingTopup = setuMixin.getPendingTopup;
874
+ getPolarTopupStatus = setuMixin.getPolarTopupStatus;
875
+ getRazorpayTopupEstimate = setuMixin.getRazorpayTopupEstimate;
876
+ createRazorpayOrder = setuMixin.createRazorpayOrder;
877
+ verifyRazorpayPayment = setuMixin.verifyRazorpayPayment;
878
+ getAuthStatus = authMixin.getAuthStatus;
879
+ setupSetuWallet = authMixin.setupSetuWallet;
880
+ importSetuWallet = authMixin.importSetuWallet;
881
+ exportSetuWallet = authMixin.exportSetuWallet;
882
+ addProvider = authMixin.addProvider;
883
+ removeProvider = authMixin.removeProvider;
884
+ completeOnboarding = authMixin.completeOnboarding;
885
+ getOAuthStartUrl = authMixin.getOAuthStartUrl;
886
+ getOAuthUrl = authMixin.getOAuthUrl;
887
+ exchangeOAuthCode = authMixin.exchangeOAuthCode;
888
+ startCopilotDeviceFlow = authMixin.startCopilotDeviceFlow;
889
+ pollCopilotDeviceFlow = authMixin.pollCopilotDeviceFlow;
890
+ getCopilotAuthMethods = authMixin.getCopilotAuthMethods;
891
+ saveCopilotToken = authMixin.saveCopilotToken;
892
+ importCopilotTokenFromGh = authMixin.importCopilotTokenFromGh;
893
+ getCopilotDiagnostics = authMixin.getCopilotDiagnostics;
894
+ getProviderUsage = authMixin.getProviderUsage;
895
+ listSkills = skillsMixin.listSkills;
896
+ getSkill = skillsMixin.getSkill;
897
+ getSkillFiles = skillsMixin.getSkillFiles;
898
+ getSkillFileContent = skillsMixin.getSkillFileContent;
899
+ }
900
+ var apiClient = new ApiClient;
901
+
902
+ // src/hooks/useConfig.ts
903
+ function useConfig() {
904
+ return useQuery({
905
+ queryKey: ["config"],
906
+ queryFn: () => apiClient.getConfig(),
907
+ staleTime: 30000
908
+ });
909
+ }
910
+ function useModels(provider) {
911
+ return useQuery({
912
+ queryKey: ["models", provider],
913
+ queryFn: () => provider ? apiClient.getModels(provider) : null,
914
+ enabled: !!provider
915
+ });
916
+ }
917
+ function useAllModels() {
918
+ return useQuery({
919
+ queryKey: ["models", "all"],
920
+ queryFn: () => apiClient.getAllModels()
921
+ });
922
+ }
923
+ function useUpdateDefaults() {
924
+ const queryClient = useQueryClient();
925
+ return useMutation({
926
+ mutationFn: (data) => apiClient.updateDefaults(data),
927
+ onSuccess: () => {
928
+ queryClient.invalidateQueries({ queryKey: ["config"] });
929
+ }
930
+ });
931
+ }
932
+ // src/hooks/usePreferences.ts
933
+ import { useCallback, useMemo, useSyncExternalStore } from "react";
934
+ var STORAGE_KEY = "otto-preferences";
935
+ var DEFAULT_PREFERENCES = {
936
+ vimMode: false,
937
+ compactThread: true
938
+ };
939
+ function resolveInitialPreferences() {
940
+ if (typeof window === "undefined") {
941
+ return DEFAULT_PREFERENCES;
942
+ }
943
+ try {
944
+ const stored = window.localStorage.getItem(STORAGE_KEY);
945
+ if (stored) {
946
+ const parsed = JSON.parse(stored);
947
+ return {
948
+ ...DEFAULT_PREFERENCES,
949
+ ...parsed
950
+ };
951
+ }
952
+ } catch (error) {
953
+ console.warn("Failed to load preferences", error);
954
+ }
955
+ return DEFAULT_PREFERENCES;
956
+ }
957
+ var preferences = resolveInitialPreferences();
958
+ var listeners = new Set;
959
+ function getSnapshot() {
960
+ return preferences;
961
+ }
962
+ function getServerSnapshot() {
963
+ return DEFAULT_PREFERENCES;
964
+ }
965
+ function subscribe(listener) {
966
+ listeners.add(listener);
967
+ return () => listeners.delete(listener);
968
+ }
969
+ function notifyListeners() {
970
+ for (const listener of listeners) {
971
+ listener();
972
+ }
973
+ }
974
+ function updateStore(updates) {
975
+ preferences = { ...preferences, ...updates };
976
+ if (typeof window !== "undefined") {
977
+ try {
978
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(preferences));
979
+ } catch (error) {
980
+ console.warn("Failed to persist preferences", error);
981
+ }
982
+ }
983
+ notifyListeners();
984
+ }
985
+ function usePreferences() {
986
+ const currentPreferences = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
987
+ const updatePreferences = useCallback((updates) => {
988
+ updateStore(updates);
989
+ }, []);
990
+ return useMemo(() => ({ preferences: currentPreferences, updatePreferences }), [currentPreferences, updatePreferences]);
991
+ }
992
+ // src/hooks/useFiles.ts
993
+ import { useQuery as useQuery2 } from "@tanstack/react-query";
994
+ function useFiles() {
995
+ return useQuery2({
996
+ queryKey: ["files"],
997
+ queryFn: async () => {
998
+ const result = await apiClient.listFiles();
999
+ return result;
1000
+ },
1001
+ staleTime: 1e4,
1002
+ refetchOnWindowFocus: true,
1003
+ retry: 1
1004
+ });
1005
+ }
1006
+ // src/hooks/useGit.ts
1007
+ import { useQuery as useQuery3, useMutation as useMutation2, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
1008
+
1009
+ // src/stores/gitStore.ts
1010
+ import { create as create8 } from "zustand";
1011
+
1012
+ // src/stores/sessionFilesStore.ts
1013
+ import { create as create7 } from "zustand";
1014
+
1015
+ // src/stores/researchStore.ts
1016
+ import { create as create6 } from "zustand";
1017
+
1018
+ // src/stores/settingsStore.ts
1019
+ import { create as create5 } from "zustand";
1020
+
1021
+ // src/stores/tunnelStore.ts
1022
+ import { create as create4 } from "zustand";
1023
+
1024
+ // src/stores/fileBrowserStore.ts
1025
+ import { create as create3 } from "zustand";
1026
+
1027
+ // src/stores/mcpStore.ts
1028
+ import { create as create2 } from "zustand";
1029
+
1030
+ // src/stores/skillsStore.ts
1031
+ import { create } from "zustand";
1032
+ var useSkillsStore = create((set) => ({
1033
+ isExpanded: false,
1034
+ skills: [],
1035
+ selectedSkill: null,
1036
+ isViewerOpen: false,
1037
+ viewingFile: null,
1038
+ toggleSidebar: () => {
1039
+ set((state) => {
1040
+ const newExpanded = !state.isExpanded;
1041
+ if (newExpanded) {
1042
+ useGitStore.getState().collapseSidebar();
1043
+ useSessionFilesStore.getState().collapseSidebar();
1044
+ useResearchStore.getState().collapseSidebar();
1045
+ useSettingsStore.getState().collapseSidebar();
1046
+ useTunnelStore.getState().collapseSidebar();
1047
+ useFileBrowserStore.getState().collapseSidebar();
1048
+ useMCPStore.getState().collapseSidebar();
1049
+ }
1050
+ return { isExpanded: newExpanded };
1051
+ });
1052
+ },
1053
+ expandSidebar: () => {
1054
+ useGitStore.getState().collapseSidebar();
1055
+ useSessionFilesStore.getState().collapseSidebar();
1056
+ useResearchStore.getState().collapseSidebar();
1057
+ useSettingsStore.getState().collapseSidebar();
1058
+ useTunnelStore.getState().collapseSidebar();
1059
+ useFileBrowserStore.getState().collapseSidebar();
1060
+ useMCPStore.getState().collapseSidebar();
1061
+ set({ isExpanded: true });
1062
+ },
1063
+ collapseSidebar: () => set({ isExpanded: false }),
1064
+ setSkills: (skills) => set({ skills }),
1065
+ selectSkill: (name) => set({ selectedSkill: name, isViewerOpen: false, viewingFile: null }),
1066
+ openViewer: (file) => set({ isViewerOpen: true, viewingFile: file }),
1067
+ closeViewer: () => set({ isViewerOpen: false, viewingFile: null })
1068
+ }));
1069
+
1070
+ // src/stores/mcpStore.ts
1071
+ var useMCPStore = create2((set) => ({
1072
+ isExpanded: false,
1073
+ servers: [],
1074
+ loading: new Set,
1075
+ authUrls: new Map,
1076
+ copilotDevice: null,
1077
+ toggleSidebar: () => {
1078
+ set((state) => {
1079
+ const newExpanded = !state.isExpanded;
1080
+ if (newExpanded) {
1081
+ useGitStore.getState().collapseSidebar();
1082
+ useSessionFilesStore.getState().collapseSidebar();
1083
+ useResearchStore.getState().collapseSidebar();
1084
+ useSettingsStore.getState().collapseSidebar();
1085
+ useTunnelStore.getState().collapseSidebar();
1086
+ useFileBrowserStore.getState().collapseSidebar();
1087
+ useSkillsStore.getState().collapseSidebar();
1088
+ }
1089
+ return { isExpanded: newExpanded };
1090
+ });
1091
+ },
1092
+ expandSidebar: () => {
1093
+ useGitStore.getState().collapseSidebar();
1094
+ useSessionFilesStore.getState().collapseSidebar();
1095
+ useResearchStore.getState().collapseSidebar();
1096
+ useSettingsStore.getState().collapseSidebar();
1097
+ useTunnelStore.getState().collapseSidebar();
1098
+ useFileBrowserStore.getState().collapseSidebar();
1099
+ useSkillsStore.getState().collapseSidebar();
1100
+ set({ isExpanded: true });
1101
+ },
1102
+ collapseSidebar: () => set({ isExpanded: false }),
1103
+ setServers: (servers) => set({ servers }),
1104
+ setLoading: (name, loading) => set((state) => {
1105
+ const next = new Set(state.loading);
1106
+ if (loading)
1107
+ next.add(name);
1108
+ else
1109
+ next.delete(name);
1110
+ return { loading: next };
1111
+ }),
1112
+ updateServer: (name, updates) => set((state) => ({
1113
+ servers: state.servers.map((s) => s.name === name ? { ...s, ...updates } : s)
1114
+ })),
1115
+ setAuthUrl: (name, url) => set((state) => {
1116
+ const next = new Map(state.authUrls);
1117
+ if (url)
1118
+ next.set(name, url);
1119
+ else
1120
+ next.delete(name);
1121
+ return { authUrls: next };
1122
+ }),
1123
+ setCopilotDevice: (info) => set({ copilotDevice: info })
1124
+ }));
1125
+
1126
+ // src/stores/fileBrowserStore.ts
1127
+ var useFileBrowserStore = create3((set) => ({
1128
+ isExpanded: false,
1129
+ selectedFile: null,
1130
+ isViewerOpen: false,
1131
+ expandedDirs: new Set,
1132
+ toggleSidebar: () => {
1133
+ set((state) => {
1134
+ const newExpanded = !state.isExpanded;
1135
+ if (newExpanded) {
1136
+ useGitStore.getState().collapseSidebar();
1137
+ useSessionFilesStore.getState().collapseSidebar();
1138
+ useResearchStore.getState().collapseSidebar();
1139
+ useSettingsStore.getState().collapseSidebar();
1140
+ useTunnelStore.getState().collapseSidebar();
1141
+ useMCPStore.getState().collapseSidebar();
1142
+ useSkillsStore.getState().collapseSidebar();
1143
+ }
1144
+ return { isExpanded: newExpanded };
1145
+ });
1146
+ },
1147
+ expandSidebar: () => {
1148
+ useGitStore.getState().collapseSidebar();
1149
+ useSessionFilesStore.getState().collapseSidebar();
1150
+ useResearchStore.getState().collapseSidebar();
1151
+ useSettingsStore.getState().collapseSidebar();
1152
+ useTunnelStore.getState().collapseSidebar();
1153
+ useMCPStore.getState().collapseSidebar();
1154
+ useSkillsStore.getState().collapseSidebar();
1155
+ set({ isExpanded: true });
1156
+ },
1157
+ collapseSidebar: () => set({
1158
+ isExpanded: false,
1159
+ isViewerOpen: false,
1160
+ selectedFile: null
1161
+ }),
1162
+ openFile: (path) => set({
1163
+ selectedFile: path,
1164
+ isViewerOpen: true
1165
+ }),
1166
+ closeViewer: () => set({
1167
+ isViewerOpen: false,
1168
+ selectedFile: null
1169
+ }),
1170
+ toggleDir: (path) => set((state) => {
1171
+ const next = new Set(state.expandedDirs);
1172
+ if (next.has(path)) {
1173
+ next.delete(path);
1174
+ } else {
1175
+ next.add(path);
1176
+ }
1177
+ return { expandedDirs: next };
1178
+ })
1179
+ }));
1180
+
1181
+ // src/stores/tunnelStore.ts
1182
+ var useTunnelStore = create4((set) => ({
1183
+ isExpanded: false,
1184
+ status: "idle",
1185
+ url: null,
1186
+ qrCode: null,
1187
+ error: null,
1188
+ progress: null,
1189
+ toggleSidebar: () => {
1190
+ set((state) => {
1191
+ const newExpanded = !state.isExpanded;
1192
+ if (newExpanded) {
1193
+ useGitStore.getState().collapseSidebar();
1194
+ useSessionFilesStore.getState().collapseSidebar();
1195
+ useSettingsStore.getState().collapseSidebar();
1196
+ useResearchStore.getState().collapseSidebar();
1197
+ useFileBrowserStore.getState().collapseSidebar();
1198
+ useMCPStore.getState().collapseSidebar();
1199
+ useSkillsStore.getState().collapseSidebar();
1200
+ }
1201
+ return { isExpanded: newExpanded };
1202
+ });
1203
+ },
1204
+ expandSidebar: () => {
1205
+ useGitStore.getState().collapseSidebar();
1206
+ useSessionFilesStore.getState().collapseSidebar();
1207
+ useSettingsStore.getState().collapseSidebar();
1208
+ useResearchStore.getState().collapseSidebar();
1209
+ useFileBrowserStore.getState().collapseSidebar();
1210
+ useMCPStore.getState().collapseSidebar();
1211
+ useSkillsStore.getState().collapseSidebar();
1212
+ set({ isExpanded: true });
1213
+ },
1214
+ collapseSidebar: () => set({ isExpanded: false }),
1215
+ setStatus: (status) => set({ status }),
1216
+ setUrl: (url) => set({ url }),
1217
+ setQrCode: (qrCode) => set({ qrCode }),
1218
+ setError: (error) => set({ error }),
1219
+ setProgress: (progress) => set({ progress }),
1220
+ reset: () => set({
1221
+ status: "idle",
1222
+ url: null,
1223
+ qrCode: null,
1224
+ error: null,
1225
+ progress: null
1226
+ })
1227
+ }));
1228
+
1229
+ // src/stores/settingsStore.ts
1230
+ var useSettingsStore = create5((set) => ({
1231
+ isExpanded: false,
1232
+ toggleSidebar: () => {
1233
+ set((state) => {
1234
+ const newExpanded = !state.isExpanded;
1235
+ if (newExpanded) {
1236
+ useGitStore.getState().collapseSidebar();
1237
+ useSessionFilesStore.getState().collapseSidebar();
1238
+ useResearchStore.getState().collapseSidebar();
1239
+ useTunnelStore.getState().collapseSidebar();
1240
+ useFileBrowserStore.getState().collapseSidebar();
1241
+ useMCPStore.getState().collapseSidebar();
1242
+ useSkillsStore.getState().collapseSidebar();
1243
+ }
1244
+ return { isExpanded: newExpanded };
1245
+ });
1246
+ },
1247
+ expandSidebar: () => set({ isExpanded: true }),
1248
+ collapseSidebar: () => set({ isExpanded: false })
1249
+ }));
1250
+
1251
+ // src/stores/researchStore.ts
1252
+ var useResearchStore = create6((set, get) => ({
1253
+ isExpanded: false,
1254
+ activeResearchSessionId: null,
1255
+ parentSessionId: null,
1256
+ toggleSidebar: () => {
1257
+ set((state) => {
1258
+ const newExpanded = !state.isExpanded;
1259
+ if (newExpanded) {
1260
+ useGitStore.getState().collapseSidebar();
1261
+ useSessionFilesStore.getState().collapseSidebar();
1262
+ useSettingsStore.getState().collapseSidebar();
1263
+ useTunnelStore.getState().collapseSidebar();
1264
+ useFileBrowserStore.getState().collapseSidebar();
1265
+ useMCPStore.getState().collapseSidebar();
1266
+ useSkillsStore.getState().collapseSidebar();
1267
+ }
1268
+ return { isExpanded: newExpanded };
1269
+ });
1270
+ },
1271
+ expandSidebar: () => {
1272
+ useGitStore.getState().collapseSidebar();
1273
+ useSessionFilesStore.getState().collapseSidebar();
1274
+ useSettingsStore.getState().collapseSidebar();
1275
+ useTunnelStore.getState().collapseSidebar();
1276
+ useFileBrowserStore.getState().collapseSidebar();
1277
+ useMCPStore.getState().collapseSidebar();
1278
+ useSkillsStore.getState().collapseSidebar();
1279
+ set({ isExpanded: true });
1280
+ },
1281
+ collapseSidebar: () => set({ isExpanded: false }),
1282
+ selectResearchSession: (id) => set({ activeResearchSessionId: id }),
1283
+ setParentSessionId: (id) => {
1284
+ const currentParentId = get().parentSessionId;
1285
+ if (currentParentId !== id) {
1286
+ set({
1287
+ parentSessionId: id,
1288
+ activeResearchSessionId: null
1289
+ });
1290
+ }
1291
+ },
1292
+ reset: () => set({
1293
+ activeResearchSessionId: null,
1294
+ parentSessionId: null
1295
+ })
1296
+ }));
1297
+
1298
+ // src/stores/sessionFilesStore.ts
1299
+ var useSessionFilesStore = create7((set) => ({
1300
+ isExpanded: false,
1301
+ selectedFile: null,
1302
+ allOperations: [],
1303
+ selectedOperationIndex: 0,
1304
+ isDiffOpen: false,
1305
+ toggleSidebar: () => {
1306
+ set((state) => {
1307
+ const newExpanded = !state.isExpanded;
1308
+ if (newExpanded) {
1309
+ useGitStore.getState().collapseSidebar();
1310
+ useResearchStore.getState().collapseSidebar();
1311
+ useSettingsStore.getState().collapseSidebar();
1312
+ useTunnelStore.getState().collapseSidebar();
1313
+ useFileBrowserStore.getState().collapseSidebar();
1314
+ useMCPStore.getState().collapseSidebar();
1315
+ useSkillsStore.getState().collapseSidebar();
1316
+ }
1317
+ return { isExpanded: newExpanded };
1318
+ });
1319
+ },
1320
+ expandSidebar: () => {
1321
+ useGitStore.getState().collapseSidebar();
1322
+ useResearchStore.getState().collapseSidebar();
1323
+ useSettingsStore.getState().collapseSidebar();
1324
+ useTunnelStore.getState().collapseSidebar();
1325
+ useFileBrowserStore.getState().collapseSidebar();
1326
+ useMCPStore.getState().collapseSidebar();
1327
+ useSkillsStore.getState().collapseSidebar();
1328
+ set({ isExpanded: true });
1329
+ },
1330
+ collapseSidebar: () => set({
1331
+ isExpanded: false,
1332
+ isDiffOpen: false,
1333
+ selectedFile: null,
1334
+ allOperations: [],
1335
+ selectedOperationIndex: 0
1336
+ }),
1337
+ openDiff: (file, operations) => set({
1338
+ selectedFile: file,
1339
+ allOperations: operations,
1340
+ selectedOperationIndex: operations.length - 1,
1341
+ isDiffOpen: true,
1342
+ isExpanded: true
1343
+ }),
1344
+ selectOperation: (index) => set({ selectedOperationIndex: index }),
1345
+ closeDiff: () => set({
1346
+ isDiffOpen: false,
1347
+ selectedFile: null,
1348
+ allOperations: [],
1349
+ selectedOperationIndex: 0
1350
+ })
1351
+ }));
1352
+
1353
+ // src/stores/gitStore.ts
1354
+ var useGitStore = create8((set) => ({
1355
+ isExpanded: false,
1356
+ activeSessionId: null,
1357
+ selectedFile: null,
1358
+ selectedFileStaged: false,
1359
+ isDiffOpen: false,
1360
+ isCommitModalOpen: false,
1361
+ commitSessionId: null,
1362
+ wasSessionListCollapsed: false,
1363
+ toggleSidebar: () => {
1364
+ set((state) => {
1365
+ const newExpanded = !state.isExpanded;
1366
+ if (newExpanded) {
1367
+ useSessionFilesStore.getState().collapseSidebar();
1368
+ useResearchStore.getState().collapseSidebar();
1369
+ useSettingsStore.getState().collapseSidebar();
1370
+ useTunnelStore.getState().collapseSidebar();
1371
+ useFileBrowserStore.getState().collapseSidebar();
1372
+ useMCPStore.getState().collapseSidebar();
1373
+ useSkillsStore.getState().collapseSidebar();
1374
+ }
1375
+ return { isExpanded: newExpanded };
1376
+ });
1377
+ },
1378
+ expandSidebar: () => set({ isExpanded: true }),
1379
+ collapseSidebar: () => set({ isExpanded: false, isDiffOpen: false, selectedFile: null }),
1380
+ openDiff: (file, staged) => set({
1381
+ selectedFile: file,
1382
+ selectedFileStaged: staged,
1383
+ isDiffOpen: true,
1384
+ isExpanded: true
1385
+ }),
1386
+ closeDiff: () => set({
1387
+ isDiffOpen: false,
1388
+ selectedFile: null
1389
+ }),
1390
+ switchFile: (file, staged) => set({
1391
+ selectedFile: file,
1392
+ selectedFileStaged: staged
1393
+ }),
1394
+ openCommitModal: () => set((state) => ({
1395
+ isCommitModalOpen: true,
1396
+ commitSessionId: state.activeSessionId
1397
+ })),
1398
+ openCommitModalForSession: (sessionId) => set({ isCommitModalOpen: true, commitSessionId: sessionId }),
1399
+ closeCommitModal: () => set({ isCommitModalOpen: false, commitSessionId: null }),
1400
+ setActiveSessionId: (sessionId) => set({ activeSessionId: sessionId }),
1401
+ setSessionListCollapsed: (collapsed) => set({ wasSessionListCollapsed: collapsed })
1402
+ }));
1403
+
1404
+ // src/hooks/useGit.ts
1405
+ function useGitStatus() {
1406
+ const isExpanded = useGitStore((state) => state.isExpanded);
1407
+ return useQuery3({
1408
+ queryKey: ["git", "status"],
1409
+ queryFn: () => apiClient.getGitStatus(),
1410
+ refetchInterval: isExpanded ? 5000 : false,
1411
+ retry: 1,
1412
+ staleTime: 3000
1413
+ });
1414
+ }
1415
+ function useGitDiff(file, staged = false) {
1416
+ return useQuery3({
1417
+ queryKey: ["git", "diff", file, staged],
1418
+ queryFn: () => file ? apiClient.getGitDiff(file, staged) : null,
1419
+ enabled: !!file,
1420
+ retry: 1,
1421
+ refetchInterval: false
1422
+ });
1423
+ }
1424
+ function useGitBranch() {
1425
+ const isExpanded = useGitStore((state) => state.isExpanded);
1426
+ return useQuery3({
1427
+ queryKey: ["git", "branch"],
1428
+ queryFn: () => apiClient.getGitBranch(),
1429
+ refetchInterval: isExpanded ? 1e4 : false,
1430
+ retry: 1,
1431
+ staleTime: 5000
1432
+ });
1433
+ }
1434
+ function useGenerateCommitMessage(sessionId) {
1435
+ return useMutation2({
1436
+ mutationFn: () => apiClient.generateCommitMessage(sessionId ?? undefined)
1437
+ });
1438
+ }
1439
+ function useStageFiles() {
1440
+ const queryClient = useQueryClient2();
1441
+ return useMutation2({
1442
+ mutationFn: (files) => apiClient.stageFiles(files),
1443
+ onSuccess: () => {
1444
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1445
+ }
1446
+ });
1447
+ }
1448
+ function useUnstageFiles() {
1449
+ const queryClient = useQueryClient2();
1450
+ return useMutation2({
1451
+ mutationFn: (files) => apiClient.unstageFiles(files),
1452
+ onSuccess: () => {
1453
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1454
+ }
1455
+ });
1456
+ }
1457
+ function useRestoreFiles() {
1458
+ const queryClient = useQueryClient2();
1459
+ return useMutation2({
1460
+ mutationFn: (files) => apiClient.restoreFiles(files),
1461
+ onSuccess: () => {
1462
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1463
+ }
1464
+ });
1465
+ }
1466
+ function useDeleteFiles() {
1467
+ const queryClient = useQueryClient2();
1468
+ return useMutation2({
1469
+ mutationFn: (files) => apiClient.deleteFiles(files),
1470
+ onSuccess: () => {
1471
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1472
+ }
1473
+ });
1474
+ }
1475
+ function useCommitChanges() {
1476
+ const queryClient = useQueryClient2();
1477
+ return useMutation2({
1478
+ mutationFn: (message) => apiClient.commitChanges(message),
1479
+ onSuccess: () => {
1480
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1481
+ queryClient.invalidateQueries({ queryKey: ["git", "branch"] });
1482
+ }
1483
+ });
1484
+ }
1485
+ function usePushCommits() {
1486
+ const queryClient = useQueryClient2();
1487
+ return useMutation2({
1488
+ mutationFn: () => apiClient.pushCommits(),
1489
+ onSuccess: () => {
1490
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1491
+ queryClient.invalidateQueries({ queryKey: ["git", "branch"] });
1492
+ }
1493
+ });
1494
+ }
1495
+ function usePullChanges() {
1496
+ const queryClient = useQueryClient2();
1497
+ return useMutation2({
1498
+ mutationFn: () => apiClient.pullChanges(),
1499
+ onSuccess: () => {
1500
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1501
+ queryClient.invalidateQueries({ queryKey: ["git", "branch"] });
1502
+ }
1503
+ });
1504
+ }
1505
+ function useGitInit() {
1506
+ const queryClient = useQueryClient2();
1507
+ return useMutation2({
1508
+ mutationFn: () => apiClient.initGitRepo(),
1509
+ onSuccess: () => {
1510
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1511
+ queryClient.invalidateQueries({ queryKey: ["git", "branch"] });
1512
+ }
1513
+ });
1514
+ }
1515
+ function useGitRemotes() {
1516
+ const isExpanded = useGitStore((state) => state.isExpanded);
1517
+ return useQuery3({
1518
+ queryKey: ["git", "remotes"],
1519
+ queryFn: () => apiClient.getRemotes(),
1520
+ enabled: isExpanded,
1521
+ retry: 1,
1522
+ staleTime: 1e4
1523
+ });
1524
+ }
1525
+ function useAddRemote() {
1526
+ const queryClient = useQueryClient2();
1527
+ return useMutation2({
1528
+ mutationFn: ({ name, url }) => apiClient.addRemote(name, url),
1529
+ onSuccess: () => {
1530
+ queryClient.invalidateQueries({ queryKey: ["git", "remotes"] });
1531
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1532
+ }
1533
+ });
1534
+ }
1535
+ function useRemoveRemote() {
1536
+ const queryClient = useQueryClient2();
1537
+ return useMutation2({
1538
+ mutationFn: (name) => apiClient.removeRemote(name),
1539
+ onSuccess: () => {
1540
+ queryClient.invalidateQueries({ queryKey: ["git", "remotes"] });
1541
+ queryClient.invalidateQueries({ queryKey: ["git", "status"] });
1542
+ }
1543
+ });
1544
+ }
1545
+ // src/hooks/useMessages.ts
1546
+ import { useQuery as useQuery4, useMutation as useMutation4, useQueryClient as useQueryClient4 } from "@tanstack/react-query";
1547
+
1548
+ // src/hooks/useSessions.ts
1549
+ import {
1550
+ useInfiniteQuery,
1551
+ useMutation as useMutation3,
1552
+ useQueryClient as useQueryClient3
1553
+ } from "@tanstack/react-query";
1554
+ import { useMemo as useMemo2 } from "react";
1555
+ var SESSIONS_PAGE_SIZE = 50;
1556
+ var sessionsQueryKey = ["sessions", "list"];
1557
+ function useSessionsInfinite() {
1558
+ return useInfiniteQuery({
1559
+ queryKey: sessionsQueryKey,
1560
+ queryFn: ({ pageParam = 0 }) => apiClient.getSessionsPage({
1561
+ limit: SESSIONS_PAGE_SIZE,
1562
+ offset: pageParam
1563
+ }),
1564
+ getNextPageParam: (lastPage) => lastPage.nextOffset ?? undefined,
1565
+ initialPageParam: 0,
1566
+ staleTime: 30000,
1567
+ refetchInterval: 30000,
1568
+ refetchOnWindowFocus: false
1569
+ });
1570
+ }
1571
+ function useSessions() {
1572
+ const query = useSessionsInfinite();
1573
+ const data = useMemo2(() => {
1574
+ if (!query.data?.pages)
1575
+ return [];
1576
+ return query.data.pages.flatMap((p) => p.items ?? []);
1577
+ }, [query.data]);
1578
+ return {
1579
+ data,
1580
+ isLoading: query.isLoading,
1581
+ isError: query.isError,
1582
+ error: query.error,
1583
+ hasNextPage: query.hasNextPage,
1584
+ fetchNextPage: query.fetchNextPage,
1585
+ isFetchingNextPage: query.isFetchingNextPage
1586
+ };
1587
+ }
1588
+ function useSession(sessionId) {
1589
+ const { data: sessions } = useSessions();
1590
+ return sessions?.find((s) => s.id === sessionId);
1591
+ }
1592
+ function useCreateSession() {
1593
+ const queryClient = useQueryClient3();
1594
+ return useMutation3({
1595
+ mutationFn: (data) => apiClient.createSession(data),
1596
+ onSuccess: (newSession) => {
1597
+ queryClient.setQueryData(sessionsQueryKey, (old) => {
1598
+ if (!old)
1599
+ return old;
1600
+ const firstPage = old.pages[0];
1601
+ if (!firstPage)
1602
+ return old;
1603
+ return {
1604
+ ...old,
1605
+ pages: [
1606
+ { ...firstPage, items: [newSession, ...firstPage.items] },
1607
+ ...old.pages.slice(1)
1608
+ ]
1609
+ };
1610
+ });
1611
+ queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
1612
+ }
1613
+ });
1614
+ }
1615
+ function useUpdateSession(sessionId) {
1616
+ const queryClient = useQueryClient3();
1617
+ return useMutation3({
1618
+ mutationFn: (data) => apiClient.updateSession(sessionId, data),
1619
+ onSuccess: async () => {
1620
+ await queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
1621
+ await queryClient.invalidateQueries({ queryKey: ["session", sessionId] });
1622
+ }
1623
+ });
1624
+ }
1625
+ function useDeleteSession() {
1626
+ const queryClient = useQueryClient3();
1627
+ return useMutation3({
1628
+ mutationFn: (sessionId) => apiClient.deleteSession(sessionId),
1629
+ onSuccess: () => {
1630
+ queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
1631
+ }
1632
+ });
1633
+ }
1634
+
1635
+ // src/hooks/useMessages.ts
1636
+ function useMessages(sessionId) {
1637
+ return useQuery4({
1638
+ queryKey: ["messages", sessionId],
1639
+ queryFn: () => {
1640
+ if (!sessionId) {
1641
+ throw new Error("Session ID is required");
1642
+ }
1643
+ return apiClient.getMessages(sessionId);
1644
+ },
1645
+ enabled: !!sessionId
1646
+ });
1647
+ }
1648
+ function useSendMessage(sessionId) {
1649
+ const queryClient = useQueryClient4();
1650
+ return useMutation4({
1651
+ mutationFn: (data) => apiClient.sendMessage(sessionId, data),
1652
+ onSuccess: () => {
1653
+ queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
1654
+ queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
1655
+ }
1656
+ });
1657
+ }
1658
+ // src/hooks/useSessionStream.ts
1659
+ import { useEffect, useRef } from "react";
1660
+ import { useQueryClient as useQueryClient5 } from "@tanstack/react-query";
1661
+
1662
+ // src/lib/sse-client.ts
1663
+ class SSEClient {
1664
+ abortController = null;
1665
+ handlers = new Map;
1666
+ running = false;
1667
+ async connect(url) {
1668
+ if (this.abortController) {
1669
+ this.abortController.abort();
1670
+ }
1671
+ this.abortController = new AbortController;
1672
+ this.running = true;
1673
+ try {
1674
+ const response = await fetch(url, {
1675
+ headers: { Accept: "text/event-stream" },
1676
+ signal: this.abortController.signal
1677
+ });
1678
+ if (!response.ok) {
1679
+ console.error("[SSE] Connection failed:", response.status);
1680
+ return;
1681
+ }
1682
+ const reader = response.body?.getReader();
1683
+ if (!reader) {
1684
+ console.error("[SSE] No response body");
1685
+ return;
1686
+ }
1687
+ const decoder = new TextDecoder;
1688
+ let buffer = "";
1689
+ while (this.running) {
1690
+ const { done, value } = await reader.read();
1691
+ if (done)
1692
+ break;
1693
+ buffer += decoder.decode(value, { stream: true });
1694
+ let idx = buffer.indexOf(`
1695
+
1696
+ `);
1697
+ while (idx !== -1) {
1698
+ const raw = buffer.slice(0, idx);
1699
+ buffer = buffer.slice(idx + 2);
1700
+ const lines = raw.split(`
1701
+ `);
1702
+ let eventType = "message";
1703
+ let data = "";
1704
+ for (const line of lines) {
1705
+ if (line.startsWith("event: ")) {
1706
+ eventType = line.slice(7).trim();
1707
+ } else if (line.startsWith("data: ")) {
1708
+ data += (data ? `
1709
+ ` : "") + line.slice(6);
1710
+ } else if (line.startsWith(":")) {}
1711
+ }
1712
+ if (data) {
1713
+ try {
1714
+ const payload = JSON.parse(data);
1715
+ this.emit({ type: eventType, payload });
1716
+ } catch (error) {
1717
+ console.error(`[SSE] Failed to parse ${eventType}:`, error);
1718
+ }
1719
+ }
1720
+ idx = buffer.indexOf(`
1721
+
1722
+ `);
1723
+ }
1724
+ }
1725
+ } catch (error) {
1726
+ if (error instanceof Error && error.name === "AbortError") {} else if (error instanceof TypeError && error.message === "Load failed") {} else {
1727
+ console.error("[SSE] Connection error:", error);
1728
+ }
1729
+ }
1730
+ }
1731
+ disconnect() {
1732
+ this.running = false;
1733
+ if (this.abortController) {
1734
+ this.abortController.abort();
1735
+ this.abortController = null;
1736
+ }
1737
+ }
1738
+ on(eventType, handler) {
1739
+ if (!this.handlers.has(eventType)) {
1740
+ this.handlers.set(eventType, new Set);
1741
+ }
1742
+ this.handlers.get(eventType)?.add(handler);
1743
+ return () => {
1744
+ this.off(eventType, handler);
1745
+ };
1746
+ }
1747
+ off(eventType, handler) {
1748
+ const handlers = this.handlers.get(eventType);
1749
+ if (handlers) {
1750
+ handlers.delete(handler);
1751
+ if (handlers.size === 0) {
1752
+ this.handlers.delete(eventType);
1753
+ }
1754
+ }
1755
+ }
1756
+ emit(event) {
1757
+ const handlers = this.handlers.get(event.type);
1758
+ if (handlers) {
1759
+ for (const handler of handlers) {
1760
+ handler(event);
1761
+ }
1762
+ }
1763
+ const allHandlers = this.handlers.get("*");
1764
+ if (allHandlers) {
1765
+ for (const handler of allHandlers) {
1766
+ handler(event);
1767
+ }
1768
+ }
1769
+ }
1770
+ }
1771
+
1772
+ // src/stores/toolApprovalStore.ts
1773
+ import { create as create9 } from "zustand";
1774
+ var useToolApprovalStore = create9((set) => ({
1775
+ pendingApprovals: [],
1776
+ addPendingApproval: (approval) => set((state) => ({
1777
+ pendingApprovals: [...state.pendingApprovals, approval]
1778
+ })),
1779
+ removePendingApproval: (callId) => set((state) => ({
1780
+ pendingApprovals: state.pendingApprovals.filter((a) => a.callId !== callId)
1781
+ })),
1782
+ updatePendingApproval: (callId, args) => set((state) => ({
1783
+ pendingApprovals: state.pendingApprovals.map((a) => a.callId === callId ? { ...a, args } : a)
1784
+ })),
1785
+ clearPendingApprovals: () => set({ pendingApprovals: [] }),
1786
+ setPendingApprovals: (approvals) => set({ pendingApprovals: approvals })
1787
+ }));
1788
+
1789
+ // src/hooks/useSessionStream.ts
1790
+ function useSessionStream(sessionId) {
1791
+ const queryClient = useQueryClient5();
1792
+ const clientRef = useRef(null);
1793
+ const assistantMessageIdRef = useRef(null);
1794
+ const lastInvalidationRef = useRef(0);
1795
+ const {
1796
+ addPendingApproval,
1797
+ removePendingApproval,
1798
+ updatePendingApproval,
1799
+ setPendingApprovals
1800
+ } = useToolApprovalStore();
1801
+ useEffect(() => {
1802
+ if (!sessionId) {
1803
+ console.log("[useSessionStream] No sessionId, skipping");
1804
+ return;
1805
+ }
1806
+ assistantMessageIdRef.current = null;
1807
+ let lastSessionInvalidation = 0;
1808
+ apiClient.getPendingApprovals(sessionId).then((result) => {
1809
+ if (result.ok && result.pending.length > 0) {
1810
+ setPendingApprovals(result.pending);
1811
+ } else {
1812
+ setPendingApprovals([]);
1813
+ }
1814
+ }).catch(() => {
1815
+ setPendingApprovals([]);
1816
+ });
1817
+ const client2 = new SSEClient;
1818
+ clientRef.current = client2;
1819
+ const url = apiClient.getStreamUrl(sessionId);
1820
+ console.log("[useSessionStream] Connecting to stream:", url);
1821
+ client2.connect(url);
1822
+ const resolveAssistantTargetIndex = (messages) => {
1823
+ if (assistantMessageIdRef.current) {
1824
+ const byId = messages.findIndex((message) => message.id === assistantMessageIdRef.current);
1825
+ if (byId !== -1)
1826
+ return byId;
1827
+ }
1828
+ for (let i = messages.length - 1;i >= 0; i -= 1) {
1829
+ const candidate = messages[i];
1830
+ if (candidate.role === "assistant" && candidate.status !== "complete") {
1831
+ return i;
1832
+ }
1833
+ }
1834
+ return -1;
1835
+ };
1836
+ const extractText = (part) => {
1837
+ if (part.contentJson && typeof part.contentJson === "object" && !Array.isArray(part.contentJson) && "text" in part.contentJson) {
1838
+ return String(part.contentJson.text ?? "");
1839
+ }
1840
+ if (typeof part.content === "string") {
1841
+ try {
1842
+ const parsed = JSON.parse(part.content);
1843
+ if (parsed && typeof parsed.text === "string")
1844
+ return parsed.text;
1845
+ } catch {}
1846
+ return part.content;
1847
+ }
1848
+ return "";
1849
+ };
1850
+ const getOptimisticPartIndex = (parts, stepIndex) => {
1851
+ if (typeof stepIndex !== "number") {
1852
+ return parts.length;
1853
+ }
1854
+ const sameStepIndexes = parts.filter((part) => part.stepIndex === stepIndex).map((part) => part.index).filter((index) => Number.isFinite(index));
1855
+ if (sameStepIndexes.length > 0) {
1856
+ return Math.max(...sameStepIndexes) + 0.001;
1857
+ }
1858
+ const previousStepIndexes = parts.filter((part) => typeof part.stepIndex === "number" && part.stepIndex < stepIndex).map((part) => part.index).filter((index) => Number.isFinite(index));
1859
+ const nextStepIndexes = parts.filter((part) => typeof part.stepIndex === "number" && part.stepIndex > stepIndex).map((part) => part.index).filter((index) => Number.isFinite(index));
1860
+ const lowerBound = previousStepIndexes.length > 0 ? Math.max(...previousStepIndexes) : null;
1861
+ const upperBound = nextStepIndexes.length > 0 ? Math.min(...nextStepIndexes) : null;
1862
+ if (lowerBound !== null && upperBound !== null) {
1863
+ return (lowerBound + upperBound) / 2;
1864
+ }
1865
+ if (lowerBound !== null) {
1866
+ return lowerBound + 1;
1867
+ }
1868
+ if (upperBound !== null) {
1869
+ return upperBound - 1;
1870
+ }
1871
+ return parts.length;
1872
+ };
1873
+ const applyReasoningDelta = (payload) => {
1874
+ const messageId = typeof payload?.messageId === "string" ? payload.messageId : null;
1875
+ const partId = typeof payload?.partId === "string" ? payload.partId : null;
1876
+ const delta = typeof payload?.delta === "string" ? payload.delta : null;
1877
+ if (!messageId || !partId || delta === null)
1878
+ return;
1879
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
1880
+ if (!oldMessages)
1881
+ return oldMessages;
1882
+ const nextMessages = [...oldMessages];
1883
+ const messageIndex = nextMessages.findIndex((message) => message.id === messageId);
1884
+ if (messageIndex === -1)
1885
+ return oldMessages;
1886
+ const targetMessage = nextMessages[messageIndex];
1887
+ const parts = targetMessage.parts ? [...targetMessage.parts] : [];
1888
+ let partIndex = parts.findIndex((part) => part.id === partId);
1889
+ const stepIndex = typeof payload?.stepIndex === "number" ? payload.stepIndex : null;
1890
+ if (partIndex === -1) {
1891
+ const newPart = {
1892
+ id: partId,
1893
+ messageId,
1894
+ index: getOptimisticPartIndex(parts, stepIndex),
1895
+ stepIndex,
1896
+ type: "reasoning",
1897
+ content: JSON.stringify({ text: delta }),
1898
+ contentJson: { text: delta },
1899
+ agent: targetMessage.agent,
1900
+ provider: targetMessage.provider,
1901
+ model: targetMessage.model,
1902
+ startedAt: Date.now(),
1903
+ completedAt: null,
1904
+ toolName: null,
1905
+ toolCallId: null,
1906
+ toolDurationMs: null
1907
+ };
1908
+ parts.push(newPart);
1909
+ partIndex = parts.length - 1;
1910
+ } else {
1911
+ const existing = parts[partIndex];
1912
+ const previous = extractText(existing);
1913
+ const nextText = `${previous}${delta}`;
1914
+ parts[partIndex] = {
1915
+ ...existing,
1916
+ content: JSON.stringify({ text: nextText }),
1917
+ contentJson: { text: nextText },
1918
+ stepIndex: stepIndex ?? existing.stepIndex ?? null,
1919
+ completedAt: null
1920
+ };
1921
+ }
1922
+ nextMessages[messageIndex] = { ...targetMessage, parts };
1923
+ return nextMessages;
1924
+ });
1925
+ };
1926
+ const applyMessageDelta = (payload) => {
1927
+ const messageId = typeof payload?.messageId === "string" ? payload.messageId : null;
1928
+ const partId = typeof payload?.partId === "string" ? payload.partId : null;
1929
+ const delta = typeof payload?.delta === "string" ? payload.delta : null;
1930
+ if (!messageId || !partId || delta === null)
1931
+ return;
1932
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
1933
+ if (!oldMessages)
1934
+ return oldMessages;
1935
+ const nextMessages = [...oldMessages];
1936
+ const messageIndex = nextMessages.findIndex((message) => message.id === messageId);
1937
+ if (messageIndex === -1)
1938
+ return oldMessages;
1939
+ const targetMessage = nextMessages[messageIndex];
1940
+ const parts = targetMessage.parts ? [...targetMessage.parts] : [];
1941
+ let partIndex = parts.findIndex((part) => part.id === partId);
1942
+ const stepIndex = typeof payload?.stepIndex === "number" ? payload.stepIndex : null;
1943
+ if (partIndex === -1) {
1944
+ const newPart = {
1945
+ id: partId,
1946
+ messageId,
1947
+ index: getOptimisticPartIndex(parts, stepIndex),
1948
+ stepIndex,
1949
+ type: "text",
1950
+ content: JSON.stringify({ text: delta }),
1951
+ contentJson: { text: delta },
1952
+ agent: targetMessage.agent,
1953
+ provider: targetMessage.provider,
1954
+ model: targetMessage.model,
1955
+ startedAt: Date.now(),
1956
+ completedAt: null,
1957
+ toolName: null,
1958
+ toolCallId: null,
1959
+ toolDurationMs: null
1960
+ };
1961
+ parts.push(newPart);
1962
+ partIndex = parts.length - 1;
1963
+ } else {
1964
+ const existing = parts[partIndex];
1965
+ const previous = extractText(existing);
1966
+ const nextText = `${previous}${delta}`;
1967
+ parts[partIndex] = {
1968
+ ...existing,
1969
+ content: JSON.stringify({ text: nextText }),
1970
+ contentJson: { text: nextText },
1971
+ stepIndex: stepIndex ?? existing.stepIndex ?? null,
1972
+ completedAt: null
1973
+ };
1974
+ }
1975
+ nextMessages[messageIndex] = { ...targetMessage, parts };
1976
+ return nextMessages;
1977
+ });
1978
+ };
1979
+ const upsertEphemeralToolCall = (payload) => {
1980
+ if (!payload)
1981
+ return;
1982
+ const callId = typeof payload.callId === "string" ? payload.callId : null;
1983
+ const name = typeof payload.name === "string" ? payload.name : null;
1984
+ if (!name)
1985
+ return;
1986
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
1987
+ if (!oldMessages)
1988
+ return oldMessages;
1989
+ const nextMessages = [...oldMessages];
1990
+ let targetIndex = resolveAssistantTargetIndex(nextMessages);
1991
+ if (typeof payload.messageId === "string") {
1992
+ const explicitIndex = nextMessages.findIndex((message) => message.id === payload.messageId);
1993
+ if (explicitIndex !== -1)
1994
+ targetIndex = explicitIndex;
1995
+ }
1996
+ if (targetIndex === -1)
1997
+ return oldMessages;
1998
+ const targetMessage = nextMessages[targetIndex];
1999
+ const parts = targetMessage.parts ? [...targetMessage.parts] : [];
2000
+ let partIndex = -1;
2001
+ if (callId) {
2002
+ partIndex = parts.findIndex((part) => part.toolCallId === callId && part.ephemeral);
2003
+ }
2004
+ if (partIndex === -1 && !callId) {
2005
+ partIndex = parts.findIndex((part) => part.ephemeral && part.toolName === name);
2006
+ }
2007
+ const args = payload.args;
2008
+ const stepIndex = typeof payload.stepIndex === "number" ? payload.stepIndex : null;
2009
+ const contentJsonBase = { name };
2010
+ if (callId)
2011
+ contentJsonBase.callId = callId;
2012
+ if (args !== undefined)
2013
+ contentJsonBase.args = args;
2014
+ if (partIndex === -1) {
2015
+ const newPart = {
2016
+ id: callId ? `ephemeral-tool-call-${callId}` : `ephemeral-tool-call-${name}-${Date.now()}`,
2017
+ messageId: targetMessage.id,
2018
+ index: getOptimisticPartIndex(parts, stepIndex),
2019
+ stepIndex,
2020
+ type: "tool_call",
2021
+ content: JSON.stringify(contentJsonBase),
2022
+ contentJson: contentJsonBase,
2023
+ agent: targetMessage.agent,
2024
+ provider: targetMessage.provider,
2025
+ model: targetMessage.model,
2026
+ startedAt: Date.now(),
2027
+ completedAt: null,
2028
+ toolName: name,
2029
+ toolCallId: callId,
2030
+ toolDurationMs: null,
2031
+ ephemeral: true
2032
+ };
2033
+ parts.push(newPart);
2034
+ } else {
2035
+ const existing = parts[partIndex];
2036
+ const nextContentJson = {
2037
+ ...typeof existing.contentJson === "object" && !Array.isArray(existing.contentJson) ? existing.contentJson : {},
2038
+ name
2039
+ };
2040
+ if (callId)
2041
+ nextContentJson.callId = callId;
2042
+ if (args !== undefined)
2043
+ nextContentJson.args = args;
2044
+ parts[partIndex] = {
2045
+ ...existing,
2046
+ content: JSON.stringify(nextContentJson),
2047
+ contentJson: nextContentJson,
2048
+ stepIndex: stepIndex ?? existing.stepIndex ?? null,
2049
+ toolCallId: callId ?? existing.toolCallId,
2050
+ toolName: name
2051
+ };
2052
+ }
2053
+ nextMessages[targetIndex] = { ...targetMessage, parts };
2054
+ return nextMessages;
2055
+ });
2056
+ };
2057
+ const resolveEphemeralToolCall = (payload) => {
2058
+ const callId = typeof payload?.callId === "string" ? payload.callId : null;
2059
+ if (!callId)
2060
+ return;
2061
+ const payloadName = typeof payload?.name === "string" ? payload.name : null;
2062
+ const payloadStepIndex = typeof payload?.stepIndex === "number" ? payload.stepIndex : null;
2063
+ const payloadResult = payload?.result;
2064
+ const payloadArtifact = payload?.artifact;
2065
+ const payloadArgs = payload?.args;
2066
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
2067
+ if (!oldMessages)
2068
+ return oldMessages;
2069
+ let changed = false;
2070
+ const now = Date.now();
2071
+ const nextMessages = oldMessages.map((message) => {
2072
+ if (!message.parts?.length)
2073
+ return message;
2074
+ let messageChanged = false;
2075
+ const updatedParts = message.parts.map((part) => {
2076
+ if (!(part.ephemeral && part.toolCallId === callId)) {
2077
+ return part;
2078
+ }
2079
+ messageChanged = true;
2080
+ changed = true;
2081
+ const nextContentJson = {
2082
+ ...typeof part.contentJson === "object" && !Array.isArray(part.contentJson) ? part.contentJson : {},
2083
+ name: payloadName ?? part.toolName ?? "tool",
2084
+ callId
2085
+ };
2086
+ if (payloadArgs !== undefined)
2087
+ nextContentJson.args = payloadArgs;
2088
+ if (payloadResult !== undefined)
2089
+ nextContentJson.result = payloadResult;
2090
+ if (payloadArtifact !== undefined)
2091
+ nextContentJson.artifact = payloadArtifact;
2092
+ const durationMs = part.startedAt && Number.isFinite(part.startedAt) ? Math.max(0, now - part.startedAt) : part.toolDurationMs;
2093
+ const resolvedPart = {
2094
+ ...part,
2095
+ type: "tool_result",
2096
+ content: JSON.stringify(nextContentJson),
2097
+ contentJson: nextContentJson,
2098
+ stepIndex: payloadStepIndex ?? part.stepIndex ?? null,
2099
+ completedAt: now,
2100
+ toolName: payloadName ?? part.toolName,
2101
+ toolDurationMs: durationMs ?? null
2102
+ };
2103
+ return resolvedPart;
2104
+ });
2105
+ if (!messageChanged)
2106
+ return message;
2107
+ return { ...message, parts: updatedParts };
2108
+ });
2109
+ return changed ? nextMessages : oldMessages;
2110
+ });
2111
+ };
2112
+ const removeEphemeralToolCall = (payload) => {
2113
+ const callId = typeof payload?.callId === "string" ? payload.callId : null;
2114
+ if (!callId)
2115
+ return;
2116
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
2117
+ if (!oldMessages)
2118
+ return oldMessages;
2119
+ let changed = false;
2120
+ const nextMessages = oldMessages.map((message) => {
2121
+ if (!message.parts?.length)
2122
+ return message;
2123
+ const filtered = message.parts.filter((part) => !(part.ephemeral && part.toolCallId === callId));
2124
+ if (filtered.length === message.parts.length)
2125
+ return message;
2126
+ changed = true;
2127
+ return { ...message, parts: filtered };
2128
+ });
2129
+ return changed ? nextMessages : oldMessages;
2130
+ });
2131
+ };
2132
+ const clearEphemeralForMessage = (messageId) => {
2133
+ if (!messageId)
2134
+ return;
2135
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
2136
+ if (!oldMessages)
2137
+ return oldMessages;
2138
+ const targetIndex = oldMessages.findIndex((message) => message.id === messageId);
2139
+ if (targetIndex === -1)
2140
+ return oldMessages;
2141
+ const target = oldMessages[targetIndex];
2142
+ if (!target.parts?.some((part) => part.ephemeral && part.type === "tool_call"))
2143
+ return oldMessages;
2144
+ const nextMessages = [...oldMessages];
2145
+ nextMessages[targetIndex] = {
2146
+ ...target,
2147
+ parts: target.parts?.filter((part) => !(part.ephemeral && part.type === "tool_call")) ?? []
2148
+ };
2149
+ return nextMessages;
2150
+ });
2151
+ };
2152
+ const markMessageCompleted = (payload) => {
2153
+ const id = typeof payload?.id === "string" ? payload.id : null;
2154
+ if (!id)
2155
+ return;
2156
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
2157
+ if (!oldMessages)
2158
+ return oldMessages;
2159
+ const nextMessages = [...oldMessages];
2160
+ const messageIndex = nextMessages.findIndex((message) => message.id === id);
2161
+ if (messageIndex === -1)
2162
+ return oldMessages;
2163
+ const existing = nextMessages[messageIndex];
2164
+ nextMessages[messageIndex] = {
2165
+ ...existing,
2166
+ status: "complete",
2167
+ completedAt: Date.now()
2168
+ };
2169
+ return nextMessages;
2170
+ });
2171
+ };
2172
+ const throttledInvalidate = () => {
2173
+ const now = Date.now();
2174
+ if (now - lastInvalidationRef.current < 500) {
2175
+ return;
2176
+ }
2177
+ lastInvalidationRef.current = now;
2178
+ queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
2179
+ };
2180
+ const invalidatingEvents = new Set([
2181
+ "message.completed",
2182
+ "message.updated",
2183
+ "finish-step",
2184
+ "error"
2185
+ ]);
2186
+ const unsubscribe = client2.on("*", (event) => {
2187
+ const payload = event.payload;
2188
+ switch (event.type) {
2189
+ case "message.created": {
2190
+ const role = typeof payload?.role === "string" ? payload.role : null;
2191
+ const id = typeof payload?.id === "string" ? payload.id : null;
2192
+ if (role === "assistant" && id) {
2193
+ assistantMessageIdRef.current = id;
2194
+ }
2195
+ if (id && role) {
2196
+ const agent = typeof payload?.agent === "string" ? payload.agent : "";
2197
+ const provider = typeof payload?.provider === "string" ? payload.provider : "";
2198
+ const model = typeof payload?.model === "string" ? payload.model : "";
2199
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
2200
+ if (!oldMessages)
2201
+ return oldMessages;
2202
+ if (oldMessages.some((m) => m.id === id))
2203
+ return oldMessages;
2204
+ const newMessage = {
2205
+ id,
2206
+ sessionId,
2207
+ role,
2208
+ status: "pending",
2209
+ agent,
2210
+ provider,
2211
+ model,
2212
+ createdAt: Date.now(),
2213
+ completedAt: null,
2214
+ latencyMs: null,
2215
+ promptTokens: null,
2216
+ completionTokens: null,
2217
+ totalTokens: null,
2218
+ error: null,
2219
+ parts: []
2220
+ };
2221
+ const next = [...oldMessages, newMessage];
2222
+ next.sort((a, b) => a.createdAt - b.createdAt);
2223
+ return next;
2224
+ });
2225
+ throttledInvalidate();
2226
+ }
2227
+ break;
2228
+ }
2229
+ case "message.part.delta": {
2230
+ applyMessageDelta(payload);
2231
+ break;
2232
+ }
2233
+ case "reasoning.delta": {
2234
+ applyReasoningDelta(payload);
2235
+ break;
2236
+ }
2237
+ case "message.completed": {
2238
+ const id = typeof payload?.id === "string" ? payload.id : null;
2239
+ if (id && assistantMessageIdRef.current === id) {
2240
+ assistantMessageIdRef.current = null;
2241
+ }
2242
+ markMessageCompleted(payload);
2243
+ clearEphemeralForMessage(id);
2244
+ queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
2245
+ break;
2246
+ }
2247
+ case "tool.delta": {
2248
+ const channel = typeof payload?.channel === "string" ? payload.channel : null;
2249
+ if (channel === "input") {
2250
+ upsertEphemeralToolCall(payload);
2251
+ }
2252
+ break;
2253
+ }
2254
+ case "tool.call": {
2255
+ upsertEphemeralToolCall(payload);
2256
+ break;
2257
+ }
2258
+ case "tool.result": {
2259
+ resolveEphemeralToolCall(payload);
2260
+ break;
2261
+ }
2262
+ case "tool.approval.required": {
2263
+ const callId = typeof payload?.callId === "string" ? payload.callId : null;
2264
+ const toolName = typeof payload?.toolName === "string" ? payload.toolName : null;
2265
+ const messageId = typeof payload?.messageId === "string" ? payload.messageId : null;
2266
+ const args = payload?.args;
2267
+ if (callId && toolName && messageId) {
2268
+ addPendingApproval({
2269
+ callId,
2270
+ toolName,
2271
+ args,
2272
+ messageId,
2273
+ createdAt: Date.now()
2274
+ });
2275
+ }
2276
+ break;
2277
+ }
2278
+ case "tool.approval.resolved": {
2279
+ const callId = typeof payload?.callId === "string" ? payload.callId : null;
2280
+ if (callId) {
2281
+ removePendingApproval(callId);
2282
+ }
2283
+ break;
2284
+ }
2285
+ case "tool.approval.updated": {
2286
+ const callId = typeof payload?.callId === "string" ? payload.callId : null;
2287
+ const args = payload?.args;
2288
+ if (callId) {
2289
+ updatePendingApproval(callId, args);
2290
+ }
2291
+ break;
2292
+ }
2293
+ case "error": {
2294
+ removeEphemeralToolCall(payload);
2295
+ const messageId = typeof payload?.messageId === "string" ? payload.messageId : null;
2296
+ if (messageId) {
2297
+ clearEphemeralForMessage(messageId);
2298
+ }
2299
+ break;
2300
+ }
2301
+ case "message.updated": {
2302
+ const id = typeof payload?.id === "string" ? payload.id : null;
2303
+ const status = typeof payload?.status === "string" ? payload.status : null;
2304
+ if (id && status) {
2305
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
2306
+ if (!oldMessages)
2307
+ return oldMessages;
2308
+ const idx = oldMessages.findIndex((m) => m.id === id);
2309
+ if (idx === -1)
2310
+ return oldMessages;
2311
+ const next = [...oldMessages];
2312
+ next[idx] = {
2313
+ ...next[idx],
2314
+ status
2315
+ };
2316
+ return next;
2317
+ });
2318
+ }
2319
+ break;
2320
+ }
2321
+ case "queue.updated": {
2322
+ const queueState = {
2323
+ currentMessageId: payload?.currentMessageId,
2324
+ queuedMessages: payload?.queuedMessages ?? [],
2325
+ queueLength: payload?.queueLength ?? 0
2326
+ };
2327
+ queryClient.setQueryData(["queueState", sessionId], queueState);
2328
+ break;
2329
+ }
2330
+ default:
2331
+ break;
2332
+ }
2333
+ if (invalidatingEvents.has(event.type)) {
2334
+ throttledInvalidate();
2335
+ }
2336
+ if (event.type === "finish-step") {
2337
+ const now = Date.now();
2338
+ if (now - lastSessionInvalidation >= 2000) {
2339
+ lastSessionInvalidation = now;
2340
+ queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
2341
+ }
2342
+ }
2343
+ });
2344
+ return () => {
2345
+ unsubscribe();
2346
+ client2.disconnect();
2347
+ };
2348
+ }, [
2349
+ sessionId,
2350
+ queryClient,
2351
+ addPendingApproval,
2352
+ removePendingApproval,
2353
+ setPendingApprovals,
2354
+ updatePendingApproval
2355
+ ]);
2356
+ }
2357
+ // src/hooks/useTheme.ts
2358
+ import { useEffect as useEffect2, useState, useCallback as useCallback2, useMemo as useMemo3 } from "react";
2359
+ var STORAGE_KEY2 = "otto-theme";
2360
+ function resolveInitialTheme() {
2361
+ if (typeof window === "undefined") {
2362
+ return "dark";
2363
+ }
2364
+ const stored = window.localStorage.getItem(STORAGE_KEY2);
2365
+ if (stored === "light" || stored === "dark") {
2366
+ return stored;
2367
+ }
2368
+ if (window.matchMedia?.("(prefers-color-scheme: light)").matches) {
2369
+ return "light";
2370
+ }
2371
+ return "dark";
2372
+ }
2373
+ function useTheme() {
2374
+ const [theme, setTheme] = useState(() => resolveInitialTheme());
2375
+ useEffect2(() => {
2376
+ if (typeof document === "undefined")
2377
+ return;
2378
+ const root = document.documentElement;
2379
+ if (theme === "dark") {
2380
+ root.classList.add("dark");
2381
+ } else {
2382
+ root.classList.remove("dark");
2383
+ }
2384
+ try {
2385
+ window.localStorage.setItem(STORAGE_KEY2, theme);
2386
+ } catch (error) {
2387
+ console.warn("Failed to persist theme preference", error);
2388
+ }
2389
+ if (window.parent && window.parent !== window) {
2390
+ window.parent.postMessage({ type: "otto-set-theme", theme }, "*");
2391
+ }
2392
+ }, [theme]);
2393
+ useEffect2(() => {
2394
+ if (typeof window === "undefined")
2395
+ return;
2396
+ const handler = (e) => {
2397
+ if (e.data?.type === "otto-set-theme" && (e.data.theme === "light" || e.data.theme === "dark")) {
2398
+ setTheme(e.data.theme);
2399
+ }
2400
+ };
2401
+ window.addEventListener("message", handler);
2402
+ return () => window.removeEventListener("message", handler);
2403
+ }, []);
2404
+ const toggleTheme = useCallback2(() => {
2405
+ setTheme((prev) => prev === "dark" ? "light" : "dark");
2406
+ }, []);
2407
+ return useMemo3(() => ({ theme, setTheme, toggleTheme }), [theme, toggleTheme]);
2408
+ }
2409
+ // src/hooks/useWorkingDirectory.ts
2410
+ import { useEffect as useEffect3, useState as useState2 } from "react";
2411
+ function useWorkingDirectory() {
2412
+ const [dirName, setDirName] = useState2(null);
2413
+ useEffect3(() => {
2414
+ const fetchWorkingDirectory = async () => {
2415
+ try {
2416
+ const win = window;
2417
+ const baseUrl = win.OTTO_SERVER_URL || API_BASE_URL;
2418
+ const url = `${baseUrl}/v1/config/cwd`;
2419
+ console.log("[useWorkingDirectory] Fetching from:", url);
2420
+ const response = await fetch(url);
2421
+ if (!response.ok) {
2422
+ console.error("[useWorkingDirectory] Failed:", response.status, response.statusText);
2423
+ throw new Error(`Failed to fetch working directory: ${response.status}`);
2424
+ }
2425
+ const data = await response.json();
2426
+ console.log("[useWorkingDirectory] Success:", data);
2427
+ if (data.dirName) {
2428
+ console.log("[useWorkingDirectory] Setting title to:", data.dirName);
2429
+ setDirName(data.dirName);
2430
+ document.title = data.dirName;
2431
+ }
2432
+ } catch (error) {
2433
+ console.error("[useWorkingDirectory] Error:", error);
2434
+ document.title = "otto";
2435
+ }
2436
+ };
2437
+ fetchWorkingDirectory();
2438
+ }, []);
2439
+ return dirName;
2440
+ }
2441
+ // src/hooks/useKeyboardShortcuts.ts
2442
+ import { useEffect as useEffect4, useCallback as useCallback3 } from "react";
2443
+
2444
+ // src/stores/focusStore.ts
2445
+ import { create as create10 } from "zustand";
2446
+ var useFocusStore = create10((set) => ({
2447
+ currentFocus: null,
2448
+ sessionIndex: 0,
2449
+ gitFileIndex: 0,
2450
+ setFocus: (area) => set({ currentFocus: area }),
2451
+ setSessionIndex: (index) => set({ sessionIndex: index }),
2452
+ setGitFileIndex: (index) => set({ gitFileIndex: index }),
2453
+ resetGitFileIndex: () => set({ gitFileIndex: 0 }),
2454
+ resetSessionIndex: () => set({ sessionIndex: 0 })
2455
+ }));
2456
+
2457
+ // src/stores/sidebarStore.ts
2458
+ import { create as create11 } from "zustand";
2459
+ import { persist } from "zustand/middleware";
2460
+ var useSidebarStore = create11()(persist((set) => ({
2461
+ isCollapsed: false,
2462
+ toggleCollapse: () => set((state) => ({ isCollapsed: !state.isCollapsed })),
2463
+ setCollapsed: (collapsed) => set({ isCollapsed: collapsed })
2464
+ }), {
2465
+ name: "sidebar-storage"
2466
+ }));
2467
+
2468
+ // src/stores/filePickerStore.ts
2469
+ import { create as create12 } from "zustand";
2470
+ var useFilePickerStore = create12((set) => ({
2471
+ isOpen: false,
2472
+ open: () => set({ isOpen: true }),
2473
+ close: () => set({ isOpen: false }),
2474
+ toggle: () => set((state) => ({ isOpen: !state.isOpen }))
2475
+ }));
2476
+
2477
+ // src/hooks/useKeyboardShortcuts.ts
2478
+ function useKeyboardShortcuts({
2479
+ sessionIds,
2480
+ activeSessionId,
2481
+ gitFiles,
2482
+ onSelectSession,
2483
+ onNewSession,
2484
+ onStageFile,
2485
+ onUnstageFile,
2486
+ onRestoreFile,
2487
+ onDeleteFile,
2488
+ onStageAll,
2489
+ onUnstageAll,
2490
+ onOpenCommitModal,
2491
+ onViewDiff,
2492
+ onReturnToInput
2493
+ }) {
2494
+ const {
2495
+ currentFocus,
2496
+ sessionIndex,
2497
+ gitFileIndex,
2498
+ setFocus,
2499
+ setSessionIndex,
2500
+ setGitFileIndex,
2501
+ resetGitFileIndex
2502
+ } = useFocusStore();
2503
+ const {
2504
+ setCollapsed: setSessionListCollapsed,
2505
+ toggleCollapse: toggleSessionList
2506
+ } = useSidebarStore();
2507
+ const { isExpanded: isGitExpanded, toggleSidebar: toggleGit } = useGitStore();
2508
+ const closeDiff = useGitStore((state) => state.closeDiff);
2509
+ const toggleResearch = useResearchStore((state) => state.toggleSidebar);
2510
+ const currentSessionIndex = sessionIds.indexOf(activeSessionId || "");
2511
+ const handleKeyDown = useCallback3((e) => {
2512
+ const target = e.target;
2513
+ const isInInput = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
2514
+ const isInTerminal = !!target.closest("[data-terminal-viewer]");
2515
+ if ((e.ctrlKey || e.metaKey) && e.key === "h") {
2516
+ e.preventDefault();
2517
+ if (currentFocus === "sessions") {
2518
+ document.activeElement?.blur();
2519
+ setFocus("input");
2520
+ setSessionListCollapsed(true);
2521
+ setTimeout(() => onReturnToInput?.(), 50);
2522
+ } else if (currentFocus === "git") {
2523
+ document.activeElement?.blur();
2524
+ setFocus("input");
2525
+ toggleGit();
2526
+ closeDiff();
2527
+ setTimeout(() => onReturnToInput?.(), 50);
2528
+ } else {
2529
+ document.activeElement?.blur();
2530
+ setFocus("sessions");
2531
+ setSessionListCollapsed(false);
2532
+ if (currentSessionIndex >= 0) {
2533
+ setSessionIndex(currentSessionIndex);
2534
+ }
2535
+ }
2536
+ return;
2537
+ }
2538
+ if ((e.ctrlKey || e.metaKey) && e.key === "l") {
2539
+ e.preventDefault();
2540
+ if (currentFocus === "git") {
2541
+ document.activeElement?.blur();
2542
+ setFocus("input");
2543
+ toggleGit();
2544
+ closeDiff();
2545
+ setTimeout(() => onReturnToInput?.(), 50);
2546
+ } else if (currentFocus === "sessions") {
2547
+ document.activeElement?.blur();
2548
+ setFocus("input");
2549
+ setSessionListCollapsed(true);
2550
+ setTimeout(() => onReturnToInput?.(), 50);
2551
+ } else {
2552
+ document.activeElement?.blur();
2553
+ if (!isGitExpanded) {
2554
+ toggleGit();
2555
+ }
2556
+ setFocus("git");
2557
+ resetGitFileIndex();
2558
+ }
2559
+ return;
2560
+ }
2561
+ if ((e.ctrlKey || e.metaKey) && e.key === "/") {
2562
+ e.preventDefault();
2563
+ toggleSessionList();
2564
+ return;
2565
+ }
2566
+ if ((e.ctrlKey || e.metaKey) && e.key === "\\") {
2567
+ e.preventDefault();
2568
+ toggleGit();
2569
+ return;
2570
+ }
2571
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "r") {
2572
+ e.preventDefault();
2573
+ toggleResearch();
2574
+ return;
2575
+ }
2576
+ if ((e.ctrlKey || e.metaKey) && e.key === "n") {
2577
+ e.preventDefault();
2578
+ onNewSession();
2579
+ return;
2580
+ }
2581
+ if ((e.ctrlKey || e.metaKey) && e.key === "p") {
2582
+ e.preventDefault();
2583
+ useFilePickerStore.getState().toggle();
2584
+ return;
2585
+ }
2586
+ if (e.key === "Escape" && !isInTerminal || e.key === "q" && !isInInput && (currentFocus === "sessions" || currentFocus === "git")) {
2587
+ e.preventDefault();
2588
+ if (currentFocus === "sessions") {
2589
+ setSessionListCollapsed(true);
2590
+ } else if (currentFocus === "git") {
2591
+ toggleGit();
2592
+ closeDiff();
2593
+ }
2594
+ setFocus("input");
2595
+ onReturnToInput?.();
2596
+ return;
2597
+ }
2598
+ if (currentFocus === "sessions" && !isInInput) {
2599
+ if (e.key === "j" && sessionIds.length > 0) {
2600
+ e.preventDefault();
2601
+ const nextIndex = Math.min(sessionIndex + 1, sessionIds.length - 1);
2602
+ setSessionIndex(nextIndex);
2603
+ return;
2604
+ }
2605
+ if (e.key === "k" && sessionIds.length > 0) {
2606
+ e.preventDefault();
2607
+ const prevIndex = Math.max(sessionIndex - 1, 0);
2608
+ setSessionIndex(prevIndex);
2609
+ return;
2610
+ }
2611
+ if (e.key === "Enter" && sessionIds[sessionIndex]) {
2612
+ e.preventDefault();
2613
+ onSelectSession(sessionIds[sessionIndex]);
2614
+ setFocus("input");
2615
+ return;
2616
+ }
2617
+ }
2618
+ if (currentFocus === "git" && !isInInput) {
2619
+ if (e.key === "j" && gitFiles.length > 0) {
2620
+ e.preventDefault();
2621
+ const nextIndex = Math.min(gitFileIndex + 1, gitFiles.length - 1);
2622
+ setGitFileIndex(nextIndex);
2623
+ return;
2624
+ }
2625
+ if (e.key === "k" && gitFiles.length > 0) {
2626
+ e.preventDefault();
2627
+ const prevIndex = Math.max(gitFileIndex - 1, 0);
2628
+ setGitFileIndex(prevIndex);
2629
+ return;
2630
+ }
2631
+ if (e.key === " " && gitFiles[gitFileIndex]) {
2632
+ e.preventDefault();
2633
+ const file = gitFiles[gitFileIndex];
2634
+ if (file.staged) {
2635
+ onUnstageFile?.(file.path);
2636
+ } else {
2637
+ onStageFile?.(file.path);
2638
+ }
2639
+ return;
2640
+ }
2641
+ if (e.key === "a") {
2642
+ e.preventDefault();
2643
+ onStageAll?.();
2644
+ return;
2645
+ }
2646
+ if (e.key === "u") {
2647
+ e.preventDefault();
2648
+ onUnstageAll?.();
2649
+ return;
2650
+ }
2651
+ if (e.key === "R" && gitFiles[gitFileIndex]) {
2652
+ e.preventDefault();
2653
+ const file = gitFiles[gitFileIndex];
2654
+ const canRestore = !file.staged && file.status !== "untracked" && file.status !== "added";
2655
+ if (canRestore) {
2656
+ onRestoreFile?.(file.path);
2657
+ }
2658
+ return;
2659
+ }
2660
+ if (e.shiftKey && e.key === "D" || e.key === "Backspace") {
2661
+ e.preventDefault();
2662
+ const file = gitFiles[gitFileIndex];
2663
+ if (file && !file.staged && file.status === "untracked") {
2664
+ onDeleteFile?.(file.path);
2665
+ }
2666
+ return;
2667
+ }
2668
+ if (e.key === "c") {
2669
+ e.preventDefault();
2670
+ onOpenCommitModal?.();
2671
+ return;
2672
+ }
2673
+ if (e.key === "Enter" && gitFiles[gitFileIndex]) {
2674
+ e.preventDefault();
2675
+ const file = gitFiles[gitFileIndex];
2676
+ onViewDiff?.(file.path, file.staged);
2677
+ return;
2678
+ }
2679
+ }
2680
+ }, [
2681
+ currentFocus,
2682
+ sessionIndex,
2683
+ gitFileIndex,
2684
+ sessionIds,
2685
+ gitFiles,
2686
+ currentSessionIndex,
2687
+ isGitExpanded,
2688
+ setFocus,
2689
+ setSessionIndex,
2690
+ setGitFileIndex,
2691
+ resetGitFileIndex,
2692
+ setSessionListCollapsed,
2693
+ toggleGit,
2694
+ toggleResearch,
2695
+ toggleSessionList,
2696
+ onSelectSession,
2697
+ onNewSession,
2698
+ onStageFile,
2699
+ onUnstageFile,
2700
+ onRestoreFile,
2701
+ onDeleteFile,
2702
+ onStageAll,
2703
+ onUnstageAll,
2704
+ onOpenCommitModal,
2705
+ onViewDiff,
2706
+ onReturnToInput,
2707
+ closeDiff
2708
+ ]);
2709
+ useEffect4(() => {
2710
+ window.addEventListener("keydown", handleKeyDown);
2711
+ return () => window.removeEventListener("keydown", handleKeyDown);
2712
+ }, [handleKeyDown]);
2713
+ return {
2714
+ currentFocus,
2715
+ sessionIndex,
2716
+ gitFileIndex
2717
+ };
2718
+ }
2719
+ // src/hooks/useImageUpload.ts
2720
+ import {
2721
+ useState as useState3,
2722
+ useCallback as useCallback4,
2723
+ useEffect as useEffect5
2724
+ } from "react";
2725
+ var SUPPORTED_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
2726
+ function generateId() {
2727
+ return `img-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
2728
+ }
2729
+ async function fileToBase64(file) {
2730
+ return new Promise((resolve, reject) => {
2731
+ const reader = new FileReader;
2732
+ reader.onload = () => {
2733
+ const result = reader.result;
2734
+ const base64 = result.split(",")[1];
2735
+ resolve(base64);
2736
+ };
2737
+ reader.onerror = reject;
2738
+ reader.readAsDataURL(file);
2739
+ });
2740
+ }
2741
+ async function fileToPreview(file) {
2742
+ return new Promise((resolve, reject) => {
2743
+ const reader = new FileReader;
2744
+ reader.onload = () => resolve(reader.result);
2745
+ reader.onerror = reject;
2746
+ reader.readAsDataURL(file);
2747
+ });
2748
+ }
2749
+ function useImageUpload(options = {}) {
2750
+ const { maxImages = 5, maxSizeMB = 5, pageWide = true } = options;
2751
+ const [images, setImages] = useState3([]);
2752
+ const [isDragging, setIsDragging] = useState3(false);
2753
+ const [error, setError] = useState3(null);
2754
+ const maxSizeBytes = maxSizeMB * 1024 * 1024;
2755
+ const validateFile = useCallback4((file) => {
2756
+ if (!SUPPORTED_TYPES.includes(file.type)) {
2757
+ return `Unsupported file type: ${file.type}. Supported: PNG, JPEG, GIF, WebP`;
2758
+ }
2759
+ if (file.size > maxSizeBytes) {
2760
+ return `File too large: ${(file.size / 1024 / 1024).toFixed(1)}MB. Max: ${maxSizeMB}MB`;
2761
+ }
2762
+ return null;
2763
+ }, [maxSizeBytes, maxSizeMB]);
2764
+ const addImages = useCallback4(async (files) => {
2765
+ setError(null);
2766
+ const fileArray = Array.from(files);
2767
+ const remaining = maxImages - images.length;
2768
+ if (remaining <= 0) {
2769
+ setError(`Maximum ${maxImages} images allowed`);
2770
+ return;
2771
+ }
2772
+ const filesToAdd = fileArray.slice(0, remaining);
2773
+ const newImages = [];
2774
+ for (const file of filesToAdd) {
2775
+ const validationError = validateFile(file);
2776
+ if (validationError) {
2777
+ setError(validationError);
2778
+ continue;
2779
+ }
2780
+ try {
2781
+ const [preview, data] = await Promise.all([
2782
+ fileToPreview(file),
2783
+ fileToBase64(file)
2784
+ ]);
2785
+ newImages.push({
2786
+ id: generateId(),
2787
+ file,
2788
+ preview,
2789
+ data,
2790
+ mediaType: file.type
2791
+ });
2792
+ } catch {
2793
+ setError("Failed to process image");
2794
+ }
2795
+ }
2796
+ if (newImages.length > 0) {
2797
+ setImages((prev) => [...prev, ...newImages]);
2798
+ }
2799
+ }, [images.length, maxImages, validateFile]);
2800
+ const removeImage = useCallback4((id) => {
2801
+ setImages((prev) => prev.filter((img) => img.id !== id));
2802
+ setError(null);
2803
+ }, []);
2804
+ const clearImages = useCallback4(() => {
2805
+ setImages([]);
2806
+ setError(null);
2807
+ }, []);
2808
+ const handleDragEnter = useCallback4((e) => {
2809
+ e.preventDefault();
2810
+ e.stopPropagation();
2811
+ if (e.dataTransfer.types.includes("Files")) {
2812
+ setIsDragging(true);
2813
+ }
2814
+ }, []);
2815
+ const handleDragLeave = useCallback4((e) => {
2816
+ e.preventDefault();
2817
+ e.stopPropagation();
2818
+ const rect = e.currentTarget.getBoundingClientRect();
2819
+ const x = e.clientX;
2820
+ const y = e.clientY;
2821
+ if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
2822
+ setIsDragging(false);
2823
+ }
2824
+ }, []);
2825
+ const handleDragOver = useCallback4((e) => {
2826
+ e.preventDefault();
2827
+ e.stopPropagation();
2828
+ }, []);
2829
+ const handleDrop = useCallback4((e) => {
2830
+ e.preventDefault();
2831
+ e.stopPropagation();
2832
+ setIsDragging(false);
2833
+ const files = e.dataTransfer.files;
2834
+ if (files.length > 0) {
2835
+ const imageFiles = Array.from(files).filter((f) => f.type.startsWith("image/"));
2836
+ if (imageFiles.length > 0) {
2837
+ addImages(imageFiles);
2838
+ }
2839
+ }
2840
+ }, [addImages]);
2841
+ const handlePaste = useCallback4((e) => {
2842
+ const items = e.clipboardData?.items;
2843
+ if (!items)
2844
+ return;
2845
+ const imageFiles = [];
2846
+ for (const item of Array.from(items)) {
2847
+ if (item.type.startsWith("image/")) {
2848
+ const file = item.getAsFile();
2849
+ if (file) {
2850
+ imageFiles.push(file);
2851
+ }
2852
+ }
2853
+ }
2854
+ if (imageFiles.length > 0) {
2855
+ e.preventDefault();
2856
+ addImages(imageFiles);
2857
+ }
2858
+ }, [addImages]);
2859
+ useEffect5(() => {
2860
+ if (!pageWide)
2861
+ return;
2862
+ let dragCounter = 0;
2863
+ const onDragEnter = (e) => {
2864
+ e.preventDefault();
2865
+ if (e.dataTransfer?.types.includes("Files")) {
2866
+ dragCounter++;
2867
+ if (dragCounter === 1) {
2868
+ setIsDragging(true);
2869
+ }
2870
+ }
2871
+ };
2872
+ const onDragLeave = (e) => {
2873
+ e.preventDefault();
2874
+ dragCounter--;
2875
+ if (dragCounter === 0) {
2876
+ setIsDragging(false);
2877
+ }
2878
+ };
2879
+ const onDragOver = (e) => {
2880
+ e.preventDefault();
2881
+ };
2882
+ const onDrop = (e) => {
2883
+ e.preventDefault();
2884
+ dragCounter = 0;
2885
+ setIsDragging(false);
2886
+ const files = e.dataTransfer?.files;
2887
+ if (files && files.length > 0) {
2888
+ const imageFiles = Array.from(files).filter((f) => f.type.startsWith("image/"));
2889
+ if (imageFiles.length > 0) {
2890
+ addImages(imageFiles);
2891
+ }
2892
+ }
2893
+ };
2894
+ document.addEventListener("dragenter", onDragEnter);
2895
+ document.addEventListener("dragleave", onDragLeave);
2896
+ document.addEventListener("dragover", onDragOver);
2897
+ document.addEventListener("drop", onDrop);
2898
+ return () => {
2899
+ document.removeEventListener("dragenter", onDragEnter);
2900
+ document.removeEventListener("dragleave", onDragLeave);
2901
+ document.removeEventListener("dragover", onDragOver);
2902
+ document.removeEventListener("drop", onDrop);
2903
+ };
2904
+ }, [pageWide, addImages]);
2905
+ return {
2906
+ images,
2907
+ isDragging,
2908
+ error,
2909
+ addImages,
2910
+ removeImage,
2911
+ clearImages,
2912
+ handleDragEnter,
2913
+ handleDragLeave,
2914
+ handleDragOver,
2915
+ handleDrop,
2916
+ handlePaste
2917
+ };
2918
+ }
2919
+ // src/hooks/useFileUpload.ts
2920
+ import {
2921
+ useState as useState4,
2922
+ useCallback as useCallback5,
2923
+ useEffect as useEffect6
2924
+ } from "react";
2925
+ var IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
2926
+ var PDF_TYPES = ["application/pdf"];
2927
+ var TEXT_TYPES = [
2928
+ "text/plain",
2929
+ "text/markdown",
2930
+ "text/x-markdown",
2931
+ "application/json",
2932
+ "text/csv",
2933
+ "text/xml",
2934
+ "application/xml",
2935
+ "text/yaml",
2936
+ "text/x-yaml",
2937
+ "application/x-yaml",
2938
+ "text/html",
2939
+ "text/css",
2940
+ "text/javascript",
2941
+ "application/javascript",
2942
+ "application/typescript"
2943
+ ];
2944
+ var TEXT_EXTENSIONS = [
2945
+ ".txt",
2946
+ ".md",
2947
+ ".markdown",
2948
+ ".json",
2949
+ ".csv",
2950
+ ".xml",
2951
+ ".yaml",
2952
+ ".yml",
2953
+ ".html",
2954
+ ".css",
2955
+ ".js",
2956
+ ".ts",
2957
+ ".jsx",
2958
+ ".tsx",
2959
+ ".py",
2960
+ ".rs",
2961
+ ".go",
2962
+ ".java",
2963
+ ".c",
2964
+ ".cpp",
2965
+ ".h",
2966
+ ".hpp",
2967
+ ".rb",
2968
+ ".php",
2969
+ ".sh",
2970
+ ".bash",
2971
+ ".zsh",
2972
+ ".toml",
2973
+ ".ini",
2974
+ ".cfg",
2975
+ ".env",
2976
+ ".log",
2977
+ ".sql",
2978
+ ".graphql",
2979
+ ".svelte",
2980
+ ".vue"
2981
+ ];
2982
+ var SUPPORTED_TYPES2 = [...IMAGE_TYPES, ...PDF_TYPES, ...TEXT_TYPES];
2983
+ function generateId2() {
2984
+ return `file-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
2985
+ }
2986
+ function getFileType(file) {
2987
+ if (IMAGE_TYPES.includes(file.type))
2988
+ return "image";
2989
+ if (PDF_TYPES.includes(file.type))
2990
+ return "pdf";
2991
+ if (TEXT_TYPES.includes(file.type))
2992
+ return "text";
2993
+ const ext = file.name.toLowerCase().slice(file.name.lastIndexOf("."));
2994
+ if (TEXT_EXTENSIONS.includes(ext))
2995
+ return "text";
2996
+ return null;
2997
+ }
2998
+ function isSupported(file) {
2999
+ if (SUPPORTED_TYPES2.includes(file.type))
3000
+ return true;
3001
+ const ext = file.name.toLowerCase().slice(file.name.lastIndexOf("."));
3002
+ return TEXT_EXTENSIONS.includes(ext);
3003
+ }
3004
+ async function fileToBase642(file) {
3005
+ return new Promise((resolve, reject) => {
3006
+ const reader = new FileReader;
3007
+ reader.onload = () => {
3008
+ const result = reader.result;
3009
+ const base64 = result.split(",")[1];
3010
+ resolve(base64);
3011
+ };
3012
+ reader.onerror = reject;
3013
+ reader.readAsDataURL(file);
3014
+ });
3015
+ }
3016
+ async function fileToText(file) {
3017
+ return new Promise((resolve, reject) => {
3018
+ const reader = new FileReader;
3019
+ reader.onload = () => resolve(reader.result);
3020
+ reader.onerror = reject;
3021
+ reader.readAsText(file);
3022
+ });
3023
+ }
3024
+ async function fileToPreview2(file) {
3025
+ return new Promise((resolve, reject) => {
3026
+ const reader = new FileReader;
3027
+ reader.onload = () => resolve(reader.result);
3028
+ reader.onerror = reject;
3029
+ reader.readAsDataURL(file);
3030
+ });
3031
+ }
3032
+ function useFileUpload(options = {}) {
3033
+ const {
3034
+ maxFiles = 10,
3035
+ maxSizeMB = 10,
3036
+ pageWide = true,
3037
+ supportsImages = true,
3038
+ supportsFileAttachments = true,
3039
+ onError
3040
+ } = options;
3041
+ const [files, setFiles] = useState4([]);
3042
+ const [isDragging, setIsDragging] = useState4(false);
3043
+ const [error, setError] = useState4(null);
3044
+ const maxSizeBytes = maxSizeMB * 1024 * 1024;
3045
+ const validateFile = useCallback5((file) => {
3046
+ if (!supportsImages && IMAGE_TYPES.includes(file.type)) {
3047
+ return "This model does not support image attachments";
3048
+ }
3049
+ if (!supportsFileAttachments && PDF_TYPES.includes(file.type)) {
3050
+ return "This model does not support PDF attachments";
3051
+ }
3052
+ if (!isSupported(file)) {
3053
+ const ext = file.name.slice(file.name.lastIndexOf("."));
3054
+ return `Unsupported file type: ${ext || file.type || "unknown"}`;
3055
+ }
3056
+ if (file.size > maxSizeBytes) {
3057
+ return `File too large: ${(file.size / 1024 / 1024).toFixed(1)}MB. Max: ${maxSizeMB}MB`;
3058
+ }
3059
+ return null;
3060
+ }, [maxSizeBytes, maxSizeMB, supportsImages, supportsFileAttachments]);
3061
+ const addFiles = useCallback5(async (inputFiles) => {
3062
+ setError(null);
3063
+ const fileArray = Array.from(inputFiles);
3064
+ const remaining = maxFiles - files.length;
3065
+ if (remaining <= 0) {
3066
+ const msg = `Maximum ${maxFiles} files allowed`;
3067
+ setError(msg);
3068
+ onError?.(msg);
3069
+ return;
3070
+ }
3071
+ const filesToAdd = fileArray.slice(0, remaining);
3072
+ const newFiles = [];
3073
+ for (const file of filesToAdd) {
3074
+ const validationError = validateFile(file);
3075
+ if (validationError) {
3076
+ setError(validationError);
3077
+ onError?.(validationError);
3078
+ continue;
3079
+ }
3080
+ const fileType = getFileType(file);
3081
+ if (!fileType)
3082
+ continue;
3083
+ try {
3084
+ let preview;
3085
+ let data;
3086
+ let textContent;
3087
+ let mediaType = file.type;
3088
+ if (fileType === "image") {
3089
+ [preview, data] = await Promise.all([
3090
+ fileToPreview2(file),
3091
+ fileToBase642(file)
3092
+ ]);
3093
+ } else if (fileType === "pdf") {
3094
+ data = await fileToBase642(file);
3095
+ mediaType = "application/pdf";
3096
+ } else {
3097
+ textContent = await fileToText(file);
3098
+ data = textContent;
3099
+ if (!mediaType) {
3100
+ const ext = file.name.toLowerCase();
3101
+ mediaType = ext.endsWith(".md") || ext.endsWith(".markdown") ? "text/markdown" : "text/plain";
3102
+ }
3103
+ }
3104
+ newFiles.push({
3105
+ id: generateId2(),
3106
+ file,
3107
+ type: fileType,
3108
+ name: file.name,
3109
+ preview,
3110
+ data,
3111
+ mediaType,
3112
+ textContent
3113
+ });
3114
+ } catch {
3115
+ const msg = `Failed to process file: ${file.name}`;
3116
+ setError(msg);
3117
+ onError?.(msg);
3118
+ }
3119
+ }
3120
+ if (newFiles.length > 0) {
3121
+ setFiles((prev) => [...prev, ...newFiles]);
3122
+ }
3123
+ }, [files.length, maxFiles, validateFile, onError]);
3124
+ const removeFile = useCallback5((id) => {
3125
+ setFiles((prev) => prev.filter((f) => f.id !== id));
3126
+ setError(null);
3127
+ }, []);
3128
+ const clearFiles = useCallback5(() => {
3129
+ setFiles([]);
3130
+ setError(null);
3131
+ }, []);
3132
+ const handleDragEnter = useCallback5((e) => {
3133
+ e.preventDefault();
3134
+ e.stopPropagation();
3135
+ if (e.dataTransfer.types.includes("Files")) {
3136
+ setIsDragging(true);
3137
+ }
3138
+ }, []);
3139
+ const handleDragLeave = useCallback5((e) => {
3140
+ e.preventDefault();
3141
+ e.stopPropagation();
3142
+ const rect = e.currentTarget.getBoundingClientRect();
3143
+ const x = e.clientX;
3144
+ const y = e.clientY;
3145
+ if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
3146
+ setIsDragging(false);
3147
+ }
3148
+ }, []);
3149
+ const handleDragOver = useCallback5((e) => {
3150
+ e.preventDefault();
3151
+ e.stopPropagation();
3152
+ }, []);
3153
+ const handleDrop = useCallback5((e) => {
3154
+ e.preventDefault();
3155
+ e.stopPropagation();
3156
+ setIsDragging(false);
3157
+ const droppedFiles = e.dataTransfer.files;
3158
+ if (droppedFiles.length > 0) {
3159
+ addFiles(Array.from(droppedFiles));
3160
+ }
3161
+ }, [addFiles]);
3162
+ const handlePaste = useCallback5((e) => {
3163
+ const items = e.clipboardData?.items;
3164
+ if (!items)
3165
+ return;
3166
+ const pastedFiles = [];
3167
+ for (const item of Array.from(items)) {
3168
+ if (item.kind === "file") {
3169
+ const file = item.getAsFile();
3170
+ if (file) {
3171
+ pastedFiles.push(file);
3172
+ }
3173
+ }
3174
+ }
3175
+ if (pastedFiles.length > 0) {
3176
+ e.preventDefault();
3177
+ addFiles(pastedFiles);
3178
+ }
3179
+ }, [addFiles]);
3180
+ useEffect6(() => {
3181
+ if (!pageWide)
3182
+ return;
3183
+ let dragCounter = 0;
3184
+ const onDragEnter = (e) => {
3185
+ e.preventDefault();
3186
+ if (e.dataTransfer?.types.includes("Files")) {
3187
+ dragCounter++;
3188
+ if (dragCounter === 1) {
3189
+ setIsDragging(true);
3190
+ }
3191
+ }
3192
+ };
3193
+ const onDragLeave = (e) => {
3194
+ e.preventDefault();
3195
+ dragCounter--;
3196
+ if (dragCounter === 0) {
3197
+ setIsDragging(false);
3198
+ }
3199
+ };
3200
+ const onDragOver = (e) => {
3201
+ e.preventDefault();
3202
+ };
3203
+ const onDrop = (e) => {
3204
+ e.preventDefault();
3205
+ dragCounter = 0;
3206
+ setIsDragging(false);
3207
+ const droppedFiles = e.dataTransfer?.files;
3208
+ if (droppedFiles && droppedFiles.length > 0) {
3209
+ addFiles(Array.from(droppedFiles));
3210
+ }
3211
+ };
3212
+ document.addEventListener("dragenter", onDragEnter);
3213
+ document.addEventListener("dragleave", onDragLeave);
3214
+ document.addEventListener("dragover", onDragOver);
3215
+ document.addEventListener("drop", onDrop);
3216
+ return () => {
3217
+ document.removeEventListener("dragenter", onDragEnter);
3218
+ document.removeEventListener("dragleave", onDragLeave);
3219
+ document.removeEventListener("dragover", onDragOver);
3220
+ document.removeEventListener("drop", onDrop);
3221
+ };
3222
+ }, [pageWide, addFiles]);
3223
+ const images = files.filter((f) => f.type === "image");
3224
+ const documents = files.filter((f) => f.type === "pdf" || f.type === "text");
3225
+ return {
3226
+ files,
3227
+ images,
3228
+ documents,
3229
+ isDragging,
3230
+ error,
3231
+ addFiles,
3232
+ removeFile,
3233
+ clearFiles,
3234
+ handleDragEnter,
3235
+ handleDragLeave,
3236
+ handleDragOver,
3237
+ handleDrop,
3238
+ handlePaste
3239
+ };
3240
+ }
3241
+ // src/hooks/useSessionFiles.ts
3242
+ import { useQuery as useQuery5 } from "@tanstack/react-query";
3243
+ function useSessionFiles(sessionId) {
3244
+ const isExpanded = useSessionFilesStore((state) => state.isExpanded);
3245
+ return useQuery5({
3246
+ queryKey: ["session", sessionId, "files"],
3247
+ queryFn: () => sessionId ? apiClient.getSessionFiles(sessionId) : null,
3248
+ enabled: !!sessionId,
3249
+ refetchInterval: isExpanded ? 5000 : false,
3250
+ retry: 1,
3251
+ staleTime: 3000
3252
+ });
3253
+ }
3254
+ // src/hooks/useQueueState.ts
3255
+ import { useQuery as useQuery6 } from "@tanstack/react-query";
3256
+ var defaultQueueState = {
3257
+ currentMessageId: null,
3258
+ queuedMessages: [],
3259
+ queueLength: 0
3260
+ };
3261
+ function useQueueState(sessionId) {
3262
+ const { data } = useQuery6({
3263
+ queryKey: ["queueState", sessionId],
3264
+ queryFn: () => defaultQueueState,
3265
+ enabled: !!sessionId,
3266
+ initialData: defaultQueueState,
3267
+ staleTime: Infinity
3268
+ });
3269
+ return data ?? defaultQueueState;
3270
+ }
3271
+ function useMessageQueuePosition(sessionId, messageId) {
3272
+ const queueState = useQueueState(sessionId);
3273
+ if (!sessionId || !queueState) {
3274
+ return { isQueued: false, isRunning: false, position: null };
3275
+ }
3276
+ if (queueState.currentMessageId === messageId) {
3277
+ return { isQueued: false, isRunning: true, position: null };
3278
+ }
3279
+ const queuedItem = queueState.queuedMessages.find((item) => item.messageId === messageId);
3280
+ if (queuedItem) {
3281
+ return { isQueued: true, isRunning: false, position: queuedItem.position };
3282
+ }
3283
+ return { isQueued: false, isRunning: false, position: null };
3284
+ }
3285
+ // src/hooks/useBranch.ts
3286
+ import { useQuery as useQuery7, useMutation as useMutation5, useQueryClient as useQueryClient6 } from "@tanstack/react-query";
3287
+ function useCreateBranch(sessionId) {
3288
+ const queryClient = useQueryClient6();
3289
+ return useMutation5({
3290
+ mutationFn: (data) => {
3291
+ if (!sessionId)
3292
+ throw new Error("No session ID");
3293
+ return apiClient.createBranch(sessionId, data);
3294
+ },
3295
+ onSuccess: () => {
3296
+ queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
3297
+ if (sessionId) {
3298
+ queryClient.invalidateQueries({
3299
+ queryKey: ["branches", sessionId]
3300
+ });
3301
+ }
3302
+ }
3303
+ });
3304
+ }
3305
+ function useBranches(sessionId) {
3306
+ return useQuery7({
3307
+ queryKey: ["branches", sessionId],
3308
+ queryFn: () => {
3309
+ if (!sessionId)
3310
+ throw new Error("No session ID");
3311
+ return apiClient.listBranches(sessionId);
3312
+ },
3313
+ enabled: Boolean(sessionId)
3314
+ });
3315
+ }
3316
+ function useParentSession(sessionId) {
3317
+ return useQuery7({
3318
+ queryKey: ["parentSession", sessionId],
3319
+ queryFn: () => {
3320
+ if (!sessionId)
3321
+ throw new Error("No session ID");
3322
+ return apiClient.getParentSession(sessionId);
3323
+ },
3324
+ enabled: Boolean(sessionId)
3325
+ });
3326
+ }
3327
+ // src/hooks/useResearch.ts
3328
+ import { useQuery as useQuery8, useMutation as useMutation6, useQueryClient as useQueryClient7 } from "@tanstack/react-query";
3329
+
3330
+ // src/stores/pendingResearchStore.ts
3331
+ import { create as create13 } from "zustand";
3332
+ var usePendingResearchStore = create13((set, get) => ({
3333
+ pendingContexts: new Map,
3334
+ addContext: (parentSessionId, context) => {
3335
+ set((state) => {
3336
+ const newMap = new Map(state.pendingContexts);
3337
+ const existing = newMap.get(parentSessionId) || [];
3338
+ if (!existing.some((c) => c.id === context.id)) {
3339
+ newMap.set(parentSessionId, [...existing, context]);
3340
+ }
3341
+ return { pendingContexts: newMap };
3342
+ });
3343
+ },
3344
+ removeContext: (parentSessionId, contextId) => {
3345
+ set((state) => {
3346
+ const newMap = new Map(state.pendingContexts);
3347
+ const existing = newMap.get(parentSessionId) || [];
3348
+ newMap.set(parentSessionId, existing.filter((c) => c.id !== contextId));
3349
+ return { pendingContexts: newMap };
3350
+ });
3351
+ },
3352
+ getContexts: (parentSessionId) => {
3353
+ return get().pendingContexts.get(parentSessionId) || [];
3354
+ },
3355
+ clearContexts: (parentSessionId) => {
3356
+ set((state) => {
3357
+ const newMap = new Map(state.pendingContexts);
3358
+ newMap.delete(parentSessionId);
3359
+ return { pendingContexts: newMap };
3360
+ });
3361
+ },
3362
+ consumeContexts: (parentSessionId) => {
3363
+ const contexts = get().getContexts(parentSessionId);
3364
+ get().clearContexts(parentSessionId);
3365
+ return contexts;
3366
+ }
3367
+ }));
3368
+
3369
+ // src/hooks/useResearch.ts
3370
+ class ResearchApiClient {
3371
+ get baseUrl() {
3372
+ const win = window;
3373
+ if (win.OTTO_SERVER_URL) {
3374
+ return win.OTTO_SERVER_URL;
3375
+ }
3376
+ if (import.meta.env?.VITE_API_BASE_URL) {
3377
+ return import.meta.env.VITE_API_BASE_URL;
3378
+ }
3379
+ return API_BASE_URL;
3380
+ }
3381
+ async listResearchSessions(parentSessionId) {
3382
+ const response = await fetch(`${this.baseUrl}/v1/sessions/${parentSessionId}/research`, {
3383
+ method: "GET",
3384
+ headers: { "Content-Type": "application/json" }
3385
+ });
3386
+ if (!response.ok) {
3387
+ const error = await response.json().catch(() => ({ error: "Failed to fetch research sessions" }));
3388
+ throw new Error(error.error || "Failed to fetch research sessions");
3389
+ }
3390
+ return response.json();
3391
+ }
3392
+ async createResearchSession(parentSessionId, data) {
3393
+ const response = await fetch(`${this.baseUrl}/v1/sessions/${parentSessionId}/research`, {
3394
+ method: "POST",
3395
+ headers: { "Content-Type": "application/json" },
3396
+ body: JSON.stringify(data)
3397
+ });
3398
+ if (!response.ok) {
3399
+ const error = await response.json().catch(() => ({ error: "Failed to create research session" }));
3400
+ throw new Error(error.error || "Failed to create research session");
3401
+ }
3402
+ return response.json();
3403
+ }
3404
+ async deleteResearchSession(researchId) {
3405
+ const response = await fetch(`${this.baseUrl}/v1/research/${researchId}`, {
3406
+ method: "DELETE",
3407
+ headers: { "Content-Type": "application/json" }
3408
+ });
3409
+ if (!response.ok) {
3410
+ const error = await response.json().catch(() => ({ error: "Failed to delete research session" }));
3411
+ throw new Error(error.error || "Failed to delete research session");
3412
+ }
3413
+ return response.json();
3414
+ }
3415
+ async injectContext(parentSessionId, researchSessionId, label) {
3416
+ const response = await fetch(`${this.baseUrl}/v1/sessions/${parentSessionId}/inject`, {
3417
+ method: "POST",
3418
+ headers: { "Content-Type": "application/json" },
3419
+ body: JSON.stringify({ researchSessionId, label })
3420
+ });
3421
+ if (!response.ok) {
3422
+ const error = await response.json().catch(() => ({ error: "Failed to inject context" }));
3423
+ throw new Error(error.error || "Failed to inject context");
3424
+ }
3425
+ return response.json();
3426
+ }
3427
+ async exportToNewSession(researchId, data) {
3428
+ const response = await fetch(`${this.baseUrl}/v1/research/${researchId}/export`, {
3429
+ method: "POST",
3430
+ headers: { "Content-Type": "application/json" },
3431
+ body: JSON.stringify(data ?? {})
3432
+ });
3433
+ if (!response.ok) {
3434
+ const error = await response.json().catch(() => ({ error: "Failed to export to session" }));
3435
+ throw new Error(error.error || "Failed to export to session");
3436
+ }
3437
+ return response.json();
3438
+ }
3439
+ }
3440
+ var researchApi = new ResearchApiClient;
3441
+ function useResearchSessions(parentSessionId) {
3442
+ return useQuery8({
3443
+ queryKey: ["research", "sessions", parentSessionId],
3444
+ queryFn: () => researchApi.listResearchSessions(parentSessionId),
3445
+ enabled: !!parentSessionId,
3446
+ staleTime: 30000
3447
+ });
3448
+ }
3449
+ function useCreateResearchSession() {
3450
+ const queryClient = useQueryClient7();
3451
+ return useMutation6({
3452
+ mutationFn: ({
3453
+ parentSessionId,
3454
+ data
3455
+ }) => researchApi.createResearchSession(parentSessionId, data ?? {}),
3456
+ onSuccess: (_, { parentSessionId }) => {
3457
+ queryClient.invalidateQueries({
3458
+ queryKey: ["research", "sessions", parentSessionId]
3459
+ });
3460
+ }
3461
+ });
3462
+ }
3463
+ function useDeleteResearchSession() {
3464
+ const queryClient = useQueryClient7();
3465
+ return useMutation6({
3466
+ mutationFn: (researchId) => researchApi.deleteResearchSession(researchId),
3467
+ onSuccess: () => {
3468
+ queryClient.invalidateQueries({ queryKey: ["research", "sessions"] });
3469
+ }
3470
+ });
3471
+ }
3472
+ function useInjectContext() {
3473
+ const addContext = usePendingResearchStore((state) => state.addContext);
3474
+ return useMutation6({
3475
+ mutationFn: ({
3476
+ parentSessionId,
3477
+ researchSessionId,
3478
+ label
3479
+ }) => researchApi.injectContext(parentSessionId, researchSessionId, label),
3480
+ onSuccess: (data, { parentSessionId }) => {
3481
+ addContext(parentSessionId, {
3482
+ id: data.sessionId,
3483
+ sessionId: data.sessionId,
3484
+ label: data.label,
3485
+ content: data.content
3486
+ });
3487
+ }
3488
+ });
3489
+ }
3490
+ function useExportToSession() {
3491
+ const queryClient = useQueryClient7();
3492
+ return useMutation6({
3493
+ mutationFn: ({
3494
+ researchId,
3495
+ data
3496
+ }) => researchApi.exportToNewSession(researchId, data),
3497
+ onSuccess: () => {
3498
+ queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
3499
+ }
3500
+ });
3501
+ }
3502
+ // src/hooks/useSetuPayments.ts
3503
+ import { useEffect as useEffect7, useRef as useRef2 } from "react";
3504
+
3505
+ // src/stores/toastStore.ts
3506
+ import { create as create14 } from "zustand";
3507
+ var toastId = 0;
3508
+ var useToastStore = create14((set) => ({
3509
+ toasts: [],
3510
+ addToast: (toast) => {
3511
+ const id = `toast-${++toastId}`;
3512
+ set((state) => ({
3513
+ toasts: [...state.toasts, { ...toast, id }]
3514
+ }));
3515
+ return id;
3516
+ },
3517
+ removeToast: (id) => set((state) => ({
3518
+ toasts: state.toasts.filter((t) => t.id !== id)
3519
+ })),
3520
+ updateToast: (id, updates) => set((state) => ({
3521
+ toasts: state.toasts.map((t) => t.id === id ? { ...t, ...updates } : t)
3522
+ })),
3523
+ clearToasts: () => set({ toasts: [] })
3524
+ }));
3525
+ function toast(message, type = "default", duration = 4000) {
3526
+ const id = useToastStore.getState().addToast({ message, type, duration });
3527
+ if (duration > 0) {
3528
+ setTimeout(() => {
3529
+ useToastStore.getState().removeToast(id);
3530
+ }, duration);
3531
+ }
3532
+ return id;
3533
+ }
3534
+ toast.success = (message, duration = 4000) => toast(message, "success", duration);
3535
+ toast.error = (message, duration = 5000) => toast(message, "error", duration);
3536
+ toast.info = (message, duration = 4000) => toast(message, "default", duration);
3537
+ toast.loading = (message) => toast(message, "loading", 0);
3538
+ toast.successWithAction = (message, action, duration = 6000) => {
3539
+ const id = useToastStore.getState().addToast({
3540
+ message,
3541
+ type: "success",
3542
+ duration,
3543
+ action
3544
+ });
3545
+ if (duration > 0) {
3546
+ setTimeout(() => {
3547
+ useToastStore.getState().removeToast(id);
3548
+ }, duration);
3549
+ }
3550
+ return id;
3551
+ };
3552
+
3553
+ // src/stores/setuStore.ts
3554
+ import { create as create15 } from "zustand";
3555
+ var useSetuStore = create15((set) => ({
3556
+ balance: null,
3557
+ usdcBalance: null,
3558
+ network: "mainnet",
3559
+ isPaymentPending: false,
3560
+ lastPaymentAmount: null,
3561
+ walletAddress: null,
3562
+ isLoading: false,
3563
+ isTopupModalOpen: false,
3564
+ scope: null,
3565
+ payg: null,
3566
+ subscription: null,
3567
+ limits: null,
3568
+ setBalance: (balance) => set({ balance }),
3569
+ setUsdcBalance: (usdcBalance) => set({ usdcBalance }),
3570
+ setNetwork: (network) => set({ network }),
3571
+ setPaymentPending: (isPaymentPending) => set({ isPaymentPending }),
3572
+ setLastPaymentAmount: (lastPaymentAmount) => set({ lastPaymentAmount }),
3573
+ setWalletAddress: (walletAddress) => set({ walletAddress }),
3574
+ setLoading: (isLoading) => set({ isLoading }),
3575
+ openTopupModal: () => set({ isTopupModalOpen: true }),
3576
+ closeTopupModal: () => set({ isTopupModalOpen: false }),
3577
+ setScope: (scope) => set({ scope }),
3578
+ setPayg: (payg) => set({ payg }),
3579
+ setSubscription: (subscription) => set({ subscription }),
3580
+ setLimits: (limits) => set({ limits })
3581
+ }));
3582
+
3583
+ // src/stores/topupApprovalStore.ts
3584
+ import { create as create16 } from "zustand";
3585
+ var useTopupApprovalStore = create16((set) => ({
3586
+ pendingTopup: null,
3587
+ isProcessing: false,
3588
+ selectedMethod: null,
3589
+ setPendingTopup: (pendingTopup) => set({ pendingTopup, isProcessing: false, selectedMethod: null }),
3590
+ setProcessing: (isProcessing) => set({ isProcessing }),
3591
+ setSelectedMethod: (selectedMethod) => set({ selectedMethod }),
3592
+ clearPendingTopup: () => set({ pendingTopup: null, isProcessing: false, selectedMethod: null })
3593
+ }));
3594
+
3595
+ // src/hooks/useSetuPayments.ts
3596
+ function useSetuPayments(sessionId) {
3597
+ const clientRef = useRef2(null);
3598
+ const loadingToastIdRef = useRef2(null);
3599
+ const setBalance = useSetuStore((s) => s.setBalance);
3600
+ const setPaymentPending = useSetuStore((s) => s.setPaymentPending);
3601
+ const removeToast = useToastStore((s) => s.removeToast);
3602
+ const updateToast = useToastStore((s) => s.updateToast);
3603
+ const setPendingTopup = useTopupApprovalStore((s) => s.setPendingTopup);
3604
+ const clearPendingTopup = useTopupApprovalStore((s) => s.clearPendingTopup);
3605
+ useEffect7(() => {
3606
+ if (!sessionId)
3607
+ return;
3608
+ const client2 = new SSEClient;
3609
+ clientRef.current = client2;
3610
+ const url = apiClient.getStreamUrl(sessionId);
3611
+ client2.connect(url);
3612
+ const unsubscribe = client2.on("*", (event) => {
3613
+ const payload = event.payload;
3614
+ switch (event.type) {
3615
+ case "setu.topup.required": {
3616
+ const amountUsd = typeof payload?.amountUsd === "number" ? payload.amountUsd : 0;
3617
+ const currentBalance = typeof payload?.currentBalance === "number" ? payload.currentBalance : 0;
3618
+ const minTopupUsd = typeof payload?.minTopupUsd === "number" ? payload.minTopupUsd : 5;
3619
+ const suggestedTopupUsd = typeof payload?.suggestedTopupUsd === "number" ? payload.suggestedTopupUsd : 10;
3620
+ const messageId = typeof payload?.messageId === "string" ? payload.messageId : "";
3621
+ setPendingTopup({
3622
+ sessionId,
3623
+ messageId,
3624
+ amountUsd,
3625
+ currentBalance,
3626
+ minTopupUsd,
3627
+ suggestedTopupUsd
3628
+ });
3629
+ break;
3630
+ }
3631
+ case "setu.topup.method_selected": {
3632
+ const method = payload?.method;
3633
+ if (method === "crypto") {
3634
+ setPaymentPending(true);
3635
+ loadingToastIdRef.current = toast.loading("\uD83D\uDCB3 Processing crypto payment...");
3636
+ }
3637
+ break;
3638
+ }
3639
+ case "setu.topup.cancelled": {
3640
+ clearPendingTopup();
3641
+ const reason = typeof payload?.reason === "string" ? payload.reason : "Request cancelled";
3642
+ toast(`⚠️ ${reason}`);
3643
+ break;
3644
+ }
3645
+ case "setu.payment.required": {
3646
+ const amountUsd = typeof payload?.amountUsd === "number" ? payload.amountUsd : 0;
3647
+ setPaymentPending(true);
3648
+ if (!loadingToastIdRef.current) {
3649
+ loadingToastIdRef.current = toast.loading(`\uD83D\uDCB3 Payment required: $${amountUsd.toFixed(2)}`);
3650
+ }
3651
+ break;
3652
+ }
3653
+ case "setu.payment.signing": {
3654
+ if (loadingToastIdRef.current) {
3655
+ updateToast(loadingToastIdRef.current, {
3656
+ message: "✍️ Signing transaction..."
3657
+ });
3658
+ } else {
3659
+ loadingToastIdRef.current = toast.loading("✍️ Signing transaction...");
3660
+ }
3661
+ break;
3662
+ }
3663
+ case "setu.payment.complete": {
3664
+ clearPendingTopup();
3665
+ const rawAmount = payload?.amountUsd;
3666
+ const rawBalance = payload?.newBalance;
3667
+ const transactionId = typeof payload?.transactionId === "string" ? payload.transactionId : undefined;
3668
+ const amountUsd = typeof rawAmount === "number" ? rawAmount : typeof rawAmount === "string" ? parseFloat(rawAmount) : 0;
3669
+ const newBalance = typeof rawBalance === "number" ? rawBalance : typeof rawBalance === "string" ? parseFloat(rawBalance) : 0;
3670
+ setBalance(newBalance);
3671
+ setPaymentPending(false);
3672
+ if (loadingToastIdRef.current) {
3673
+ removeToast(loadingToastIdRef.current);
3674
+ loadingToastIdRef.current = null;
3675
+ }
3676
+ const message = `✅ Paid $${amountUsd.toFixed(2)}`;
3677
+ if (transactionId) {
3678
+ toast.successWithAction(message, {
3679
+ label: "View Tx",
3680
+ href: `https://orbmarkets.io/tx/${transactionId}`
3681
+ });
3682
+ } else {
3683
+ toast.success(message);
3684
+ }
3685
+ break;
3686
+ }
3687
+ case "setu.fiat.checkout_created": {
3688
+ clearPendingTopup();
3689
+ setPaymentPending(false);
3690
+ if (loadingToastIdRef.current) {
3691
+ removeToast(loadingToastIdRef.current);
3692
+ loadingToastIdRef.current = null;
3693
+ }
3694
+ toast.success("\uD83D\uDCB3 Complete payment, then retry your message");
3695
+ break;
3696
+ }
3697
+ case "setu.balance.updated": {
3698
+ const rawBalance = payload?.balanceRemaining;
3699
+ const newBalance = typeof rawBalance === "number" ? rawBalance : typeof rawBalance === "string" ? parseFloat(rawBalance) : null;
3700
+ if (newBalance !== null && !Number.isNaN(newBalance)) {
3701
+ setBalance(newBalance);
3702
+ }
3703
+ break;
3704
+ }
3705
+ case "setu.payment.error": {
3706
+ clearPendingTopup();
3707
+ const error = typeof payload?.error === "string" ? payload.error : "Payment failed";
3708
+ setPaymentPending(false);
3709
+ if (loadingToastIdRef.current) {
3710
+ removeToast(loadingToastIdRef.current);
3711
+ loadingToastIdRef.current = null;
3712
+ }
3713
+ toast.error(`❌ ${error}`);
3714
+ break;
3715
+ }
3716
+ default:
3717
+ break;
3718
+ }
3719
+ });
3720
+ return () => {
3721
+ unsubscribe();
3722
+ client2.disconnect();
3723
+ };
3724
+ }, [
3725
+ sessionId,
3726
+ setBalance,
3727
+ setPaymentPending,
3728
+ removeToast,
3729
+ updateToast,
3730
+ setPendingTopup,
3731
+ clearPendingTopup
3732
+ ]);
3733
+ }
3734
+ // src/hooks/useSetuBalance.ts
3735
+ import { useEffect as useEffect8, useCallback as useCallback6 } from "react";
3736
+ function useSetuBalance(providerName) {
3737
+ const setBalance = useSetuStore((s) => s.setBalance);
3738
+ const setUsdcBalance = useSetuStore((s) => s.setUsdcBalance);
3739
+ const setWalletAddress = useSetuStore((s) => s.setWalletAddress);
3740
+ const setLoading = useSetuStore((s) => s.setLoading);
3741
+ const setScope = useSetuStore((s) => s.setScope);
3742
+ const setPayg = useSetuStore((s) => s.setPayg);
3743
+ const setSubscription = useSetuStore((s) => s.setSubscription);
3744
+ const setLimits = useSetuStore((s) => s.setLimits);
3745
+ const balance = useSetuStore((s) => s.balance);
3746
+ const usdcBalance = useSetuStore((s) => s.usdcBalance);
3747
+ const network = useSetuStore((s) => s.network);
3748
+ const fetchBalance = useCallback6(async () => {
3749
+ if (providerName !== "setu") {
3750
+ return;
3751
+ }
3752
+ setLoading(true);
3753
+ try {
3754
+ const [setuData, usdcData, walletData] = await Promise.all([
3755
+ apiClient.getSetuBalance(),
3756
+ apiClient.getSetuUsdcBalance(network),
3757
+ apiClient.getSetuWallet()
3758
+ ]);
3759
+ if (setuData) {
3760
+ setBalance(setuData.balance);
3761
+ setWalletAddress(setuData.walletAddress);
3762
+ setScope(setuData.scope ?? null);
3763
+ setPayg(setuData.payg ?? null);
3764
+ setSubscription(setuData.subscription ?? null);
3765
+ setLimits(setuData.limits ?? null);
3766
+ } else if (walletData?.configured && walletData.publicKey) {
3767
+ setWalletAddress(walletData.publicKey);
3768
+ }
3769
+ if (usdcData) {
3770
+ setUsdcBalance(usdcData.usdcBalance);
3771
+ if (!setuData && usdcData.walletAddress) {
3772
+ setWalletAddress(usdcData.walletAddress);
3773
+ }
3774
+ }
3775
+ } catch {} finally {
3776
+ setLoading(false);
3777
+ }
3778
+ }, [
3779
+ providerName,
3780
+ network,
3781
+ setBalance,
3782
+ setUsdcBalance,
3783
+ setWalletAddress,
3784
+ setLoading,
3785
+ setScope,
3786
+ setPayg,
3787
+ setSubscription,
3788
+ setLimits
3789
+ ]);
3790
+ useEffect8(() => {
3791
+ if (providerName === "setu" && (balance === null || usdcBalance === null)) {
3792
+ fetchBalance();
3793
+ }
3794
+ }, [providerName, balance, usdcBalance, fetchBalance]);
3795
+ return {
3796
+ fetchBalance
3797
+ };
3798
+ }
3799
+ // src/hooks/useShareStatus.ts
3800
+ import { useQuery as useQuery9 } from "@tanstack/react-query";
3801
+ function useShareStatus(sessionId) {
3802
+ const { data, isLoading, error } = useQuery9({
3803
+ queryKey: ["share-status", sessionId],
3804
+ queryFn: () => apiClient.getShareStatus(sessionId),
3805
+ enabled: !!sessionId,
3806
+ staleTime: 30000
3807
+ });
3808
+ return { data, isLoading, error };
3809
+ }
3810
+ // src/hooks/useToolApprovalShortcuts.ts
3811
+ import { useEffect as useEffect9, useCallback as useCallback7 } from "react";
3812
+ function useToolApprovalShortcuts(sessionId) {
3813
+ const { pendingApprovals, removePendingApproval } = useToolApprovalStore();
3814
+ const sessionPendingApprovals = pendingApprovals;
3815
+ const handleApprove = useCallback7(async (callId) => {
3816
+ if (!sessionId)
3817
+ return;
3818
+ try {
3819
+ await apiClient.approveToolCall(sessionId, callId, true);
3820
+ removePendingApproval(callId);
3821
+ } catch (error) {
3822
+ console.error("Failed to approve tool call:", error);
3823
+ }
3824
+ }, [sessionId, removePendingApproval]);
3825
+ const handleReject = useCallback7(async (callId) => {
3826
+ if (!sessionId)
3827
+ return;
3828
+ try {
3829
+ await apiClient.approveToolCall(sessionId, callId, false);
3830
+ removePendingApproval(callId);
3831
+ } catch (error) {
3832
+ console.error("Failed to reject tool call:", error);
3833
+ }
3834
+ }, [sessionId, removePendingApproval]);
3835
+ const handleApproveAll = useCallback7(async () => {
3836
+ if (!sessionId)
3837
+ return;
3838
+ try {
3839
+ await Promise.all(sessionPendingApprovals.map((a) => apiClient.approveToolCall(sessionId, a.callId, true)));
3840
+ for (const a of sessionPendingApprovals) {
3841
+ removePendingApproval(a.callId);
3842
+ }
3843
+ } catch (error) {
3844
+ console.error("Failed to approve all tool calls:", error);
3845
+ }
3846
+ }, [sessionId, sessionPendingApprovals, removePendingApproval]);
3847
+ useEffect9(() => {
3848
+ if (!sessionId || sessionPendingApprovals.length === 0)
3849
+ return;
3850
+ const handleKeyDown = (e) => {
3851
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target?.isContentEditable) {
3852
+ return;
3853
+ }
3854
+ const firstPending = sessionPendingApprovals[0];
3855
+ if (e.key === "y" || e.key === "Y") {
3856
+ e.preventDefault();
3857
+ handleApprove(firstPending.callId);
3858
+ } else if (e.key === "n" || e.key === "N" || e.key === "Escape") {
3859
+ e.preventDefault();
3860
+ handleReject(firstPending.callId);
3861
+ } else if (e.key === "a" || e.key === "A") {
3862
+ e.preventDefault();
3863
+ handleApproveAll();
3864
+ }
3865
+ };
3866
+ window.addEventListener("keydown", handleKeyDown);
3867
+ return () => window.removeEventListener("keydown", handleKeyDown);
3868
+ }, [
3869
+ sessionId,
3870
+ sessionPendingApprovals,
3871
+ handleApprove,
3872
+ handleReject,
3873
+ handleApproveAll
3874
+ ]);
3875
+ }
3876
+ // src/hooks/useTopupCallback.ts
3877
+ import { useEffect as useEffect10, useRef as useRef3 } from "react";
3878
+ var STORAGE_KEY3 = "pendingPolarCheckout";
3879
+ function useTopupCallback() {
3880
+ const hasHandled = useRef3(false);
3881
+ const loadingToastId = useRef3(null);
3882
+ const setBalance = useSetuStore((s) => s.setBalance);
3883
+ const removeToast = useToastStore((s) => s.removeToast);
3884
+ useEffect10(() => {
3885
+ if (hasHandled.current)
3886
+ return;
3887
+ const params = new URLSearchParams(window.location.search);
3888
+ const topupStatus = params.get("topup");
3889
+ const pendingCheckoutId = localStorage.getItem(STORAGE_KEY3);
3890
+ if (topupStatus === "pending" || pendingCheckoutId) {
3891
+ hasHandled.current = true;
3892
+ const url = new URL(window.location.href);
3893
+ url.searchParams.delete("topup");
3894
+ window.history.replaceState({}, "", url.toString());
3895
+ if (!pendingCheckoutId) {
3896
+ toast.info("Checking top-up status...");
3897
+ return;
3898
+ }
3899
+ loadingToastId.current = toast.loading("Verifying top-up...");
3900
+ let attempts = 0;
3901
+ const maxAttempts = 10;
3902
+ const delayMs = 2000;
3903
+ const dismissLoading = () => {
3904
+ if (loadingToastId.current) {
3905
+ removeToast(loadingToastId.current);
3906
+ loadingToastId.current = null;
3907
+ }
3908
+ };
3909
+ const checkStatus = async () => {
3910
+ attempts++;
3911
+ try {
3912
+ const status = await apiClient.getPolarTopupStatus(pendingCheckoutId);
3913
+ if (status?.confirmed) {
3914
+ localStorage.removeItem(STORAGE_KEY3);
3915
+ dismissLoading();
3916
+ const balanceData = await apiClient.getSetuBalance();
3917
+ if (balanceData?.balance !== undefined) {
3918
+ setBalance(balanceData.balance);
3919
+ }
3920
+ toast.success(`Top-up confirmed! +$${status.amountUsd?.toFixed(2)} credited`);
3921
+ return;
3922
+ }
3923
+ if (attempts < maxAttempts) {
3924
+ setTimeout(checkStatus, delayMs);
3925
+ } else {
3926
+ localStorage.removeItem(STORAGE_KEY3);
3927
+ dismissLoading();
3928
+ toast.info("Top-up is still processing. Balance will update automatically.");
3929
+ }
3930
+ } catch {
3931
+ if (attempts < maxAttempts) {
3932
+ setTimeout(checkStatus, delayMs);
3933
+ } else {
3934
+ localStorage.removeItem(STORAGE_KEY3);
3935
+ dismissLoading();
3936
+ toast.error("Could not verify top-up. Please check your balance.");
3937
+ }
3938
+ }
3939
+ };
3940
+ setTimeout(checkStatus, 1500);
3941
+ }
3942
+ }, [setBalance, removeToast]);
3943
+ }
3944
+ // src/hooks/useAuthStatus.ts
3945
+ import { useEffect as useEffect11, useCallback as useCallback8, useState as useState5, useRef as useRef4 } from "react";
3946
+ import { useQueryClient as useQueryClient8 } from "@tanstack/react-query";
3947
+
3948
+ // src/stores/onboardingStore.ts
3949
+ import { create as create17 } from "zustand";
3950
+ var STEPS = ["wallet", "defaults"];
3951
+ var useOnboardingStore = create17((set, get) => ({
3952
+ isOpen: false,
3953
+ currentStep: "wallet",
3954
+ manageMode: false,
3955
+ isLoading: false,
3956
+ error: null,
3957
+ authStatus: null,
3958
+ setOpen: (isOpen) => set({ isOpen }),
3959
+ setStep: (currentStep) => set({ currentStep }),
3960
+ setManageMode: (manageMode) => set({ manageMode }),
3961
+ setLoading: (isLoading) => set({ isLoading }),
3962
+ setError: (error) => set({ error }),
3963
+ setAuthStatus: (authStatus) => set({ authStatus }),
3964
+ nextStep: () => {
3965
+ const { currentStep } = get();
3966
+ const currentIndex = STEPS.indexOf(currentStep);
3967
+ if (currentIndex < STEPS.length - 1) {
3968
+ set({ currentStep: STEPS[currentIndex + 1] });
3969
+ }
3970
+ },
3971
+ prevStep: () => {
3972
+ const { currentStep } = get();
3973
+ const currentIndex = STEPS.indexOf(currentStep);
3974
+ if (currentIndex > 0) {
3975
+ set({ currentStep: STEPS[currentIndex - 1] });
3976
+ }
3977
+ },
3978
+ reset: () => set({
3979
+ isOpen: false,
3980
+ currentStep: "wallet",
3981
+ manageMode: false,
3982
+ isLoading: false,
3983
+ error: null
3984
+ })
3985
+ }));
3986
+
3987
+ // src/hooks/useAuthStatus.ts
3988
+ var isInIframe = typeof window !== "undefined" && window.self !== window.top;
3989
+ function useAuthStatus() {
3990
+ const setAuthStatus = useOnboardingStore((s) => s.setAuthStatus);
3991
+ const setOpen = useOnboardingStore((s) => s.setOpen);
3992
+ const setLoading = useOnboardingStore((s) => s.setLoading);
3993
+ const setError = useOnboardingStore((s) => s.setError);
3994
+ const authStatus = useOnboardingStore((s) => s.authStatus);
3995
+ const isOpen = useOnboardingStore((s) => s.isOpen);
3996
+ const queryClient = useQueryClient8();
3997
+ const [initialized, setInitialized] = useState5(false);
3998
+ const [oauthPolling, setOauthPolling] = useState5(false);
3999
+ const oauthPollingRef = useRef4(null);
4000
+ const preOauthProvidersRef = useRef4(new Set);
4001
+ const fetchAuthStatus = useCallback8(async () => {
4002
+ setLoading(true);
4003
+ setError(null);
4004
+ try {
4005
+ const status = await apiClient.getAuthStatus();
4006
+ setAuthStatus(status);
4007
+ queryClient.invalidateQueries({ queryKey: ["config"] });
4008
+ queryClient.invalidateQueries({ queryKey: ["models"] });
4009
+ return status;
4010
+ } catch (err) {
4011
+ const message = err instanceof Error ? err.message : "Failed to load";
4012
+ setError(message);
4013
+ return null;
4014
+ } finally {
4015
+ setLoading(false);
4016
+ }
4017
+ }, [setAuthStatus, setLoading, setError, queryClient]);
4018
+ const checkOnboarding = useCallback8(async () => {
4019
+ const status = await fetchAuthStatus();
4020
+ if (status) {
4021
+ const hasAnyProvider = Object.values(status.providers).some((p) => p.configured);
4022
+ const needsOnboarding = !status.onboardingComplete || !hasAnyProvider || !status.setu.configured;
4023
+ if (needsOnboarding) {
4024
+ setOpen(true);
4025
+ }
4026
+ }
4027
+ setInitialized(true);
4028
+ }, [fetchAuthStatus, setOpen]);
4029
+ const setupWallet = useCallback8(async () => {
4030
+ setLoading(true);
4031
+ setError(null);
4032
+ try {
4033
+ const result = await apiClient.setupSetuWallet();
4034
+ await fetchAuthStatus();
4035
+ return result;
4036
+ } catch (err) {
4037
+ const message = err instanceof Error ? err.message : "Setup failed";
4038
+ setError(message);
4039
+ throw err;
4040
+ } finally {
4041
+ setLoading(false);
4042
+ }
4043
+ }, [fetchAuthStatus, setLoading, setError]);
4044
+ const importWallet = useCallback8(async (privateKey) => {
4045
+ setLoading(true);
4046
+ setError(null);
4047
+ try {
4048
+ const result = await apiClient.importSetuWallet(privateKey);
4049
+ await fetchAuthStatus();
4050
+ return result;
4051
+ } catch (err) {
4052
+ const message = err instanceof Error ? err.message : "Import failed";
4053
+ setError(message);
4054
+ throw err;
4055
+ } finally {
4056
+ setLoading(false);
4057
+ }
4058
+ }, [fetchAuthStatus, setLoading, setError]);
4059
+ const addProvider = useCallback8(async (provider, apiKey) => {
4060
+ setLoading(true);
4061
+ setError(null);
4062
+ try {
4063
+ const result = await apiClient.addProvider(provider, apiKey);
4064
+ await fetchAuthStatus();
4065
+ return result;
4066
+ } catch (err) {
4067
+ const message = err instanceof Error ? err.message : "Failed to add provider";
4068
+ setError(message);
4069
+ throw err;
4070
+ } finally {
4071
+ setLoading(false);
4072
+ }
4073
+ }, [fetchAuthStatus, setLoading, setError]);
4074
+ const removeProvider = useCallback8(async (provider) => {
4075
+ setLoading(true);
4076
+ setError(null);
4077
+ try {
4078
+ const result = await apiClient.removeProvider(provider);
4079
+ await fetchAuthStatus();
4080
+ return result;
4081
+ } catch (err) {
4082
+ const message = err instanceof Error ? err.message : "Failed to remove provider";
4083
+ setError(message);
4084
+ throw err;
4085
+ } finally {
4086
+ setLoading(false);
4087
+ }
4088
+ }, [fetchAuthStatus, setLoading, setError]);
4089
+ const completeOnboarding = useCallback8(async () => {
4090
+ setLoading(true);
4091
+ setError(null);
4092
+ try {
4093
+ await apiClient.completeOnboarding();
4094
+ await fetchAuthStatus();
4095
+ setOpen(false);
4096
+ } catch (err) {
4097
+ const message = err instanceof Error ? err.message : "Failed to complete onboarding";
4098
+ setError(message);
4099
+ throw err;
4100
+ } finally {
4101
+ setLoading(false);
4102
+ }
4103
+ }, [fetchAuthStatus, setLoading, setError, setOpen]);
4104
+ const snapshotConfiguredProviders = useCallback8(() => {
4105
+ const status = useOnboardingStore.getState().authStatus;
4106
+ if (status) {
4107
+ preOauthProvidersRef.current = new Set(Object.entries(status.providers).filter(([, p]) => p.configured).map(([id]) => id));
4108
+ }
4109
+ }, []);
4110
+ const startOAuth = useCallback8((provider, mode) => {
4111
+ const url = apiClient.getOAuthStartUrl(provider, mode);
4112
+ if (isInIframe) {
4113
+ snapshotConfiguredProviders();
4114
+ window.parent.postMessage({ type: "otto-open-url", url }, "*");
4115
+ setOauthPolling(true);
4116
+ return null;
4117
+ }
4118
+ const width = 600;
4119
+ const height = 700;
4120
+ const left = window.screenX + (window.outerWidth - width) / 2;
4121
+ const top = window.screenY + (window.outerHeight - height) / 2;
4122
+ const popup = window.open(url, "oauth_popup", `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no`);
4123
+ return popup;
4124
+ }, [snapshotConfiguredProviders]);
4125
+ const startOAuthManual = useCallback8(async (provider, mode) => {
4126
+ const { url, sessionId } = await apiClient.getOAuthUrl(provider, mode);
4127
+ if (isInIframe) {
4128
+ snapshotConfiguredProviders();
4129
+ window.parent.postMessage({ type: "otto-open-url", url }, "*");
4130
+ setOauthPolling(true);
4131
+ return { popup: null, sessionId };
4132
+ }
4133
+ const width = 600;
4134
+ const height = 700;
4135
+ const left = window.screenX + (window.outerWidth - width) / 2;
4136
+ const top = window.screenY + (window.outerHeight - height) / 2;
4137
+ const popup = window.open(url, "oauth_popup", `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no`);
4138
+ return { popup, sessionId };
4139
+ }, [snapshotConfiguredProviders]);
4140
+ const exchangeOAuthCode = useCallback8(async (provider, code, sessionId) => {
4141
+ setLoading(true);
4142
+ setError(null);
4143
+ try {
4144
+ await apiClient.exchangeOAuthCode(provider, code, sessionId);
4145
+ await fetchAuthStatus();
4146
+ return true;
4147
+ } catch (err) {
4148
+ const message = err instanceof Error ? err.message : "Failed to exchange code";
4149
+ setError(message);
4150
+ throw err;
4151
+ } finally {
4152
+ setLoading(false);
4153
+ }
4154
+ }, [fetchAuthStatus, setLoading, setError]);
4155
+ useEffect11(() => {
4156
+ if (!oauthPolling || !isInIframe)
4157
+ return;
4158
+ oauthPollingRef.current = setInterval(() => {
4159
+ fetchAuthStatus();
4160
+ }, 3000);
4161
+ const timeout = setTimeout(() => {
4162
+ setOauthPolling(false);
4163
+ }, 300000);
4164
+ return () => {
4165
+ clearInterval(oauthPollingRef.current);
4166
+ clearTimeout(timeout);
4167
+ };
4168
+ }, [oauthPolling, fetchAuthStatus]);
4169
+ useEffect11(() => {
4170
+ if (!oauthPolling || !authStatus)
4171
+ return;
4172
+ const currentConfigured = Object.entries(authStatus.providers).filter(([, p]) => p.configured);
4173
+ const hasNewProvider = currentConfigured.some(([id]) => !preOauthProvidersRef.current.has(id));
4174
+ if (hasNewProvider) {
4175
+ setOauthPolling(false);
4176
+ }
4177
+ }, [authStatus, oauthPolling]);
4178
+ useEffect11(() => {
4179
+ const handleOAuthMessage = (event) => {
4180
+ if (event.data?.type === "oauth-success") {
4181
+ fetchAuthStatus();
4182
+ }
4183
+ };
4184
+ window.addEventListener("message", handleOAuthMessage);
4185
+ return () => window.removeEventListener("message", handleOAuthMessage);
4186
+ }, [fetchAuthStatus]);
4187
+ const pollCopilotDeviceFlow = useCallback8(async (sessionId) => {
4188
+ const result = await apiClient.pollCopilotDeviceFlow(sessionId);
4189
+ if (result.status === "complete") {
4190
+ await fetchAuthStatus();
4191
+ }
4192
+ return result;
4193
+ }, [fetchAuthStatus]);
4194
+ const saveCopilotToken = useCallback8(async (token) => {
4195
+ setLoading(true);
4196
+ setError(null);
4197
+ try {
4198
+ const result = await apiClient.saveCopilotToken(token);
4199
+ await fetchAuthStatus();
4200
+ return result;
4201
+ } catch (err) {
4202
+ const message = err instanceof Error ? err.message : "Failed to save Copilot token";
4203
+ setError(message);
4204
+ throw err;
4205
+ } finally {
4206
+ setLoading(false);
4207
+ }
4208
+ }, [fetchAuthStatus, setLoading, setError]);
4209
+ const importCopilotTokenFromGh = useCallback8(async () => {
4210
+ setLoading(true);
4211
+ setError(null);
4212
+ try {
4213
+ const result = await apiClient.importCopilotTokenFromGh();
4214
+ await fetchAuthStatus();
4215
+ return result;
4216
+ } catch (err) {
4217
+ const message = err instanceof Error ? err.message : "Failed to import Copilot token from GH";
4218
+ setError(message);
4219
+ throw err;
4220
+ } finally {
4221
+ setLoading(false);
4222
+ }
4223
+ }, [fetchAuthStatus, setLoading, setError]);
4224
+ return {
4225
+ authStatus,
4226
+ isOpen,
4227
+ initialized,
4228
+ fetchAuthStatus,
4229
+ checkOnboarding,
4230
+ setupWallet,
4231
+ importWallet,
4232
+ addProvider,
4233
+ removeProvider,
4234
+ completeOnboarding,
4235
+ startOAuth,
4236
+ startOAuthManual,
4237
+ exchangeOAuthCode,
4238
+ startCopilotDeviceFlow: apiClient.startCopilotDeviceFlow.bind(apiClient),
4239
+ pollCopilotDeviceFlow,
4240
+ getCopilotAuthMethods: apiClient.getCopilotAuthMethods.bind(apiClient),
4241
+ saveCopilotToken,
4242
+ importCopilotTokenFromGh,
4243
+ getCopilotDiagnostics: apiClient.getCopilotDiagnostics.bind(apiClient)
4244
+ };
4245
+ }
4246
+ // src/hooks/useTunnel.ts
4247
+ import { useQuery as useQuery10, useMutation as useMutation7, useQueryClient as useQueryClient9 } from "@tanstack/react-query";
4248
+ import { useEffect as useEffect12, useCallback as useCallback9, useRef as useRef5 } from "react";
4249
+ async function fetchTunnelStatus() {
4250
+ const response = await fetch(`${API_BASE_URL}/v1/tunnel/status`);
4251
+ if (!response.ok) {
4252
+ throw new Error("Failed to fetch tunnel status");
4253
+ }
4254
+ return response.json();
4255
+ }
4256
+ async function startTunnel() {
4257
+ const response = await fetch(`${API_BASE_URL}/v1/tunnel/start`, {
4258
+ method: "POST",
4259
+ headers: { "Content-Type": "application/json" },
4260
+ body: JSON.stringify({})
4261
+ });
4262
+ return response.json();
4263
+ }
4264
+ async function stopTunnel() {
4265
+ const response = await fetch(`${API_BASE_URL}/v1/tunnel/stop`, {
4266
+ method: "POST"
4267
+ });
4268
+ return response.json();
4269
+ }
4270
+ async function fetchTunnelQr() {
4271
+ const response = await fetch(`${API_BASE_URL}/v1/tunnel/qr`);
4272
+ return response.json();
4273
+ }
4274
+ function useTunnelStatus() {
4275
+ const setStatus = useTunnelStore((s) => s.setStatus);
4276
+ const setUrl = useTunnelStore((s) => s.setUrl);
4277
+ const setError = useTunnelStore((s) => s.setError);
4278
+ const query = useQuery10({
4279
+ queryKey: ["tunnel", "status"],
4280
+ queryFn: fetchTunnelStatus,
4281
+ refetchInterval: 3000
4282
+ });
4283
+ useEffect12(() => {
4284
+ if (query.data) {
4285
+ setStatus(query.data.status);
4286
+ setUrl(query.data.url);
4287
+ setError(query.data.error);
4288
+ }
4289
+ }, [query.data, setStatus, setUrl, setError]);
4290
+ return query;
4291
+ }
4292
+ function useStartTunnel() {
4293
+ const queryClient = useQueryClient9();
4294
+ const setStatus = useTunnelStore((s) => s.setStatus);
4295
+ const setUrl = useTunnelStore((s) => s.setUrl);
4296
+ const setError = useTunnelStore((s) => s.setError);
4297
+ const setProgress = useTunnelStore((s) => s.setProgress);
4298
+ return useMutation7({
4299
+ mutationFn: () => startTunnel(),
4300
+ onMutate: () => {
4301
+ setStatus("starting");
4302
+ setProgress("Connecting...");
4303
+ setError(null);
4304
+ },
4305
+ onSuccess: (data) => {
4306
+ if (data.ok && data.url) {
4307
+ setStatus("connected");
4308
+ setUrl(data.url);
4309
+ setProgress(null);
4310
+ } else if (!data.ok) {
4311
+ setStatus("error");
4312
+ setError(data.error || "Failed to start tunnel");
4313
+ setProgress(null);
4314
+ }
4315
+ queryClient.invalidateQueries({ queryKey: ["tunnel"] });
4316
+ },
4317
+ onError: (error) => {
4318
+ setStatus("error");
4319
+ setError(error.message);
4320
+ setProgress(null);
4321
+ }
4322
+ });
4323
+ }
4324
+ function useStopTunnel() {
4325
+ const queryClient = useQueryClient9();
4326
+ const reset = useTunnelStore((s) => s.reset);
4327
+ return useMutation7({
4328
+ mutationFn: stopTunnel,
4329
+ onSuccess: () => {
4330
+ reset();
4331
+ queryClient.invalidateQueries({ queryKey: ["tunnel"] });
4332
+ }
4333
+ });
4334
+ }
4335
+ function useTunnelQr() {
4336
+ const url = useTunnelStore((s) => s.url);
4337
+ const setQrCode = useTunnelStore((s) => s.setQrCode);
4338
+ const query = useQuery10({
4339
+ queryKey: ["tunnel", "qr", url],
4340
+ queryFn: fetchTunnelQr,
4341
+ enabled: !!url
4342
+ });
4343
+ useEffect12(() => {
4344
+ if (query.data?.ok && query.data.qrCode) {
4345
+ setQrCode(query.data.qrCode);
4346
+ }
4347
+ }, [query.data, setQrCode]);
4348
+ return query;
4349
+ }
4350
+ function useTunnelStream() {
4351
+ const setStatus = useTunnelStore((s) => s.setStatus);
4352
+ const setUrl = useTunnelStore((s) => s.setUrl);
4353
+ const setError = useTunnelStore((s) => s.setError);
4354
+ const setProgress = useTunnelStore((s) => s.setProgress);
4355
+ const isExpanded = useTunnelStore((s) => s.isExpanded);
4356
+ const eventSourceRef = useRef5(null);
4357
+ const connect = useCallback9(() => {
4358
+ if (eventSourceRef.current) {
4359
+ eventSourceRef.current.close();
4360
+ }
4361
+ const es = new EventSource(`${API_BASE_URL}/v1/tunnel/stream`);
4362
+ eventSourceRef.current = es;
4363
+ es.onmessage = (event) => {
4364
+ try {
4365
+ const data = JSON.parse(event.data);
4366
+ if (data.type === "status") {
4367
+ setStatus(data.status);
4368
+ setUrl(data.url);
4369
+ setError(data.error);
4370
+ setProgress(data.progress);
4371
+ }
4372
+ } catch {}
4373
+ };
4374
+ es.onerror = () => {
4375
+ es.close();
4376
+ eventSourceRef.current = null;
4377
+ };
4378
+ return () => {
4379
+ es.close();
4380
+ eventSourceRef.current = null;
4381
+ };
4382
+ }, [setStatus, setUrl, setError, setProgress]);
4383
+ useEffect12(() => {
4384
+ if (isExpanded) {
4385
+ const cleanup = connect();
4386
+ return cleanup;
4387
+ }
4388
+ return () => {
4389
+ if (eventSourceRef.current) {
4390
+ eventSourceRef.current.close();
4391
+ eventSourceRef.current = null;
4392
+ }
4393
+ };
4394
+ }, [isExpanded, connect]);
4395
+ return { connect };
4396
+ }
4397
+ // src/hooks/useProviderUsage.ts
4398
+ import { useEffect as useEffect13, useCallback as useCallback10 } from "react";
4399
+
4400
+ // src/stores/usageStore.ts
4401
+ import { create as create18 } from "zustand";
4402
+ var useUsageStore = create18((set) => ({
4403
+ usage: {},
4404
+ isLoading: {},
4405
+ lastFetched: {},
4406
+ isModalOpen: false,
4407
+ modalProvider: null,
4408
+ setUsage: (provider, data) => set((s) => ({ usage: { ...s.usage, [provider]: data } })),
4409
+ setLoading: (provider, loading) => set((s) => ({ isLoading: { ...s.isLoading, [provider]: loading } })),
4410
+ setLastFetched: (provider, time) => set((s) => ({ lastFetched: { ...s.lastFetched, [provider]: time } })),
4411
+ openModal: (provider) => set({ isModalOpen: true, modalProvider: provider }),
4412
+ closeModal: () => set({ isModalOpen: false, modalProvider: null })
4413
+ }));
4414
+
4415
+ // src/hooks/useProviderUsage.ts
4416
+ var POLL_INTERVAL = 60000;
4417
+ var STALE_THRESHOLD = 30000;
4418
+ function useProviderUsage(provider, authType) {
4419
+ const setUsage = useUsageStore((s) => s.setUsage);
4420
+ const setLoading = useUsageStore((s) => s.setLoading);
4421
+ const setLastFetched = useUsageStore((s) => s.setLastFetched);
4422
+ const usage = useUsageStore((s) => provider ? s.usage[provider] : undefined);
4423
+ const lastFetched = useUsageStore((s) => provider ? s.lastFetched[provider] : 0);
4424
+ const isOAuthProvider = authType === "oauth" && (provider === "anthropic" || provider === "openai");
4425
+ const fetchUsage = useCallback10(async () => {
4426
+ if (!provider || !isOAuthProvider)
4427
+ return;
4428
+ setLoading(provider, true);
4429
+ try {
4430
+ const data = await apiClient.getProviderUsage(provider);
4431
+ setUsage(provider, data);
4432
+ setLastFetched(provider, Date.now());
4433
+ } catch {} finally {
4434
+ setLoading(provider, false);
4435
+ }
4436
+ }, [provider, isOAuthProvider, setUsage, setLoading, setLastFetched]);
4437
+ useEffect13(() => {
4438
+ if (!isOAuthProvider)
4439
+ return;
4440
+ const isStale = !lastFetched || Date.now() - lastFetched > STALE_THRESHOLD;
4441
+ if (isStale) {
4442
+ fetchUsage();
4443
+ }
4444
+ const interval = setInterval(fetchUsage, POLL_INTERVAL);
4445
+ return () => clearInterval(interval);
4446
+ }, [isOAuthProvider, fetchUsage, lastFetched]);
4447
+ return {
4448
+ usage,
4449
+ fetchUsage,
4450
+ isOAuthProvider
4451
+ };
4452
+ }
4453
+ // src/hooks/useFileBrowser.ts
4454
+ import { useQuery as useQuery11 } from "@tanstack/react-query";
4455
+ function useFileTree(dirPath, enabled = true) {
4456
+ return useQuery11({
4457
+ queryKey: ["files", "tree", dirPath],
4458
+ queryFn: () => apiClient.getFileTree(dirPath),
4459
+ enabled,
4460
+ staleTime: 1e4,
4461
+ retry: 1
4462
+ });
4463
+ }
4464
+ function useFileContent(filePath) {
4465
+ return useQuery11({
4466
+ queryKey: ["files", "read", filePath],
4467
+ queryFn: () => filePath ? apiClient.readFileContent(filePath) : null,
4468
+ enabled: !!filePath,
4469
+ staleTime: 5000,
4470
+ retry: 1
4471
+ });
4472
+ }
4473
+ function useGitDiffFullFile(file, staged = false, enabled = false) {
4474
+ return useQuery11({
4475
+ queryKey: ["git", "diff", "fullFile", file, staged],
4476
+ queryFn: () => file ? apiClient.getGitDiffFullFile(file, staged) : null,
4477
+ enabled: enabled && !!file,
4478
+ retry: 1,
4479
+ refetchInterval: false
4480
+ });
4481
+ }
4482
+ // src/hooks/useMCP.ts
4483
+ import { useQuery as useQuery12, useMutation as useMutation8, useQueryClient as useQueryClient10 } from "@tanstack/react-query";
4484
+ import { useEffect as useEffect14, useRef as useRef6, useCallback as useCallback11 } from "react";
4485
+ import {
4486
+ listMcpServers,
4487
+ startMcpServer,
4488
+ stopMcpServer,
4489
+ addMcpServer,
4490
+ removeMcpServer,
4491
+ initiateMcpAuth,
4492
+ revokeMcpAuth,
4493
+ getMcpAuthStatus,
4494
+ completeMcpAuth
4495
+ } from "@ottocode/api";
4496
+ function useMCPServers() {
4497
+ const setServers = useMCPStore((s) => s.setServers);
4498
+ const query = useQuery12({
4499
+ queryKey: ["mcp", "servers"],
4500
+ queryFn: async () => {
4501
+ const { data } = await listMcpServers();
4502
+ return data;
4503
+ },
4504
+ refetchInterval: 1e4
4505
+ });
4506
+ useEffect14(() => {
4507
+ if (query.data?.servers) {
4508
+ setServers(query.data.servers);
4509
+ }
4510
+ }, [query.data, setServers]);
4511
+ return query;
4512
+ }
4513
+ function useStartMCPServer() {
4514
+ const queryClient = useQueryClient10();
4515
+ return useMutation8({
4516
+ mutationFn: async (name) => {
4517
+ const { data, error } = await startMcpServer({
4518
+ path: { name }
4519
+ });
4520
+ if (error)
4521
+ throw new Error("Failed to start server");
4522
+ const result = data;
4523
+ if (!result.ok)
4524
+ throw new Error(result.error || "Failed to start server");
4525
+ return result;
4526
+ },
4527
+ onSettled: () => {
4528
+ queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
4529
+ }
4530
+ });
4531
+ }
4532
+ function useStopMCPServer() {
4533
+ const queryClient = useQueryClient10();
4534
+ const setLoading = useMCPStore((s) => s.setLoading);
4535
+ return useMutation8({
4536
+ mutationFn: async (name) => {
4537
+ setLoading(name, true);
4538
+ const { data, error } = await stopMcpServer({
4539
+ path: { name }
4540
+ });
4541
+ if (error)
4542
+ throw new Error("Failed to stop server");
4543
+ const result = data;
4544
+ if (!result.ok)
4545
+ throw new Error(result.error || "Failed to stop server");
4546
+ return result;
4547
+ },
4548
+ onSettled: (_data, _error, name) => {
4549
+ setLoading(name, false);
4550
+ queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
4551
+ }
4552
+ });
4553
+ }
4554
+ function useAddMCPServer() {
4555
+ const queryClient = useQueryClient10();
4556
+ return useMutation8({
4557
+ mutationFn: async (params) => {
4558
+ const { data, error } = await addMcpServer({
4559
+ body: params
4560
+ });
4561
+ if (error)
4562
+ throw new Error("Failed to add MCP server");
4563
+ const result = data;
4564
+ if (!result.ok)
4565
+ throw new Error(result.error || "Failed to add MCP server");
4566
+ return result;
4567
+ },
4568
+ onSuccess: () => {
4569
+ queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
4570
+ }
4571
+ });
4572
+ }
4573
+ function useRemoveMCPServer() {
4574
+ const queryClient = useQueryClient10();
4575
+ return useMutation8({
4576
+ mutationFn: async (name) => {
4577
+ const { data, error } = await removeMcpServer({
4578
+ path: { name }
4579
+ });
4580
+ if (error)
4581
+ throw new Error("Failed to remove MCP server");
4582
+ const result = data;
4583
+ if (!result.ok)
4584
+ throw new Error(result.error || "Failed to remove MCP server");
4585
+ return result;
4586
+ },
4587
+ onSuccess: () => {
4588
+ queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
4589
+ }
4590
+ });
4591
+ }
4592
+ function useAuthenticateMCPServer() {
4593
+ const queryClient = useQueryClient10();
4594
+ return useMutation8({
4595
+ mutationFn: async (name) => {
4596
+ const { data, error } = await initiateMcpAuth({
4597
+ path: { name }
4598
+ });
4599
+ if (error)
4600
+ throw new Error("Failed to initiate auth");
4601
+ const result = data;
4602
+ if (!result.ok)
4603
+ throw new Error(result.error || "Failed to initiate auth");
4604
+ return result;
4605
+ },
4606
+ onSettled: () => {
4607
+ queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
4608
+ }
4609
+ });
4610
+ }
4611
+ function useRevokeMCPAuth() {
4612
+ const queryClient = useQueryClient10();
4613
+ return useMutation8({
4614
+ mutationFn: async (name) => {
4615
+ const { data, error } = await revokeMcpAuth({
4616
+ path: { name }
4617
+ });
4618
+ if (error)
4619
+ throw new Error("Failed to revoke auth");
4620
+ const result = data;
4621
+ if (!result.ok)
4622
+ throw new Error(result.error || "Failed to revoke auth");
4623
+ return result;
4624
+ },
4625
+ onSuccess: () => {
4626
+ queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
4627
+ }
4628
+ });
4629
+ }
4630
+ function useMCPAuthStatus(name) {
4631
+ return useQuery12({
4632
+ queryKey: ["mcp", "auth", name],
4633
+ queryFn: async () => {
4634
+ if (!name)
4635
+ return { authenticated: false };
4636
+ const { data } = await getMcpAuthStatus({
4637
+ path: { name }
4638
+ });
4639
+ return data;
4640
+ },
4641
+ enabled: !!name,
4642
+ refetchInterval: 3000
4643
+ });
4644
+ }
4645
+ function useCopilotDevicePoller() {
4646
+ const copilotDevice = useMCPStore((s) => s.copilotDevice);
4647
+ const setCopilotDevice = useMCPStore((s) => s.setCopilotDevice);
4648
+ const setLoading = useMCPStore((s) => s.setLoading);
4649
+ const queryClient = useQueryClient10();
4650
+ const timerRef = useRef6(null);
4651
+ const stopPolling = useCallback11(() => {
4652
+ if (timerRef.current) {
4653
+ clearInterval(timerRef.current);
4654
+ timerRef.current = null;
4655
+ }
4656
+ }, []);
4657
+ useEffect14(() => {
4658
+ if (!copilotDevice) {
4659
+ stopPolling();
4660
+ return;
4661
+ }
4662
+ const { sessionId, serverName, interval } = copilotDevice;
4663
+ const pollMs = (interval || 5) * 1000 + 1000;
4664
+ timerRef.current = setInterval(async () => {
4665
+ try {
4666
+ const { data } = await completeMcpAuth({
4667
+ path: { name: serverName },
4668
+ body: { sessionId }
4669
+ });
4670
+ const result = data;
4671
+ if (result?.status === "complete") {
4672
+ stopPolling();
4673
+ setCopilotDevice(null);
4674
+ setLoading(serverName, false);
4675
+ queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
4676
+ } else if (result?.status === "error") {
4677
+ stopPolling();
4678
+ setCopilotDevice(null);
4679
+ setLoading(serverName, false);
4680
+ }
4681
+ } catch {
4682
+ stopPolling();
4683
+ setCopilotDevice(null);
4684
+ setLoading(serverName, false);
4685
+ }
4686
+ }, pollMs);
4687
+ return stopPolling;
4688
+ }, [copilotDevice, setCopilotDevice, setLoading, queryClient, stopPolling]);
4689
+ return copilotDevice;
4690
+ }
4691
+ // src/hooks/useSkills.ts
4692
+ import { useQuery as useQuery13 } from "@tanstack/react-query";
4693
+ import { useEffect as useEffect15 } from "react";
4694
+ function useSkills() {
4695
+ const setSkills = useSkillsStore((s) => s.setSkills);
4696
+ const query = useQuery13({
4697
+ queryKey: ["skills"],
4698
+ queryFn: async () => {
4699
+ return apiClient.listSkills();
4700
+ },
4701
+ refetchInterval: 30000
4702
+ });
4703
+ useEffect15(() => {
4704
+ if (query.data?.skills) {
4705
+ setSkills(query.data.skills);
4706
+ }
4707
+ }, [query.data, setSkills]);
4708
+ return query;
4709
+ }
4710
+ function useSkillDetail(name) {
4711
+ return useQuery13({
4712
+ queryKey: ["skills", name],
4713
+ queryFn: async () => {
4714
+ if (!name)
4715
+ return null;
4716
+ return apiClient.getSkill(name);
4717
+ },
4718
+ enabled: !!name
4719
+ });
4720
+ }
4721
+ function useSkillFiles(name) {
4722
+ return useQuery13({
4723
+ queryKey: ["skills", name, "files"],
4724
+ queryFn: async () => {
4725
+ if (!name)
4726
+ return null;
4727
+ return apiClient.getSkillFiles(name);
4728
+ },
4729
+ enabled: !!name
4730
+ });
4731
+ }
4732
+ function useSkillFileContent(name, filePath) {
4733
+ return useQuery13({
4734
+ queryKey: ["skills", name, "files", filePath],
4735
+ queryFn: async () => {
4736
+ if (!name || !filePath)
4737
+ return null;
4738
+ return apiClient.getSkillFileContent(name, filePath);
4739
+ },
4740
+ enabled: !!name && !!filePath
4741
+ });
4742
+ }
4743
+ export {
4744
+ useWorkingDirectory,
4745
+ useUpdateSession,
4746
+ useUpdateDefaults,
4747
+ useUnstageFiles,
4748
+ useTunnelStream,
4749
+ useTunnelStatus,
4750
+ useTunnelQr,
4751
+ useTopupCallback,
4752
+ useToolApprovalShortcuts,
4753
+ useTheme,
4754
+ useStopTunnel,
4755
+ useStopMCPServer,
4756
+ useStartTunnel,
4757
+ useStartMCPServer,
4758
+ useStageFiles,
4759
+ useSkills,
4760
+ useSkillFiles,
4761
+ useSkillFileContent,
4762
+ useSkillDetail,
4763
+ useShareStatus,
4764
+ useSetuPayments,
4765
+ useSetuBalance,
4766
+ useSessionsInfinite,
4767
+ useSessions,
4768
+ useSessionStream,
4769
+ useSessionFiles,
4770
+ useSession,
4771
+ useSendMessage,
4772
+ useRevokeMCPAuth,
4773
+ useRestoreFiles,
4774
+ useResearchSessions,
4775
+ useRemoveRemote,
4776
+ useRemoveMCPServer,
4777
+ useQueueState,
4778
+ usePushCommits,
4779
+ usePullChanges,
4780
+ useProviderUsage,
4781
+ usePreferences,
4782
+ useParentSession,
4783
+ useModels,
4784
+ useMessages,
4785
+ useMessageQueuePosition,
4786
+ useMCPServers,
4787
+ useMCPAuthStatus,
4788
+ useKeyboardShortcuts,
4789
+ useInjectContext,
4790
+ useImageUpload,
4791
+ useGitStatus,
4792
+ useGitRemotes,
4793
+ useGitInit,
4794
+ useGitDiffFullFile,
4795
+ useGitDiff,
4796
+ useGitBranch,
4797
+ useGenerateCommitMessage,
4798
+ useFiles,
4799
+ useFileUpload,
4800
+ useFileTree,
4801
+ useFileContent,
4802
+ useExportToSession,
4803
+ useDeleteSession,
4804
+ useDeleteResearchSession,
4805
+ useDeleteFiles,
4806
+ useCreateSession,
4807
+ useCreateResearchSession,
4808
+ useCreateBranch,
4809
+ useCopilotDevicePoller,
4810
+ useConfig,
4811
+ useCommitChanges,
4812
+ useBranches,
4813
+ useAuthenticateMCPServer,
4814
+ useAuthStatus,
4815
+ useAllModels,
4816
+ useAddRemote,
4817
+ useAddMCPServer,
4818
+ sessionsQueryKey
4819
+ };
4820
+
4821
+ //# debugId=381FB663C2D4B88E64756E2164756E21