@data-club/ai-hub 0.0.1

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 (344) hide show
  1. package/dist/auth/passwordPolicy.d.ts +55 -0
  2. package/dist/auth/passwordPolicy.d.ts.map +1 -0
  3. package/dist/auth/passwordPolicy.js +63 -0
  4. package/dist/auth/passwordPolicy.js.map +1 -0
  5. package/dist/auth/texts.d.ts +815 -0
  6. package/dist/auth/texts.d.ts.map +1 -0
  7. package/dist/auth/texts.js +429 -0
  8. package/dist/auth/texts.js.map +1 -0
  9. package/dist/client/AIHubClient.d.ts +1058 -0
  10. package/dist/client/AIHubClient.d.ts.map +1 -0
  11. package/dist/client/AIHubClient.js +950 -0
  12. package/dist/client/AIHubClient.js.map +1 -0
  13. package/dist/client/index.d.ts +11 -0
  14. package/dist/client/index.d.ts.map +1 -0
  15. package/dist/client/index.js +9 -0
  16. package/dist/client/index.js.map +1 -0
  17. package/dist/client/sseParser.d.ts +41 -0
  18. package/dist/client/sseParser.d.ts.map +1 -0
  19. package/dist/client/sseParser.js +153 -0
  20. package/dist/client/sseParser.js.map +1 -0
  21. package/dist/client/tokenStorage.d.ts +56 -0
  22. package/dist/client/tokenStorage.d.ts.map +1 -0
  23. package/dist/client/tokenStorage.js +116 -0
  24. package/dist/client/tokenStorage.js.map +1 -0
  25. package/dist/components/AgentsPicker/index.d.ts +70 -0
  26. package/dist/components/AgentsPicker/index.d.ts.map +1 -0
  27. package/dist/components/AgentsPicker/index.js +63 -0
  28. package/dist/components/AgentsPicker/index.js.map +1 -0
  29. package/dist/components/AuthGate/index.d.ts +79 -0
  30. package/dist/components/AuthGate/index.d.ts.map +1 -0
  31. package/dist/components/AuthGate/index.js +61 -0
  32. package/dist/components/AuthGate/index.js.map +1 -0
  33. package/dist/components/CacheBadge/index.d.ts +88 -0
  34. package/dist/components/CacheBadge/index.d.ts.map +1 -0
  35. package/dist/components/CacheBadge/index.js +127 -0
  36. package/dist/components/CacheBadge/index.js.map +1 -0
  37. package/dist/components/ChangePassword/index.d.ts +109 -0
  38. package/dist/components/ChangePassword/index.d.ts.map +1 -0
  39. package/dist/components/ChangePassword/index.js +142 -0
  40. package/dist/components/ChangePassword/index.js.map +1 -0
  41. package/dist/components/ChatShell/ShellHeader.d.ts +44 -0
  42. package/dist/components/ChatShell/ShellHeader.d.ts.map +1 -0
  43. package/dist/components/ChatShell/ShellHeader.js +14 -0
  44. package/dist/components/ChatShell/ShellHeader.js.map +1 -0
  45. package/dist/components/ChatShell/UserMenu.d.ts +48 -0
  46. package/dist/components/ChatShell/UserMenu.d.ts.map +1 -0
  47. package/dist/components/ChatShell/UserMenu.js +86 -0
  48. package/dist/components/ChatShell/UserMenu.js.map +1 -0
  49. package/dist/components/ChatShell/index.d.ts +98 -0
  50. package/dist/components/ChatShell/index.d.ts.map +1 -0
  51. package/dist/components/ChatShell/index.js +124 -0
  52. package/dist/components/ChatShell/index.js.map +1 -0
  53. package/dist/components/DataClubAIHub/index.d.ts +132 -0
  54. package/dist/components/DataClubAIHub/index.d.ts.map +1 -0
  55. package/dist/components/DataClubAIHub/index.js +124 -0
  56. package/dist/components/DataClubAIHub/index.js.map +1 -0
  57. package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts +89 -0
  58. package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts.map +1 -0
  59. package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js +199 -0
  60. package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js.map +1 -0
  61. package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts +33 -0
  62. package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts.map +1 -0
  63. package/dist/components/DataClubAIHubAdmin/AgentsPage.js +137 -0
  64. package/dist/components/DataClubAIHubAdmin/AgentsPage.js.map +1 -0
  65. package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts +50 -0
  66. package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts.map +1 -0
  67. package/dist/components/DataClubAIHubAdmin/CreateUserModal.js +93 -0
  68. package/dist/components/DataClubAIHubAdmin/CreateUserModal.js.map +1 -0
  69. package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts +49 -0
  70. package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts.map +1 -0
  71. package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js +94 -0
  72. package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js.map +1 -0
  73. package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts +37 -0
  74. package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts.map +1 -0
  75. package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js +44 -0
  76. package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js.map +1 -0
  77. package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts +56 -0
  78. package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts.map +1 -0
  79. package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js +158 -0
  80. package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js.map +1 -0
  81. package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts +29 -0
  82. package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts.map +1 -0
  83. package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js +183 -0
  84. package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js.map +1 -0
  85. package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts +38 -0
  86. package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts.map +1 -0
  87. package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js +294 -0
  88. package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js.map +1 -0
  89. package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts +36 -0
  90. package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts.map +1 -0
  91. package/dist/components/DataClubAIHubAdmin/SettingsPage.js +373 -0
  92. package/dist/components/DataClubAIHubAdmin/SettingsPage.js.map +1 -0
  93. package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts +47 -0
  94. package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts.map +1 -0
  95. package/dist/components/DataClubAIHubAdmin/UsersPage.js +233 -0
  96. package/dist/components/DataClubAIHubAdmin/UsersPage.js.map +1 -0
  97. package/dist/components/DataClubAIHubAdmin/index.d.ts +87 -0
  98. package/dist/components/DataClubAIHubAdmin/index.d.ts.map +1 -0
  99. package/dist/components/DataClubAIHubAdmin/index.js +137 -0
  100. package/dist/components/DataClubAIHubAdmin/index.js.map +1 -0
  101. package/dist/components/DataClubAIHubLogin/index.d.ts +113 -0
  102. package/dist/components/DataClubAIHubLogin/index.d.ts.map +1 -0
  103. package/dist/components/DataClubAIHubLogin/index.js +123 -0
  104. package/dist/components/DataClubAIHubLogin/index.js.map +1 -0
  105. package/dist/components/EffortControl/index.d.ts +57 -0
  106. package/dist/components/EffortControl/index.d.ts.map +1 -0
  107. package/dist/components/EffortControl/index.js +84 -0
  108. package/dist/components/EffortControl/index.js.map +1 -0
  109. package/dist/components/Layout/index.d.ts +63 -0
  110. package/dist/components/Layout/index.d.ts.map +1 -0
  111. package/dist/components/Layout/index.js +45 -0
  112. package/dist/components/Layout/index.js.map +1 -0
  113. package/dist/components/MainColumn/index.d.ts +69 -0
  114. package/dist/components/MainColumn/index.d.ts.map +1 -0
  115. package/dist/components/MainColumn/index.js +47 -0
  116. package/dist/components/MainColumn/index.js.map +1 -0
  117. package/dist/components/MarkdownRenderer/index.d.ts +66 -0
  118. package/dist/components/MarkdownRenderer/index.d.ts.map +1 -0
  119. package/dist/components/MarkdownRenderer/index.js +153 -0
  120. package/dist/components/MarkdownRenderer/index.js.map +1 -0
  121. package/dist/components/MessageInput/index.d.ts +85 -0
  122. package/dist/components/MessageInput/index.d.ts.map +1 -0
  123. package/dist/components/MessageInput/index.js +139 -0
  124. package/dist/components/MessageInput/index.js.map +1 -0
  125. package/dist/components/MessageList/index.d.ts +156 -0
  126. package/dist/components/MessageList/index.d.ts.map +1 -0
  127. package/dist/components/MessageList/index.js +352 -0
  128. package/dist/components/MessageList/index.js.map +1 -0
  129. package/dist/components/ReasoningSelector/index.d.ts +77 -0
  130. package/dist/components/ReasoningSelector/index.d.ts.map +1 -0
  131. package/dist/components/ReasoningSelector/index.js +156 -0
  132. package/dist/components/ReasoningSelector/index.js.map +1 -0
  133. package/dist/components/ReasoningView/index.d.ts +94 -0
  134. package/dist/components/ReasoningView/index.d.ts.map +1 -0
  135. package/dist/components/ReasoningView/index.js +83 -0
  136. package/dist/components/ReasoningView/index.js.map +1 -0
  137. package/dist/components/ScrollToPresentButton/index.d.ts +54 -0
  138. package/dist/components/ScrollToPresentButton/index.d.ts.map +1 -0
  139. package/dist/components/ScrollToPresentButton/index.js +44 -0
  140. package/dist/components/ScrollToPresentButton/index.js.map +1 -0
  141. package/dist/components/SessionsPanel/SessionTitleModal.d.ts +79 -0
  142. package/dist/components/SessionsPanel/SessionTitleModal.d.ts.map +1 -0
  143. package/dist/components/SessionsPanel/SessionTitleModal.js +102 -0
  144. package/dist/components/SessionsPanel/SessionTitleModal.js.map +1 -0
  145. package/dist/components/SessionsPanel/index.d.ts +100 -0
  146. package/dist/components/SessionsPanel/index.d.ts.map +1 -0
  147. package/dist/components/SessionsPanel/index.js +176 -0
  148. package/dist/components/SessionsPanel/index.js.map +1 -0
  149. package/dist/components/SystemNotice/index.d.ts +66 -0
  150. package/dist/components/SystemNotice/index.d.ts.map +1 -0
  151. package/dist/components/SystemNotice/index.js +81 -0
  152. package/dist/components/SystemNotice/index.js.map +1 -0
  153. package/dist/components/ThemeSwitcher/index.d.ts +66 -0
  154. package/dist/components/ThemeSwitcher/index.d.ts.map +1 -0
  155. package/dist/components/ThemeSwitcher/index.js +73 -0
  156. package/dist/components/ThemeSwitcher/index.js.map +1 -0
  157. package/dist/components/ToolCallView/formatContent.d.ts +36 -0
  158. package/dist/components/ToolCallView/formatContent.d.ts.map +1 -0
  159. package/dist/components/ToolCallView/formatContent.js +53 -0
  160. package/dist/components/ToolCallView/formatContent.js.map +1 -0
  161. package/dist/components/ToolCallView/index.d.ts +120 -0
  162. package/dist/components/ToolCallView/index.d.ts.map +1 -0
  163. package/dist/components/ToolCallView/index.js +104 -0
  164. package/dist/components/ToolCallView/index.js.map +1 -0
  165. package/dist/components/TpmBanner/index.d.ts +53 -0
  166. package/dist/components/TpmBanner/index.d.ts.map +1 -0
  167. package/dist/components/TpmBanner/index.js +116 -0
  168. package/dist/components/TpmBanner/index.js.map +1 -0
  169. package/dist/components/TurnStatusIndicator/index.d.ts +77 -0
  170. package/dist/components/TurnStatusIndicator/index.d.ts.map +1 -0
  171. package/dist/components/TurnStatusIndicator/index.js +122 -0
  172. package/dist/components/TurnStatusIndicator/index.js.map +1 -0
  173. package/dist/components/internal/PasswordInput.d.ts +62 -0
  174. package/dist/components/internal/PasswordInput.d.ts.map +1 -0
  175. package/dist/components/internal/PasswordInput.js +42 -0
  176. package/dist/components/internal/PasswordInput.js.map +1 -0
  177. package/dist/context/AIHubContext.d.ts +220 -0
  178. package/dist/context/AIHubContext.d.ts.map +1 -0
  179. package/dist/context/AIHubContext.js +19 -0
  180. package/dist/context/AIHubContext.js.map +1 -0
  181. package/dist/context/AIHubProvider.d.ts +75 -0
  182. package/dist/context/AIHubProvider.d.ts.map +1 -0
  183. package/dist/context/AIHubProvider.js +954 -0
  184. package/dist/context/AIHubProvider.js.map +1 -0
  185. package/dist/context/index.d.ts +9 -0
  186. package/dist/context/index.d.ts.map +1 -0
  187. package/dist/context/index.js +9 -0
  188. package/dist/context/index.js.map +1 -0
  189. package/dist/context/theme.d.ts +43 -0
  190. package/dist/context/theme.d.ts.map +1 -0
  191. package/dist/context/theme.js +70 -0
  192. package/dist/context/theme.js.map +1 -0
  193. package/dist/context/useAIHub.d.ts +104 -0
  194. package/dist/context/useAIHub.d.ts.map +1 -0
  195. package/dist/context/useAIHub.js +63 -0
  196. package/dist/context/useAIHub.js.map +1 -0
  197. package/dist/context/useAIHubAuth.d.ts +44 -0
  198. package/dist/context/useAIHubAuth.d.ts.map +1 -0
  199. package/dist/context/useAIHubAuth.js +35 -0
  200. package/dist/context/useAIHubAuth.js.map +1 -0
  201. package/dist/index.d.ts +76 -0
  202. package/dist/index.d.ts.map +1 -0
  203. package/dist/index.js +57 -0
  204. package/dist/index.js.map +1 -0
  205. package/dist/primitives/index.d.ts +99 -0
  206. package/dist/primitives/index.d.ts.map +1 -0
  207. package/dist/primitives/index.js +97 -0
  208. package/dist/primitives/index.js.map +1 -0
  209. package/dist/state/affordances.d.ts +182 -0
  210. package/dist/state/affordances.d.ts.map +1 -0
  211. package/dist/state/affordances.js +189 -0
  212. package/dist/state/affordances.js.map +1 -0
  213. package/dist/state/stateReducer.d.ts +72 -0
  214. package/dist/state/stateReducer.d.ts.map +1 -0
  215. package/dist/state/stateReducer.js +122 -0
  216. package/dist/state/stateReducer.js.map +1 -0
  217. package/dist/stories/EffortControl.stories.d.ts +14 -0
  218. package/dist/stories/EffortControl.stories.d.ts.map +1 -0
  219. package/dist/stories/EffortControl.stories.js +43 -0
  220. package/dist/stories/EffortControl.stories.js.map +1 -0
  221. package/dist/stories/MarkdownRenderer.stories.d.ts +25 -0
  222. package/dist/stories/MarkdownRenderer.stories.d.ts.map +1 -0
  223. package/dist/stories/MarkdownRenderer.stories.js +178 -0
  224. package/dist/stories/MarkdownRenderer.stories.js.map +1 -0
  225. package/dist/stories/MessageBubble.stories.d.ts +21 -0
  226. package/dist/stories/MessageBubble.stories.d.ts.map +1 -0
  227. package/dist/stories/MessageBubble.stories.js +59 -0
  228. package/dist/stories/MessageBubble.stories.js.map +1 -0
  229. package/dist/stories/ReasoningView.stories.d.ts +18 -0
  230. package/dist/stories/ReasoningView.stories.d.ts.map +1 -0
  231. package/dist/stories/ReasoningView.stories.js +62 -0
  232. package/dist/stories/ReasoningView.stories.js.map +1 -0
  233. package/dist/stories/ScrollToPresentButton.stories.d.ts +18 -0
  234. package/dist/stories/ScrollToPresentButton.stories.d.ts.map +1 -0
  235. package/dist/stories/ScrollToPresentButton.stories.js +34 -0
  236. package/dist/stories/ScrollToPresentButton.stories.js.map +1 -0
  237. package/dist/stories/SystemNotice.stories.d.ts +17 -0
  238. package/dist/stories/SystemNotice.stories.d.ts.map +1 -0
  239. package/dist/stories/SystemNotice.stories.js +42 -0
  240. package/dist/stories/SystemNotice.stories.js.map +1 -0
  241. package/dist/stories/ThemeSwitcher.stories.d.ts +15 -0
  242. package/dist/stories/ThemeSwitcher.stories.d.ts.map +1 -0
  243. package/dist/stories/ThemeSwitcher.stories.js +35 -0
  244. package/dist/stories/ThemeSwitcher.stories.js.map +1 -0
  245. package/dist/stories/ToolCallView.stories.d.ts +20 -0
  246. package/dist/stories/ToolCallView.stories.d.ts.map +1 -0
  247. package/dist/stories/ToolCallView.stories.js +65 -0
  248. package/dist/stories/ToolCallView.stories.js.map +1 -0
  249. package/dist/stories/TpmBanner.stories.d.ts +24 -0
  250. package/dist/stories/TpmBanner.stories.d.ts.map +1 -0
  251. package/dist/stories/TpmBanner.stories.js +63 -0
  252. package/dist/stories/TpmBanner.stories.js.map +1 -0
  253. package/dist/stories/TurnStatusIndicator.stories.d.ts +25 -0
  254. package/dist/stories/TurnStatusIndicator.stories.d.ts.map +1 -0
  255. package/dist/stories/TurnStatusIndicator.stories.js +56 -0
  256. package/dist/stories/TurnStatusIndicator.stories.js.map +1 -0
  257. package/dist/stories/_mockProvider.d.ts +44 -0
  258. package/dist/stories/_mockProvider.d.ts.map +1 -0
  259. package/dist/stories/_mockProvider.js +84 -0
  260. package/dist/stories/_mockProvider.js.map +1 -0
  261. package/dist/types.d.ts +787 -0
  262. package/dist/types.d.ts.map +1 -0
  263. package/dist/types.js +19 -0
  264. package/dist/types.js.map +1 -0
  265. package/dist/webcomponent/defineWebComponent.d.ts +106 -0
  266. package/dist/webcomponent/defineWebComponent.d.ts.map +1 -0
  267. package/dist/webcomponent/defineWebComponent.js +126 -0
  268. package/dist/webcomponent/defineWebComponent.js.map +1 -0
  269. package/dist/webcomponent/index.d.ts +7 -0
  270. package/dist/webcomponent/index.d.ts.map +1 -0
  271. package/dist/webcomponent/index.js +7 -0
  272. package/dist/webcomponent/index.js.map +1 -0
  273. package/package.json +63 -0
  274. package/src/__lint-canary__/step1-lint-canary.ts.disabled +28 -0
  275. package/src/auth/passwordPolicy.ts +75 -0
  276. package/src/auth/texts.ts +1294 -0
  277. package/src/client/AIHubClient.ts +1718 -0
  278. package/src/client/index.ts +30 -0
  279. package/src/client/sseParser.ts +168 -0
  280. package/src/client/tokenStorage.ts +131 -0
  281. package/src/components/AgentsPicker/index.tsx +147 -0
  282. package/src/components/AuthGate/index.tsx +132 -0
  283. package/src/components/CacheBadge/index.tsx +245 -0
  284. package/src/components/ChangePassword/index.tsx +375 -0
  285. package/src/components/ChatShell/ShellHeader.tsx +80 -0
  286. package/src/components/ChatShell/UserMenu.tsx +196 -0
  287. package/src/components/ChatShell/index.tsx +307 -0
  288. package/src/components/DataClubAIHub/index.tsx +300 -0
  289. package/src/components/DataClubAIHubAdmin/AgentEditorModal.tsx +505 -0
  290. package/src/components/DataClubAIHubAdmin/AgentsPage.tsx +342 -0
  291. package/src/components/DataClubAIHubAdmin/CreateUserModal.tsx +214 -0
  292. package/src/components/DataClubAIHubAdmin/PasswordRevealModal.tsx +204 -0
  293. package/src/components/DataClubAIHubAdmin/ResetPasswordModal.tsx +122 -0
  294. package/src/components/DataClubAIHubAdmin/RotatingMessageEditorModal.tsx +408 -0
  295. package/src/components/DataClubAIHubAdmin/RotatingMessagesPage.tsx +373 -0
  296. package/src/components/DataClubAIHubAdmin/SessionsOverviewPage.tsx +639 -0
  297. package/src/components/DataClubAIHubAdmin/SettingsPage.tsx +979 -0
  298. package/src/components/DataClubAIHubAdmin/UsersPage.tsx +495 -0
  299. package/src/components/DataClubAIHubAdmin/index.tsx +277 -0
  300. package/src/components/DataClubAIHubLogin/index.tsx +282 -0
  301. package/src/components/EffortControl/index.tsx +138 -0
  302. package/src/components/Layout/index.tsx +92 -0
  303. package/src/components/MainColumn/index.tsx +131 -0
  304. package/src/components/MarkdownRenderer/index.tsx +256 -0
  305. package/src/components/MessageInput/index.tsx +283 -0
  306. package/src/components/MessageList/index.tsx +753 -0
  307. package/src/components/ReasoningSelector/index.tsx +268 -0
  308. package/src/components/ReasoningView/index.tsx +183 -0
  309. package/src/components/ScrollToPresentButton/index.tsx +85 -0
  310. package/src/components/SessionsPanel/SessionTitleModal.tsx +256 -0
  311. package/src/components/SessionsPanel/index.tsx +406 -0
  312. package/src/components/SystemNotice/index.tsx +151 -0
  313. package/src/components/ThemeSwitcher/index.tsx +136 -0
  314. package/src/components/ToolCallView/formatContent.ts +50 -0
  315. package/src/components/ToolCallView/index.tsx +274 -0
  316. package/src/components/TpmBanner/index.tsx +172 -0
  317. package/src/components/TurnStatusIndicator/index.tsx +203 -0
  318. package/src/components/internal/PasswordInput.tsx +145 -0
  319. package/src/context/AIHubContext.ts +249 -0
  320. package/src/context/AIHubProvider.tsx +1084 -0
  321. package/src/context/index.ts +9 -0
  322. package/src/context/theme.ts +76 -0
  323. package/src/context/useAIHub.ts +151 -0
  324. package/src/context/useAIHubAuth.ts +65 -0
  325. package/src/index.ts +275 -0
  326. package/src/primitives/index.ts +101 -0
  327. package/src/state/affordances.ts +321 -0
  328. package/src/state/stateReducer.ts +131 -0
  329. package/src/stories/EffortControl.stories.tsx +63 -0
  330. package/src/stories/MarkdownRenderer.stories.tsx +201 -0
  331. package/src/stories/MessageBubble.stories.tsx +101 -0
  332. package/src/stories/ReasoningView.stories.tsx +81 -0
  333. package/src/stories/ScrollToPresentButton.stories.tsx +52 -0
  334. package/src/stories/SystemNotice.stories.tsx +59 -0
  335. package/src/stories/ThemeSwitcher.stories.tsx +55 -0
  336. package/src/stories/ToolCallView.stories.tsx +95 -0
  337. package/src/stories/TpmBanner.stories.tsx +93 -0
  338. package/src/stories/TurnStatusIndicator.stories.tsx +86 -0
  339. package/src/stories/_mockProvider.tsx +116 -0
  340. package/src/styles/CLASSES.md +356 -0
  341. package/src/styles/default.css +2937 -0
  342. package/src/types.ts +867 -0
  343. package/src/webcomponent/defineWebComponent.tsx +193 -0
  344. package/src/webcomponent/index.ts +11 -0
