@athenaintel/react 0.23.2 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/README.md +46 -1
  2. package/dist/assets/AssetPanel.cjs +1 -1
  3. package/dist/assets/AssetPanel.cjs.map +1 -1
  4. package/dist/assets/AssetPanel.js +1 -1
  5. package/dist/assets/AssetPanel.js.map +1 -1
  6. package/dist/assets/AthenaAssetEmbed.cjs +1 -1
  7. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  8. package/dist/assets/AthenaAssetEmbed.js +1 -1
  9. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  10. package/dist/chat/AthenaChat.cjs +138 -46
  11. package/dist/chat/AthenaChat.cjs.map +1 -1
  12. package/dist/chat/AthenaChat.d.ts +37 -1
  13. package/dist/chat/AthenaChat.js +139 -47
  14. package/dist/chat/AthenaChat.js.map +1 -1
  15. package/dist/chat/StatewireApprovalCard.cjs +3 -18
  16. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  17. package/dist/chat/StatewireApprovalCard.js +3 -18
  18. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  19. package/dist/chat/StatewireClientToolBridge.cjs +2 -16
  20. package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
  21. package/dist/chat/StatewireClientToolBridge.js +2 -16
  22. package/dist/chat/StatewireClientToolBridge.js.map +1 -1
  23. package/dist/chat/StatewireQueuedMessages.cjs +5 -16
  24. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  25. package/dist/chat/StatewireQueuedMessages.js +5 -16
  26. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  27. package/dist/chat/WaitingOnYouBanner.cjs +3 -18
  28. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
  29. package/dist/chat/WaitingOnYouBanner.js +3 -18
  30. package/dist/chat/WaitingOnYouBanner.js.map +1 -1
  31. package/dist/chat/athena-composer-controls.js +1 -1
  32. package/dist/chat/run-activity.cjs +0 -16
  33. package/dist/chat/run-activity.cjs.map +1 -1
  34. package/dist/chat/run-activity.js +0 -16
  35. package/dist/chat/run-activity.js.map +1 -1
  36. package/dist/chat/statewire-client-tools.cjs +21 -17
  37. package/dist/chat/statewire-client-tools.cjs.map +1 -1
  38. package/dist/chat/statewire-client-tools.d.ts +27 -0
  39. package/dist/chat/statewire-client-tools.js +21 -17
  40. package/dist/chat/statewire-client-tools.js.map +1 -1
  41. package/dist/chat/super-grouping/activity-timeline.cjs +0 -16
  42. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  43. package/dist/chat/super-grouping/activity-timeline.js +0 -16
  44. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  45. package/dist/chat/tiptap-text.cjs +1 -17
  46. package/dist/chat/tiptap-text.cjs.map +1 -1
  47. package/dist/chat/tiptap-text.js +1 -17
  48. package/dist/chat/tiptap-text.js.map +1 -1
  49. package/dist/chat/use-athena-run-live.cjs +0 -15
  50. package/dist/chat/use-athena-run-live.cjs.map +1 -1
  51. package/dist/chat/use-athena-run-live.d.ts +7 -0
  52. package/dist/chat/use-athena-run-live.js +0 -15
  53. package/dist/chat/use-athena-run-live.js.map +1 -1
  54. package/dist/chat/use-statewire-approval-request.cjs +4 -16
  55. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  56. package/dist/chat/use-statewire-approval-request.d.ts +2 -0
  57. package/dist/chat/use-statewire-approval-request.js +4 -16
  58. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  59. package/dist/chat-ui/composer/tiptap-composer.cjs +17 -17
  60. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  61. package/dist/chat-ui/composer/tiptap-composer.js +17 -17
  62. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  63. package/dist/chat-ui/index.cjs +0 -16
  64. package/dist/chat-ui/index.cjs.map +1 -1
  65. package/dist/chat-ui/index.js +0 -16
  66. package/dist/chat-ui/index.js.map +1 -1
  67. package/dist/chat-ui/mentions/mention-chip.cjs +1 -16
  68. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  69. package/dist/chat-ui/mentions/mention-chip.js +1 -16
  70. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  71. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +4 -6
  72. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs.map +1 -1
  73. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js +4 -6
  74. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
  75. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +2 -9
  76. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
  77. package/dist/chat-ui/mentions/suggestions/data/sources/index.js +2 -9
  78. package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
  79. package/dist/chat-ui/messages/message-components.cjs +5 -19
  80. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  81. package/dist/chat-ui/messages/message-components.js +5 -19
  82. package/dist/chat-ui/messages/message-components.js.map +1 -1
  83. package/dist/chat-ui/messages/runtime-message.cjs +1 -16
  84. package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
  85. package/dist/chat-ui/messages/runtime-message.js +1 -16
  86. package/dist/chat-ui/messages/runtime-message.js.map +1 -1
  87. package/dist/chat-ui/messages/statewire-continue-run.cjs +3 -15
  88. package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
  89. package/dist/chat-ui/messages/statewire-continue-run.js +3 -15
  90. package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
  91. package/dist/chat-ui/messages/user-edit-composer.cjs +0 -16
  92. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  93. package/dist/chat-ui/messages/user-edit-composer.js +0 -16
  94. package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
  95. package/dist/chat-ui/messages/wake-marker.cjs +1 -16
  96. package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
  97. package/dist/chat-ui/messages/wake-marker.js +1 -16
  98. package/dist/chat-ui/messages/wake-marker.js.map +1 -1
  99. package/dist/chat-ui/statewire.cjs +0 -16
  100. package/dist/chat-ui/statewire.cjs.map +1 -1
  101. package/dist/chat-ui/statewire.js +0 -16
  102. package/dist/chat-ui/statewire.js.map +1 -1
  103. package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -17
  104. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  105. package/dist/chat-ui/thread/statewire-history-edge.js +2 -17
  106. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  107. package/dist/composer/composer-handoff.cjs +121 -0
  108. package/dist/composer/composer-handoff.cjs.map +1 -0
  109. package/dist/composer/composer-handoff.d.ts +89 -0
  110. package/dist/composer/composer-handoff.js +121 -0
  111. package/dist/composer/composer-handoff.js.map +1 -0
  112. package/dist/composer/index.d.ts +2 -0
  113. package/dist/composer/useAppendToComposer.cjs +19 -7
  114. package/dist/composer/useAppendToComposer.cjs.map +1 -1
  115. package/dist/composer/useAppendToComposer.d.ts +22 -4
  116. package/dist/composer/useAppendToComposer.js +20 -8
  117. package/dist/composer/useAppendToComposer.js.map +1 -1
  118. package/dist/composer/useSendMessage.cjs +26 -8
  119. package/dist/composer/useSendMessage.cjs.map +1 -1
  120. package/dist/composer/useSendMessage.d.ts +8 -0
  121. package/dist/composer/useSendMessage.js +27 -9
  122. package/dist/composer/useSendMessage.js.map +1 -1
  123. package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
  124. package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
  125. package/dist/diagnostics/athena-diagnostics.js.map +1 -1
  126. package/dist/diagnostics/errors.cjs +2 -0
  127. package/dist/diagnostics/errors.cjs.map +1 -1
  128. package/dist/diagnostics/errors.d.ts +2 -0
  129. package/dist/diagnostics/errors.js +2 -0
  130. package/dist/diagnostics/errors.js.map +1 -1
  131. package/dist/dictation/DictationStrip.cjs +61 -0
  132. package/dist/dictation/DictationStrip.cjs.map +1 -0
  133. package/dist/dictation/DictationStrip.d.ts +27 -0
  134. package/dist/dictation/DictationStrip.js +61 -0
  135. package/dist/dictation/DictationStrip.js.map +1 -0
  136. package/dist/dictation/audio-level-history.cjs +68 -0
  137. package/dist/dictation/audio-level-history.cjs.map +1 -0
  138. package/dist/dictation/audio-level-history.d.ts +27 -0
  139. package/dist/dictation/audio-level-history.js +68 -0
  140. package/dist/dictation/audio-level-history.js.map +1 -0
  141. package/dist/dictation/dictation-controls.cjs +60 -0
  142. package/dist/dictation/dictation-controls.cjs.map +1 -0
  143. package/dist/dictation/dictation-controls.d.ts +15 -0
  144. package/dist/dictation/dictation-controls.js +60 -0
  145. package/dist/dictation/dictation-controls.js.map +1 -0
  146. package/dist/dictation/dictation-engine.cjs +128 -0
  147. package/dist/dictation/dictation-engine.cjs.map +1 -0
  148. package/dist/dictation/dictation-engine.d.ts +95 -0
  149. package/dist/dictation/dictation-engine.js +128 -0
  150. package/dist/dictation/dictation-engine.js.map +1 -0
  151. package/dist/dictation/dictation-state.cjs +68 -0
  152. package/dist/dictation/dictation-state.cjs.map +1 -0
  153. package/dist/dictation/dictation-state.d.ts +58 -0
  154. package/dist/dictation/dictation-state.js +68 -0
  155. package/dist/dictation/dictation-state.js.map +1 -0
  156. package/dist/dictation/recorded-dictation-engine.cjs +153 -0
  157. package/dist/dictation/recorded-dictation-engine.cjs.map +1 -0
  158. package/dist/dictation/recorded-dictation-engine.d.ts +66 -0
  159. package/dist/dictation/recorded-dictation-engine.js +153 -0
  160. package/dist/dictation/recorded-dictation-engine.js.map +1 -0
  161. package/dist/dictation/use-athena-dictation-engine.cjs +64 -0
  162. package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -0
  163. package/dist/dictation/use-athena-dictation-engine.d.ts +25 -0
  164. package/dist/dictation/use-athena-dictation-engine.js +64 -0
  165. package/dist/dictation/use-athena-dictation-engine.js.map +1 -0
  166. package/dist/dictation/use-dictation.cjs +215 -0
  167. package/dist/dictation/use-dictation.cjs.map +1 -0
  168. package/dist/dictation/use-dictation.d.ts +63 -0
  169. package/dist/dictation/use-dictation.js +215 -0
  170. package/dist/dictation/use-dictation.js.map +1 -0
  171. package/dist/index.cjs +41 -39
  172. package/dist/index.cjs.map +1 -1
  173. package/dist/index.d.ts +14 -0
  174. package/dist/index.js +23 -21
  175. package/dist/index.js.map +1 -1
  176. package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
  177. package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
  178. package/dist/packages/agent-runtime/src/errors.cjs +18 -0
  179. package/dist/packages/agent-runtime/src/errors.cjs.map +1 -1
  180. package/dist/packages/agent-runtime/src/errors.js +19 -1
  181. package/dist/packages/agent-runtime/src/errors.js.map +1 -1
  182. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +1 -0
  183. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  184. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +1 -0
  185. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  186. package/dist/packages/agent-runtime/src/interrupts.cjs +61 -12
  187. package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
  188. package/dist/packages/agent-runtime/src/interrupts.js +61 -12
  189. package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
  190. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +49 -0
  191. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  192. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +49 -0
  193. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  194. package/dist/packages/agent-runtime/src/thread-status.js +1 -1
  195. package/dist/packages/agent-runtime/src/tool-metadata.cjs +1 -37
  196. package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
  197. package/dist/packages/agent-runtime/src/tool-metadata.js +1 -37
  198. package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
  199. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +0 -1
  200. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
  201. package/dist/packages/agent-runtime/src/use-deep-agent-config.js +0 -1
  202. package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -1
  203. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  204. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  205. package/dist/packages/react/package.json.cjs +1 -1
  206. package/dist/packages/react/package.json.js +1 -1
  207. package/dist/provider/AthenaProvider.cjs +45 -20
  208. package/dist/provider/AthenaProvider.cjs.map +1 -1
  209. package/dist/provider/AthenaProvider.d.ts +1 -1
  210. package/dist/provider/AthenaProvider.js +46 -21
  211. package/dist/provider/AthenaProvider.js.map +1 -1
  212. package/dist/runtime/statewire-lifecycle.cjs +0 -16
  213. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  214. package/dist/runtime/statewire-lifecycle.js +0 -16
  215. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  216. package/dist/runtime/useAthenaStatewireRuntime.cjs +3 -17
  217. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  218. package/dist/runtime/useAthenaStatewireRuntime.js +3 -17
  219. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  220. package/dist/styles.css +1 -1
  221. package/dist/threads/statewire-thread-list.cjs +4 -17
  222. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  223. package/dist/threads/statewire-thread-list.js +4 -17
  224. package/dist/threads/statewire-thread-list.js.map +1 -1
  225. package/dist/threads/use-rest-conversation-history.cjs +2 -16
  226. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  227. package/dist/threads/use-rest-conversation-history.js +2 -16
  228. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  229. package/dist/threads/use-statewire-conversation-history.cjs +2 -16
  230. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  231. package/dist/threads/use-statewire-conversation-history.js +2 -16
  232. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  233. package/dist/toolkits/index.cjs +0 -7
  234. package/dist/toolkits/index.cjs.map +1 -1
  235. package/dist/toolkits/index.d.ts +0 -2
  236. package/dist/toolkits/index.js +0 -7
  237. package/dist/toolkits/index.js.map +1 -1
  238. package/dist/tools/index.cjs +4 -20
  239. package/dist/tools/index.cjs.map +1 -1
  240. package/dist/tools/index.js +1 -17
  241. package/dist/tools/index.js.map +1 -1
  242. package/dist/tools/misc-tool-uis.cjs +2 -17
  243. package/dist/tools/misc-tool-uis.cjs.map +1 -1
  244. package/dist/tools/misc-tool-uis.js +2 -17
  245. package/dist/tools/misc-tool-uis.js.map +1 -1
  246. package/dist/tools/primitives.cjs +1 -16
  247. package/dist/tools/primitives.cjs.map +1 -1
  248. package/dist/tools/primitives.js +1 -16
  249. package/dist/tools/primitives.js.map +1 -1
  250. package/dist/tools/tool-fallback.cjs +2 -17
  251. package/dist/tools/tool-fallback.cjs.map +1 -1
  252. package/dist/tools/tool-fallback.js +2 -17
  253. package/dist/tools/tool-fallback.js.map +1 -1
  254. package/dist/tools/voice-update-tool-ui.cjs +1 -17
  255. package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
  256. package/dist/tools/voice-update-tool-ui.js +1 -17
  257. package/dist/tools/voice-update-tool-ui.js.map +1 -1
  258. package/package.json +1 -1
  259. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +0 -32
  260. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +0 -1
  261. package/dist/node_modules/qrcode-generator/dist/qrcode.js +0 -31
  262. package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +0 -1
  263. package/dist/node_modules/zod/v4/classic/errors.cjs +0 -44
  264. package/dist/node_modules/zod/v4/classic/errors.cjs.map +0 -1
  265. package/dist/node_modules/zod/v4/classic/errors.js +0 -44
  266. package/dist/node_modules/zod/v4/classic/errors.js.map +0 -1
  267. package/dist/node_modules/zod/v4/classic/iso.cjs +0 -43
  268. package/dist/node_modules/zod/v4/classic/iso.cjs.map +0 -1
  269. package/dist/node_modules/zod/v4/classic/iso.js +0 -43
  270. package/dist/node_modules/zod/v4/classic/iso.js.map +0 -1
  271. package/dist/node_modules/zod/v4/classic/parse.cjs +0 -29
  272. package/dist/node_modules/zod/v4/classic/parse.cjs.map +0 -1
  273. package/dist/node_modules/zod/v4/classic/parse.js +0 -29
  274. package/dist/node_modules/zod/v4/classic/parse.js.map +0 -1
  275. package/dist/node_modules/zod/v4/classic/schemas.cjs +0 -871
  276. package/dist/node_modules/zod/v4/classic/schemas.cjs.map +0 -1
  277. package/dist/node_modules/zod/v4/classic/schemas.js +0 -871
  278. package/dist/node_modules/zod/v4/classic/schemas.js.map +0 -1
  279. package/dist/node_modules/zod/v4/core/api.cjs +0 -570
  280. package/dist/node_modules/zod/v4/core/api.cjs.map +0 -1
  281. package/dist/node_modules/zod/v4/core/api.js +0 -570
  282. package/dist/node_modules/zod/v4/core/api.js.map +0 -1
  283. package/dist/node_modules/zod/v4/core/checks.cjs +0 -411
  284. package/dist/node_modules/zod/v4/core/checks.cjs.map +0 -1
  285. package/dist/node_modules/zod/v4/core/checks.js +0 -411
  286. package/dist/node_modules/zod/v4/core/checks.js.map +0 -1
  287. package/dist/node_modules/zod/v4/core/core.cjs +0 -77
  288. package/dist/node_modules/zod/v4/core/core.cjs.map +0 -1
  289. package/dist/node_modules/zod/v4/core/core.js +0 -77
  290. package/dist/node_modules/zod/v4/core/core.js.map +0 -1
  291. package/dist/node_modules/zod/v4/core/doc.cjs +0 -38
  292. package/dist/node_modules/zod/v4/core/doc.cjs.map +0 -1
  293. package/dist/node_modules/zod/v4/core/doc.js +0 -38
  294. package/dist/node_modules/zod/v4/core/doc.js.map +0 -1
  295. package/dist/node_modules/zod/v4/core/errors.cjs +0 -76
  296. package/dist/node_modules/zod/v4/core/errors.cjs.map +0 -1
  297. package/dist/node_modules/zod/v4/core/errors.js +0 -76
  298. package/dist/node_modules/zod/v4/core/errors.js.map +0 -1
  299. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +0 -312
  300. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +0 -1
  301. package/dist/node_modules/zod/v4/core/json-schema-processors.js +0 -312
  302. package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +0 -1
  303. package/dist/node_modules/zod/v4/core/parse.cjs +0 -96
  304. package/dist/node_modules/zod/v4/core/parse.cjs.map +0 -1
  305. package/dist/node_modules/zod/v4/core/parse.js +0 -96
  306. package/dist/node_modules/zod/v4/core/parse.js.map +0 -1
  307. package/dist/node_modules/zod/v4/core/regexes.cjs +0 -86
  308. package/dist/node_modules/zod/v4/core/regexes.cjs.map +0 -1
  309. package/dist/node_modules/zod/v4/core/regexes.js +0 -86
  310. package/dist/node_modules/zod/v4/core/regexes.js.map +0 -1
  311. package/dist/node_modules/zod/v4/core/registries.cjs +0 -52
  312. package/dist/node_modules/zod/v4/core/registries.cjs.map +0 -1
  313. package/dist/node_modules/zod/v4/core/registries.js +0 -52
  314. package/dist/node_modules/zod/v4/core/registries.js.map +0 -1
  315. package/dist/node_modules/zod/v4/core/schemas.cjs +0 -1412
  316. package/dist/node_modules/zod/v4/core/schemas.cjs.map +0 -1
  317. package/dist/node_modules/zod/v4/core/schemas.js +0 -1413
  318. package/dist/node_modules/zod/v4/core/schemas.js.map +0 -1
  319. package/dist/node_modules/zod/v4/core/to-json-schema.cjs +0 -370
  320. package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +0 -1
  321. package/dist/node_modules/zod/v4/core/to-json-schema.js +0 -370
  322. package/dist/node_modules/zod/v4/core/to-json-schema.js.map +0 -1
  323. package/dist/node_modules/zod/v4/core/util.cjs +0 -444
  324. package/dist/node_modules/zod/v4/core/util.cjs.map +0 -1
  325. package/dist/node_modules/zod/v4/core/util.js +0 -444
  326. package/dist/node_modules/zod/v4/core/util.js.map +0 -1
  327. package/dist/node_modules/zod/v4/core/versions.cjs +0 -9
  328. package/dist/node_modules/zod/v4/core/versions.cjs.map +0 -1
  329. package/dist/node_modules/zod/v4/core/versions.js +0 -9
  330. package/dist/node_modules/zod/v4/core/versions.js.map +0 -1
  331. package/dist/packages/agent-runtime/src/thread-handoff.cjs +0 -85
  332. package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +0 -1
  333. package/dist/packages/agent-runtime/src/thread-handoff.js +0 -85
  334. package/dist/packages/agent-runtime/src/thread-handoff.js.map +0 -1
