@buoy-gg/ask-buoy 7.0.36 → 7.0.38

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 (312) hide show
  1. package/README.md +58 -27
  2. package/lib/commonjs/blocks/AskBlocks.js +1 -338
  3. package/lib/commonjs/blocks/BlockRenderer.js +1 -262
  4. package/lib/commonjs/blocks/ChromePrototypes.js +1 -514
  5. package/lib/commonjs/blocks/DataBlocks.js +1 -781
  6. package/lib/commonjs/blocks/ExtraBlocks.js +1 -1860
  7. package/lib/commonjs/blocks/MediaBlocks.js +1 -156
  8. package/lib/commonjs/blocks/ReceiptBlocks.js +1 -586
  9. package/lib/commonjs/blocks/blockText.js +1 -60
  10. package/lib/commonjs/blocks/blockText.test.js +1 -53
  11. package/lib/commonjs/blocks/context.js +1 -42
  12. package/lib/commonjs/blocks/icons.js +1 -96
  13. package/lib/commonjs/blocks/index.js +1 -95
  14. package/lib/commonjs/blocks/listRow.test.js +1 -55
  15. package/lib/commonjs/blocks/pageState.js +1 -34
  16. package/lib/commonjs/blocks/pageState.test.js +1 -57
  17. package/lib/commonjs/blocks/primitives.js +1 -457
  18. package/lib/commonjs/blocks/primitivesIndex.js +1 -62
  19. package/lib/commonjs/components/ApprovalCard.js +1 -244
  20. package/lib/commonjs/components/AskBuoyHeader.js +1 -196
  21. package/lib/commonjs/components/AskBuoyIcon.js +1 -19
  22. package/lib/commonjs/components/AskBuoyModal.js +1 -479
  23. package/lib/commonjs/components/AskBuoySettings.js +1 -119
  24. package/lib/commonjs/components/ChangesBar.js +1 -211
  25. package/lib/commonjs/components/Composer.js +1 -255
  26. package/lib/commonjs/components/EmptyState.js +1 -161
  27. package/lib/commonjs/components/KeyboardPad.js +1 -80
  28. package/lib/commonjs/components/Markdown.js +1 -310
  29. package/lib/commonjs/components/MessageList.js +1 -930
  30. package/lib/commonjs/components/QueuedMessages.js +1 -145
  31. package/lib/commonjs/components/ThinkingStrip.js +1 -223
  32. package/lib/commonjs/components/TypingDots.js +1 -63
  33. package/lib/commonjs/components/markdownParse.js +1 -131
  34. package/lib/commonjs/components/markdownParse.test.js +1 -99
  35. package/lib/commonjs/components/useCopyAction.js +1 -50
  36. package/lib/commonjs/index.js +1 -252
  37. package/lib/commonjs/preset.js +1 -90
  38. package/lib/commonjs/store/activityGroups.js +1 -110
  39. package/lib/commonjs/store/activityGroups.test.js +1 -78
  40. package/lib/commonjs/store/askBuoySettings.js +1 -72
  41. package/lib/commonjs/store/askBuoyStore.js +1 -547
  42. package/lib/commonjs/store/blockPlacement.js +1 -64
  43. package/lib/commonjs/store/blockPlacement.test.js +1 -123
  44. package/lib/commonjs/store/queue.test.js +1 -150
  45. package/lib/commonjs/store/resetConversation.js +1 -77
  46. package/lib/commonjs/store/restorePending.test.js +1 -63
  47. package/lib/commonjs/store/restoredIds.test.js +1 -47
  48. package/lib/commonjs/store/suggestions.js +1 -54
  49. package/lib/commonjs/store/suggestions.test.js +1 -58
  50. package/lib/commonjs/store/systemLine.test.js +1 -77
  51. package/lib/commonjs/store/ticketPrompt.js +1 -17
  52. package/lib/commonjs/store/traceText.js +1 -81
  53. package/lib/commonjs/store/traceText.test.js +1 -112
  54. package/lib/commonjs/store/transcriptPersistence.js +1 -297
  55. package/lib/commonjs/store/transcriptPersistence.test.js +1 -333
  56. package/lib/commonjs/store/turnRendering.test.js +1 -151
  57. package/lib/commonjs/store/useAskBuoy.js +1 -622
  58. package/lib/commonjs/store/useAskBuoySettings.js +1 -14
  59. package/lib/commonjs/sync/askBuoySyncAdapter.js +1 -140
  60. package/lib/commonjs/sync/askBuoySyncAdapter.test.js +1 -141
  61. package/lib/commonjs/sync/wire.js +1 -58
  62. package/lib/commonjs/types.js +1 -6
  63. package/lib/module/blocks/AskBlocks.js +1 -331
  64. package/lib/module/blocks/BlockRenderer.js +1 -257
  65. package/lib/module/blocks/ChromePrototypes.js +1 -503
  66. package/lib/module/blocks/DataBlocks.js +1 -767
  67. package/lib/module/blocks/ExtraBlocks.js +1 -1833
  68. package/lib/module/blocks/MediaBlocks.js +1 -151
  69. package/lib/module/blocks/ReceiptBlocks.js +1 -574
  70. package/lib/module/blocks/blockText.js +1 -55
  71. package/lib/module/blocks/blockText.test.js +1 -53
  72. package/lib/module/blocks/context.js +1 -35
  73. package/lib/module/blocks/icons.js +1 -91
  74. package/lib/module/blocks/index.js +1 -8
  75. package/lib/module/blocks/listRow.test.js +1 -56
  76. package/lib/module/blocks/pageState.js +1 -30
  77. package/lib/module/blocks/pageState.test.js +1 -56
  78. package/lib/module/blocks/primitives.js +1 -445
  79. package/lib/module/blocks/primitivesIndex.js +1 -5
  80. package/lib/module/components/ApprovalCard.js +1 -239
  81. package/lib/module/components/AskBuoyHeader.js +1 -193
  82. package/lib/module/components/AskBuoyIcon.js +1 -8
  83. package/lib/module/components/AskBuoyModal.js +1 -474
  84. package/lib/module/components/AskBuoySettings.js +1 -114
  85. package/lib/module/components/ChangesBar.js +1 -206
  86. package/lib/module/components/Composer.js +1 -250
  87. package/lib/module/components/EmptyState.js +1 -156
  88. package/lib/module/components/KeyboardPad.js +1 -75
  89. package/lib/module/components/Markdown.js +1 -305
  90. package/lib/module/components/MessageList.js +1 -924
  91. package/lib/module/components/QueuedMessages.js +1 -140
  92. package/lib/module/components/ThinkingStrip.js +1 -218
  93. package/lib/module/components/TypingDots.js +1 -58
  94. package/lib/module/components/markdownParse.js +1 -125
  95. package/lib/module/components/markdownParse.test.js +1 -98
  96. package/lib/module/components/useCopyAction.js +1 -46
  97. package/lib/module/index.js +1 -34
  98. package/lib/module/preset.js +1 -73
  99. package/lib/module/store/activityGroups.js +1 -103
  100. package/lib/module/store/activityGroups.test.js +1 -77
  101. package/lib/module/store/askBuoySettings.js +1 -67
  102. package/lib/module/store/askBuoyStore.js +1 -541
  103. package/lib/module/store/blockPlacement.js +1 -59
  104. package/lib/module/store/blockPlacement.test.js +1 -122
  105. package/lib/module/store/queue.test.js +1 -149
  106. package/lib/module/store/resetConversation.js +1 -72
  107. package/lib/module/store/restorePending.test.js +1 -62
  108. package/lib/module/store/restoredIds.test.js +1 -46
  109. package/lib/module/store/suggestions.js +1 -49
  110. package/lib/module/store/suggestions.test.js +1 -57
  111. package/lib/module/store/systemLine.test.js +1 -76
  112. package/lib/module/store/ticketPrompt.js +1 -13
  113. package/lib/module/store/traceText.js +1 -75
  114. package/lib/module/store/traceText.test.js +1 -111
  115. package/lib/module/store/transcriptPersistence.js +1 -287
  116. package/lib/module/store/transcriptPersistence.test.js +1 -332
  117. package/lib/module/store/turnRendering.test.js +1 -150
  118. package/lib/module/store/useAskBuoy.js +1 -616
  119. package/lib/module/store/useAskBuoySettings.js +1 -9
  120. package/lib/module/sync/askBuoySyncAdapter.js +1 -135
  121. package/lib/module/sync/askBuoySyncAdapter.test.js +1 -140
  122. package/lib/module/sync/wire.js +1 -47
  123. package/lib/module/types.js +1 -4
  124. package/lib/typescript/components/ApprovalCard.d.ts +9 -1
  125. package/lib/typescript/preset.d.ts +4 -2
  126. package/lib/typescript/store/askBuoyStore.d.ts +32 -4
  127. package/lib/typescript/store/useAskBuoy.d.ts +3 -2
  128. package/lib/typescript/sync/askBuoySyncAdapter.d.ts +3 -0
  129. package/package.json +9 -9
  130. package/lib/commonjs/blocks/AskBlocks.js.map +0 -1
  131. package/lib/commonjs/blocks/BlockRenderer.js.map +0 -1
  132. package/lib/commonjs/blocks/ChromePrototypes.js.map +0 -1
  133. package/lib/commonjs/blocks/DataBlocks.js.map +0 -1
  134. package/lib/commonjs/blocks/ExtraBlocks.js.map +0 -1
  135. package/lib/commonjs/blocks/MediaBlocks.js.map +0 -1
  136. package/lib/commonjs/blocks/ReceiptBlocks.js.map +0 -1
  137. package/lib/commonjs/blocks/blockText.js.map +0 -1
  138. package/lib/commonjs/blocks/blockText.test.js.map +0 -1
  139. package/lib/commonjs/blocks/context.js.map +0 -1
  140. package/lib/commonjs/blocks/icons.js.map +0 -1
  141. package/lib/commonjs/blocks/index.js.map +0 -1
  142. package/lib/commonjs/blocks/listRow.test.js.map +0 -1
  143. package/lib/commonjs/blocks/pageState.js.map +0 -1
  144. package/lib/commonjs/blocks/pageState.test.js.map +0 -1
  145. package/lib/commonjs/blocks/primitives.js.map +0 -1
  146. package/lib/commonjs/blocks/primitivesIndex.js.map +0 -1
  147. package/lib/commonjs/components/ApprovalCard.js.map +0 -1
  148. package/lib/commonjs/components/AskBuoyHeader.js.map +0 -1
  149. package/lib/commonjs/components/AskBuoyIcon.js.map +0 -1
  150. package/lib/commonjs/components/AskBuoyModal.js.map +0 -1
  151. package/lib/commonjs/components/AskBuoySettings.js.map +0 -1
  152. package/lib/commonjs/components/ChangesBar.js.map +0 -1
  153. package/lib/commonjs/components/Composer.js.map +0 -1
  154. package/lib/commonjs/components/EmptyState.js.map +0 -1
  155. package/lib/commonjs/components/KeyboardPad.js.map +0 -1
  156. package/lib/commonjs/components/Markdown.js.map +0 -1
  157. package/lib/commonjs/components/MessageList.js.map +0 -1
  158. package/lib/commonjs/components/QueuedMessages.js.map +0 -1
  159. package/lib/commonjs/components/ThinkingStrip.js.map +0 -1
  160. package/lib/commonjs/components/TypingDots.js.map +0 -1
  161. package/lib/commonjs/components/markdownParse.js.map +0 -1
  162. package/lib/commonjs/components/markdownParse.test.js.map +0 -1
  163. package/lib/commonjs/components/useCopyAction.js.map +0 -1
  164. package/lib/commonjs/index.js.map +0 -1
  165. package/lib/commonjs/preset.js.map +0 -1
  166. package/lib/commonjs/store/activityGroups.js.map +0 -1
  167. package/lib/commonjs/store/activityGroups.test.js.map +0 -1
  168. package/lib/commonjs/store/askBuoySettings.js.map +0 -1
  169. package/lib/commonjs/store/askBuoyStore.js.map +0 -1
  170. package/lib/commonjs/store/blockPlacement.js.map +0 -1
  171. package/lib/commonjs/store/blockPlacement.test.js.map +0 -1
  172. package/lib/commonjs/store/queue.test.js.map +0 -1
  173. package/lib/commonjs/store/resetConversation.js.map +0 -1
  174. package/lib/commonjs/store/restorePending.test.js.map +0 -1
  175. package/lib/commonjs/store/restoredIds.test.js.map +0 -1
  176. package/lib/commonjs/store/suggestions.js.map +0 -1
  177. package/lib/commonjs/store/suggestions.test.js.map +0 -1
  178. package/lib/commonjs/store/systemLine.test.js.map +0 -1
  179. package/lib/commonjs/store/ticketPrompt.js.map +0 -1
  180. package/lib/commonjs/store/traceText.js.map +0 -1
  181. package/lib/commonjs/store/traceText.test.js.map +0 -1
  182. package/lib/commonjs/store/transcriptPersistence.js.map +0 -1
  183. package/lib/commonjs/store/transcriptPersistence.test.js.map +0 -1
  184. package/lib/commonjs/store/turnRendering.test.js.map +0 -1
  185. package/lib/commonjs/store/useAskBuoy.js.map +0 -1
  186. package/lib/commonjs/store/useAskBuoySettings.js.map +0 -1
  187. package/lib/commonjs/sync/askBuoySyncAdapter.js.map +0 -1
  188. package/lib/commonjs/sync/askBuoySyncAdapter.test.js.map +0 -1
  189. package/lib/commonjs/sync/wire.js.map +0 -1
  190. package/lib/commonjs/types.js.map +0 -1
  191. package/lib/module/blocks/AskBlocks.js.map +0 -1
  192. package/lib/module/blocks/BlockRenderer.js.map +0 -1
  193. package/lib/module/blocks/ChromePrototypes.js.map +0 -1
  194. package/lib/module/blocks/DataBlocks.js.map +0 -1
  195. package/lib/module/blocks/ExtraBlocks.js.map +0 -1
  196. package/lib/module/blocks/MediaBlocks.js.map +0 -1
  197. package/lib/module/blocks/ReceiptBlocks.js.map +0 -1
  198. package/lib/module/blocks/blockText.js.map +0 -1
  199. package/lib/module/blocks/blockText.test.js.map +0 -1
  200. package/lib/module/blocks/context.js.map +0 -1
  201. package/lib/module/blocks/icons.js.map +0 -1
  202. package/lib/module/blocks/index.js.map +0 -1
  203. package/lib/module/blocks/listRow.test.js.map +0 -1
  204. package/lib/module/blocks/pageState.js.map +0 -1
  205. package/lib/module/blocks/pageState.test.js.map +0 -1
  206. package/lib/module/blocks/primitives.js.map +0 -1
  207. package/lib/module/blocks/primitivesIndex.js.map +0 -1
  208. package/lib/module/components/ApprovalCard.js.map +0 -1
  209. package/lib/module/components/AskBuoyHeader.js.map +0 -1
  210. package/lib/module/components/AskBuoyIcon.js.map +0 -1
  211. package/lib/module/components/AskBuoyModal.js.map +0 -1
  212. package/lib/module/components/AskBuoySettings.js.map +0 -1
  213. package/lib/module/components/ChangesBar.js.map +0 -1
  214. package/lib/module/components/Composer.js.map +0 -1
  215. package/lib/module/components/EmptyState.js.map +0 -1
  216. package/lib/module/components/KeyboardPad.js.map +0 -1
  217. package/lib/module/components/Markdown.js.map +0 -1
  218. package/lib/module/components/MessageList.js.map +0 -1
  219. package/lib/module/components/QueuedMessages.js.map +0 -1
  220. package/lib/module/components/ThinkingStrip.js.map +0 -1
  221. package/lib/module/components/TypingDots.js.map +0 -1
  222. package/lib/module/components/markdownParse.js.map +0 -1
  223. package/lib/module/components/markdownParse.test.js.map +0 -1
  224. package/lib/module/components/useCopyAction.js.map +0 -1
  225. package/lib/module/index.js.map +0 -1
  226. package/lib/module/preset.js.map +0 -1
  227. package/lib/module/store/activityGroups.js.map +0 -1
  228. package/lib/module/store/activityGroups.test.js.map +0 -1
  229. package/lib/module/store/askBuoySettings.js.map +0 -1
  230. package/lib/module/store/askBuoyStore.js.map +0 -1
  231. package/lib/module/store/blockPlacement.js.map +0 -1
  232. package/lib/module/store/blockPlacement.test.js.map +0 -1
  233. package/lib/module/store/queue.test.js.map +0 -1
  234. package/lib/module/store/resetConversation.js.map +0 -1
  235. package/lib/module/store/restorePending.test.js.map +0 -1
  236. package/lib/module/store/restoredIds.test.js.map +0 -1
  237. package/lib/module/store/suggestions.js.map +0 -1
  238. package/lib/module/store/suggestions.test.js.map +0 -1
  239. package/lib/module/store/systemLine.test.js.map +0 -1
  240. package/lib/module/store/ticketPrompt.js.map +0 -1
  241. package/lib/module/store/traceText.js.map +0 -1
  242. package/lib/module/store/traceText.test.js.map +0 -1
  243. package/lib/module/store/transcriptPersistence.js.map +0 -1
  244. package/lib/module/store/transcriptPersistence.test.js.map +0 -1
  245. package/lib/module/store/turnRendering.test.js.map +0 -1
  246. package/lib/module/store/useAskBuoy.js.map +0 -1
  247. package/lib/module/store/useAskBuoySettings.js.map +0 -1
  248. package/lib/module/sync/askBuoySyncAdapter.js.map +0 -1
  249. package/lib/module/sync/askBuoySyncAdapter.test.js.map +0 -1
  250. package/lib/module/sync/wire.js.map +0 -1
  251. package/lib/module/types.js.map +0 -1
  252. package/lib/typescript/blocks/AskBlocks.d.ts.map +0 -1
  253. package/lib/typescript/blocks/BlockRenderer.d.ts.map +0 -1
  254. package/lib/typescript/blocks/ChromePrototypes.d.ts.map +0 -1
  255. package/lib/typescript/blocks/DataBlocks.d.ts.map +0 -1
  256. package/lib/typescript/blocks/ExtraBlocks.d.ts.map +0 -1
  257. package/lib/typescript/blocks/MediaBlocks.d.ts.map +0 -1
  258. package/lib/typescript/blocks/ReceiptBlocks.d.ts.map +0 -1
  259. package/lib/typescript/blocks/blockText.d.ts.map +0 -1
  260. package/lib/typescript/blocks/blockText.test.d.ts.map +0 -1
  261. package/lib/typescript/blocks/context.d.ts.map +0 -1
  262. package/lib/typescript/blocks/icons.d.ts.map +0 -1
  263. package/lib/typescript/blocks/index.d.ts.map +0 -1
  264. package/lib/typescript/blocks/listRow.test.d.ts.map +0 -1
  265. package/lib/typescript/blocks/pageState.d.ts.map +0 -1
  266. package/lib/typescript/blocks/pageState.test.d.ts.map +0 -1
  267. package/lib/typescript/blocks/primitives.d.ts.map +0 -1
  268. package/lib/typescript/blocks/primitivesIndex.d.ts.map +0 -1
  269. package/lib/typescript/components/ApprovalCard.d.ts.map +0 -1
  270. package/lib/typescript/components/AskBuoyHeader.d.ts.map +0 -1
  271. package/lib/typescript/components/AskBuoyIcon.d.ts.map +0 -1
  272. package/lib/typescript/components/AskBuoyModal.d.ts.map +0 -1
  273. package/lib/typescript/components/AskBuoySettings.d.ts.map +0 -1
  274. package/lib/typescript/components/ChangesBar.d.ts.map +0 -1
  275. package/lib/typescript/components/Composer.d.ts.map +0 -1
  276. package/lib/typescript/components/EmptyState.d.ts.map +0 -1
  277. package/lib/typescript/components/KeyboardPad.d.ts.map +0 -1
  278. package/lib/typescript/components/Markdown.d.ts.map +0 -1
  279. package/lib/typescript/components/MessageList.d.ts.map +0 -1
  280. package/lib/typescript/components/QueuedMessages.d.ts.map +0 -1
  281. package/lib/typescript/components/ThinkingStrip.d.ts.map +0 -1
  282. package/lib/typescript/components/TypingDots.d.ts.map +0 -1
  283. package/lib/typescript/components/markdownParse.d.ts.map +0 -1
  284. package/lib/typescript/components/markdownParse.test.d.ts.map +0 -1
  285. package/lib/typescript/components/useCopyAction.d.ts.map +0 -1
  286. package/lib/typescript/index.d.ts.map +0 -1
  287. package/lib/typescript/preset.d.ts.map +0 -1
  288. package/lib/typescript/store/activityGroups.d.ts.map +0 -1
  289. package/lib/typescript/store/activityGroups.test.d.ts.map +0 -1
  290. package/lib/typescript/store/askBuoySettings.d.ts.map +0 -1
  291. package/lib/typescript/store/askBuoyStore.d.ts.map +0 -1
  292. package/lib/typescript/store/blockPlacement.d.ts.map +0 -1
  293. package/lib/typescript/store/blockPlacement.test.d.ts.map +0 -1
  294. package/lib/typescript/store/queue.test.d.ts.map +0 -1
  295. package/lib/typescript/store/resetConversation.d.ts.map +0 -1
  296. package/lib/typescript/store/restorePending.test.d.ts.map +0 -1
  297. package/lib/typescript/store/restoredIds.test.d.ts.map +0 -1
  298. package/lib/typescript/store/suggestions.d.ts.map +0 -1
  299. package/lib/typescript/store/suggestions.test.d.ts.map +0 -1
  300. package/lib/typescript/store/systemLine.test.d.ts.map +0 -1
  301. package/lib/typescript/store/ticketPrompt.d.ts.map +0 -1
  302. package/lib/typescript/store/traceText.d.ts.map +0 -1
  303. package/lib/typescript/store/traceText.test.d.ts.map +0 -1
  304. package/lib/typescript/store/transcriptPersistence.d.ts.map +0 -1
  305. package/lib/typescript/store/transcriptPersistence.test.d.ts.map +0 -1
  306. package/lib/typescript/store/turnRendering.test.d.ts.map +0 -1
  307. package/lib/typescript/store/useAskBuoy.d.ts.map +0 -1
  308. package/lib/typescript/store/useAskBuoySettings.d.ts.map +0 -1
  309. package/lib/typescript/sync/askBuoySyncAdapter.d.ts.map +0 -1
  310. package/lib/typescript/sync/askBuoySyncAdapter.test.d.ts.map +0 -1
  311. package/lib/typescript/sync/wire.d.ts.map +0 -1
  312. package/lib/typescript/types.d.ts.map +0 -1