@@ -0,0 +1,950 @@
1
+ /**
2
+ * `AIHubClient` — the typed TypeScript SDK for `@data-club/ai-hub-server`.
3
+ *
4
+ * Used internally by `<AIHubProvider>` to power every component in the
5
+ * library; also exported for apps that build entirely custom UIs without
6
+ * the React layer (§7.8).
7
+ *
8
+ * Design notes:
9
+ *
10
+ * - **Wire is snake_case; SDK arguments are camelCase.** The translation is
11
+ * per-method and explicit so the reader can see exactly which fields cross
12
+ * the boundary. The only header-level camelCase translation is on
13
+ * `login()`'s response: `must_change_password` → `mustChangePassword`
14
+ * (per §6.3.4). `user.*` and `deployment.*` keep their snake_case keys —
15
+ * they're echoed through to React state and the BEM CSS contract is
16
+ * already snake_case-aware.
17
+ * - **SSE uses `fetch` + `ReadableStream`, not `EventSource`** (§9.5 — the
18
+ * browser API can't carry custom auth headers).
19
+ * - **No silent refresh** (§6.7). A 401 on any protected request fires the
20
+ * `onUnauthorized` callback exactly once; the provider then routes the
21
+ * user back to `<DataClubAIHubLogin />`.
22
+ * - **Empty-title rejection on `createSession`** is enforced client-side
23
+ * before the request goes out so we don't burn an HTTP round-trip on a
24
+ * case the server will 400 anyway.
25
+ *
26
+ * @see SPECIFICATIONS.md §7.8 — client SDK
27
+ * @see SPECIFICATIONS.md §6.3 — API surface
28
+ * @see SPECIFICATIONS.md §6.7 — auth model
29
+ */
30
+ import { parseSseStream } from "./sseParser.js";
31
+ import { resolveTokenStorage, } from "./tokenStorage.js";
32
+ /* ────────────────────────────────────────────────────────────────────────── */
33
+ /* Internal helpers */
34
+ /* ────────────────────────────────────────────────────────────────────────── */
35
+ /**
36
+ * Thrown for any HTTP-level failure (non-2xx, network drop, etc.). The
37
+ * `code` discriminates by §7.2's `AIHubError` codes; the message is safe
38
+ * to render to end-users (it's either the server's `error.message` or a
39
+ * fixed transport-level string).
40
+ */
41
+ export class AIHubRequestError extends Error {
42
+ /** Machine-readable code; clients branch on this rather than the message. */
43
+ name = "AIHubRequestError";
44
+ /** Discriminator matching `AIHubError.code`. */
45
+ code;
46
+ /** HTTP status when one was available. */
47
+ status;
48
+ /** Pino `request_id` from the server for log correlation. */
49
+ request_id;
50
+ /**
51
+ * @param code - the error discriminator.
52
+ * @param message - the human-readable message.
53
+ * @param status - HTTP status, when available.
54
+ * @param requestId - request id, when available.
55
+ */
56
+ constructor(code, message, status, requestId) {
57
+ super(message);
58
+ this.code = code;
59
+ if (status !== undefined)
60
+ this.status = status;
61
+ if (requestId !== undefined)
62
+ this.request_id = requestId;
63
+ }
64
+ }
65
+ /* ────────────────────────────────────────────────────────────────────────── */
66
+ /* AIHubClient */
67
+ /* ────────────────────────────────────────────────────────────────────────── */
68
+ /**
69
+ * The typed SDK every React surface in the library is built on.
70
+ *
71
+ * The client owns the JWT lifecycle (storage + clearing on 401) and the
72
+ * SSE plumbing; everything else is a thin HTTP wrapper that translates
73
+ * camelCase arguments into the server's snake_case wire format.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const client = new AIHubClient({ baseUrl: "/api/ai-hub" });
78
+ * const { token, mustChangePassword, user } = await client.login("alice", "secret");
79
+ * if (!mustChangePassword) {
80
+ * for await (const event of client.streamChat({
81
+ * sessionId: session.id,
82
+ * userMessage: "Hello.",
83
+ * })) {
84
+ * console.log(event);
85
+ * }
86
+ * }
87
+ * ```
88
+ */
89
+ export class AIHubClient {
90
+ baseUrl;
91
+ fetchImpl;
92
+ tokenStore;
93
+ onUnauthorized;
94
+ /** Guards against firing `onUnauthorized` more than once per 401 storm. */
95
+ unauthorizedFired = false;
96
+ /**
97
+ * @param options - constructor options. `baseUrl` is required; everything
98
+ * else has a sensible default.
99
+ */
100
+ constructor(options) {
101
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
102
+ this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
103
+ this.tokenStore = resolveTokenStorage(options.tokenStorage ?? "localStorage");
104
+ if (options.onUnauthorized)
105
+ this.onUnauthorized = options.onUnauthorized;
106
+ }
107
+ /* ───── token surface ──────────────────────────────────────────────── */
108
+ /** Returns the currently-stored token, or null. */
109
+ getToken() {
110
+ return this.tokenStore.get();
111
+ }
112
+ /**
113
+ * Replaces the stored token. Used by the provider after `login` /
114
+ * `changePassword` to persist the JWT before the next request fires.
115
+ *
116
+ * @param token - the bearer JWT to store.
117
+ */
118
+ setToken(token) {
119
+ this.tokenStore.set(token);
120
+ this.unauthorizedFired = false;
121
+ }
122
+ /** Clears the stored token. Idempotent. */
123
+ clearToken() {
124
+ this.tokenStore.clear();
125
+ }
126
+ /* ───── auth ───────────────────────────────────────────────────────── */
127
+ /**
128
+ * `POST /auth/login`. On success, persists the returned token automatically
129
+ * so subsequent calls authenticate without the caller threading the JWT
130
+ * through.
131
+ *
132
+ * @param username - the user's login id.
133
+ * @param password - plaintext password (sent over HTTPS to the server).
134
+ * @returns the login response, with `mustChangePassword` camelcased.
135
+ */
136
+ async login(username, password) {
137
+ const raw = await this.requestJson("POST", "/auth/login", {
138
+ body: { username, password },
139
+ authenticated: false,
140
+ });
141
+ this.setToken(raw.token);
142
+ const response = {
143
+ token: raw.token,
144
+ mustChangePassword: raw.must_change_password,
145
+ user: raw.user,
146
+ };
147
+ if (raw.deployment)
148
+ response.deployment = raw.deployment;
149
+ return response;
150
+ }
151
+ /**
152
+ * `POST /auth/change-password`. Returns the fresh session token (the
153
+ * change-only token is consumed by this call).
154
+ *
155
+ * @param currentPassword - the user's existing password, or null when
156
+ * the caller is authenticating with a password-change-only token
157
+ * (forced-change flow — §8.5).
158
+ * @param newPassword - the new password. Must satisfy the §8.5 policy
159
+ * (≥8 chars, ≥2 of {letters, digits, special}).
160
+ * @returns the new session token, persisted automatically.
161
+ */
162
+ async changePassword(currentPassword, newPassword) {
163
+ const body = { new_password: newPassword };
164
+ if (currentPassword !== null)
165
+ body.current_password = currentPassword;
166
+ const result = await this.requestJson("POST", "/auth/change-password", { body });
167
+ this.setToken(result.token);
168
+ return result;
169
+ }
170
+ /**
171
+ * `POST /auth/logout`. Server-side this is a no-op in v1 (no token
172
+ * blacklist — §6.3.4); we issue it for symmetry and to give the server
173
+ * an audit-log entry. Always clears the local token afterward, even
174
+ * when the request fails.
175
+ */
176
+ async logout() {
177
+ try {
178
+ await this.requestJson("POST", "/auth/logout", { body: {} });
179
+ }
180
+ catch {
181
+ /* network errors here are not fatal — clear and move on */
182
+ }
183
+ finally {
184
+ this.clearToken();
185
+ }
186
+ }
187
+ /**
188
+ * `GET /auth/me` — fetches the `{ user, deployment }` envelope.
189
+ */
190
+ async me() {
191
+ return this.requestJson("GET", "/auth/me");
192
+ }
193
+ /**
194
+ * `GET /feature-toggles` — fetches the deployment-wide UI feature
195
+ * toggles set by `super_admin` (§8.4). **Unauthenticated** — no token
196
+ * required, every role gets the same response. The provider calls this
197
+ * on mount so the §8.4 precedence ("server toggle > React prop >
198
+ * built-in default") applies before the chat shell first renders.
199
+ *
200
+ * Missing keys are returned absent in the response (not coerced to
201
+ * `false`) so the client can distinguish "explicitly off" from "fall
202
+ * through to the React prop default."
203
+ *
204
+ * @returns the wire-format toggles blob; may be empty (`{}`) when the
205
+ * super_admin hasn't set anything.
206
+ */
207
+ async getFeatureToggles() {
208
+ const result = await this.requestJson("GET", "/feature-toggles");
209
+ return result.feature_toggles ?? {};
210
+ }
211
+ /**
212
+ * `PATCH /auth/me/preferences` — updates the caller's stored
213
+ * `theme_preference`. Returns the refreshed envelope.
214
+ *
215
+ * Rejected with `AIHubRequestError(code: "theme_switcher_disabled")` (HTTP
216
+ * 409) when the deployment has the switcher turned off (§7.6).
217
+ *
218
+ * @param prefs - the preferences patch (currently only `theme`).
219
+ * @param prefs.theme - new theme preference (`"light"` or `"dark"`).
220
+ */
221
+ async updatePreferences(prefs) {
222
+ return this.requestJson("PATCH", "/auth/me/preferences", { body: prefs });
223
+ }
224
+ /* ───── sessions ───────────────────────────────────────────────────── */
225
+ /**
226
+ * `GET /sessions?agent_id=...&include_archived=...`.
227
+ *
228
+ * @param options - filter arguments; `agentId` is required.
229
+ */
230
+ async listSessions(options) {
231
+ const query = new URLSearchParams({ agent_id: options.agentId });
232
+ if (options.includeArchived)
233
+ query.set("include_archived", "true");
234
+ const result = await this.requestJson("GET", `/sessions?${query.toString()}`);
235
+ return result.sessions;
236
+ }
237
+ /**
238
+ * `POST /sessions`.
239
+ *
240
+ * Client-side validation: title must be 1..80 chars. Empty / oversize
241
+ * titles throw `AIHubRequestError(code: "validation")` synchronously
242
+ * without an HTTP round-trip.
243
+ *
244
+ * @param options - the agent id + non-empty title.
245
+ * @returns the created session row.
246
+ */
247
+ async createSession(options) {
248
+ const title = options.title;
249
+ if (typeof title !== "string" || title.length === 0) {
250
+ throw new AIHubRequestError("validation", "Session title is required (1..80 characters).");
251
+ }
252
+ if (title.length > 80) {
253
+ throw new AIHubRequestError("validation", "Session title must be 80 characters or fewer.");
254
+ }
255
+ return this.requestJson("POST", "/sessions", {
256
+ body: { agent_id: options.agentId, title },
257
+ });
258
+ }
259
+ /** `GET /sessions/:id`. */
260
+ async getSession(id) {
261
+ return this.requestJson("GET", `/sessions/${encodeURIComponent(id)}`);
262
+ }
263
+ /**
264
+ * `PATCH /sessions/:id` — rename and/or archive in one call. An empty
265
+ * patch (`{}`) returns the row unchanged.
266
+ *
267
+ * @param id - the session id.
268
+ * @param patch - the fields to update.
269
+ */
270
+ async patchSession(id, patch) {
271
+ const body = {};
272
+ if (patch.title !== undefined)
273
+ body.title = patch.title;
274
+ if (patch.isArchived !== undefined)
275
+ body.is_archived = patch.isArchived;
276
+ return this.requestJson("PATCH", `/sessions/${encodeURIComponent(id)}`, { body });
277
+ }
278
+ /**
279
+ * `PATCH /sessions/:id/reasoning` — update the user's per-session reasoning
280
+ * settings. The server applies them to the agent's `model_settings`
281
+ * automatically before the next chat turn.
282
+ *
283
+ * Returns the refreshed session including `capabilities`, so the caller
284
+ * can re-render UI controls without a separate GET.
285
+ */
286
+ async patchSessionReasoning(id, patch) {
287
+ const body = {};
288
+ if (patch.effort !== undefined)
289
+ body.effort = patch.effort;
290
+ if (patch.verbosity !== undefined)
291
+ body.verbosity = patch.verbosity;
292
+ if (patch.thinkingBudget !== undefined)
293
+ body.thinking_budget = patch.thinkingBudget;
294
+ return this.requestJson("PATCH", `/sessions/${encodeURIComponent(id)}/reasoning`, { body });
295
+ }
296
+ /** `GET /sessions/:id/messages`. */
297
+ async getSessionMessages(id) {
298
+ const result = await this.requestJson("GET", `/sessions/${encodeURIComponent(id)}/messages`);
299
+ return result.messages;
300
+ }
301
+ /* ───── message overrides ──────────────────────────────────────────── */
302
+ /**
303
+ * `GET /sessions/:id/message-overrides` — returns the caller's per-message
304
+ * content substitutions for this session's underlying Letta conversation.
305
+ *
306
+ * Overrides are keyed by `ordinal_position` (0-based index into Letta's
307
+ * chronologically-sorted message list). They're applied automatically by
308
+ * `getSessionMessages` — callers don't need to merge manually; this
309
+ * endpoint exists so custom Tier-3 UIs can render an edit affordance
310
+ * next to each message that has an override.
311
+ *
312
+ * @param sessionId - the ai-hub session id.
313
+ * @returns the overrides sorted by `ordinal_position` ascending.
314
+ */
315
+ async listMessageOverrides(sessionId) {
316
+ const result = await this.requestJson("GET", `/sessions/${encodeURIComponent(sessionId)}/message-overrides`);
317
+ return result.overrides;
318
+ }
319
+ /**
320
+ * `PUT /sessions/:id/message-overrides/:position` — upsert one or both
321
+ * override layers for the message at `ordinalPosition`.
322
+ *
323
+ * Pass `systemOverride` to set the LLM-facing view, `userOverride` to
324
+ * set the UI-facing view, or both. At least one must be provided.
325
+ * **Merge semantics**: only the layer(s) you pass are written; an
326
+ * existing value on the other layer is preserved.
327
+ *
328
+ * **Validation gates** (server-enforced):
329
+ *
330
+ * - `409 turn_in_progress` — a turn is currently live for this session.
331
+ * - `409 message_not_found` — no Letta message exists at this position
332
+ * yet (typical when a tier-3 caller tries to override a message
333
+ * before it has landed; wait for the turn to finish first).
334
+ *
335
+ * Pass empty string for `userOverride` to visually hide the message
336
+ * (the bubble slot stays — use `deleteMessageOverride` to remove the
337
+ * slot entirely).
338
+ *
339
+ * @param sessionId - the ai-hub session id.
340
+ * @param ordinalPosition - the 0-based index in Letta's message list.
341
+ * @param override - one or both layers to write.
342
+ * @returns the resulting override row.
343
+ */
344
+ async setMessageOverride(sessionId, ordinalPosition, override) {
345
+ const body = {};
346
+ if (override.systemOverride !== undefined)
347
+ body.system_override = override.systemOverride;
348
+ if (override.userOverride !== undefined)
349
+ body.user_override = override.userOverride;
350
+ return this.requestJson("PUT", `/sessions/${encodeURIComponent(sessionId)}/message-overrides/${ordinalPosition}`, { body });
351
+ }
352
+ /**
353
+ * `DELETE /sessions/:id/message-overrides/:position` — remove an override.
354
+ *
355
+ * Idempotent: returns successfully whether or not the row existed.
356
+ *
357
+ * @param sessionId - the ai-hub session id.
358
+ * @param ordinalPosition - the 0-based index in Letta's message list.
359
+ */
360
+ async deleteMessageOverride(sessionId, ordinalPosition) {
361
+ await this.requestJson("DELETE", `/sessions/${encodeURIComponent(sessionId)}/message-overrides/${ordinalPosition}`);
362
+ }
363
+ /* ───── agents ─────────────────────────────────────────────────────── */
364
+ /**
365
+ * `GET /agents` — returns the user-facing agent catalog (non-archived
366
+ * only) with each entry's `session_count` scoped to the caller.
367
+ */
368
+ async listAgents() {
369
+ const result = await this.requestJson("GET", "/agents");
370
+ return result.agents;
371
+ }
372
+ /* ───── chat ───────────────────────────────────────────────────────── */
373
+ /**
374
+ * `POST /chat` — opens an SSE stream. Returns an async iterable of
375
+ * `AIHubEvent`s; consume with `for await (const event of …)`.
376
+ *
377
+ * The iterable terminates when the server emits `stream_end` (the bytes
378
+ * stop after that). To abort early, pass `opts.signal`.
379
+ *
380
+ * @param body - the request body (sessionId, userMessage, optional effort,
381
+ * optional idempotencyKey).
382
+ * @param opts - optional abort signal and resumption hint.
383
+ */
384
+ streamChat(body, opts = {}) {
385
+ const wireBody = {
386
+ session_id: body.sessionId,
387
+ user_message: body.userMessage,
388
+ };
389
+ if (body.effort !== undefined)
390
+ wireBody.effort = body.effort;
391
+ if (body.idempotencyKey !== undefined) {
392
+ wireBody.idempotency_key = body.idempotencyKey;
393
+ }
394
+ if (body.userOverride !== undefined) {
395
+ wireBody.user_override = body.userOverride;
396
+ }
397
+ return this.streamRequest("POST", "/chat", wireBody, opts);
398
+ }
399
+ /**
400
+ * `GET /chat/:turn_id/events?from_seq=N` — resumption replay then
401
+ * live-attach.
402
+ *
403
+ * @param turnId - the turn to resume.
404
+ * @param opts - optional abort signal and `fromSeq` cursor (defaults to
405
+ * `-1` so every persisted event is replayed).
406
+ */
407
+ getTurnEvents(turnId, opts = {}) {
408
+ const fromSeq = opts.fromSeq ?? -1;
409
+ const path = `/chat/${encodeURIComponent(turnId)}/events?from_seq=${encodeURIComponent(String(fromSeq))}`;
410
+ return this.streamRequest("GET", path, undefined, opts);
411
+ }
412
+ /** `POST /chat/:turn_id/cancel`. */
413
+ async cancelTurn(turnId) {
414
+ return this.requestJson("POST", `/chat/${encodeURIComponent(turnId)}/cancel`, { body: {} });
415
+ }
416
+ /* ───── admin: users ───────────────────────────────────────────────── */
417
+ /**
418
+ * `GET /admin/users` — fetches every user row in the deployment.
419
+ *
420
+ * Caller must be `admin` or `super_admin`; the server returns 403
421
+ * otherwise (the SDK surfaces it as `AIHubRequestError(code: "forbidden")`).
422
+ *
423
+ * @returns the user rows, sorted alphabetically by username.
424
+ * @throws {AIHubRequestError} `forbidden` for `user` / `power_user` callers.
425
+ */
426
+ async listAdminUsers() {
427
+ const result = await this.requestJson("GET", "/admin/users");
428
+ return result.users;
429
+ }
430
+ /**
431
+ * `POST /admin/users` — creates a user; the server generates the password.
432
+ *
433
+ * Returns the new row plus the **one-time** plaintext password
434
+ * (`generatedPassword`). The caller is responsible for surfacing it
435
+ * exactly once in the UI — there is no way to retrieve it later.
436
+ *
437
+ * Admins cannot create `super_admin`s (server returns 403 if the body
438
+ * requests it). On username collision returns 409 `username_taken`.
439
+ *
440
+ * @param body - the new user's identity + initial role.
441
+ * @returns the created row and the one-time password.
442
+ * @throws {AIHubRequestError} `forbidden` (caller role insufficient or
443
+ * admin tried to grant super_admin), `validation` (malformed body),
444
+ * or `username_taken` (409).
445
+ */
446
+ async createAdminUser(body) {
447
+ const wireBody = { username: body.username };
448
+ if (body.displayName !== undefined) {
449
+ wireBody.display_name = body.displayName;
450
+ }
451
+ if (body.role !== undefined)
452
+ wireBody.role = body.role;
453
+ const result = await this.requestJson("POST", "/admin/users", { body: wireBody });
454
+ return {
455
+ user: result.user,
456
+ generatedPassword: result.generated_password,
457
+ };
458
+ }
459
+ /**
460
+ * `PATCH /admin/users/:id/role` — changes a user's role.
461
+ *
462
+ * The server enforces the admin-shield + super-admin-min-count rules
463
+ * transactionally (§8.1). Violations come back as 403 with codes like
464
+ * `admin_shield`, `super_admin_min_count`, or `forbidden`.
465
+ *
466
+ * @param id - the target user's id.
467
+ * @param role - the new role.
468
+ * @returns the updated row.
469
+ * @throws {AIHubRequestError} `forbidden` (shield / min-count / caller
470
+ * cannot grant this role).
471
+ */
472
+ async patchAdminUserRole(id, role) {
473
+ const result = await this.requestJson("PATCH", `/admin/users/${encodeURIComponent(id)}/role`, { body: { role } });
474
+ return result.user;
475
+ }
476
+ /**
477
+ * `POST /admin/users/:id/reset-password` — issues a fresh password.
478
+ *
479
+ * Sets `is_initial_password = true` on the target. The new plaintext is
480
+ * returned **exactly once** in `generatedPassword`; the admin UI must
481
+ * surface it via the one-time-reveal modal.
482
+ *
483
+ * @param id - the target user's id.
484
+ * @returns the one-time plaintext.
485
+ * @throws {AIHubRequestError} `forbidden` (shield / min-count).
486
+ */
487
+ async resetAdminUserPassword(id) {
488
+ const result = await this.requestJson("POST", `/admin/users/${encodeURIComponent(id)}/reset-password`, { body: {} });
489
+ return { generatedPassword: result.generated_password };
490
+ }
491
+ /**
492
+ * `DELETE /admin/users/:id` — hard-deletes a user.
493
+ *
494
+ * Cascades to that user's sessions via the FK. The server blocks
495
+ * self-delete (returns 403) so a careless click can't brick the caller's
496
+ * own account; shield + min-count apply as for role change.
497
+ *
498
+ * @param id - the target user's id.
499
+ * @throws {AIHubRequestError} `forbidden` (shield, min-count, or
500
+ * attempting to delete oneself).
501
+ */
502
+ async deleteAdminUser(id) {
503
+ await this.requestJson("DELETE", `/admin/users/${encodeURIComponent(id)}`);
504
+ }
505
+ /* ───── admin: agents ──────────────────────────────────────────────── */
506
+ /**
507
+ * `GET /admin/agents` — every ai-hub agent in the deployment, including
508
+ * archived ones. Caller must be `power_user`+ per §8.2.
509
+ *
510
+ * Sort order: ascending by `display_name` (the server's `ORDER BY`).
511
+ *
512
+ * @returns the agent rows.
513
+ * @throws {AIHubRequestError} `forbidden` for `user` callers.
514
+ */
515
+ async listAdminAgents() {
516
+ const result = await this.requestJson("GET", "/admin/agents");
517
+ return result.agents;
518
+ }
519
+ /**
520
+ * `POST /admin/agents` — register a new ai-hub agent in the catalog.
521
+ *
522
+ * The server validates `lettaAgentId` against Letta; if the id isn't
523
+ * resolvable the response is 400 `letta_agent_not_found` (surface inline
524
+ * on the form field).
525
+ *
526
+ * @param body - the display name + assigned Letta agent id.
527
+ * @returns the newly-created row.
528
+ * @throws {AIHubRequestError} `letta_agent_not_found`, `validation`, `forbidden`.
529
+ */
530
+ async createAdminAgent(body) {
531
+ const wire = {
532
+ display_name: body.displayName,
533
+ letta_agent_id: body.lettaAgentId,
534
+ };
535
+ if (body.sandboxSeedName !== undefined) {
536
+ wire.sandbox_seed_name = body.sandboxSeedName;
537
+ }
538
+ if (body.forkSourceConversationId !== undefined) {
539
+ wire.fork_source_conversation_id = body.forkSourceConversationId;
540
+ }
541
+ return this.requestJson("POST", "/admin/agents", {
542
+ body: wire,
543
+ });
544
+ }
545
+ /**
546
+ * `PATCH /admin/agents/:id` — update one or more fields. Every field is
547
+ * optional; an empty patch is a 200 no-op.
548
+ *
549
+ * Archive / unarchive flows through this same endpoint via `isArchived`.
550
+ *
551
+ * @param id - the ai-hub agent id.
552
+ * @param body - the fields to update.
553
+ * @returns the refreshed row.
554
+ * @throws {AIHubRequestError} `letta_agent_not_found` if `lettaAgentId`
555
+ * was supplied and doesn't resolve in Letta.
556
+ */
557
+ async patchAdminAgent(id, body) {
558
+ const wire = {};
559
+ if (body.displayName !== undefined)
560
+ wire.display_name = body.displayName;
561
+ if (body.lettaAgentId !== undefined)
562
+ wire.letta_agent_id = body.lettaAgentId;
563
+ if (body.isArchived !== undefined)
564
+ wire.is_archived = body.isArchived;
565
+ if (body.sandboxSeedName !== undefined) {
566
+ wire.sandbox_seed_name = body.sandboxSeedName;
567
+ }
568
+ if (body.forkSourceConversationId !== undefined) {
569
+ wire.fork_source_conversation_id = body.forkSourceConversationId;
570
+ }
571
+ return this.requestJson("PATCH", `/admin/agents/${encodeURIComponent(id)}`, { body: wire });
572
+ }
573
+ /* ───── admin: rotating messages ────────────────────────────────────── */
574
+ /**
575
+ * `GET /admin/rotating-messages` — every rotating starting message in the
576
+ * deployment (archived included), sorted by display name. `power_user`+.
577
+ *
578
+ * @returns the rows.
579
+ * @throws {AIHubRequestError} `forbidden` for `user` callers.
580
+ */
581
+ async listAdminRotatingMessages() {
582
+ const result = await this.requestJson("GET", "/admin/rotating-messages");
583
+ return result.rotating_messages;
584
+ }
585
+ /**
586
+ * `POST /admin/rotating-messages` — create a rotating starting message. The
587
+ * server validates `lettaAgentId` against Letta (`letta_agent_not_found`)
588
+ * and the schedule shape (`invalid_schedule`).
589
+ *
590
+ * @param body - the prompt, agent, and schedule config.
591
+ * @returns the newly-created row (including its `rmsg-…` id).
592
+ * @throws {AIHubRequestError} `letta_agent_not_found`, `invalid_schedule`, `validation`, `forbidden`.
593
+ */
594
+ async createAdminRotatingMessage(body) {
595
+ const wire = {
596
+ display_name: body.displayName,
597
+ prompt: body.prompt,
598
+ letta_agent_id: body.lettaAgentId,
599
+ schedule_kind: body.scheduleKind,
600
+ };
601
+ if (body.intervalSeconds !== undefined)
602
+ wire.interval_seconds = body.intervalSeconds;
603
+ if (body.dailyTime !== undefined)
604
+ wire.daily_time = body.dailyTime;
605
+ if (body.cronExpression !== undefined)
606
+ wire.cron_expression = body.cronExpression;
607
+ return this.requestJson("POST", "/admin/rotating-messages", { body: wire });
608
+ }
609
+ /**
610
+ * `PATCH /admin/rotating-messages/:id` — update one or more fields. Every
611
+ * field is optional; an empty patch is a 200 no-op. Changing the schedule
612
+ * recomputes the next rotation time.
613
+ *
614
+ * @param id - the rotating message id.
615
+ * @param body - the fields to update.
616
+ * @returns the refreshed row.
617
+ * @throws {AIHubRequestError} `letta_agent_not_found`, `invalid_schedule`, `not_found`, `forbidden`.
618
+ */
619
+ async patchAdminRotatingMessage(id, body) {
620
+ const wire = {};
621
+ if (body.displayName !== undefined)
622
+ wire.display_name = body.displayName;
623
+ if (body.prompt !== undefined)
624
+ wire.prompt = body.prompt;
625
+ if (body.lettaAgentId !== undefined)
626
+ wire.letta_agent_id = body.lettaAgentId;
627
+ if (body.scheduleKind !== undefined)
628
+ wire.schedule_kind = body.scheduleKind;
629
+ if (body.intervalSeconds !== undefined)
630
+ wire.interval_seconds = body.intervalSeconds;
631
+ if (body.dailyTime !== undefined)
632
+ wire.daily_time = body.dailyTime;
633
+ if (body.cronExpression !== undefined)
634
+ wire.cron_expression = body.cronExpression;
635
+ if (body.isArchived !== undefined)
636
+ wire.is_archived = body.isArchived;
637
+ return this.requestJson("PATCH", `/admin/rotating-messages/${encodeURIComponent(id)}`, { body: wire });
638
+ }
639
+ /**
640
+ * `POST /admin/rotating-messages/:id/rotate-now` — run a rotation inline
641
+ * (preview / force-refresh). May take a while — it spawns the Letta Code
642
+ * subprocess like a real chat turn.
643
+ *
644
+ * @param id - the rotating message id.
645
+ * @returns the refreshed row plus the attempt's outcome.
646
+ * @throws {AIHubRequestError} `not_found`, `forbidden`.
647
+ */
648
+ async rotateAdminRotatingMessageNow(id) {
649
+ return this.requestJson("POST", `/admin/rotating-messages/${encodeURIComponent(id)}/rotate-now`, { body: {} });
650
+ }
651
+ /* ───── admin: sessions overview ───────────────────────────────────── */
652
+ /**
653
+ * `GET /admin/sessions` — paginated read-only browser over every session
654
+ * in the deployment. `power_user`+ per §8.2.
655
+ *
656
+ * Each row carries `owner_username` + `agent_display_name` joined
657
+ * server-side. Use `nextCursor` (when non-null) as the `cursor` for the
658
+ * next page.
659
+ *
660
+ * @param options - filter + pagination args. All optional.
661
+ * @returns the page plus the next-cursor.
662
+ */
663
+ async listAdminSessions(options = {}) {
664
+ const query = new URLSearchParams();
665
+ if (options.ownerId !== undefined)
666
+ query.set("owner_id", options.ownerId);
667
+ if (options.agentId !== undefined)
668
+ query.set("agent_id", options.agentId);
669
+ if (options.status !== undefined)
670
+ query.set("status", options.status);
671
+ if (options.search !== undefined && options.search.length > 0) {
672
+ query.set("search", options.search);
673
+ }
674
+ if (options.cursor !== undefined)
675
+ query.set("cursor", options.cursor);
676
+ if (options.limit !== undefined)
677
+ query.set("limit", String(options.limit));
678
+ const qs = query.toString();
679
+ const path = qs.length > 0 ? `/admin/sessions?${qs}` : "/admin/sessions";
680
+ const result = await this.requestJson("GET", path);
681
+ return { sessions: result.sessions, nextCursor: result.next_cursor };
682
+ }
683
+ /**
684
+ * `GET /admin/sessions/:id/messages` — read-only history for any session
685
+ * (cross-user). Returns the same `AIHubMessage[]` shape as the user-facing
686
+ * `getSessionMessages` so the same `<MessageBubble />` rendering can be
687
+ * reused.
688
+ *
689
+ * @param id - any session id in the deployment.
690
+ * @returns the projected message list.
691
+ * @throws {AIHubRequestError} `forbidden` for `user` callers, `not_found`
692
+ * if the session id doesn't exist.
693
+ */
694
+ async getAdminSessionMessages(id) {
695
+ const result = await this.requestJson("GET", `/admin/sessions/${encodeURIComponent(id)}/messages`);
696
+ return result.messages;
697
+ }
698
+ /**
699
+ * Step-12.5 — `POST /admin/sessions/:id/purge-sandbox`.
700
+ *
701
+ * Removes the per-session sandbox folder from disk + clears
702
+ * `session.sandbox_dir`. Idempotent: 204 on success regardless of
703
+ * whether the session ever had a sandbox. Rejects with
704
+ * `409 turn_running` if a chat turn is live for the session.
705
+ *
706
+ * `super_admin` only — `admin` / `power_user` callers get 403.
707
+ *
708
+ * @param id - the ai-hub session id.
709
+ * @throws {AIHubRequestError} `turn_running`, `forbidden`, `not_found`.
710
+ */
711
+ async purgeAdminSessionSandbox(id) {
712
+ await this.requestJson("POST", `/admin/sessions/${encodeURIComponent(id)}/purge-sandbox`);
713
+ }
714
+ /* ───── admin: settings ────────────────────────────────────────────── */
715
+ /**
716
+ * `GET /admin/settings` — deployment-wide configuration (super_admin only).
717
+ *
718
+ * The Letta API key is never returned in plaintext; the response carries
719
+ * `letta_api_key_set` + `letta_api_key_masked` so the UI can render
720
+ * "configured / not configured" affordances.
721
+ */
722
+ async getAdminSettings() {
723
+ return this.requestJson("GET", "/admin/settings");
724
+ }
725
+ /**
726
+ * `PATCH /admin/settings` — update one or more deployment settings.
727
+ *
728
+ * The API key, when supplied, is encrypted under `AI_HUB_ENCRYPTION_KEY`
729
+ * server-side. Theme fields live as separate columns and follow the same
730
+ * precedence as the React props (server > prop > built-in default).
731
+ *
732
+ * @param body - the patch. Each field independent.
733
+ * @returns the refreshed settings view.
734
+ */
735
+ async patchAdminSettings(body) {
736
+ const wire = {};
737
+ if (body.lettaBaseUrl !== undefined)
738
+ wire.letta_base_url = body.lettaBaseUrl;
739
+ if (body.lettaAdeUrl !== undefined)
740
+ wire.letta_ade_url = body.lettaAdeUrl;
741
+ if (body.lettaApiKey !== undefined)
742
+ wire.letta_api_key = body.lettaApiKey;
743
+ if (body.theme !== undefined) {
744
+ const themeWire = {};
745
+ if (body.theme.default !== undefined)
746
+ themeWire.default = body.theme.default;
747
+ if (body.theme.switcherEnabled !== undefined) {
748
+ themeWire.switcher_enabled = body.theme.switcherEnabled;
749
+ }
750
+ wire.theme = themeWire;
751
+ }
752
+ return this.requestJson("PATCH", "/admin/settings", {
753
+ body: wire,
754
+ });
755
+ }
756
+ /**
757
+ * `GET /admin/settings/feature-toggles` — the §8.4 toggle blob the
758
+ * super-admin sees. Same payload the `getFeatureToggles` (unauthenticated)
759
+ * endpoint returns, but gated and intended for the settings editor.
760
+ */
761
+ async getAdminFeatureToggles() {
762
+ const result = await this.requestJson("GET", "/admin/settings/feature-toggles");
763
+ return result.feature_toggles ?? {};
764
+ }
765
+ /**
766
+ * `PATCH /admin/settings/feature-toggles` — merge-patch the §8.4 toggle
767
+ * blob. Unknown keys are rejected with 400; the server's strict schema is
768
+ * the source of truth for which toggles are accepted.
769
+ *
770
+ * @param toggles - the partial — only the keys you want to write.
771
+ */
772
+ async patchAdminFeatureToggles(toggles) {
773
+ const result = await this.requestJson("PATCH", "/admin/settings/feature-toggles", { body: toggles });
774
+ return result.feature_toggles;
775
+ }
776
+ /**
777
+ * `POST /admin/settings/rotate-signing-key` — replace the JWT signing key
778
+ * in place. **Every outstanding JWT is invalidated immediately**, including
779
+ * the caller's own. The next protected request from the UI will 401 and
780
+ * the provider routes back to login (§6.7).
781
+ *
782
+ * Returns nothing — the new key never leaves the server.
783
+ */
784
+ async rotateAdminSigningKey() {
785
+ await this.requestJson("POST", "/admin/settings/rotate-signing-key", {
786
+ body: {},
787
+ });
788
+ }
789
+ /**
790
+ * `POST /admin/settings/rotate-metrics-token` — rotate the `/metrics`
791
+ * bearer token. The new token is returned **exactly once** in
792
+ * `{ token }`; subsequent `getAdminSettings` calls do NOT echo it back.
793
+ * The admin must copy it into their Prometheus / Alloy config before
794
+ * dismissing the reveal modal (§6.10).
795
+ *
796
+ * Does NOT invalidate the caller's JWT — only the metrics-scraper bearer
797
+ * changes.
798
+ */
799
+ async rotateAdminMetricsToken() {
800
+ return this.requestJson("POST", "/admin/settings/rotate-metrics-token", { body: {} });
801
+ }
802
+ /* ───── internal request plumbing ─────────────────────────────────── */
803
+ /**
804
+ * Sends a JSON request and parses the JSON response. Centralizes auth
805
+ * header injection, 401 handling, and error normalization.
806
+ *
807
+ * @param method - HTTP method.
808
+ * @param path - path under `baseUrl`.
809
+ * @param opts - optional body and unauthenticated-mode flag.
810
+ * @param opts.body - JSON body to send (omit for GET / DELETE).
811
+ * @param opts.authenticated - set to `false` to skip the bearer header
812
+ * (login is the only such caller). Defaults to true.
813
+ * @returns the parsed response.
814
+ */
815
+ async requestJson(method, path, opts = {}) {
816
+ const headers = { Accept: "application/json" };
817
+ const authenticated = opts.authenticated !== false;
818
+ if (authenticated) {
819
+ const token = this.tokenStore.get();
820
+ if (token)
821
+ headers.Authorization = `Bearer ${token}`;
822
+ }
823
+ let body;
824
+ if (opts.body !== undefined) {
825
+ headers["Content-Type"] = "application/json";
826
+ body = JSON.stringify(opts.body);
827
+ }
828
+ let response;
829
+ try {
830
+ response = await this.fetchImpl(`${this.baseUrl}${path}`, {
831
+ method,
832
+ headers,
833
+ ...(body !== undefined ? { body } : {}),
834
+ });
835
+ }
836
+ catch (err) {
837
+ throw new AIHubRequestError("network", err instanceof Error ? err.message : "Network request failed.");
838
+ }
839
+ if (response.status === 401) {
840
+ this.handleUnauthorized();
841
+ }
842
+ if (!response.ok) {
843
+ throw await this.errorFromResponse(response);
844
+ }
845
+ // 204 / 205 / empty body
846
+ const ct = response.headers.get("content-type") ?? "";
847
+ if (response.status === 204 || !ct.includes("application/json")) {
848
+ return undefined;
849
+ }
850
+ return (await response.json());
851
+ }
852
+ /**
853
+ * Shared backbone for `streamChat` and `getTurnEvents`. Yields events
854
+ * from an SSE response; throws `AIHubRequestError` on 4xx/5xx before the
855
+ * stream opens.
856
+ *
857
+ * @param method - HTTP method.
858
+ * @param path - path under `baseUrl`.
859
+ * @param body - optional JSON body (POST /chat only).
860
+ * @param opts - abort signal.
861
+ * @yields each ai-hub event from the SSE stream until `stream_end`.
862
+ */
863
+ async *streamRequest(method, path, body, opts) {
864
+ const headers = { Accept: "text/event-stream" };
865
+ const token = this.tokenStore.get();
866
+ if (token)
867
+ headers.Authorization = `Bearer ${token}`;
868
+ let bodyInit;
869
+ if (body !== undefined) {
870
+ headers["Content-Type"] = "application/json";
871
+ bodyInit = JSON.stringify(body);
872
+ }
873
+ let response;
874
+ try {
875
+ response = await this.fetchImpl(`${this.baseUrl}${path}`, {
876
+ method,
877
+ headers,
878
+ ...(bodyInit !== undefined ? { body: bodyInit } : {}),
879
+ ...(opts.signal !== undefined ? { signal: opts.signal } : {}),
880
+ });
881
+ }
882
+ catch (err) {
883
+ throw new AIHubRequestError("network", err instanceof Error ? err.message : "SSE request failed.");
884
+ }
885
+ if (response.status === 401)
886
+ this.handleUnauthorized();
887
+ if (!response.ok)
888
+ throw await this.errorFromResponse(response);
889
+ if (!response.body) {
890
+ throw new AIHubRequestError("internal", "SSE response had no body.");
891
+ }
892
+ for await (const event of parseSseStream(response.body)) {
893
+ yield event;
894
+ }
895
+ }
896
+ /**
897
+ * Clears the token and fires the unauthorized callback (at most once per
898
+ * 401 storm — the flag resets on next `setToken`).
899
+ */
900
+ handleUnauthorized() {
901
+ this.clearToken();
902
+ if (this.unauthorizedFired)
903
+ return;
904
+ this.unauthorizedFired = true;
905
+ this.onUnauthorized?.();
906
+ }
907
+ /**
908
+ * Builds an `AIHubRequestError` from a non-ok server response. Reads the
909
+ * server's `{ error: { code, message, request_id } }` envelope when
910
+ * present; falls back to status-code-derived defaults otherwise.
911
+ *
912
+ * @param response - the fetch response (status >= 400).
913
+ * @returns the normalized error.
914
+ */
915
+ async errorFromResponse(response) {
916
+ let envelope = {};
917
+ try {
918
+ const ct = response.headers.get("content-type") ?? "";
919
+ if (ct.includes("application/json")) {
920
+ envelope = (await response.json());
921
+ }
922
+ }
923
+ catch {
924
+ /* malformed body — fall through to defaults */
925
+ }
926
+ const errorBlock = envelope.error;
927
+ const wireCode = errorBlock?.code;
928
+ const message = errorBlock?.message ?? `Request failed with status ${response.status}.`;
929
+ const code = wireCode ?? defaultCodeForStatus(response.status);
930
+ return new AIHubRequestError(code, message, response.status, errorBlock?.request_id);
931
+ }
932
+ }
933
+ /**
934
+ * Maps an HTTP status to a default `AIHubError.code` when the server didn't
935
+ * include one in the response envelope.
936
+ */
937
+ function defaultCodeForStatus(status) {
938
+ if (status === 401)
939
+ return "unauthorized";
940
+ if (status === 403)
941
+ return "forbidden";
942
+ if (status === 404)
943
+ return "not_found";
944
+ if (status >= 400 && status < 500)
945
+ return "validation";
946
+ if (status >= 500)
947
+ return "internal";
948
+ return "internal";
949
+ }
950
+ //# sourceMappingURL=AIHubClient.js.map