@@ -1,24 +1,9 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useAuiState } from "@assistant-ui/react";
3
- import "../../packages/agent-runtime/src/generated/runconfig-contract.js";
4
3
  import { useState, useRef, useEffect, useLayoutEffect } from "react";
5
- import "@assistant-ui/tap";
6
- import "statewire";
7
- import { isStatewireChannelTeardown } from "../../packages/agent-runtime/src/errors.js";
8
- import "../../packages/agent-runtime/src/statewire-suspended.js";
9
- import "../../packages/agent-runtime/src/conversation-list.js";
10
- import "@tanstack/react-query";
11
- import "../../packages/agent-runtime/src/statewire-state.js";
12
- import "../../packages/agent-runtime/src/messages.js";
13
- import { useDeepAgentWindowSync } from "../../packages/agent-runtime/src/use-deep-agent-window-sync.js";
14
- import "@assistant-ui/store";
15
- import "../../packages/agent-runtime/src/quota-safe-local-storage-session.js";
16
- import "../../packages/agent-runtime/src/statewire-attach-observer.js";
17
- import "../../packages/agent-runtime/src/thread-status.js";
18
- import "@assistant-ui/react-statewire";
19
- import "@assistant-ui/core";
20
- import "../../packages/agent-runtime/src/thread-handoff.js";
21
4
  import { useStatewireToolOutputs } from "../../provider/statewire-window-context.js";
