@ottocode/web-sdk 0.1.226 → 0.1.227

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