@@ -1,622 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.useAskBuoy = useAskBuoy;
7
- exports.useAskBuoyState = useAskBuoyState;
8
- var _react = require("react");
9
- var _reactNative = require("react-native");
10
- var _sharedUi = require("@buoy-gg/shared-ui");
11
- var _agentCore = require("@buoy-gg/agent-core");
12
- var _askBuoyStore = require("./askBuoyStore");
13
- var _resetConversation = require("./resetConversation");
14
- var _transcriptPersistence = require("./transcriptPersistence");
15
- var _useAskBuoySettings = require("./useAskBuoySettings");
16
- /**
17
- * Wire the sheet to the agent.
18
- *
19
- * Two things here are load-bearing:
20
- *
21
- * 1. DISPATCH GOES THROUGH `dispatchToolAction`, the same on-device seam a
22
- * Scenario step and a desktop click use. No second adapter map, nothing
23
- * that can drift from what the broker sees.
24
- * 2. THE SESSION IS REBUILT WHEN THE TOOL LIST CHANGES. The adapter map is a
25
- * memo whose contents depend on which packages resolved and whether a
26
- * QueryClientProvider is above FloatingDevTools — it is per-app and per
27
- * render, never a constant. A session that cached the tool list at open
28
- * would offer the model tools that are no longer there.
29
- */
30
-
31
- const isRelease = typeof __DEV__ !== "undefined" && !__DEV__;
32
-
33
- /**
34
- * What this device can dispatch, right now.
35
- *
36
- * `getSnapshot` is folded in because half of Buoy's read surface is not an
37
- * action at all — console entries, network events, routes and env live only in
38
- * the adapter's snapshot. Without it the model is offered a console tool whose
39
- * sole action wipes the logs, and no way to read them.
40
- *
41
- * READ FRESH, never cached. Packages register over the first frames, and some
42
- * only once the thing they wrap exists — route-events publishes its actions
43
- * when the navigation container is up, long after this hook first runs. A
44
- * snapshot taken at mount froze that: the agent was handed a route tool that
45
- * could only `getSnapshot`, so asked to navigate it guessed three tool names
46
- * that do not exist, spent 41 seconds, and told the user this build cannot
47
- * move between screens. It can.
48
- */
49
- /**
50
- * The question a block asked, but ONLY when the model can no longer remember
51
- * asking it — i.e. the bubble came back from disk. In a live conversation the
52
- * model has the question in its own history and repeating it would put the
53
- * words in the user's mouth twice.
54
- *
55
- * The wording field differs by block kind: `question` on choice and
56
- * multiSelect, `label` on input / select / slider / dateTime, `title` on
57
- * confirm and form.
58
- */
59
- function questionBehind(blockId) {
60
- for (const m of _askBuoyStore.askBuoyStore.getState().messages) {
61
- if (!m.restored) continue;
62
- for (const placed of m.blocks) {
63
- const b = placed.block;
64
- if (b?.id !== blockId) continue;
65
- const asked = b.question ?? b.label ?? b.title;
66
- return typeof asked === "string" && asked.trim() ? asked.trim() : undefined;
67
- }
68
- }
69
- return undefined;
70
- }
71
- function readAvailableActions() {
72
- try {
73
- const actions = (0, _sharedUi.listToolActions)();
74
- for (const toolId of (0, _sharedUi.listSnapshotToolIds)()) {
75
- if (!actions[toolId]) actions[toolId] = [];
76
- if (!actions[toolId].includes(_agentCore.SNAPSHOT_ACTION)) {
77
- actions[toolId] = [_agentCore.SNAPSHOT_ACTION, ...actions[toolId]];
78
- }
79
- }
80
- return actions;
81
- } catch {
82
- return {};
83
- }
84
- }
85
-
86
- /** Long enough to coalesce a streaming answer's deltas, short enough to beat a crash. */
87
- const SAVE_DEBOUNCE_MS = 600;
88
- function useAskBuoyState() {
89
- return (0, _react.useSyncExternalStore)(_askBuoyStore.askBuoyStore.subscribe, _askBuoyStore.askBuoyStore.getState, _askBuoyStore.askBuoyStore.getState);
90
- }
91
- function useAskBuoy(config) {
92
- const state = useAskBuoyState();
93
- const sessionRef = (0, _react.useRef)(null);
94
- /** One restore attempt per mount — see the note at the read. */
95
- const restoredRef = (0, _react.useRef)(false);
96
- const abortRef = (0, _react.useRef)(null);
97
- // Read through a ref where a stable callback needs the LATEST config (the
98
- // onEvent tap) without re-creating the callback per render.
99
- const configRef = (0, _react.useRef)(config);
100
- configRef.current = config;
101
- const configured = !!config?.endpoint && !!config?.model;
102
- /**
103
- * On by default. The conversation surviving a reload is what makes the tool
104
- * usable while iterating, and it is the only thing that survives a crash —
105
- * but it puts the chat on disk, so a host handling regulated data can say no.
106
- */
107
- const persist = config?.persistTranscript !== false;
108
- /**
109
- * The device's "don't ask me" toggle, read here rather than only in the
110
- * settings view so it is loaded off disk before the first turn — a
111
- * preference that only took effect once you had opened Settings would ask
112
- * once, every launch, which is the thing it exists to stop.
113
- */
114
- const {
115
- bypassApprovals,
116
- readOnly
117
- } = (0, _useAskBuoySettings.useAskBuoySettings)();
118
- // Through refs where the session is BUILT: the flags are not session
119
- // identity, and putting them in that effect's deps would rebuild the
120
- // conversation on a toggle. The effects below push changes instead.
121
- const bypassRef = (0, _react.useRef)(bypassApprovals);
122
- bypassRef.current = bypassApprovals;
123
- const readOnlyRef = (0, _react.useRef)(readOnly);
124
- readOnlyRef.current = readOnly;
125
-
126
- /**
127
- * The policy a human-initiated dispatch runs under — block buttons and a
128
- * restored approval. The session applies the device toggles itself; these
129
- * two paths take `config.policy` directly, so without this the Read only
130
- * toggle governed the model's calls and not the buttons the model drew.
131
- * App OR device: the toggle narrows, never widens.
132
- */
133
- const effectivePolicy = (0, _react.useCallback)(() => readOnlyRef.current ? {
134
- ...(config?.policy ?? {}),
135
- readOnly: true
136
- } : config?.policy ?? {}, [config]);
137
-
138
- /**
139
- * Re-read once the registry says it is up. This is what the surrounding code
140
- * has always assumed happens — the session effect's note about `toolKey`
141
- * changing "a frame or two after open" was describing a recomputation that,
142
- * with an empty dependency list, never actually ran.
143
- */
144
- const [registryTick, setRegistryTick] = (0, _react.useState)(0);
145
- (0, _react.useEffect)(() => {
146
- let cancelled = false;
147
- void (0, _sharedUi.waitForToolRegistry)().then(() => {
148
- if (!cancelled) setRegistryTick(n => n + 1);
149
- });
150
- return () => {
151
- cancelled = true;
152
- };
153
- }, []);
154
- const availableActions = (0, _react.useMemo)(() => readAvailableActions(), [registryTick]);
155
- const toolKey = Object.keys(availableActions).sort().join(",");
156
- (0, _react.useEffect)(() => {
157
- _askBuoyStore.askBuoyStore.setConfigured(configured);
158
- // Stamped on the store so an exported run says what it ran against.
159
- _askBuoyStore.askBuoyStore.setMeta({
160
- model: config?.model,
161
- protocol: config?.protocol ?? (config ? "anthropic" : undefined),
162
- appName: config?.appName,
163
- platform: _reactNative.Platform.OS,
164
- build: isRelease ? "release" : "development"
165
- });
166
- if (!configured || !config) {
167
- sessionRef.current = null;
168
- _askBuoyStore.askBuoyStore.setNotice("Ask Buoy isn't set up in this app yet. A developer needs to point it at your team's AI endpoint.");
169
- return;
170
- }
171
- // The classic first-run mistake, caught statically: a provider's own URL
172
- // with the wrong protocol fails with a raw error that says nothing about
173
- // the one-line fix. ONLY the canonical provider hosts are checked — a
174
- // gateway may legitimately speak either protocol on any path (our own dev
175
- // gateway serves OpenAI framing on /v1/messages, and a path-based version
176
- // of this check flagged it on the first device run).
177
- const protocol = config.protocol ?? "anthropic";
178
- if (isRelease && config.apiKey) {
179
- _askBuoyStore.askBuoyStore.setNotice("This build carries a plaintext apiKey — that's dev-only and it ships to every install. Use the headers callback with your own gateway for distributed builds.");
180
- } else if (protocol === "anthropic" && /^https?:\/\/api\.openai\.com\//i.test(config.endpoint)) {
181
- _askBuoyStore.askBuoyStore.setNotice('This endpoint is api.openai.com but the protocol is "anthropic". Set protocol: "openai" in the askBuoy config.');
182
- } else if (protocol === "openai" && /^https?:\/\/api\.anthropic\.com\//i.test(config.endpoint)) {
183
- _askBuoyStore.askBuoyStore.setNotice('This endpoint is api.anthropic.com but the protocol is "openai". Remove protocol or set it to "anthropic".');
184
- } else {
185
- _askBuoyStore.askBuoyStore.setNotice(null);
186
- }
187
-
188
- // Reuse the held session while nothing material changed. The transcript
189
- // already survives close/reopen in the module store; without this the
190
- // session and ledger did not — reopening the sheet swapped in an empty
191
- // ledger (undo bar gone, agent changes still live in the app) and a blank
192
- // model memory behind a transcript that still showed the conversation.
193
- // The tool list is deliberately NOT part of this signature. Packages
194
- // register a frame or two apart, so `toolKey` legitimately changes right
195
- // after open — and keying on it silently rebuilt the session mid-chat,
196
- // throwing away the conversation, the model's memory and the undo ledger
197
- // while the transcript (a separate store) kept rendering as if nothing
198
- // happened. Verified on device: a destructive change went unrecorded
199
- // because it landed in a ledger that had just been replaced.
200
- const signature = [config.endpoint, config.protocol ?? "anthropic", config.model].join("|");
201
- const held = _askBuoyStore.askBuoyStore.getHeldSession();
202
- if (held && held.signature === signature) {
203
- sessionRef.current = held.session;
204
- held.session.setAvailableActions(availableActions);
205
- held.session.setApprovalBypass(bypassRef.current);
206
- held.session.setReadOnly(readOnlyRef.current);
207
- _askBuoyStore.askBuoyStore.setLedger(held.session.ledger);
208
- return;
209
- }
210
- const session = (0, _agentCore.createAskBuoySession)({
211
- endpoint: config.endpoint,
212
- protocol: config.protocol ?? "anthropic",
213
- model: config.model,
214
- headers: config.headers,
215
- apiKey: config.apiKey,
216
- maxTokens: config.maxTokens,
217
- anthropicVersion: config.anthropicVersion,
218
- requestOverrides: config.requestOverrides,
219
- // Host-declared custom tools join the built-in catalog. toProviderTools
220
- // still filters against the live registry, so a descriptor whose
221
- // `custom:<id>` adapter never registered is simply not offered.
222
- catalog: config.tools?.length ? [..._agentCore.CATALOG, ...config.tools] : undefined,
223
- dispatch: _sharedUi.dispatchToolAction,
224
- readSnapshot: _sharedUi.readToolSnapshot,
225
- availableActions,
226
- policy: config.policy,
227
- context: config.context,
228
- isRelease,
229
- appName: config.appName,
230
- platform: _reactNative.Platform.OS,
231
- requestApproval: input => _askBuoyStore.askBuoyStore.askApproval(input)
232
- });
233
- sessionRef.current = session;
234
- session.setApprovalBypass(bypassRef.current);
235
- session.setReadOnly(readOnlyRef.current);
236
- _askBuoyStore.askBuoyStore.holdSession(session, signature);
237
- _askBuoyStore.askBuoyStore.setLedger(session.ledger);
238
-
239
- /**
240
- * Bring the last conversation back — both halves of it.
241
- *
242
- * The transcript and the model's memory are separate things (the store and
243
- * the session), and restoring only the first is worse than restoring
244
- * neither: the user reads a conversation the agent has no recollection of,
245
- * and "undo that" or "now do the same for Pikachu" lands on an empty
246
- * history. So the session is restored in the same pass, and only into a
247
- * session this effect just built.
248
- */
249
- // The registry publishes on mount, a frame or two after first paint. Warm
250
- // the grounding once it is there so the first question does not pay for it.
251
- let cancelled = false;
252
- void (0, _sharedUi.waitForToolRegistry)().then(ready => {
253
- if (ready && !cancelled) void session.prime();
254
- });
255
-
256
- /**
257
- * NOT gated on `cancelled`, and once per mount.
258
- *
259
- * The disk read races the tool registry, and it was losing about half the
260
- * time. `availableActions` legitimately changes a frame or two after open
261
- * (see the note above) — that re-runs this effect, whose cleanup sets
262
- * `cancelled`, and the in-flight restore then threw its result away. The
263
- * second pass takes the held-session branch and returns early, so nothing
264
- * ever retried: the conversation was on disk, read correctly, and dropped
265
- * on the floor. Caught by the e2e, where one test restored and the next
266
- * did not, with no difference between them.
267
- *
268
- * Safe to leave running: `hydrate` refuses if anything has been said since,
269
- * and `restore` refuses a session that already has history. Resolved
270
- * against `sessionRef.current` rather than the captured `session` so a
271
- * genuine reconfiguration mid-read restores into the session in use.
272
- */
273
- if (persist && !restoredRef.current) {
274
- restoredRef.current = true;
275
- void (0, _transcriptPersistence.readTranscript)().then(saved => {
276
- if (!saved) return;
277
- const hydrated = _askBuoyStore.askBuoyStore.hydrate({
278
- messages: saved.messages,
279
- answers: saved.answers,
280
- usage: saved.usage,
281
- queued: saved.queued
282
- });
283
- if (!hydrated) return;
284
- sessionRef.current?.restore(saved.history);
285
- // The undo bar, back with the transcript that promised it.
286
- if (saved.ledger?.length) sessionRef.current?.ledger.restore(saved.ledger);
287
- // A question the user never answered comes back with the conversation.
288
- // Losing it silently reads as consent they never gave: nothing was
289
- // tapped, so the change is still waiting as far as they know.
290
- if (saved.pending) _askBuoyStore.askBuoyStore.restorePending(saved.pending);
291
- });
292
- }
293
- return () => {
294
- cancelled = true;
295
- };
296
- }, [configured, config, availableActions, toolKey, persist]);
297
-
298
- /**
299
- * Push a toggle made mid-conversation. Declared after the effect that builds
300
- * the session, so on mount it lands on a session that already exists, and on
301
- * a later flip it reaches the held one without disturbing the transcript.
302
- */
303
- (0, _react.useEffect)(() => {
304
- sessionRef.current?.setApprovalBypass(bypassApprovals);
305
- }, [bypassApprovals]);
306
- (0, _react.useEffect)(() => {
307
- sessionRef.current?.setReadOnly(readOnly);
308
- }, [readOnly]);
309
-
310
- /**
311
- * Save on change: coalesced while a turn streams, immediate the moment it ends.
312
- *
313
- * The debounce exists because a streaming answer changes the store on every
314
- * delta, and writing a whole transcript per token would cost more than the
315
- * turn does. But applying it to the END of a turn as well left a window
316
- * where the finished answer was on screen and not yet on disk — a crash or a
317
- * fast reload inside it lost the whole turn, which is precisely the case
318
- * this feature exists for. An e2e that relaunched as soon as the answer
319
- * rendered lost it every time, and that is a real user doing a fast reload,
320
- * not a harness artefact.
321
- *
322
- * Saving still happens DURING a turn as well, which is what lets a bubble
323
- * the app died in come back marked interrupted rather than blank.
324
- */
325
- const messages = state.messages;
326
- const answers = state.answers;
327
- const usage = state.usage;
328
- const busy = state.busy;
329
- /**
330
- * In the deps because an approval is the one thing that changes nothing
331
- * else. `approval-required` adds no bubble and no tool row — the engine
332
- * yields it and then parks — so without this the save that carries the card
333
- * to disk never fires, and the reload it is meant to survive loses it.
334
- */
335
- const pending = state.pending;
336
- const queued = state.queued;
337
- (0, _react.useEffect)(() => {
338
- if (!persist) return;
339
- if (messages.length === 0) return;
340
- const write = () => {
341
- void (0, _transcriptPersistence.writeTranscript)((0, _transcriptPersistence.buildPayload)({
342
- messages,
343
- history: sessionRef.current?.messages ?? [],
344
- answers,
345
- usage,
346
- pending,
347
- ledger: _askBuoyStore.askBuoyStore.getLedger()?.list(),
348
- queued
349
- }));
350
- };
351
- if (!busy) {
352
- write();
353
- return;
354
- }
355
- const timer = setTimeout(write, SAVE_DEBOUNCE_MS);
356
- return () => clearTimeout(timer);
357
- }, [persist, busy, messages, answers, usage, pending, queued]);
358
-
359
- /** Stream one turn into a fresh assistant bubble. Callers set up the user side. */
360
- const runTurn = (0, _react.useCallback)(text => {
361
- const session = sessionRef.current;
362
- if (!session) return;
363
- _askBuoyStore.askBuoyStore.beginTurn();
364
- const messageId = _askBuoyStore.askBuoyStore.startAssistant();
365
- const controller = new AbortController();
366
- abortRef.current = controller;
367
-
368
- // The truth about this device, re-read at the top of every turn. A tool
369
- // that registered since the sheet opened is dispatchable NOW, and the
370
- // model is only offered what this list says it has.
371
- session.setAvailableActions(readAvailableActions());
372
- const onEvent = configRef.current?.onEvent;
373
- void (async () => {
374
- try {
375
- for await (const ev of session.send(text, controller.signal)) {
376
- _askBuoyStore.askBuoyStore.applyEvent(messageId, ev);
377
- if (onEvent) {
378
- try {
379
- onEvent(ev);
380
- } catch {
381
- // The audit tap must never take the chat down.
382
- }
383
- }
384
- }
385
- } catch (err) {
386
- // Stop is not a failure. The abort comes back as a cancelled fetch,
387
- // and reporting that as an error told the user "couldn't reach your AI
388
- // endpoint, check the connection" about the thing they had just asked
389
- // for — with `FetchRequestCanceledException` printed underneath.
390
- if (!controller.signal.aborted) {
391
- _askBuoyStore.askBuoyStore.applyEvent(messageId, {
392
- type: "error",
393
- message: err instanceof Error ? err.message : "Something went wrong talking to your AI endpoint."
394
- });
395
- }
396
- } finally {
397
- // Marked HERE, not only in the catch. The engine now reads the abort
398
- // between calls and returns cleanly, so most stops never throw — and
399
- // a turn that ended because the user pressed Stop must say so either
400
- // way, or the bubble just trails off as if the model ran out of words.
401
- if (controller.signal.aborted) _askBuoyStore.askBuoyStore.markStopped(messageId);
402
- _askBuoyStore.askBuoyStore.finishAssistant(messageId);
403
- abortRef.current = null;
404
- }
405
- })();
406
- }, []);
407
- const send = (0, _react.useCallback)(text => {
408
- const trimmed = text.trim();
409
- if (!sessionRef.current || !trimmed) return;
410
-
411
- // Typed mid-turn: park it behind whatever is already parked. The
412
- // composer already cleared the field, and silently dropping the words
413
- // was the worst of all outcomes — the single slot this replaced did
414
- // exactly that to every message but the last.
415
- if (_askBuoyStore.askBuoyStore.getState().busy) {
416
- _askBuoyStore.askBuoyStore.enqueue(trimmed);
417
- return;
418
- }
419
-
420
- // Typing past an Ask block IS an answer: the block is skipped and locks.
421
- // Without this the composer stayed on "Pick an option above…" forever and
422
- // a stale choice sat tappable in the history.
423
- const awaiting = _askBuoyStore.askBuoyStore.getState().awaitingBlockId;
424
- if (awaiting) _askBuoyStore.askBuoyStore.recordAnswer({
425
- blockId: awaiting,
426
- kind: "skipped"
427
- });
428
- _askBuoyStore.askBuoyStore.addUser(trimmed);
429
- runTurn(trimmed);
430
- }, [runTurn]);
431
- const retry = (0, _react.useCallback)(() => {
432
- const {
433
- messages,
434
- busy
435
- } = _askBuoyStore.askBuoyStore.getState();
436
- if (busy || !sessionRef.current) return;
437
- const lastUser = [...messages].reverse().find(m => m.role === "user");
438
- if (!lastUser) return;
439
- // The failed bubble is kept — see the note on `retry` in the interface.
440
- runTurn(lastUser.text);
441
- }, [runTurn]);
442
- const stop = (0, _react.useCallback)(() => {
443
- _askBuoyStore.askBuoyStore.requestStop();
444
- // A pending approval parks an unresolved promise inside the engine; the
445
- // abort signal is only read between calls, so Stop must also answer the
446
- // approval (as declined) or the generator hangs forever.
447
- if (_askBuoyStore.askBuoyStore.getState().pending) _askBuoyStore.askBuoyStore.answerApproval(false);
448
- // Parked messages stay parked. The queue holds after a stop (see
449
- // `queuePauseReason`), so ending the turn no longer flushes them — and
450
- // they are not dumped into the composer either: three 4,000-character
451
- // requests concatenated into one input lose their boundaries.
452
- abortRef.current?.abort();
453
- abortRef.current = null;
454
- }, []);
455
-
456
- /**
457
- * Deliver the next parked message — only when the chat is genuinely idle.
458
- *
459
- * `turnPhaseOf` + `queuePauseReason` decide, not `!busy`: the old check
460
- * fired after an error (into the same dead connection), after Stop, and into
461
- * an open Ask block as the answer to a question. See the store for the
462
- * rules; this effect just obeys them.
463
- */
464
- (0, _react.useEffect)(() => {
465
- if (state.queued.length === 0) return;
466
- if ((0, _askBuoyStore.turnPhaseOf)(state) !== "idle") return;
467
- if ((0, _askBuoyStore.queuePauseReason)(state) !== null) return;
468
- const next = _askBuoyStore.askBuoyStore.shiftQueued();
469
- if (next) send(next.text);
470
- }, [state, send]);
471
- const editQueued = (0, _react.useCallback)(id => _askBuoyStore.askBuoyStore.takeQueuedToDraft(id), []);
472
- const removeQueued = (0, _react.useCallback)(id => _askBuoyStore.askBuoyStore.removeQueued(id), []);
473
- const reset = (0, _react.useCallback)(() => {
474
- abortRef.current?.abort();
475
- abortRef.current = null;
476
- // "New conversation" means gone, not hidden. Leaving the last chat on disk
477
- // after the user asked for a clean one is the opposite of what they said.
478
- (0, _resetConversation.clearConversation)();
479
- }, []);
480
- const undoThenReset = (0, _react.useCallback)(() => (0, _resetConversation.resetConversation)({
481
- dispatch: _sharedUi.dispatchToolAction,
482
- undoFirst: true
483
- }), []);
484
-
485
- /**
486
- * Run a call whose approval outlived its turn.
487
- *
488
- * Through `runGatedAction` — the same gate a block button uses, where the
489
- * human's tap IS the approval — rather than `dispatchToolAction` raw, so the
490
- * deny list, read-only mode, the release refusals and the effect ledger all
491
- * still apply to a change made this way. Undo works on it afterwards for
492
- * exactly the same reason.
493
- */
494
- const applyRestoredApproval = (0, _react.useCallback)(async pending => {
495
- const ledger = _askBuoyStore.askBuoyStore.getLedger();
496
- if (!ledger) {
497
- _askBuoyStore.askBuoyStore.setNotice(`${pending.label} couldn't run — no active Ask Buoy session.`);
498
- return;
499
- }
500
- const catalog = config?.tools?.length ? [..._agentCore.CATALOG, ...config.tools] : undefined;
501
- // Freshness before action. The card was asked in a run of the app that
502
- // no longer exists; if what it would overwrite has moved since, writing
503
- // the saved params is a change nobody approved. Re-read, compare, refuse.
504
- if (pending.targetDigest) {
505
- const now = await (0, _agentCore.readTargetDigest)({
506
- toolId: pending.toolId,
507
- action: pending.action,
508
- params: pending.params ?? {},
509
- dispatch: _sharedUi.dispatchToolAction,
510
- catalog
511
- });
512
- if (now !== undefined && now !== pending.targetDigest) {
513
- _askBuoyStore.askBuoyStore.addNote(`“${pending.label}” wasn't applied — what it would change has moved on since the app restarted. Nothing was written. Ask again if you still want it.`);
514
- return;
515
- }
516
- }
517
- const outcome = await (0, _agentCore.runGatedAction)({
518
- toolId: pending.toolId,
519
- action: pending.action,
520
- params: pending.params ?? {},
521
- dispatch: _sharedUi.dispatchToolAction,
522
- ledger,
523
- policy: effectivePolicy(),
524
- isRelease,
525
- catalog
526
- });
527
- if (outcome.ok) {
528
- _askBuoyStore.askBuoyStore.addNote(`Applied “${pending.label}”, which was still waiting when the app restarted. Whatever else that answer was going to do is gone — ask again if there was more to it.`);
529
- } else {
530
- _askBuoyStore.askBuoyStore.setNotice(`${pending.label} failed — ${outcome.error}`);
531
- }
532
- }, [config, effectivePolicy]);
533
- const approve = (0, _react.useCallback)(approved => {
534
- const pending = _askBuoyStore.askBuoyStore.getState().pending;
535
- // Clear the card first either way: the tap is answered, and the dispatch
536
- // below is slow enough to leave it sitting there looking unanswered.
537
- _askBuoyStore.askBuoyStore.answerApproval(approved);
538
- // A live card resolves the promise the engine is parked on, and the turn
539
- // carries on from there. A RESTORED one has no turn behind it — that
540
- // generator died with the process — so Allow has to run the call itself.
541
- if (!pending?.restored) return;
542
- if (!approved) {
543
- _askBuoyStore.askBuoyStore.addNote(`Skipped “${pending.label}”. Nothing was changed.`);
544
- return;
545
- }
546
- void applyRestoredApproval(pending);
547
- }, [applyRestoredApproval]);
548
- const answer = (0, _react.useCallback)(a => {
549
- _askBuoyStore.askBuoyStore.recordAnswer(a);
550
- // Plain words to the model, never JSON: "Business checkout", "Yes, go ahead."
551
- //
552
- // Unless the question came back from disk. A restored history is rewound
553
- // to the last COMPLETE round, and an Ask block ends its turn with a
554
- // `buoy_ui` call — so the turn that asked is always rewound out of the
555
- // model's memory while its buttons stay tappable. A bare "Quantity" then
556
- // arrives with nothing behind it, and the model answers "that could mean
557
- // a couple of different things". Carry the question with the answer.
558
- const asked = questionBehind(a.blockId);
559
- send(asked ? `${asked} — ${(0, _agentCore.answerToText)(a)}` : (0, _agentCore.answerToText)(a));
560
- }, [send]);
561
-
562
- /**
563
- * Block buttons. `send` is a chat reply; `dispatch` runs the action through
564
- * `runGatedAction` — the SAME validation, policy and ledger path a model
565
- * call takes, minus the model. The first version called `dispatchToolAction`
566
- * raw, which made a model-authored button an invisible, ungoverned,
567
- * unundoable mutation while the `buoy_ui` schema promised the opposite.
568
- * The human's tap satisfies `requireApproval`; deny lists, read-only mode
569
- * and release refusals still stand. Button state is tracked so a tap shows
570
- * running → done and can't double-fire.
571
- * (`minimize` / `openTool` are the sheet's to handle; see AskBuoyModal.)
572
- */
573
- const runAction = (0, _react.useCallback)(async (action, blockId) => {
574
- if (action.send) {
575
- send(action.send);
576
- return;
577
- }
578
- if (!action.dispatch) return;
579
- const key = `${blockId}:${action.label}`;
580
- const ledger = _askBuoyStore.askBuoyStore.getLedger();
581
- if (!ledger) {
582
- _askBuoyStore.askBuoyStore.setNotice(`${action.label} failed — no active Ask Buoy session.`);
583
- return;
584
- }
585
- _askBuoyStore.askBuoyStore.setActionState(key, "running");
586
- const outcome = await (0, _agentCore.runGatedAction)({
587
- toolId: action.dispatch.toolId,
588
- action: action.dispatch.action,
589
- params: action.dispatch.params ?? {},
590
- dispatch: _sharedUi.dispatchToolAction,
591
- ledger,
592
- policy: effectivePolicy(),
593
- isRelease,
594
- // Same merged catalog the session offers, so a button on a custom
595
- // tool's block is not refused as unknown.
596
- catalog: config?.tools?.length ? [..._agentCore.CATALOG, ...config.tools] : undefined
597
- });
598
- if (outcome.ok) {
599
- _askBuoyStore.askBuoyStore.setActionState(key, "done");
600
- } else {
601
- _askBuoyStore.askBuoyStore.setActionState(key, "failed");
602
- _askBuoyStore.askBuoyStore.setNotice(`${action.label} failed — ${outcome.error}`);
603
- }
604
- }, [send, config, effectivePolicy]);
605
- return {
606
- state,
607
- send,
608
- stop,
609
- reset,
610
- undoThenReset,
611
- approve,
612
- retry,
613
- answer,
614
- runAction,
615
- editQueued,
616
- removeQueued,
617
- configured,
618
- toolCount: Object.keys(availableActions).length,
619
- availableToolIds: Object.keys(availableActions)
620
- };
621
- }
622
- //# sourceMappingURL=useAskBuoy.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.useAskBuoy=useAskBuoy,exports.useAskBuoyState=useAskBuoyState;var _sharedUi=require("@buoy-gg/shared-ui"),_react=require("react"),_reactNative=require("react-native"),_agentCore=require("@buoy-gg/agent-core"),_askBuoyStore=require("./askBuoyStore"),_resetConversation=require("./resetConversation"),_transcriptPersistence=require("./transcriptPersistence"),_useAskBuoySettings=require("./useAskBuoySettings");const isRelease="undefined"!=typeof __DEV__&&!__DEV__;function questionBehind(e){for(const t of _askBuoyStore.askBuoyStore.getState().messages)if(t.restored)for(const o of t.blocks){const t=o.block;if(t?.id!==e)continue;const s=t.question??t.label??t.title;return"string"==typeof s&&s.trim()?s.trim():void 0}}function readAvailableActions(){try{const e=(0,_sharedUi.listToolActions)();for(const t of(0,_sharedUi.listSnapshotToolIds)())e[t]||(e[t]=[]),e[t].includes(_agentCore.SNAPSHOT_ACTION)||(e[t]=[_agentCore.SNAPSHOT_ACTION,...e[t]]);return e}catch{return{}}}const SAVE_DEBOUNCE_MS=600;function useAskBuoyState(){return(0,_react.useSyncExternalStore)(_askBuoyStore.askBuoyStore.subscribe,_askBuoyStore.askBuoyStore.getState,_askBuoyStore.askBuoyStore.getState)}function useAskBuoy(e){const t=useAskBuoyState(),o=(0,_sharedUi.getUseIsPro)()(),s=(0,_react.useRef)(o);s.current=o;const r=(0,_react.useCallback)((...e)=>(0,_sharedUi.hasAccountAccess)()&&s.current?(0,_sharedUi.dispatchToolAction)(...e):Promise.reject(new Error("Ask Buoy requires Pro access.")),[]),a=(0,_react.useRef)(null),n=(0,_react.useRef)(!1),u=(0,_react.useRef)(null),i=(0,_react.useRef)(e);i.current=e;const c=!!e?.endpoint&&!!e?.model,l=!1!==e?.persistTranscript,{bypassApprovals:d,readOnly:y}=(0,_useAskBuoySettings.useAskBuoySettings)(),k=(0,_react.useRef)(d);k.current=d;const p=(0,_react.useRef)(y);p.current=y;const S=(0,_react.useCallback)(()=>p.current?{...e?.policy??{},readOnly:!0}:e?.policy??{},[e]),[_,B]=(0,_react.useState)(0);(0,_react.useEffect)(()=>{let e=!1;return(0,_sharedUi.waitForToolRegistry)().then(()=>{e||B(e=>e+1)}),()=>{e=!0}},[]);const g=(0,_react.useMemo)(()=>readAvailableActions(),[_]),h=Object.keys(g).sort().join(",");(0,_react.useEffect)(()=>{if(_askBuoyStore.askBuoyStore.setConfigured(c),_askBuoyStore.askBuoyStore.setMeta({model:e?.model,protocol:e?.protocol??(e?"anthropic":void 0),appName:e?.appName,platform:_reactNative.Platform.OS,build:isRelease?"release":"development"}),!c||!e)return a.current=null,void _askBuoyStore.askBuoyStore.setNotice("Ask Buoy isn't set up in this app yet. A developer needs to point it at your team's AI endpoint.");const t=e.protocol??"anthropic";isRelease&&e.apiKey?_askBuoyStore.askBuoyStore.setNotice("This build carries a plaintext apiKey — that's dev-only and it ships to every install. Use the headers callback with your own gateway for distributed builds."):"anthropic"===t&&/^https?:\/\/api\.openai\.com\//i.test(e.endpoint)?_askBuoyStore.askBuoyStore.setNotice('This endpoint is api.openai.com but the protocol is "anthropic". Set protocol: "openai" in the askBuoy config.'):"openai"===t&&/^https?:\/\/api\.anthropic\.com\//i.test(e.endpoint)?_askBuoyStore.askBuoyStore.setNotice('This endpoint is api.anthropic.com but the protocol is "openai". Remove protocol or set it to "anthropic".'):_askBuoyStore.askBuoyStore.setNotice(null);const o=[e.endpoint,e.protocol??"anthropic",e.model].join("|"),s=_askBuoyStore.askBuoyStore.getHeldSession();if(s&&s.signature===o)return a.current=s.session,s.session.setAvailableActions(g),s.session.setApprovalBypass(k.current),s.session.setReadOnly(p.current),void _askBuoyStore.askBuoyStore.setLedger(s.session.ledger);const u=(0,_agentCore.createAskBuoySession)({endpoint:e.endpoint,protocol:e.protocol??"anthropic",model:e.model,headers:e.headers,apiKey:e.apiKey,maxTokens:e.maxTokens,anthropicVersion:e.anthropicVersion,requestOverrides:e.requestOverrides,catalog:e.tools?.length?[..._agentCore.CATALOG,...e.tools]:void 0,dispatch:r,readSnapshot:_sharedUi.readToolSnapshot,availableActions:g,policy:e.policy,context:e.context,isRelease:isRelease,appName:e.appName,platform:_reactNative.Platform.OS,requestApproval:e=>_askBuoyStore.askBuoyStore.askApproval(e)});a.current=u,u.setApprovalBypass(k.current),u.setReadOnly(p.current),_askBuoyStore.askBuoyStore.holdSession(u,o),_askBuoyStore.askBuoyStore.setLedger(u.ledger);let i=!1;return(0,_sharedUi.waitForToolRegistry)().then(e=>{e&&!i&&u.prime()}),l&&!n.current&&(n.current=!0,(0,_transcriptPersistence.readTranscript)().then(e=>{e&&_askBuoyStore.askBuoyStore.hydrate({messages:e.messages,answers:e.answers,usage:e.usage,queued:e.queued})&&(a.current?.restore(e.history),e.ledger?.length&&a.current?.ledger.restore(e.ledger),e.pending&&_askBuoyStore.askBuoyStore.restorePending(e.pending))})),()=>{i=!0}},[c,e,g,h,l]),(0,_react.useEffect)(()=>{a.current?.setApprovalBypass(d)},[d]),(0,_react.useEffect)(()=>{a.current?.setReadOnly(y)},[y]);const f=t.messages,A=t.answers,b=t.usage,v=t.busy,m=t.pending,C=t.queued;(0,_react.useEffect)(()=>{if(!l)return;if(0===f.length)return;const e=()=>{(0,_transcriptPersistence.writeTranscript)((0,_transcriptPersistence.buildPayload)({messages:f,history:a.current?.messages??[],answers:A,usage:b,pending:m,ledger:_askBuoyStore.askBuoyStore.getLedger()?.list(),queued:C}))};if(!v)return void e();const t=setTimeout(e,SAVE_DEBOUNCE_MS);return()=>clearTimeout(t)},[l,v,f,A,b,m,C]);const w=(0,_react.useCallback)(e=>{if(!(0,_sharedUi.hasAccountAccess)()||!s.current)return;const t=a.current;if(!t)return;_askBuoyStore.askBuoyStore.beginTurn();const o=_askBuoyStore.askBuoyStore.startAssistant(),r=new AbortController;u.current=r,t.setAvailableActions(readAvailableActions());const n=i.current?.onEvent;(async()=>{try{for await(const s of t.send(e,r.signal))if(_askBuoyStore.askBuoyStore.applyEvent(o,s),n)try{n(s)}catch{}}catch(e){r.signal.aborted||_askBuoyStore.askBuoyStore.applyEvent(o,{type:"error",message:e instanceof Error?e.message:"Something went wrong talking to your AI endpoint."})}finally{r.signal.aborted&&_askBuoyStore.askBuoyStore.markStopped(o),_askBuoyStore.askBuoyStore.finishAssistant(o),u.current=null}})()},[]),T=(0,_react.useCallback)(e=>{if(!(0,_sharedUi.hasAccountAccess)()||!s.current)return;const t=e.trim();if(!a.current||!t)return;if(_askBuoyStore.askBuoyStore.getState().busy)return void _askBuoyStore.askBuoyStore.enqueue(t);const o=_askBuoyStore.askBuoyStore.getState().awaitingBlockId;o&&_askBuoyStore.askBuoyStore.recordAnswer({blockId:o,kind:"skipped"}),_askBuoyStore.askBuoyStore.addUser(t),w(t)},[w]),N=(0,_react.useCallback)(()=>{const{messages:e,busy:t}=_askBuoyStore.askBuoyStore.getState();if(t||!a.current)return;const o=[...e].reverse().find(e=>"user"===e.role);o&&w(o.text)},[w]),q=(0,_react.useCallback)(()=>{_askBuoyStore.askBuoyStore.requestStop(),_askBuoyStore.askBuoyStore.getState().pending&&_askBuoyStore.askBuoyStore.answerApproval(!1),u.current?.abort(),u.current=null},[]);(0,_react.useEffect)(()=>{const e=(0,_sharedUi.subscribeAccountAccess)(()=>{(0,_sharedUi.hasAccountAccess)()||q()});return()=>{e(),q()}},[q]),(0,_react.useEffect)(()=>{o||q()},[o,q]),(0,_react.useEffect)(()=>{if(0===t.queued.length)return;if("idle"!==(0,_askBuoyStore.turnPhaseOf)(t))return;if(null!==(0,_askBuoyStore.queuePauseReason)(t))return;const e=_askBuoyStore.askBuoyStore.shiftQueued();e&&T(e.text)},[t,T]);const R=(0,_react.useCallback)(e=>_askBuoyStore.askBuoyStore.takeQueuedToDraft(e),[]),O=(0,_react.useCallback)(e=>_askBuoyStore.askBuoyStore.removeQueued(e),[]),E=(0,_react.useCallback)(()=>{u.current?.abort(),u.current=null,(0,_resetConversation.clearConversation)()},[]),U=(0,_react.useCallback)(()=>(0,_resetConversation.resetConversation)({dispatch:_sharedUi.dispatchToolAction,undoFirst:!0}),[]),P=(0,_react.useCallback)(async t=>{const o=_askBuoyStore.askBuoyStore.getLedger();if(!o)return void _askBuoyStore.askBuoyStore.setNotice(`${t.label} couldn't run — no active Ask Buoy session.`);const s=e?.tools?.length?[..._agentCore.CATALOG,...e.tools]:void 0;if(t.targetDigest){const e=await(0,_agentCore.readTargetDigest)({toolId:t.toolId,action:t.action,params:t.params??{},dispatch:r,catalog:s});if(void 0!==e&&e!==t.targetDigest)return void _askBuoyStore.askBuoyStore.addNote(`“${t.label}” wasn't applied — what it would change has moved on since the app restarted. Nothing was written. Ask again if you still want it.`)}const a=await(0,_agentCore.runGatedAction)({toolId:t.toolId,action:t.action,params:t.params??{},dispatch:r,ledger:o,policy:S(),isRelease:isRelease,catalog:s});a.ok?_askBuoyStore.askBuoyStore.addNote(`Applied “${t.label}”, which was still waiting when the app restarted. Whatever else that answer was going to do is gone — ask again if there was more to it.`):_askBuoyStore.askBuoyStore.setNotice(`${t.label} failed — ${a.error}`)},[e,S]),I=(0,_react.useCallback)(e=>{const t=_askBuoyStore.askBuoyStore.getState().pending,o="boolean"==typeof e?e:e.approved;_askBuoyStore.askBuoyStore.answerApproval(e),t?.restored&&(o?P(t):_askBuoyStore.askBuoyStore.addNote(`Skipped “${t.label}”. Nothing was changed.`))},[P]),x=(0,_react.useCallback)(e=>{_askBuoyStore.askBuoyStore.recordAnswer(e);const t=questionBehind(e.blockId);T(t?`${t} — ${(0,_agentCore.answerToText)(e)}`:(0,_agentCore.answerToText)(e))},[T]),$=(0,_react.useCallback)(async(t,o)=>{if(!(0,_sharedUi.hasAccountAccess)()||!s.current)return void _askBuoyStore.askBuoyStore.setNotice("Ask Buoy requires Pro access.");if(t.send)return void T(t.send);if(!t.dispatch)return;const a=`${o}:${t.label}`,n=_askBuoyStore.askBuoyStore.getLedger();if(!n)return void _askBuoyStore.askBuoyStore.setNotice(`${t.label} failed — no active Ask Buoy session.`);_askBuoyStore.askBuoyStore.setActionState(a,"running");const u=await(0,_agentCore.runGatedAction)({toolId:t.dispatch.toolId,action:t.dispatch.action,params:t.dispatch.params??{},dispatch:r,ledger:n,policy:S(),isRelease:isRelease,catalog:e?.tools?.length?[..._agentCore.CATALOG,...e.tools]:void 0});u.ok?_askBuoyStore.askBuoyStore.setActionState(a,"done"):(_askBuoyStore.askBuoyStore.setActionState(a,"failed"),_askBuoyStore.askBuoyStore.setNotice(`${t.label} failed — ${u.error}`))},[T,e,S]);return{state:t,send:T,stop:q,reset:E,undoThenReset:U,approve:I,retry:N,answer:x,runAction:$,editQueued:R,removeQueued:O,configured:c,toolCount:Object.keys(g).length,availableToolIds:Object.keys(g)}}
@@ -1,14 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.useAskBuoySettings = useAskBuoySettings;
7
- var _react = require("react");
8
- var _askBuoySettings = require("./askBuoySettings");
9
- /** Subscribe the sheet to Ask Buoy's own preferences. */
10
-
11
- function useAskBuoySettings() {
12
- return (0, _react.useSyncExternalStore)(_askBuoySettings.askBuoySettings.subscribe, _askBuoySettings.askBuoySettings.get, _askBuoySettings.askBuoySettings.get);
13
- }
14
- //# sourceMappingURL=useAskBuoySettings.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.useAskBuoySettings=useAskBuoySettings;var _react=require("react"),_askBuoySettings=require("./askBuoySettings");function useAskBuoySettings(){return(0,_react.useSyncExternalStore)(_askBuoySettings.askBuoySettings.subscribe,_askBuoySettings.askBuoySettings.get,_askBuoySettings.askBuoySettings.get)}