5
+ import { useDeepAgentWindowSync } from "../../packages/agent-runtime/src/use-deep-agent-window-sync.js";
6
+ import { isStatewireChannelTeardown } from "../../packages/agent-runtime/src/errors.js";
22
7
  import { cn } from "cnfast";
23
8
  const THREAD_VIEWPORT_ATTR = "data-aui-thread-viewport";
24
9
  const HISTORY_EDGE_NEAR_MARGIN_PX = 1500;
@@ -1 +1 @@
1
- {"version":3,"file":"statewire-history-edge.js","sources":["../../../src/chat-ui/thread/statewire-history-edge.tsx"],"sourcesContent":["import { useAuiState } from \"@assistant-ui/react\";\nimport {\n type DeepAgentHistoryLoadEvent,\n type DeepAgentToolOutputStore,\n isStatewireChannelTeardown,\n useDeepAgentWindowSync,\n} from \"@athenaintel/agent-runtime\";\nimport type { FC } from \"react\";\nimport { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { useStatewireToolOutputs } from \"../../provider/statewire-window-context\";\n\n/** The attribute `ChatThreadShell` stamps on the scroll viewport. */\nexport const THREAD_VIEWPORT_ATTR = \"data-aui-thread-viewport\";\n\n/**\n * How far above the viewport the edge row counts as near: the row sits above\n * the first loaded message, so reaching it means the user has scrolled past\n * everything loaded and the next page should already be on its way.\n */\nexport const HISTORY_EDGE_NEAR_MARGIN_PX = 1500;\n\nexport interface HistoryEdgeRowProps {\n /** Whether the server holds messages older than the loaded window. */\n hasOlder: boolean;\n /** Whether the next page can be asked for; `false` with `hasOlder` is the connection's loading depth spent. Defaults to `hasOlder`. */\n canLoadOlder?: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n onLoadOlder: () => void;\n className?: string;\n /** The row element, for the viewport observer. */\n rowRef?: (element: HTMLElement | null) => void;\n}\n\n/**\n * The top edge of the LOADED history: a manual way to load the next page of\n * older messages, and the in-flight state. Renders nothing when the whole\n * transcript is loaded (or the head interest has not resolved).\n */\nexport const HistoryEdgeRow: FC<HistoryEdgeRowProps> = ({\n hasOlder,\n canLoadOlder = hasOlder,\n loading,\n onLoadOlder,\n className,\n rowRef,\n}) => {\n if (!hasOlder) return null;\n return (\n <div\n ref={rowRef}\n data-testid=\"thread-history-edge\"\n className={cn(\n \"flex w-full max-w-(--thread-max-width) items-center justify-center py-1 text-xs text-muted-foreground\",\n className,\n )}\n >\n {loading ? (\n <span data-testid=\"thread-history-edge-loading\">Loading earlier messages…</span>\n ) : canLoadOlder ? (\n <button\n type=\"button\"\n data-testid=\"thread-history-edge-load\"\n onClick={onLoadOlder}\n className=\"rounded-lg px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n Load earlier messages\n </button>\n ) : (\n <span data-testid=\"thread-history-edge-limit\">Earlier messages can't be loaded here</span>\n )}\n </div>\n );\n};\n\nexport interface StatewireHistoryEdgeProps {\n /**\n * The thread's tool-output store (the one its converter substitutes from).\n * Defaults to `AthenaProvider`'s; hosts mounting the runtime themselves pass\n * the store they gave `useDeepAgentRuntime({ toolOutputs })`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n className?: string;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: (event: DeepAgentHistoryLoadEvent) => void;\n}\n\n/**\n * The client half of the interest window for a DOM thread — mounted once per\n * thread, inside the scroll viewport above the messages (`ChatThreadShell`'s\n * `beforeMessages` slot):\n *\n * - renders the \"Load earlier messages\" edge row and loads the next page when\n * the row comes near the viewport (scrolling up) or on its button, keeping\n * the reading position when the page lands above the view;\n * - fetches cut tool outputs whole for the rendered cards, so they re-render\n * complete through the converter (`toolOutputs`).\n *\n * Inert until the head interest resolves: no row, no reads.\n */\nexport const StatewireHistoryEdge: FC<StatewireHistoryEdgeProps> = ({\n toolOutputs: toolOutputsProp,\n className,\n onHistoryLoad,\n}) => {\n const providerToolOutputs = useStatewireToolOutputs();\n const toolOutputs = toolOutputsProp ?? providerToolOutputs ?? undefined;\n const messages = useAuiState((s) => s.thread.messages);\n const [nearTop, setNearTop] = useState(false);\n const [nearTopEpoch, setNearTopEpoch] = useState(0);\n const nearTopRef = useRef(false);\n const [rowEl, setRowEl] = useState<HTMLElement | null>(null);\n const { hasOlder, canLoadOlder, loading, loadOlder } = useDeepAgentWindowSync({\n messages,\n toolOutputs,\n nearTop,\n nearTopEpoch,\n onHistoryLoad,\n onError: (error, command) => {\n // Leaving the thread settles an in-flight read as lost: lifecycle, not a failure.\n if (error instanceof Error && isStatewireChannelTeardown(error)) return;\n console.warn(`[StatewireHistoryEdge] ${command} failed:`, error);\n },\n });\n\n // The row's own visibility is the near-top level: a level, not an edge, so\n // pages chain while the user stays at the top and stop once the prepended\n // content has pushed the row away again.\n useEffect(() => {\n if (!rowEl || !hasOlder || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const near = entry.isIntersecting;\n if (nearTopRef.current === near) continue;\n nearTopRef.current = near;\n setNearTop(near);\n if (near) setNearTopEpoch((epoch) => epoch + 1);\n }\n },\n {\n root: rowEl.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`),\n rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px`,\n },\n );\n observer.observe(rowEl);\n return () => {\n observer.disconnect();\n nearTopRef.current = false;\n setNearTop(false);\n };\n }, [rowEl, hasOlder]);\n\n // A prepend (older history landing) keeps the reading position: the\n // viewport scrolls by the height the page added above the view, so the\n // message the user was on stays put. The pre-prepend height is measured\n // only while a page is in flight (a layout read per render would force a\n // reflow on every streamed token otherwise).\n const shapeRef = useRef<{ firstId: string | undefined; length: number; scrollHeight: number } | null>(\n null,\n );\n const firstId = messages[0]?.id;\n const length = messages.length;\n useLayoutEffect(() => {\n const viewport = rowEl?.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`) ?? null;\n const prev = shapeRef.current;\n const prepended =\n viewport !== null &&\n prev !== null &&\n prev.firstId !== undefined &&\n firstId !== prev.firstId &&\n length > prev.length;\n if (prepended) viewport.scrollTop += viewport.scrollHeight - prev.scrollHeight;\n shapeRef.current =\n loading || prepended || prev === null\n ? { firstId, length, scrollHeight: viewport?.scrollHeight ?? 0 }\n : { firstId, length, scrollHeight: prev.scrollHeight };\n });\n\n return (\n <HistoryEdgeRow\n rowRef={setRowEl}\n hasOlder={hasOlder}\n canLoadOlder={canLoadOlder}\n loading={loading}\n onLoadOlder={loadOlder}\n className={className}\n />\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"statewire-history-edge.js","sources":["../../../src/chat-ui/thread/statewire-history-edge.tsx"],"sourcesContent":["import { useAuiState } from \"@assistant-ui/react\";\nimport {\n type DeepAgentHistoryLoadEvent,\n type DeepAgentToolOutputStore,\n isStatewireChannelTeardown,\n useDeepAgentWindowSync,\n} from \"@athenaintel/agent-runtime\";\nimport type { FC } from \"react\";\nimport { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { useStatewireToolOutputs } from \"../../provider/statewire-window-context\";\n\n/** The attribute `ChatThreadShell` stamps on the scroll viewport. */\nexport const THREAD_VIEWPORT_ATTR = \"data-aui-thread-viewport\";\n\n/**\n * How far above the viewport the edge row counts as near: the row sits above\n * the first loaded message, so reaching it means the user has scrolled past\n * everything loaded and the next page should already be on its way.\n */\nexport const HISTORY_EDGE_NEAR_MARGIN_PX = 1500;\n\nexport interface HistoryEdgeRowProps {\n /** Whether the server holds messages older than the loaded window. */\n hasOlder: boolean;\n /** Whether the next page can be asked for; `false` with `hasOlder` is the connection's loading depth spent. Defaults to `hasOlder`. */\n canLoadOlder?: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n onLoadOlder: () => void;\n className?: string;\n /** The row element, for the viewport observer. */\n rowRef?: (element: HTMLElement | null) => void;\n}\n\n/**\n * The top edge of the LOADED history: a manual way to load the next page of\n * older messages, and the in-flight state. Renders nothing when the whole\n * transcript is loaded (or the head interest has not resolved).\n */\nexport const HistoryEdgeRow: FC<HistoryEdgeRowProps> = ({\n hasOlder,\n canLoadOlder = hasOlder,\n loading,\n onLoadOlder,\n className,\n rowRef,\n}) => {\n if (!hasOlder) return null;\n return (\n <div\n ref={rowRef}\n data-testid=\"thread-history-edge\"\n className={cn(\n \"flex w-full max-w-(--thread-max-width) items-center justify-center py-1 text-xs text-muted-foreground\",\n className,\n )}\n >\n {loading ? (\n <span data-testid=\"thread-history-edge-loading\">Loading earlier messages…</span>\n ) : canLoadOlder ? (\n <button\n type=\"button\"\n data-testid=\"thread-history-edge-load\"\n onClick={onLoadOlder}\n className=\"rounded-lg px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n Load earlier messages\n </button>\n ) : (\n <span data-testid=\"thread-history-edge-limit\">Earlier messages can't be loaded here</span>\n )}\n </div>\n );\n};\n\nexport interface StatewireHistoryEdgeProps {\n /**\n * The thread's tool-output store (the one its converter substitutes from).\n * Defaults to `AthenaProvider`'s; hosts mounting the runtime themselves pass\n * the store they gave `useDeepAgentRuntime({ toolOutputs })`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n className?: string;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: (event: DeepAgentHistoryLoadEvent) => void;\n}\n\n/**\n * The client half of the interest window for a DOM thread — mounted once per\n * thread, inside the scroll viewport above the messages (`ChatThreadShell`'s\n * `beforeMessages` slot):\n *\n * - renders the \"Load earlier messages\" edge row and loads the next page when\n * the row comes near the viewport (scrolling up) or on its button, keeping\n * the reading position when the page lands above the view;\n * - fetches cut tool outputs whole for the rendered cards, so they re-render\n * complete through the converter (`toolOutputs`).\n *\n * Inert until the head interest resolves: no row, no reads.\n */\nexport const StatewireHistoryEdge: FC<StatewireHistoryEdgeProps> = ({\n toolOutputs: toolOutputsProp,\n className,\n onHistoryLoad,\n}) => {\n const providerToolOutputs = useStatewireToolOutputs();\n const toolOutputs = toolOutputsProp ?? providerToolOutputs ?? undefined;\n const messages = useAuiState((s) => s.thread.messages);\n const [nearTop, setNearTop] = useState(false);\n const [nearTopEpoch, setNearTopEpoch] = useState(0);\n const nearTopRef = useRef(false);\n const [rowEl, setRowEl] = useState<HTMLElement | null>(null);\n const { hasOlder, canLoadOlder, loading, loadOlder } = useDeepAgentWindowSync({\n messages,\n toolOutputs,\n nearTop,\n nearTopEpoch,\n onHistoryLoad,\n onError: (error, command) => {\n // Leaving the thread settles an in-flight read as lost: lifecycle, not a failure.\n if (error instanceof Error && isStatewireChannelTeardown(error)) return;\n console.warn(`[StatewireHistoryEdge] ${command} failed:`, error);\n },\n });\n\n // The row's own visibility is the near-top level: a level, not an edge, so\n // pages chain while the user stays at the top and stop once the prepended\n // content has pushed the row away again.\n useEffect(() => {\n if (!rowEl || !hasOlder || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const near = entry.isIntersecting;\n if (nearTopRef.current === near) continue;\n nearTopRef.current = near;\n setNearTop(near);\n if (near) setNearTopEpoch((epoch) => epoch + 1);\n }\n },\n {\n root: rowEl.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`),\n rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px`,\n },\n );\n observer.observe(rowEl);\n return () => {\n observer.disconnect();\n nearTopRef.current = false;\n setNearTop(false);\n };\n }, [rowEl, hasOlder]);\n\n // A prepend (older history landing) keeps the reading position: the\n // viewport scrolls by the height the page added above the view, so the\n // message the user was on stays put. The pre-prepend height is measured\n // only while a page is in flight (a layout read per render would force a\n // reflow on every streamed token otherwise).\n const shapeRef = useRef<{ firstId: string | undefined; length: number; scrollHeight: number } | null>(\n null,\n );\n const firstId = messages[0]?.id;\n const length = messages.length;\n useLayoutEffect(() => {\n const viewport = rowEl?.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`) ?? null;\n const prev = shapeRef.current;\n const prepended =\n viewport !== null &&\n prev !== null &&\n prev.firstId !== undefined &&\n firstId !== prev.firstId &&\n length > prev.length;\n if (prepended) viewport.scrollTop += viewport.scrollHeight - prev.scrollHeight;\n shapeRef.current =\n loading || prepended || prev === null\n ? { firstId, length, scrollHeight: viewport?.scrollHeight ?? 0 }\n : { firstId, length, scrollHeight: prev.scrollHeight };\n });\n\n return (\n <HistoryEdgeRow\n rowRef={setRowEl}\n hasOlder={hasOlder}\n canLoadOlder={canLoadOlder}\n loading={loading}\n onLoadOlder={loadOlder}\n className={className}\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react$1 = require("react");
4
+ const react = require("@assistant-ui/react");
5
+ const athenaDiagnostics = require("../diagnostics/athena-diagnostics.cjs");
6
+ const errors = require("../diagnostics/errors.cjs");
7
+ const statewireThreadList = require("../threads/statewire-thread-list.cjs");
8
+ function createComposerHandoffStore() {
9
+ return { pending: [], focusOnNextMirror: false, focusEnd: null };
10
+ }
11
+ const ComposerHandoffContext = react$1.createContext(null);
12
+ function useComposerHandoffStore() {
13
+ return react$1.useContext(ComposerHandoffContext);
14
+ }
15
+ function useComposerReady() {
16
+ const list = statewireThreadList.useAthenaStatewireThreadList();
17
+ const threadLoading = react.useAuiState((s) => {
18
+ var _a;
19
+ return ((_a = s.optional.thread) == null ? void 0 : _a.isLoading) ?? false;
20
+ });
21
+ return !((list == null ? void 0 : list.isLoading) ?? false) && !threadLoading;
22
+ }
23
+ function mergeComposerDraft({
24
+ current,
25
+ write
26
+ }) {
27
+ if (write.replace || !current) return write.text;
28
+ return `${current}
29
+ ${write.text}`;
30
+ }
31
+ function applyComposerWrite({
32
+ target,
33
+ store,
34
+ write
35
+ }) {
36
+ var _a;
37
+ const current = target.composer.getState().text ?? "";
38
+ const next = mergeComposerDraft({ current, write });
39
+ target.composer.setText(next);
40
+ if (write.focus && store) {
41
+ if (next === current) (_a = store.focusEnd) == null ? void 0 : _a.call(store);
42
+ else store.focusOnNextMirror = true;
43
+ }
44
+ athenaDiagnostics.athenaDiagnostics.emit("composer.handoff", {
45
+ level: "debug",
46
+ data: { phase: "applied", send: write.send, focus: write.focus, chars: next.length }
47
+ });
48
+ if (write.send) {
49
+ return Promise.resolve(target.composer.send({ steer: false })).then(() => void 0);
50
+ }
51
+ return void 0;
52
+ }
53
+ function queueComposerWrite({
54
+ store,
55
+ write
56
+ }) {
57
+ const last = store.pending[store.pending.length - 1];
58
+ const foldable = last !== void 0 && !last.send && !write.send && !write.replace && last.threadId === write.threadId && last.resolve === void 0 && write.resolve === void 0;
59
+ if (foldable) {
60
+ last.text = mergeComposerDraft({ current: last.text, write });
61
+ last.focus = last.focus || write.focus;
62
+ } else {
63
+ store.pending.push(write);
64
+ }
65
+ athenaDiagnostics.athenaDiagnostics.emit("composer.handoff", {
66
+ level: "debug",
67
+ data: { phase: "deferred", send: write.send, focus: write.focus, queued: store.pending.length }
68
+ });
69
+ }
70
+ function dropWrite({ write, reason }) {
71
+ var _a;
72
+ const error = new errors.AthenaSdkError({
73
+ code: "composer_write_dropped",
74
+ message: `A composer ${write.send ? "send" : "draft"} queued before the chat was ready was dropped: ${reason}.`,
75
+ hint: "Make the write again from the chat that is active now.",
76
+ context: { threadId: write.threadId, send: write.send }
77
+ });
78
+ athenaDiagnostics.athenaDiagnostics.emit("composer.handoff", {
79
+ level: "warn",
80
+ error,
81
+ data: { phase: "dropped", send: write.send, reason, threadId: write.threadId }
82
+ });
83
+ (_a = write.reject) == null ? void 0 : _a.call(write, error);
84
+ }
85
+ function drainComposerHandoff({
86
+ store,
87
+ target,
88
+ threadId
89
+ }) {
90
+ var _a;
91
+ const writes = store.pending;
92
+ if (writes.length === 0) return;
93
+ store.pending = [];
94
+ for (const write of writes) {
95
+ if (write.threadId !== null && threadId !== null && write.threadId !== threadId) {
96
+ dropWrite({ write, reason: `it was made for thread ${write.threadId}, thread ${threadId} is active` });
97
+ continue;
98
+ }
99
+ const outcome = applyComposerWrite({ target, store, write });
100
+ (_a = write.resolve) == null ? void 0 : _a.call(write, outcome);
101
+ }
102
+ }
103
+ function clearComposerHandoff({
104
+ store,
105
+ reason
106
+ }) {
107
+ const writes = store.pending;
108
+ if (writes.length === 0) return;
109
+ store.pending = [];
110
+ for (const write of writes) dropWrite({ write, reason });
111
+ }
112
+ exports.ComposerHandoffContext = ComposerHandoffContext;
113
+ exports.applyComposerWrite = applyComposerWrite;
114
+ exports.clearComposerHandoff = clearComposerHandoff;
115
+ exports.createComposerHandoffStore = createComposerHandoffStore;
116
+ exports.drainComposerHandoff = drainComposerHandoff;
117
+ exports.mergeComposerDraft = mergeComposerDraft;
118
+ exports.queueComposerWrite = queueComposerWrite;
119
+ exports.useComposerHandoffStore = useComposerHandoffStore;
120
+ exports.useComposerReady = useComposerReady;
121
+ //# sourceMappingURL=composer-handoff.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer-handoff.cjs","sources":["../../src/composer/composer-handoff.ts"],"sourcesContent":["/**\n * Composer handoff: writes to the chat composer from host UI that lives\n * outside `<AthenaChat>` (a sidebar, an agenda card, a workflow button).\n *\n * Two problems it solves for `useAppendToComposer` / `useSendMessage`:\n *\n * 1. A write before the chat is ready is lost. While the thread list is still\n * resolving the active thread, or the thread's history is still loading,\n * the composer the hooks write to belongs to a mount that is about to be\n * replaced (thread-list mode keys the statewire subtree by thread), and\n * the draft goes with it. The store below lives ABOVE that mount, so a\n * write made too early waits here and the ready mount drains it.\n * 2. A prefilled draft should put the caret after the text so the user can\n * keep typing or dictating. The Tiptap editor mirrors runtime text on a\n * later tick; the `focusOnNextMirror` flag asks it to focus at the end\n * right after that mirror lands, instead of the host guessing a delay.\n *\n * Every queued write is bound to the statewire thread it was made for and\n * drains only into that thread's mount; a thread switch, an identity change\n * or a provider unmount drops what waits with a `composer_write_dropped`\n * error, never into another user's or another thread's composer.\n */\nimport { createContext, useContext } from 'react';\nimport { useAuiState } from '@assistant-ui/react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport { AthenaSdkError } from '../diagnostics/errors';\nimport { useAthenaStatewireThreadList } from '../threads/statewire-thread-list';\n\n/** The slice of the assistant-ui client the handoff writes to. */\nexport interface ComposerTarget {\n composer: {\n getState: () => { text: string };\n setText: (text: string) => void;\n send: (options?: { steer?: boolean }) => unknown;\n };\n}\n\nexport interface ComposerWrite {\n text: string;\n /** Replace the draft instead of appending on a new line. */\n replace: boolean;\n /** Submit right after writing. */\n send: boolean;\n /** Put the caret at the end of the draft once the editor shows it. */\n focus: boolean;\n /** The statewire thread the write was made for; it drains only into that thread's mount. */\n threadId: string | null;\n /** Settled once the write has been applied (and, for `send`, with the send's outcome). */\n resolve?: (outcome: Promise<void> | void) => void;\n /** Settled with `composer_write_dropped` when the write can no longer land. */\n reject?: (error: AthenaSdkError) => void;\n}\n\nexport interface ComposerHandoffStore {\n /** Writes waiting for a ready composer, oldest first. Adjacent plain appends fold together. */\n pending: ComposerWrite[];\n /** Set by a `focus` write; the editor consumes it after mirroring the draft. */\n focusOnNextMirror: boolean;\n /** Registered by the mounted editor: place the caret at the end of the draft. */\n focusEnd: (() => void) | null;\n}\n\nexport function createComposerHandoffStore(): ComposerHandoffStore {\n return { pending: [], focusOnNextMirror: false, focusEnd: null };\n}\n\nexport const ComposerHandoffContext = createContext<ComposerHandoffStore | null>(null);\n\n/** The provider's handoff store, or null outside `AthenaProvider`. */\nexport function useComposerHandoffStore(): ComposerHandoffStore | null {\n return useContext(ComposerHandoffContext);\n}\n\n/**\n * Whether a composer write lands now: in thread-list mode the list has\n * resolved which thread is active, and that thread's history has loaded.\n * Until then the composer the hooks write to is the one of a mount that is\n * about to be replaced, and the draft goes with it.\n *\n * Deliberately not the socket status: a never-started chat sits in\n * `standby` until its first send and must still take a prefill.\n */\nexport function useComposerReady(): boolean {\n const list = useAthenaStatewireThreadList();\n const threadLoading = useAuiState((s) => s.optional.thread?.isLoading ?? false);\n return !(list?.isLoading ?? false) && !threadLoading;\n}\n\n/** The draft after `write`: `replace` (or an empty draft) takes the text as is. */\nexport function mergeComposerDraft({\n current,\n write,\n}: {\n current: string;\n write: Pick<ComposerWrite, 'text' | 'replace'>;\n}): string {\n if (write.replace || !current) return write.text;\n return `${current}\\n${write.text}`;\n}\n\n/**\n * Write into the live composer. Returns the send's outcome for a `send`\n * write so callers can await it.\n */\nexport function applyComposerWrite({\n target,\n store,\n write,\n}: {\n target: ComposerTarget;\n store: ComposerHandoffStore | null;\n write: ComposerWrite;\n}): Promise<void> | void {\n const current = target.composer.getState().text ?? '';\n const next = mergeComposerDraft({ current, write });\n target.composer.setText(next);\n if (write.focus && store) {\n // An unchanged draft never re-mirrors, so focus at once; otherwise the\n // editor focuses itself right after it has shown the new text.\n if (next === current) store.focusEnd?.();\n else store.focusOnNextMirror = true;\n }\n athenaDiagnostics.emit('composer.handoff', {\n level: 'debug',\n data: { phase: 'applied', send: write.send, focus: write.focus, chars: next.length },\n });\n if (write.send) {\n return Promise.resolve(target.composer.send({ steer: false })).then(() => undefined);\n }\n return undefined;\n}\n\n/**\n * Hold a write until the composer is ready. Writes keep their order; a plain\n * append folds into a directly preceding plain append for the same thread\n * (one draft, not two), while a replace or a send is kept as its own step so\n * no message is lost and every send settles on its own.\n */\nexport function queueComposerWrite({\n store,\n write,\n}: {\n store: ComposerHandoffStore;\n write: ComposerWrite;\n}): void {\n const last = store.pending[store.pending.length - 1];\n const foldable =\n last !== undefined &&\n !last.send &&\n !write.send &&\n !write.replace &&\n last.threadId === write.threadId &&\n last.resolve === undefined &&\n write.resolve === undefined;\n if (foldable) {\n last.text = mergeComposerDraft({ current: last.text, write });\n last.focus = last.focus || write.focus;\n } else {\n store.pending.push(write);\n }\n athenaDiagnostics.emit('composer.handoff', {\n level: 'debug',\n data: { phase: 'deferred', send: write.send, focus: write.focus, queued: store.pending.length },\n });\n}\n\nfunction dropWrite({ write, reason }: { write: ComposerWrite; reason: string }): void {\n const error = new AthenaSdkError({\n code: 'composer_write_dropped',\n message: `A composer ${write.send ? 'send' : 'draft'} queued before the chat was ready was dropped: ${reason}.`,\n hint: 'Make the write again from the chat that is active now.',\n context: { threadId: write.threadId, send: write.send },\n });\n athenaDiagnostics.emit('composer.handoff', {\n level: 'warn',\n error,\n data: { phase: 'dropped', send: write.send, reason, threadId: write.threadId },\n });\n write.reject?.(error);\n}\n\n/**\n * Deliver the pending writes, in order, into a now-ready composer. A write\n * made for another thread is dropped (rejected), never applied here.\n */\nexport function drainComposerHandoff({\n store,\n target,\n threadId,\n}: {\n store: ComposerHandoffStore;\n target: ComposerTarget;\n /** The statewire thread of the mount that is draining. */\n threadId: string | null;\n}): void {\n const writes = store.pending;\n if (writes.length === 0) return;\n store.pending = [];\n for (const write of writes) {\n if (write.threadId !== null && threadId !== null && write.threadId !== threadId) {\n dropWrite({ write, reason: `it was made for thread ${write.threadId}, thread ${threadId} is active` });\n continue;\n }\n const outcome = applyComposerWrite({ target, store, write });\n write.resolve?.(outcome);\n }\n}\n\n/** Drop every pending write (identity changed, provider unmounted). */\nexport function clearComposerHandoff({\n store,\n reason,\n}: {\n store: ComposerHandoffStore;\n reason: string;\n}): void {\n const writes = store.pending;\n if (writes.length === 0) return;\n store.pending = [];\n for (const write of writes) dropWrite({ write, reason });\n}\n"],"names":["createContext","useContext","useAthenaStatewireThreadList","useAuiState","athenaDiagnostics","AthenaSdkError"],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import { AthenaSdkError } from '../diagnostics/errors';
2
+ /** The slice of the assistant-ui client the handoff writes to. */
3
+ export interface ComposerTarget {
4
+ composer: {
5
+ getState: () => {
6
+ text: string;
7
+ };
8
+ setText: (text: string) => void;
9
+ send: (options?: {
10
+ steer?: boolean;
11
+ }) => unknown;
12
+ };
13
+ }
14
+ export interface ComposerWrite {
15
+ text: string;
16
+ /** Replace the draft instead of appending on a new line. */
17
+ replace: boolean;
18
+ /** Submit right after writing. */
19
+ send: boolean;
20
+ /** Put the caret at the end of the draft once the editor shows it. */
21
+ focus: boolean;
22
+ /** The statewire thread the write was made for; it drains only into that thread's mount. */
23
+ threadId: string | null;
24
+ /** Settled once the write has been applied (and, for `send`, with the send's outcome). */
25
+ resolve?: (outcome: Promise<void> | void) => void;
26
+ /** Settled with `composer_write_dropped` when the write can no longer land. */
27
+ reject?: (error: AthenaSdkError) => void;
28
+ }
29
+ export interface ComposerHandoffStore {
30
+ /** Writes waiting for a ready composer, oldest first. Adjacent plain appends fold together. */
31
+ pending: ComposerWrite[];
32
+ /** Set by a `focus` write; the editor consumes it after mirroring the draft. */
33
+ focusOnNextMirror: boolean;
34
+ /** Registered by the mounted editor: place the caret at the end of the draft. */
35
+ focusEnd: (() => void) | null;
36
+ }
37
+ export declare function createComposerHandoffStore(): ComposerHandoffStore;
38
+ export declare const ComposerHandoffContext: import("react").Context<ComposerHandoffStore | null>;
39
+ /** The provider's handoff store, or null outside `AthenaProvider`. */
40
+ export declare function useComposerHandoffStore(): ComposerHandoffStore | null;
41
+ /**
42
+ * Whether a composer write lands now: in thread-list mode the list has
43
+ * resolved which thread is active, and that thread's history has loaded.
44
+ * Until then the composer the hooks write to is the one of a mount that is
45
+ * about to be replaced, and the draft goes with it.
46
+ *
47
+ * Deliberately not the socket status: a never-started chat sits in
48
+ * `standby` until its first send and must still take a prefill.
49
+ */
50
+ export declare function useComposerReady(): boolean;
51
+ /** The draft after `write`: `replace` (or an empty draft) takes the text as is. */
52
+ export declare function mergeComposerDraft({ current, write, }: {
53
+ current: string;
54
+ write: Pick<ComposerWrite, 'text' | 'replace'>;
55
+ }): string;
56
+ /**
57
+ * Write into the live composer. Returns the send's outcome for a `send`
58
+ * write so callers can await it.
59
+ */
60
+ export declare function applyComposerWrite({ target, store, write, }: {
61
+ target: ComposerTarget;
62
+ store: ComposerHandoffStore | null;
63
+ write: ComposerWrite;
64
+ }): Promise<void> | void;
65
+ /**
66
+ * Hold a write until the composer is ready. Writes keep their order; a plain
67
+ * append folds into a directly preceding plain append for the same thread
68
+ * (one draft, not two), while a replace or a send is kept as its own step so
69
+ * no message is lost and every send settles on its own.
70
+ */
71
+ export declare function queueComposerWrite({ store, write, }: {
72
+ store: ComposerHandoffStore;
73
+ write: ComposerWrite;
74
+ }): void;
75
+ /**
76
+ * Deliver the pending writes, in order, into a now-ready composer. A write
77
+ * made for another thread is dropped (rejected), never applied here.
78
+ */
79
+ export declare function drainComposerHandoff({ store, target, threadId, }: {
80
+ store: ComposerHandoffStore;
81
+ target: ComposerTarget;
82
+ /** The statewire thread of the mount that is draining. */
83
+ threadId: string | null;
84
+ }): void;
85
+ /** Drop every pending write (identity changed, provider unmounted). */
86
+ export declare function clearComposerHandoff({ store, reason, }: {
87
+ store: ComposerHandoffStore;
88
+ reason: string;
89
+ }): void;
@@ -0,0 +1,121 @@
1
+ import { createContext, useContext } from "react";
2
+ import { useAuiState } from "@assistant-ui/react";
3
+ import { athenaDiagnostics } from "../diagnostics/athena-diagnostics.js";
4
+ import { AthenaSdkError } from "../diagnostics/errors.js";
5
+ import { useAthenaStatewireThreadList } from "../threads/statewire-thread-list.js";
6
+ function createComposerHandoffStore() {
7
+ return { pending: [], focusOnNextMirror: false, focusEnd: null };
8
+ }
9
+ const ComposerHandoffContext = createContext(null);
10
+ function useComposerHandoffStore() {
11
+ return useContext(ComposerHandoffContext);
12
+ }
13
+ function useComposerReady() {
14
+ const list = useAthenaStatewireThreadList();
15
+ const threadLoading = useAuiState((s) => {
16
+ var _a;
17
+ return ((_a = s.optional.thread) == null ? void 0 : _a.isLoading) ?? false;
18
+ });
19
+ return !((list == null ? void 0 : list.isLoading) ?? false) && !threadLoading;
20
+ }
21
+ function mergeComposerDraft({
22
+ current,
23
+ write
24
+ }) {
25
+ if (write.replace || !current) return write.text;
26
+ return `${current}
27
+ ${write.text}`;
28
+ }
29
+ function applyComposerWrite({
30
+ target,
31
+ store,
32
+ write
33
+ }) {
34
+ var _a;
35
+ const current = target.composer.getState().text ?? "";
36
+ const next = mergeComposerDraft({ current, write });
37
+ target.composer.setText(next);
38
+ if (write.focus && store) {
39
+ if (next === current) (_a = store.focusEnd) == null ? void 0 : _a.call(store);
40
+ else store.focusOnNextMirror = true;
41
+ }
42
+ athenaDiagnostics.emit("composer.handoff", {
43
+ level: "debug",
44
+ data: { phase: "applied", send: write.send, focus: write.focus, chars: next.length }
45
+ });
46
+ if (write.send) {
47
+ return Promise.resolve(target.composer.send({ steer: false })).then(() => void 0);
48
+ }
49
+ return void 0;
50
+ }
51
+ function queueComposerWrite({
52
+ store,
53
+ write
54
+ }) {
55
+ const last = store.pending[store.pending.length - 1];
56
+ const foldable = last !== void 0 && !last.send && !write.send && !write.replace && last.threadId === write.threadId && last.resolve === void 0 && write.resolve === void 0;
57
+ if (foldable) {
58
+ last.text = mergeComposerDraft({ current: last.text, write });
59
+ last.focus = last.focus || write.focus;
60
+ } else {
61
+ store.pending.push(write);
62
+ }
63
+ athenaDiagnostics.emit("composer.handoff", {
64
+ level: "debug",
65
+ data: { phase: "deferred", send: write.send, focus: write.focus, queued: store.pending.length }
66
+ });
67
+ }
68
+ function dropWrite({ write, reason }) {
69
+ var _a;
70
+ const error = new AthenaSdkError({
71
+ code: "composer_write_dropped",
72
+ message: `A composer ${write.send ? "send" : "draft"} queued before the chat was ready was dropped: ${reason}.`,
73
+ hint: "Make the write again from the chat that is active now.",
74
+ context: { threadId: write.threadId, send: write.send }
75
+ });
76
+ athenaDiagnostics.emit("composer.handoff", {
77
+ level: "warn",
78
+ error,
79
+ data: { phase: "dropped", send: write.send, reason, threadId: write.threadId }
80
+ });
81
+ (_a = write.reject) == null ? void 0 : _a.call(write, error);
82
+ }
83
+ function drainComposerHandoff({
84
+ store,
85
+ target,
86
+ threadId
87
+ }) {
88
+ var _a;
89
+ const writes = store.pending;
90
+ if (writes.length === 0) return;
91
+ store.pending = [];
92
+ for (const write of writes) {
93
+ if (write.threadId !== null && threadId !== null && write.threadId !== threadId) {
94
+ dropWrite({ write, reason: `it was made for thread ${write.threadId}, thread ${threadId} is active` });
95
+ continue;
96
+ }
97
+ const outcome = applyComposerWrite({ target, store, write });
98
+ (_a = write.resolve) == null ? void 0 : _a.call(write, outcome);
99
+ }
100
+ }
101
+ function clearComposerHandoff({
102
+ store,
103
+ reason
104
+ }) {
105
+ const writes = store.pending;
106
+ if (writes.length === 0) return;
107
+ store.pending = [];
108
+ for (const write of writes) dropWrite({ write, reason });
109
+ }
110
+ export {
111
+ ComposerHandoffContext,
112
+ applyComposerWrite,
113
+ clearComposerHandoff,
114
+ createComposerHandoffStore,
115
+ drainComposerHandoff,
116
+ mergeComposerDraft,
117
+ queueComposerWrite,
118
+ useComposerHandoffStore,
119
+ useComposerReady
120
+ };
121
+ //# sourceMappingURL=composer-handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer-handoff.js","sources":["../../src/composer/composer-handoff.ts"],"sourcesContent":["/**\n * Composer handoff: writes to the chat composer from host UI that lives\n * outside `<AthenaChat>` (a sidebar, an agenda card, a workflow button).\n *\n * Two problems it solves for `useAppendToComposer` / `useSendMessage`:\n *\n * 1. A write before the chat is ready is lost. While the thread list is still\n * resolving the active thread, or the thread's history is still loading,\n * the composer the hooks write to belongs to a mount that is about to be\n * replaced (thread-list mode keys the statewire subtree by thread), and\n * the draft goes with it. The store below lives ABOVE that mount, so a\n * write made too early waits here and the ready mount drains it.\n * 2. A prefilled draft should put the caret after the text so the user can\n * keep typing or dictating. The Tiptap editor mirrors runtime text on a\n * later tick; the `focusOnNextMirror` flag asks it to focus at the end\n * right after that mirror lands, instead of the host guessing a delay.\n *\n * Every queued write is bound to the statewire thread it was made for and\n * drains only into that thread's mount; a thread switch, an identity change\n * or a provider unmount drops what waits with a `composer_write_dropped`\n * error, never into another user's or another thread's composer.\n */\nimport { createContext, useContext } from 'react';\nimport { useAuiState } from '@assistant-ui/react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport { AthenaSdkError } from '../diagnostics/errors';\nimport { useAthenaStatewireThreadList } from '../threads/statewire-thread-list';\n\n/** The slice of the assistant-ui client the handoff writes to. */\nexport interface ComposerTarget {\n composer: {\n getState: () => { text: string };\n setText: (text: string) => void;\n send: (options?: { steer?: boolean }) => unknown;\n };\n}\n\nexport interface ComposerWrite {\n text: string;\n /** Replace the draft instead of appending on a new line. */\n replace: boolean;\n /** Submit right after writing. */\n send: boolean;\n /** Put the caret at the end of the draft once the editor shows it. */\n focus: boolean;\n /** The statewire thread the write was made for; it drains only into that thread's mount. */\n threadId: string | null;\n /** Settled once the write has been applied (and, for `send`, with the send's outcome). */\n resolve?: (outcome: Promise<void> | void) => void;\n /** Settled with `composer_write_dropped` when the write can no longer land. */\n reject?: (error: AthenaSdkError) => void;\n}\n\nexport interface ComposerHandoffStore {\n /** Writes waiting for a ready composer, oldest first. Adjacent plain appends fold together. */\n pending: ComposerWrite[];\n /** Set by a `focus` write; the editor consumes it after mirroring the draft. */\n focusOnNextMirror: boolean;\n /** Registered by the mounted editor: place the caret at the end of the draft. */\n focusEnd: (() => void) | null;\n}\n\nexport function createComposerHandoffStore(): ComposerHandoffStore {\n return { pending: [], focusOnNextMirror: false, focusEnd: null };\n}\n\nexport const ComposerHandoffContext = createContext<ComposerHandoffStore | null>(null);\n\n/** The provider's handoff store, or null outside `AthenaProvider`. */\nexport function useComposerHandoffStore(): ComposerHandoffStore | null {\n return useContext(ComposerHandoffContext);\n}\n\n/**\n * Whether a composer write lands now: in thread-list mode the list has\n * resolved which thread is active, and that thread's history has loaded.\n * Until then the composer the hooks write to is the one of a mount that is\n * about to be replaced, and the draft goes with it.\n *\n * Deliberately not the socket status: a never-started chat sits in\n * `standby` until its first send and must still take a prefill.\n */\nexport function useComposerReady(): boolean {\n const list = useAthenaStatewireThreadList();\n const threadLoading = useAuiState((s) => s.optional.thread?.isLoading ?? false);\n return !(list?.isLoading ?? false) && !threadLoading;\n}\n\n/** The draft after `write`: `replace` (or an empty draft) takes the text as is. */\nexport function mergeComposerDraft({\n current,\n write,\n}: {\n current: string;\n write: Pick<ComposerWrite, 'text' | 'replace'>;\n}): string {\n if (write.replace || !current) return write.text;\n return `${current}\\n${write.text}`;\n}\n\n/**\n * Write into the live composer. Returns the send's outcome for a `send`\n * write so callers can await it.\n */\nexport function applyComposerWrite({\n target,\n store,\n write,\n}: {\n target: ComposerTarget;\n store: ComposerHandoffStore | null;\n write: ComposerWrite;\n}): Promise<void> | void {\n const current = target.composer.getState().text ?? '';\n const next = mergeComposerDraft({ current, write });\n target.composer.setText(next);\n if (write.focus && store) {\n // An unchanged draft never re-mirrors, so focus at once; otherwise the\n // editor focuses itself right after it has shown the new text.\n if (next === current) store.focusEnd?.();\n else store.focusOnNextMirror = true;\n }\n athenaDiagnostics.emit('composer.handoff', {\n level: 'debug',\n data: { phase: 'applied', send: write.send, focus: write.focus, chars: next.length },\n });\n if (write.send) {\n return Promise.resolve(target.composer.send({ steer: false })).then(() => undefined);\n }\n return undefined;\n}\n\n/**\n * Hold a write until the composer is ready. Writes keep their order; a plain\n * append folds into a directly preceding plain append for the same thread\n * (one draft, not two), while a replace or a send is kept as its own step so\n * no message is lost and every send settles on its own.\n */\nexport function queueComposerWrite({\n store,\n write,\n}: {\n store: ComposerHandoffStore;\n write: ComposerWrite;\n}): void {\n const last = store.pending[store.pending.length - 1];\n const foldable =\n last !== undefined &&\n !last.send &&\n !write.send &&\n !write.replace &&\n last.threadId === write.threadId &&\n last.resolve === undefined &&\n write.resolve === undefined;\n if (foldable) {\n last.text = mergeComposerDraft({ current: last.text, write });\n last.focus = last.focus || write.focus;\n } else {\n store.pending.push(write);\n }\n athenaDiagnostics.emit('composer.handoff', {\n level: 'debug',\n data: { phase: 'deferred', send: write.send, focus: write.focus, queued: store.pending.length },\n });\n}\n\nfunction dropWrite({ write, reason }: { write: ComposerWrite; reason: string }): void {\n const error = new AthenaSdkError({\n code: 'composer_write_dropped',\n message: `A composer ${write.send ? 'send' : 'draft'} queued before the chat was ready was dropped: ${reason}.`,\n hint: 'Make the write again from the chat that is active now.',\n context: { threadId: write.threadId, send: write.send },\n });\n athenaDiagnostics.emit('composer.handoff', {\n level: 'warn',\n error,\n data: { phase: 'dropped', send: write.send, reason, threadId: write.threadId },\n });\n write.reject?.(error);\n}\n\n/**\n * Deliver the pending writes, in order, into a now-ready composer. A write\n * made for another thread is dropped (rejected), never applied here.\n */\nexport function drainComposerHandoff({\n store,\n target,\n threadId,\n}: {\n store: ComposerHandoffStore;\n target: ComposerTarget;\n /** The statewire thread of the mount that is draining. */\n threadId: string | null;\n}): void {\n const writes = store.pending;\n if (writes.length === 0) return;\n store.pending = [];\n for (const write of writes) {\n if (write.threadId !== null && threadId !== null && write.threadId !== threadId) {\n dropWrite({ write, reason: `it was made for thread ${write.threadId}, thread ${threadId} is active` });\n continue;\n }\n const outcome = applyComposerWrite({ target, store, write });\n write.resolve?.(outcome);\n }\n}\n\n/** Drop every pending write (identity changed, provider unmounted). */\nexport function clearComposerHandoff({\n store,\n reason,\n}: {\n store: ComposerHandoffStore;\n reason: string;\n}): void {\n const writes = store.pending;\n if (writes.length === 0) return;\n store.pending = [];\n for (const write of writes) dropWrite({ write, reason });\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  export { useAppendToComposer } from './useAppendToComposer';
2
+ export type { AppendToComposerOptions } from './useAppendToComposer';
2
3
  export { useSendMessage } from './useSendMessage';
3
4
  export type { SendMessageOptions } from './useSendMessage';
5
+ export { useComposerReady } from './composer-handoff';
4
6
  export { useComposerAttachment } from './useComposerAttachment';
5
7
  export { useFileUpload } from './useFileUpload';
6
8
  export type { UploadedAsset, UploadProgress, UseFileUploadReturn } from './useFileUpload';
@@ -2,19 +2,31 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const react$1 = require("react");
4
4
  const react = require("@assistant-ui/react");
5
+ const threadIdContext = require("../threads/thread-id-context.cjs");
6
+ const composerHandoff = require("./composer-handoff.cjs");
5
7
  function useAppendToComposer() {
6
8
  const aui = react.useAui();
9
+ const store = composerHandoff.useComposerHandoffStore();
10
+ const threadId = react$1.useContext(threadIdContext.AthenaStatewireThreadIdContext);
11
+ const ready = composerHandoff.useComposerReady();
12
+ const readyRef = react$1.useRef(ready);
13
+ readyRef.current = ready;
7
14
  return react$1.useCallback(
8
15
  (text, opts) => {
9
- if (opts == null ? void 0 : opts.replace) {
10
- aui.composer.setText(text);
11
- } else {
12
- const current = aui.composer.getState().text;
13
- aui.composer.setText(current ? `${current}
14
- ${text}` : text);
16
+ const write = {
17
+ text,
18
+ replace: (opts == null ? void 0 : opts.replace) ?? false,
19
+ send: false,
20
+ focus: (opts == null ? void 0 : opts.focus) ?? false,
21
+ threadId
22
+ };
23
+ if (store && !readyRef.current) {
24
+ composerHandoff.queueComposerWrite({ store, write });
25
+ return;
15
26
  }
27
+ composerHandoff.applyComposerWrite({ target: aui, store, write });
16
28
  },
17
- [aui]
29
+ [aui, store, threadId]
18
30
  );
19
31
  }
20
32
  exports.useAppendToComposer = useAppendToComposer;
@@ -1 +1 @@
1
- {"version":3,"file":"useAppendToComposer.cjs","sources":["../../src/composer/useAppendToComposer.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useAui } from '@assistant-ui/react';\n\n/**\n * Hook to programmatically append text to the composer without sending it.\n *\n * Use `useSendMessage()` for workflow shortcuts or other UI that should\n * immediately submit a prompt.\n *\n * Usage:\n * const append = useAppendToComposer();\n * append(\"some text to add\");\n * append(\"quoted text\", { replace: true }); // replaces existing text\n */\nexport function useAppendToComposer() {\n const aui = useAui();\n\n return useCallback(\n (text: string, opts?: { replace?: boolean }) => {\n if (opts?.replace) {\n aui.composer.setText(text);\n } else {\n const current = aui.composer.getState().text;\n aui.composer.setText(current ? `${current}\\n${text}` : text);\n }\n },\n [aui],\n );\n}\n"],"names":["useAui","useCallback"],"mappings":";;;;AAcO,SAAS,sBAAsB;AACpC,QAAM,MAAMA,MAAAA,OAAA;AAEZ,SAAOC,QAAAA;AAAAA,IACL,CAAC,MAAc,SAAiC;AAC9C,UAAI,6BAAM,SAAS;AACjB,YAAI,SAAS,QAAQ,IAAI;AAAA,MAC3B,OAAO;AACL,cAAM,UAAU,IAAI,SAAS,SAAA,EAAW;AACxC,YAAI,SAAS,QAAQ,UAAU,GAAG,OAAO;AAAA,EAAK,IAAI,KAAK,IAAI;AAAA,MAC7D;AAAA,IACF;AAAA,IACA,CAAC,GAAG;AAAA,EAAA;AAER;;"}
1
+ {"version":3,"file":"useAppendToComposer.cjs","sources":["../../src/composer/useAppendToComposer.ts"],"sourcesContent":["import { useCallback, useContext, useRef } from 'react';\nimport { useAui } from '@assistant-ui/react';\nimport { AthenaStatewireThreadIdContext } from '../threads/thread-id-context';\nimport {\n applyComposerWrite,\n queueComposerWrite,\n useComposerHandoffStore,\n useComposerReady,\n type ComposerWrite,\n} from './composer-handoff';\n\nexport interface AppendToComposerOptions {\n /** Replace the current draft instead of appending on a new line. */\n replace?: boolean;\n /**\n * Focus the composer with the caret after the text once the editor shows\n * it — for a prefilled draft the user is meant to finish by typing or\n * dictating. Defaults to false.\n */\n focus?: boolean;\n}\n\n/**\n * Hook to programmatically append text to the composer without sending it.\n *\n * Safe to call at any time: a write made before the chat is ready (the\n * thread list still resolving the active thread, or the thread's history\n * still loading) waits in the provider and lands once the composer is\n * ready, instead of being lost with the mount it was written into. A\n * never-started chat is ready without a live socket. The write is bound to\n * the thread it was made for: if the user switches threads first, it is\n * dropped (a `composer.handoff` diagnostic says so), never written into\n * another thread.\n *\n * Use `useSendMessage()` for workflow shortcuts or other UI that should\n * immediately submit a prompt.\n *\n * Usage:\n * const append = useAppendToComposer();\n * append(\"some text to add\");\n * append(\"quoted text\", { replace: true }); // replaces existing text\n * append(\"Here is what happened: \", { replace: true, focus: true }); // caret after the text\n */\nexport function useAppendToComposer() {\n const aui = useAui();\n const store = useComposerHandoffStore();\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const ready = useComposerReady();\n // The callback stays identity-stable across readiness changes.\n const readyRef = useRef(ready);\n readyRef.current = ready;\n\n return useCallback(\n (text: string, opts?: AppendToComposerOptions) => {\n const write: ComposerWrite = {\n text,\n replace: opts?.replace ?? false,\n send: false,\n focus: opts?.focus ?? false,\n threadId,\n };\n if (store && !readyRef.current) {\n queueComposerWrite({ store, write });\n return;\n }\n applyComposerWrite({ target: aui, store, write });\n },\n [aui, store, threadId],\n );\n}\n"],"names":["useAui","useComposerHandoffStore","useContext","AthenaStatewireThreadIdContext","useComposerReady","useRef","useCallback","queueComposerWrite","applyComposerWrite"],"mappings":";;;;;;AA2CO,SAAS,sBAAsB;AACpC,QAAM,MAAMA,MAAAA,OAAA;AACZ,QAAM,QAAQC,gBAAAA,wBAAA;AACd,QAAM,WAAWC,QAAAA,WAAWC,8CAA8B;AAC1D,QAAM,QAAQC,gBAAAA,iBAAA;AAEd,QAAM,WAAWC,QAAAA,OAAO,KAAK;AAC7B,WAAS,UAAU;AAEnB,SAAOC,QAAAA;AAAAA,IACL,CAAC,MAAc,SAAmC;AAChD,YAAM,QAAuB;AAAA,QAC3B;AAAA,QACA,UAAS,6BAAM,YAAW;AAAA,QAC1B,MAAM;AAAA,QACN,QAAO,6BAAM,UAAS;AAAA,QACtB;AAAA,MAAA;AAEF,UAAI,SAAS,CAAC,SAAS,SAAS;AAC9BC,2CAAmB,EAAE,OAAO,OAAO;AACnC;AAAA,MACF;AACAC,sBAAAA,mBAAmB,EAAE,QAAQ,KAAK,OAAO,OAAO;AAAA,IAClD;AAAA,IACA,CAAC,KAAK,OAAO,QAAQ;AAAA,EAAA;AAEzB;;"}
@@ -1,14 +1,32 @@
1
+ export interface AppendToComposerOptions {
2
+ /** Replace the current draft instead of appending on a new line. */
3
+ replace?: boolean;
4
+ /**
5
+ * Focus the composer with the caret after the text once the editor shows
6
+ * it — for a prefilled draft the user is meant to finish by typing or
7
+ * dictating. Defaults to false.
8
+ */
9
+ focus?: boolean;
10
+ }
1
11
  /**
2
12
  * Hook to programmatically append text to the composer without sending it.
3
13
  *
14
+ * Safe to call at any time: a write made before the chat is ready (the
15
+ * thread list still resolving the active thread, or the thread's history
16
+ * still loading) waits in the provider and lands once the composer is
17
+ * ready, instead of being lost with the mount it was written into. A
18
+ * never-started chat is ready without a live socket. The write is bound to
19
+ * the thread it was made for: if the user switches threads first, it is
20
+ * dropped (a `composer.handoff` diagnostic says so), never written into
21
+ * another thread.
22
+ *
4
23
  * Use `useSendMessage()` for workflow shortcuts or other UI that should
5
24
  * immediately submit a prompt.
6
25
  *
7
26
  * Usage:
8
27
  * const append = useAppendToComposer();
9
28
  * append("some text to add");
10
- * append("quoted text", { replace: true }); // replaces existing text
29
+ * append("quoted text", { replace: true }); // replaces existing text
30
+ * append("Here is what happened: ", { replace: true, focus: true }); // caret after the text
11
31
  */
12
- export declare function useAppendToComposer(): (text: string, opts?: {
13
- replace?: boolean;
14
- }) => void;
32
+ export declare function useAppendToComposer(): (text: string, opts?: AppendToComposerOptions) => void;
@@ -1,18 +1,30 @@
1
- import { useCallback } from "react";
1
+ import { useContext, useRef, useCallback } from "react";
2
2
  import { useAui } from "@assistant-ui/react";
3
+ import { AthenaStatewireThreadIdContext } from "../threads/thread-id-context.js";
4
+ import { useComposerHandoffStore, useComposerReady, queueComposerWrite, applyComposerWrite } from "./composer-handoff.js";
3
5
  function useAppendToComposer() {
4
6
  const aui = useAui();
7
+ const store = useComposerHandoffStore();
8
+ const threadId = useContext(AthenaStatewireThreadIdContext);
9
+ const ready = useComposerReady();
10
+ const readyRef = useRef(ready);
11
+ readyRef.current = ready;
5
12
  return useCallback(
6
13
  (text, opts) => {
7
- if (opts == null ? void 0 : opts.replace) {
8
- aui.composer.setText(text);
9
- } else {
10
- const current = aui.composer.getState().text;
11
- aui.composer.setText(current ? `${current}
12
- ${text}` : text);
14
+ const write = {
15
+ text,
16
+ replace: (opts == null ? void 0 : opts.replace) ?? false,
17
+ send: false,
18
+ focus: (opts == null ? void 0 : opts.focus) ?? false,
19
+ threadId
20
+ };
21
+ if (store && !readyRef.current) {
22
+ queueComposerWrite({ store, write });
23
+ return;
13
24
  }
25
+ applyComposerWrite({ target: aui, store, write });
14
26
  },
15
- [aui]
27
+ [aui, store, threadId]
16
28
  );
17
29
  }
18
30
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"useAppendToComposer.js","sources":["../../src/composer/useAppendToComposer.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useAui } from '@assistant-ui/react';\n\n/**\n * Hook to programmatically append text to the composer without sending it.\n *\n * Use `useSendMessage()` for workflow shortcuts or other UI that should\n * immediately submit a prompt.\n *\n * Usage:\n * const append = useAppendToComposer();\n * append(\"some text to add\");\n * append(\"quoted text\", { replace: true }); // replaces existing text\n */\nexport function useAppendToComposer() {\n const aui = useAui();\n\n return useCallback(\n (text: string, opts?: { replace?: boolean }) => {\n if (opts?.replace) {\n aui.composer.setText(text);\n } else {\n const current = aui.composer.getState().text;\n aui.composer.setText(current ? `${current}\\n${text}` : text);\n }\n },\n [aui],\n );\n}\n"],"names":[],"mappings":";;AAcO,SAAS,sBAAsB;AACpC,QAAM,MAAM,OAAA;AAEZ,SAAO;AAAA,IACL,CAAC,MAAc,SAAiC;AAC9C,UAAI,6BAAM,SAAS;AACjB,YAAI,SAAS,QAAQ,IAAI;AAAA,MAC3B,OAAO;AACL,cAAM,UAAU,IAAI,SAAS,SAAA,EAAW;AACxC,YAAI,SAAS,QAAQ,UAAU,GAAG,OAAO;AAAA,EAAK,IAAI,KAAK,IAAI;AAAA,MAC7D;AAAA,IACF;AAAA,IACA,CAAC,GAAG;AAAA,EAAA;AAER;"}
1
+ {"version":3,"file":"useAppendToComposer.js","sources":["../../src/composer/useAppendToComposer.ts"],"sourcesContent":["import { useCallback, useContext, useRef } from 'react';\nimport { useAui } from '@assistant-ui/react';\nimport { AthenaStatewireThreadIdContext } from '../threads/thread-id-context';\nimport {\n applyComposerWrite,\n queueComposerWrite,\n useComposerHandoffStore,\n useComposerReady,\n type ComposerWrite,\n} from './composer-handoff';\n\nexport interface AppendToComposerOptions {\n /** Replace the current draft instead of appending on a new line. */\n replace?: boolean;\n /**\n * Focus the composer with the caret after the text once the editor shows\n * it — for a prefilled draft the user is meant to finish by typing or\n * dictating. Defaults to false.\n */\n focus?: boolean;\n}\n\n/**\n * Hook to programmatically append text to the composer without sending it.\n *\n * Safe to call at any time: a write made before the chat is ready (the\n * thread list still resolving the active thread, or the thread's history\n * still loading) waits in the provider and lands once the composer is\n * ready, instead of being lost with the mount it was written into. A\n * never-started chat is ready without a live socket. The write is bound to\n * the thread it was made for: if the user switches threads first, it is\n * dropped (a `composer.handoff` diagnostic says so), never written into\n * another thread.\n *\n * Use `useSendMessage()` for workflow shortcuts or other UI that should\n * immediately submit a prompt.\n *\n * Usage:\n * const append = useAppendToComposer();\n * append(\"some text to add\");\n * append(\"quoted text\", { replace: true }); // replaces existing text\n * append(\"Here is what happened: \", { replace: true, focus: true }); // caret after the text\n */\nexport function useAppendToComposer() {\n const aui = useAui();\n const store = useComposerHandoffStore();\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const ready = useComposerReady();\n // The callback stays identity-stable across readiness changes.\n const readyRef = useRef(ready);\n readyRef.current = ready;\n\n return useCallback(\n (text: string, opts?: AppendToComposerOptions) => {\n const write: ComposerWrite = {\n text,\n replace: opts?.replace ?? false,\n send: false,\n focus: opts?.focus ?? false,\n threadId,\n };\n if (store && !readyRef.current) {\n queueComposerWrite({ store, write });\n return;\n }\n applyComposerWrite({ target: aui, store, write });\n },\n [aui, store, threadId],\n );\n}\n"],"names":[],"mappings":";;;;AA2CO,SAAS,sBAAsB;AACpC,QAAM,MAAM,OAAA;AACZ,QAAM,QAAQ,wBAAA;AACd,QAAM,WAAW,WAAW,8BAA8B;AAC1D,QAAM,QAAQ,iBAAA;AAEd,QAAM,WAAW,OAAO,KAAK;AAC7B,WAAS,UAAU;AAEnB,SAAO;AAAA,IACL,CAAC,MAAc,SAAmC;AAChD,YAAM,QAAuB;AAAA,QAC3B;AAAA,QACA,UAAS,6BAAM,YAAW;AAAA,QAC1B,MAAM;AAAA,QACN,QAAO,6BAAM,UAAS;AAAA,QACtB;AAAA,MAAA;AAEF,UAAI,SAAS,CAAC,SAAS,SAAS;AAC9B,2BAAmB,EAAE,OAAO,OAAO;AACnC;AAAA,MACF;AACA,yBAAmB,EAAE,QAAQ,KAAK,OAAO,OAAO;AAAA,IAClD;AAAA,IACA,CAAC,KAAK,OAAO,QAAQ;AAAA,EAAA;AAEzB;"}