@athenaintel/react 0.23.3 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/README.md +17 -4
  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 +15 -23
  11. package/dist/chat/AthenaChat.cjs.map +1 -1
  12. package/dist/chat/AthenaChat.d.ts +9 -4
  13. package/dist/chat/AthenaChat.js +15 -23
  14. package/dist/chat/AthenaChat.js.map +1 -1
  15. package/dist/chat/StatewireApprovalCard.cjs +34 -24
  16. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  17. package/dist/chat/StatewireApprovalCard.d.ts +34 -4
  18. package/dist/chat/StatewireApprovalCard.js +34 -24
  19. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  20. package/dist/chat/StatewireClientToolBridge.cjs +2 -16
  21. package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
  22. package/dist/chat/StatewireClientToolBridge.js +2 -16
  23. package/dist/chat/StatewireClientToolBridge.js.map +1 -1
  24. package/dist/chat/StatewireQueuedMessages.cjs +5 -16
  25. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  26. package/dist/chat/StatewireQueuedMessages.js +5 -16
  27. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  28. package/dist/chat/WaitingOnYouBanner.cjs +3 -18
  29. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
  30. package/dist/chat/WaitingOnYouBanner.js +3 -18
  31. package/dist/chat/WaitingOnYouBanner.js.map +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 +1 -17
  37. package/dist/chat/statewire-client-tools.cjs.map +1 -1
  38. package/dist/chat/statewire-client-tools.js +1 -17
  39. package/dist/chat/statewire-client-tools.js.map +1 -1
  40. package/dist/chat/super-grouping/activity-timeline.cjs +0 -16
  41. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  42. package/dist/chat/super-grouping/activity-timeline.js +0 -16
  43. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  44. package/dist/chat/tiptap-text.cjs +1 -17
  45. package/dist/chat/tiptap-text.cjs.map +1 -1
  46. package/dist/chat/tiptap-text.js +1 -17
  47. package/dist/chat/tiptap-text.js.map +1 -1
  48. package/dist/chat/use-athena-run-live.cjs +0 -15
  49. package/dist/chat/use-athena-run-live.cjs.map +1 -1
  50. package/dist/chat/use-athena-run-live.js +0 -15
  51. package/dist/chat/use-athena-run-live.js.map +1 -1
  52. package/dist/chat/use-statewire-approval-request.cjs +4 -16
  53. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  54. package/dist/chat/use-statewire-approval-request.d.ts +2 -0
  55. package/dist/chat/use-statewire-approval-request.js +4 -16
  56. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  57. package/dist/chat-ui/composer/tiptap-composer.cjs +1 -16
  58. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  59. package/dist/chat-ui/composer/tiptap-composer.js +1 -16
  60. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  61. package/dist/chat-ui/index.cjs +0 -16
  62. package/dist/chat-ui/index.cjs.map +1 -1
  63. package/dist/chat-ui/index.d.ts +1 -1
  64. package/dist/chat-ui/index.js +0 -16
  65. package/dist/chat-ui/index.js.map +1 -1
  66. package/dist/chat-ui/mentions/mention-chip.cjs +1 -16
  67. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  68. package/dist/chat-ui/mentions/mention-chip.js +1 -16
  69. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  70. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +4 -6
  71. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs.map +1 -1
  72. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js +4 -6
  73. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
  74. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +2 -9
  75. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
  76. package/dist/chat-ui/mentions/suggestions/data/sources/index.js +2 -9
  77. package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
  78. package/dist/chat-ui/messages/message-components.cjs +5 -19
  79. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  80. package/dist/chat-ui/messages/message-components.js +5 -19
  81. package/dist/chat-ui/messages/message-components.js.map +1 -1
  82. package/dist/chat-ui/messages/runtime-message.cjs +1 -16
  83. package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
  84. package/dist/chat-ui/messages/runtime-message.js +1 -16
  85. package/dist/chat-ui/messages/runtime-message.js.map +1 -1
  86. package/dist/chat-ui/messages/statewire-continue-run.cjs +3 -15
  87. package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
  88. package/dist/chat-ui/messages/statewire-continue-run.js +3 -15
  89. package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
  90. package/dist/chat-ui/messages/user-edit-composer.cjs +0 -16
  91. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  92. package/dist/chat-ui/messages/user-edit-composer.js +0 -16
  93. package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
  94. package/dist/chat-ui/messages/wake-marker.cjs +1 -16
  95. package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
  96. package/dist/chat-ui/messages/wake-marker.js +1 -16
  97. package/dist/chat-ui/messages/wake-marker.js.map +1 -1
  98. package/dist/chat-ui/statewire.cjs +0 -16
  99. package/dist/chat-ui/statewire.cjs.map +1 -1
  100. package/dist/chat-ui/statewire.d.ts +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/dictation/DictationStrip.cjs +2 -1
  108. package/dist/dictation/DictationStrip.cjs.map +1 -1
  109. package/dist/dictation/DictationStrip.d.ts +2 -0
  110. package/dist/dictation/DictationStrip.js +2 -1
  111. package/dist/dictation/DictationStrip.js.map +1 -1
  112. package/dist/dictation/dictation-engine.cjs.map +1 -1
  113. package/dist/dictation/dictation-engine.d.ts +8 -0
  114. package/dist/dictation/dictation-engine.js.map +1 -1
  115. package/dist/dictation/recorded-dictation-engine.cjs +153 -0
  116. package/dist/dictation/recorded-dictation-engine.cjs.map +1 -0
  117. package/dist/dictation/recorded-dictation-engine.d.ts +66 -0
  118. package/dist/dictation/recorded-dictation-engine.js +153 -0
  119. package/dist/dictation/recorded-dictation-engine.js.map +1 -0
  120. package/dist/dictation/use-athena-dictation-engine.cjs +64 -0
  121. package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -0
  122. package/dist/dictation/use-athena-dictation-engine.d.ts +25 -0
  123. package/dist/dictation/use-athena-dictation-engine.js +64 -0
  124. package/dist/dictation/use-athena-dictation-engine.js.map +1 -0
  125. package/dist/dictation/use-dictation.cjs +17 -6
  126. package/dist/dictation/use-dictation.cjs.map +1 -1
  127. package/dist/dictation/use-dictation.d.ts +20 -4
  128. package/dist/dictation/use-dictation.js +17 -6
  129. package/dist/dictation/use-dictation.js.map +1 -1
  130. package/dist/index.cjs +29 -39
  131. package/dist/index.cjs.map +1 -1
  132. package/dist/index.d.ts +6 -3
  133. package/dist/index.js +12 -22
  134. package/dist/index.js.map +1 -1
  135. package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
  136. package/dist/packages/agent-runtime/src/conversation-list.js.map +1 -1
  137. package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
  138. package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
  139. package/dist/packages/agent-runtime/src/errors.cjs +18 -0
  140. package/dist/packages/agent-runtime/src/errors.cjs.map +1 -1
  141. package/dist/packages/agent-runtime/src/errors.js +19 -1
  142. package/dist/packages/agent-runtime/src/errors.js.map +1 -1
  143. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +2 -0
  144. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  145. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +2 -0
  146. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  147. package/dist/packages/agent-runtime/src/interrupts.cjs +83 -14
  148. package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
  149. package/dist/packages/agent-runtime/src/interrupts.js +83 -14
  150. package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
  151. package/dist/packages/agent-runtime/src/messages.cjs +2 -1
  152. package/dist/packages/agent-runtime/src/messages.cjs.map +1 -1
  153. package/dist/packages/agent-runtime/src/messages.js +2 -1
  154. package/dist/packages/agent-runtime/src/messages.js.map +1 -1
  155. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +49 -0
  156. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  157. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +49 -0
  158. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  159. package/dist/packages/agent-runtime/src/thread-status.js +1 -1
  160. package/dist/packages/agent-runtime/src/tool-metadata.cjs +1 -37
  161. package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
  162. package/dist/packages/agent-runtime/src/tool-metadata.js +1 -37
  163. package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
  164. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +0 -1
  165. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
  166. package/dist/packages/agent-runtime/src/use-deep-agent-config.js +0 -1
  167. package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -1
  168. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  169. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  170. package/dist/packages/agent-runtime/src/voice-presentation.cjs +6 -0
  171. package/dist/packages/agent-runtime/src/voice-presentation.cjs.map +1 -1
  172. package/dist/packages/agent-runtime/src/voice-presentation.js +6 -0
  173. package/dist/packages/agent-runtime/src/voice-presentation.js.map +1 -1
  174. package/dist/packages/agent-runtime/src/voice-turn-grouping.cjs +68 -0
  175. package/dist/packages/agent-runtime/src/voice-turn-grouping.cjs.map +1 -0
  176. package/dist/packages/agent-runtime/src/voice-turn-grouping.js +68 -0
  177. package/dist/packages/agent-runtime/src/voice-turn-grouping.js.map +1 -0
  178. package/dist/packages/react/package.json.cjs +1 -1
  179. package/dist/packages/react/package.json.js +1 -1
  180. package/dist/provider/AthenaProvider.cjs +5 -18
  181. package/dist/provider/AthenaProvider.cjs.map +1 -1
  182. package/dist/provider/AthenaProvider.js +5 -18
  183. package/dist/provider/AthenaProvider.js.map +1 -1
  184. package/dist/provider/theme-style-context.cjs +12 -0
  185. package/dist/provider/theme-style-context.cjs.map +1 -0
  186. package/dist/provider/theme-style-context.d.ts +8 -0
  187. package/dist/provider/theme-style-context.js +12 -0
  188. package/dist/provider/theme-style-context.js.map +1 -0
  189. package/dist/runtime/statewire-lifecycle.cjs +0 -16
  190. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  191. package/dist/runtime/statewire-lifecycle.js +0 -16
  192. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  193. package/dist/runtime/useAthenaStatewireRuntime.cjs +3 -17
  194. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  195. package/dist/runtime/useAthenaStatewireRuntime.js +3 -17
  196. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  197. package/dist/styles.css +1 -1
  198. package/dist/threads/SessionList.cjs +87 -16
  199. package/dist/threads/SessionList.cjs.map +1 -1
  200. package/dist/threads/SessionList.js +88 -17
  201. package/dist/threads/SessionList.js.map +1 -1
  202. package/dist/threads/session-list-model.cjs +13 -5
  203. package/dist/threads/session-list-model.cjs.map +1 -1
  204. package/dist/threads/session-list-model.d.ts +10 -3
  205. package/dist/threads/session-list-model.js +13 -5
  206. package/dist/threads/session-list-model.js.map +1 -1
  207. package/dist/threads/statewire-thread-list.cjs +4 -17
  208. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  209. package/dist/threads/statewire-thread-list.js +4 -17
  210. package/dist/threads/statewire-thread-list.js.map +1 -1
  211. package/dist/threads/use-rest-conversation-history.cjs +2 -16
  212. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  213. package/dist/threads/use-rest-conversation-history.js +2 -16
  214. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  215. package/dist/threads/use-statewire-conversation-history.cjs +2 -16
  216. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  217. package/dist/threads/use-statewire-conversation-history.js +2 -16
  218. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  219. package/dist/toolkits/index.cjs +0 -7
  220. package/dist/toolkits/index.cjs.map +1 -1
  221. package/dist/toolkits/index.d.ts +0 -2
  222. package/dist/toolkits/index.js +0 -7
  223. package/dist/toolkits/index.js.map +1 -1
  224. package/dist/tools/index.cjs +4 -20
  225. package/dist/tools/index.cjs.map +1 -1
  226. package/dist/tools/index.js +1 -17
  227. package/dist/tools/index.js.map +1 -1
  228. package/dist/tools/misc-tool-uis.cjs +2 -17
  229. package/dist/tools/misc-tool-uis.cjs.map +1 -1
  230. package/dist/tools/misc-tool-uis.js +2 -17
  231. package/dist/tools/misc-tool-uis.js.map +1 -1
  232. package/dist/tools/primitives.cjs +1 -16
  233. package/dist/tools/primitives.cjs.map +1 -1
  234. package/dist/tools/primitives.js +1 -16
  235. package/dist/tools/primitives.js.map +1 -1
  236. package/dist/tools/tool-fallback.cjs +2 -17
  237. package/dist/tools/tool-fallback.cjs.map +1 -1
  238. package/dist/tools/tool-fallback.js +2 -17
  239. package/dist/tools/tool-fallback.js.map +1 -1
  240. package/dist/tools/voice-update-tool-ui.cjs +1 -17
  241. package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
  242. package/dist/tools/voice-update-tool-ui.js +1 -17
  243. package/dist/tools/voice-update-tool-ui.js.map +1 -1
  244. package/dist/ui/popover.cjs +46 -0
  245. package/dist/ui/popover.cjs.map +1 -0
  246. package/dist/ui/popover.d.ts +6 -0
  247. package/dist/ui/popover.js +46 -0
  248. package/dist/ui/popover.js.map +1 -0
  249. package/package.json +2 -2
  250. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +0 -32
  251. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +0 -1
  252. package/dist/node_modules/qrcode-generator/dist/qrcode.js +0 -31
  253. package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +0 -1
  254. package/dist/node_modules/zod/v4/classic/errors.cjs +0 -44
  255. package/dist/node_modules/zod/v4/classic/errors.cjs.map +0 -1
  256. package/dist/node_modules/zod/v4/classic/errors.js +0 -44
  257. package/dist/node_modules/zod/v4/classic/errors.js.map +0 -1
  258. package/dist/node_modules/zod/v4/classic/iso.cjs +0 -43
  259. package/dist/node_modules/zod/v4/classic/iso.cjs.map +0 -1
  260. package/dist/node_modules/zod/v4/classic/iso.js +0 -43
  261. package/dist/node_modules/zod/v4/classic/iso.js.map +0 -1
  262. package/dist/node_modules/zod/v4/classic/parse.cjs +0 -29
  263. package/dist/node_modules/zod/v4/classic/parse.cjs.map +0 -1
  264. package/dist/node_modules/zod/v4/classic/parse.js +0 -29
  265. package/dist/node_modules/zod/v4/classic/parse.js.map +0 -1
  266. package/dist/node_modules/zod/v4/classic/schemas.cjs +0 -871
  267. package/dist/node_modules/zod/v4/classic/schemas.cjs.map +0 -1
  268. package/dist/node_modules/zod/v4/classic/schemas.js +0 -871
  269. package/dist/node_modules/zod/v4/classic/schemas.js.map +0 -1
  270. package/dist/node_modules/zod/v4/core/api.cjs +0 -570
  271. package/dist/node_modules/zod/v4/core/api.cjs.map +0 -1
  272. package/dist/node_modules/zod/v4/core/api.js +0 -570
  273. package/dist/node_modules/zod/v4/core/api.js.map +0 -1
  274. package/dist/node_modules/zod/v4/core/checks.cjs +0 -411
  275. package/dist/node_modules/zod/v4/core/checks.cjs.map +0 -1
  276. package/dist/node_modules/zod/v4/core/checks.js +0 -411
  277. package/dist/node_modules/zod/v4/core/checks.js.map +0 -1
  278. package/dist/node_modules/zod/v4/core/core.cjs +0 -77
  279. package/dist/node_modules/zod/v4/core/core.cjs.map +0 -1
  280. package/dist/node_modules/zod/v4/core/core.js +0 -77
  281. package/dist/node_modules/zod/v4/core/core.js.map +0 -1
  282. package/dist/node_modules/zod/v4/core/doc.cjs +0 -38
  283. package/dist/node_modules/zod/v4/core/doc.cjs.map +0 -1
  284. package/dist/node_modules/zod/v4/core/doc.js +0 -38
  285. package/dist/node_modules/zod/v4/core/doc.js.map +0 -1
  286. package/dist/node_modules/zod/v4/core/errors.cjs +0 -76
  287. package/dist/node_modules/zod/v4/core/errors.cjs.map +0 -1
  288. package/dist/node_modules/zod/v4/core/errors.js +0 -76
  289. package/dist/node_modules/zod/v4/core/errors.js.map +0 -1
  290. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +0 -312
  291. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +0 -1
  292. package/dist/node_modules/zod/v4/core/json-schema-processors.js +0 -312
  293. package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +0 -1
  294. package/dist/node_modules/zod/v4/core/parse.cjs +0 -96
  295. package/dist/node_modules/zod/v4/core/parse.cjs.map +0 -1
  296. package/dist/node_modules/zod/v4/core/parse.js +0 -96
  297. package/dist/node_modules/zod/v4/core/parse.js.map +0 -1
  298. package/dist/node_modules/zod/v4/core/regexes.cjs +0 -86
  299. package/dist/node_modules/zod/v4/core/regexes.cjs.map +0 -1
  300. package/dist/node_modules/zod/v4/core/regexes.js +0 -86
  301. package/dist/node_modules/zod/v4/core/regexes.js.map +0 -1
  302. package/dist/node_modules/zod/v4/core/registries.cjs +0 -52
  303. package/dist/node_modules/zod/v4/core/registries.cjs.map +0 -1
  304. package/dist/node_modules/zod/v4/core/registries.js +0 -52
  305. package/dist/node_modules/zod/v4/core/registries.js.map +0 -1
  306. package/dist/node_modules/zod/v4/core/schemas.cjs +0 -1412
  307. package/dist/node_modules/zod/v4/core/schemas.cjs.map +0 -1
  308. package/dist/node_modules/zod/v4/core/schemas.js +0 -1413
  309. package/dist/node_modules/zod/v4/core/schemas.js.map +0 -1
  310. package/dist/node_modules/zod/v4/core/to-json-schema.cjs +0 -370
  311. package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +0 -1
  312. package/dist/node_modules/zod/v4/core/to-json-schema.js +0 -370
  313. package/dist/node_modules/zod/v4/core/to-json-schema.js.map +0 -1
  314. package/dist/node_modules/zod/v4/core/util.cjs +0 -444
  315. package/dist/node_modules/zod/v4/core/util.cjs.map +0 -1
  316. package/dist/node_modules/zod/v4/core/util.js +0 -444
  317. package/dist/node_modules/zod/v4/core/util.js.map +0 -1
  318. package/dist/node_modules/zod/v4/core/versions.cjs +0 -9
  319. package/dist/node_modules/zod/v4/core/versions.cjs.map +0 -1
  320. package/dist/node_modules/zod/v4/core/versions.js +0 -9
  321. package/dist/node_modules/zod/v4/core/versions.js.map +0 -1
  322. package/dist/packages/agent-runtime/src/thread-handoff.cjs +0 -85
  323. package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +0 -1
  324. package/dist/packages/agent-runtime/src/thread-handoff.js +0 -85
  325. package/dist/packages/agent-runtime/src/thread-handoff.js.map +0 -1
@@ -4,7 +4,7 @@ const react = require("react");
4
4
  const athenaDiagnostics = require("../diagnostics/athena-diagnostics.cjs");
5
5
  const dictationEngine = require("./dictation-engine.cjs");
6
6
  const dictationState = require("./dictation-state.cjs");
7
- const STOP_FLUSH_TIMEOUT_MS = 1500;
7
+ const DEFAULT_STOP_FLUSH_TIMEOUT_MS = 1500;
8
8
  const EMPTY_TRANSCRIPT = { final: "", interim: "" };
9
9
  function documentLanguage() {
10
10
  if (typeof document !== "undefined" && document.documentElement.lang) {
@@ -36,6 +36,7 @@ function useDictation(options = {}) {
36
36
  const onAutoEndRef = react.useRef(onAutoEnd);
37
37
  onAutoEndRef.current = onAutoEnd;
38
38
  const stopWaitersRef = react.useRef([]);
39
+ const sessionErrorRef = react.useRef(null);
39
40
  const generationRef = react.useRef(0);
40
41
  react.useEffect(() => {
41
42
  setIsSupported(engine.isSupported());
@@ -43,7 +44,8 @@ function useDictation(options = {}) {
43
44
  const settleWaiters = react.useCallback((spoken) => {
44
45
  const waiters = stopWaitersRef.current;
45
46
  stopWaitersRef.current = [];
46
- for (const resolve of waiters) resolve(spoken);
47
+ const result = { text: spoken, error: sessionErrorRef.current };
48
+ for (const resolve of waiters) resolve(result);
47
49
  }, []);
48
50
  const releaseSession = react.useCallback(() => {
49
51
  sessionRef.current = null;
@@ -65,6 +67,7 @@ function useDictation(options = {}) {
65
67
  level: "warn",
66
68
  data: { phase: "error", engine: engine.id, kind: engineError.kind }
67
69
  });
70
+ sessionErrorRef.current = engineError;
68
71
  dispatch({ type: "error", error: engineError });
69
72
  },
70
73
  [engine.id]
@@ -74,6 +77,7 @@ function useDictation(options = {}) {
74
77
  const generation = generationRef.current + 1;
75
78
  generationRef.current = generation;
76
79
  transcriptRef.current = EMPTY_TRANSCRIPT;
80
+ sessionErrorRef.current = null;
77
81
  dispatch({ type: "start" });
78
82
  athenaDiagnostics.athenaDiagnostics.emit("composer.dictation", {
79
83
  level: "debug",
@@ -162,25 +166,31 @@ function useDictation(options = {}) {
162
166
  if (stateRef.current.status === dictationState.DICTATION_STATUSES.starting) {
163
167
  teardown();
164
168
  dispatch({ type: "cancel" });
165
- return Promise.resolve("");
169
+ return Promise.resolve({ text: "", error: null });
166
170
  }
167
- return Promise.resolve(dictationState.spokenText(transcriptRef.current));
171
+ return Promise.resolve({ text: dictationState.spokenText(transcriptRef.current), error: sessionErrorRef.current });
168
172
  }
169
173
  dispatch({ type: "stop" });
170
174
  const flushed = new Promise((resolve) => {
171
175
  stopWaitersRef.current.push(resolve);
172
176
  window.setTimeout(() => {
173
177
  if (sessionRef.current === session) {
178
+ if (!dictationState.spokenText(transcriptRef.current)) {
179
+ reportEngineError({
180
+ kind: dictationEngine.DICTATION_ENGINE_ERROR_KINDS.connection,
181
+ message: "Transcription timed out. Try again."
182
+ });
183
+ }
174
184
  session.abort();
175
185
  dispatch({ type: "ended" });
176
186
  releaseSession();
177
187
  settleWaiters(dictationState.spokenText(transcriptRef.current));
178
188
  }
179
- }, STOP_FLUSH_TIMEOUT_MS);
189
+ }, engine.flushTimeoutMs ?? DEFAULT_STOP_FLUSH_TIMEOUT_MS);
180
190
  });
181
191
  session.stop();
182
192
  return flushed;
183
- }, [releaseSession, settleWaiters, teardown]);
193
+ }, [engine.flushTimeoutMs, releaseSession, reportEngineError, settleWaiters, teardown]);
184
194
  const cancel = react.useCallback(() => {
185
195
  teardown();
186
196
  dispatch({ type: "cancel" });
@@ -195,6 +205,7 @@ function useDictation(options = {}) {
195
205
  error: state.error,
196
206
  stream,
197
207
  isSupported,
208
+ engine,
198
209
  start,
199
210
  stop,
200
211
  cancel
@@ -1 +1 @@
1
- {"version":3,"file":"use-dictation.cjs","sources":["../../src/dictation/use-dictation.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n createWebSpeechDictationEngine,\n DICTATION_ENGINE_ERROR_KINDS,\n type DictationEngine,\n type DictationEngineError,\n type DictationEngineSession,\n} from './dictation-engine';\nimport {\n DICTATION_STATUSES,\n dictationReducer,\n INITIAL_DICTATION_STATE,\n joinSpeech,\n spokenText,\n type DictationStatus,\n type DictationTranscript,\n} from './dictation-state';\n\nexport interface UseDictationOptions {\n /** Speech engine. Defaults to the browser's Web Speech API. */\n engine?: DictationEngine;\n /** BCP-47 language tag. Defaults to the document language, else `en-US`. */\n lang?: string;\n /**\n * Open the microphone through `getUserMedia` so callers get a `stream` for\n * a waveform. Web Speech opens its own capture; this second capture is\n * what the level meter reads. Defaults to true.\n */\n captureStream?: boolean;\n /**\n * The engine ended on its own (silence window, recognizer timeout) while\n * listening, without `stop()` or `cancel()`. Receives the spoken text so\n * the words are not lost; the composer puts them into the draft.\n */\n onAutoEnd?: (spoken: string) => void;\n}\n\nexport interface DictationController {\n status: DictationStatus;\n /** True while starting, listening or stopping. */\n isActive: boolean;\n transcript: DictationTranscript;\n /** `final` plus the interim tail, ready to hand to the composer. */\n text: string;\n error: DictationEngineError | null;\n /** The microphone stream while active (for a waveform), else null. */\n stream: MediaStream | null;\n /** Whether the engine can run here. Hide the control when false. */\n isSupported: boolean;\n start: () => void;\n /**\n * Finish recording. Resolves with the spoken text once the engine has\n * flushed (bounded wait), so trailing words are not lost.\n */\n stop: () => Promise<string>;\n /** Discard the session and its transcript. */\n cancel: () => void;\n}\n\n/** How long `stop()` waits for the engine's final flush before giving up. */\nconst STOP_FLUSH_TIMEOUT_MS = 1500;\n\ntype StopResolver = (spoken: string) => void;\n\nconst EMPTY_TRANSCRIPT: DictationTranscript = { final: '', interim: '' };\n\nfunction documentLanguage(): string {\n if (typeof document !== 'undefined' && document.documentElement.lang) {\n return document.documentElement.lang;\n }\n if (typeof navigator !== 'undefined' && navigator.language) return navigator.language;\n return 'en-US';\n}\n\nasync function openMicrophone(): Promise<MediaStream | null> {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) return null;\n return navigator.mediaDevices.getUserMedia({ audio: true });\n}\n\nfunction releaseStream(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) track.stop();\n}\n\n/**\n * Drive a dictation session: microphone, speech engine, and the transcript\n * state machine. The composer renders the waveform from `stream` and the\n * live caption from `transcript`; on `stop()` it appends `text` to the draft.\n */\nexport function useDictation(options: UseDictationOptions = {}): DictationController {\n const engine = useMemo(() => options.engine ?? createWebSpeechDictationEngine(), [options.engine]);\n const { lang, captureStream = true, onAutoEnd } = options;\n const [state, dispatch] = useReducer(dictationReducer, INITIAL_DICTATION_STATE);\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n const sessionRef = useRef<DictationEngineSession | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const stateRef = useRef(state);\n stateRef.current = state;\n // The authoritative transcript, updated synchronously as the engine reports\n // it. The reducer's copy renders the caption; this one is what `stop()` and\n // `onAutoEnd` hand over, so an engine that flushes its last words and ends\n // in the same tick — before React has rendered — loses nothing.\n const transcriptRef = useRef<DictationTranscript>(EMPTY_TRANSCRIPT);\n const onAutoEndRef = useRef(onAutoEnd);\n onAutoEndRef.current = onAutoEnd;\n // stop() callers waiting for the engine's final flush.\n const stopWaitersRef = useRef<StopResolver[]>([]);\n // Distinguishes a fresh session from the callbacks of a torn-down one.\n const generationRef = useRef(0);\n\n useEffect(() => {\n setIsSupported(engine.isSupported());\n }, [engine]);\n\n const settleWaiters = useCallback((spoken: string) => {\n const waiters = stopWaitersRef.current;\n stopWaitersRef.current = [];\n for (const resolve of waiters) resolve(spoken);\n }, []);\n\n const releaseSession = useCallback(() => {\n sessionRef.current = null;\n releaseStream(streamRef.current);\n streamRef.current = null;\n setStream(null);\n }, []);\n\n const teardown = useCallback(() => {\n generationRef.current += 1;\n sessionRef.current?.abort();\n releaseSession();\n transcriptRef.current = EMPTY_TRANSCRIPT;\n settleWaiters('');\n }, [releaseSession, settleWaiters]);\n\n const reportEngineError = useCallback(\n (engineError: DictationEngineError) => {\n athenaDiagnostics.emit('composer.dictation', {\n level: 'warn',\n data: { phase: 'error', engine: engine.id, kind: engineError.kind },\n });\n dispatch({ type: 'error', error: engineError });\n },\n [engine.id],\n );\n\n const start = useCallback(() => {\n if (sessionRef.current) return;\n const generation = generationRef.current + 1;\n generationRef.current = generation;\n transcriptRef.current = EMPTY_TRANSCRIPT;\n dispatch({ type: 'start' });\n athenaDiagnostics.emit('composer.dictation', {\n level: 'debug',\n data: { phase: 'start', engine: engine.id },\n });\n\n const run = async () => {\n let microphone: MediaStream | null = null;\n if (captureStream) {\n try {\n microphone = await openMicrophone();\n } catch (error) {\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n dispatch({\n type: 'error',\n error: {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message:\n error instanceof DOMException && error.name === 'NotFoundError'\n ? 'No microphone was found.'\n : 'Microphone access was denied. Allow the microphone for this site and try again.',\n },\n });\n return;\n }\n }\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n streamRef.current = microphone;\n setStream(microphone);\n\n // An engine may fail synchronously inside start() (Web Speech throws\n // when recognition.start() is refused) and run onError/onEnd before we\n // hold the session. Those callbacks flip this flag so a dead session is\n // never stored, which would make every later mic click return early.\n let terminated = false;\n const endSession = () => {\n terminated = true;\n const current = stateRef.current;\n const spoken = spokenText(transcriptRef.current);\n // No stop() is waiting: the engine gave up on its own. Hand the\n // words over so they land in the draft instead of vanishing.\n const endedOnItsOwn =\n stopWaitersRef.current.length === 0 && current.status === DICTATION_STATUSES.listening;\n // The reducer keeps an error status through 'ended', so an error\n // reported just before the engine closed stays on screen.\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spoken);\n if (endedOnItsOwn) onAutoEndRef.current?.(spoken);\n };\n let session: DictationEngineSession;\n try {\n session = engine.start({\n stream: microphone,\n lang: lang ?? documentLanguage(),\n callbacks: {\n onTranscript: (update) => {\n if (generationRef.current !== generation) return;\n transcriptRef.current = {\n final: joinSpeech(transcriptRef.current.final, update.final),\n interim: update.interim,\n };\n dispatch({ type: 'transcript', update });\n },\n onError: (engineError) => {\n if (generationRef.current !== generation) return;\n // A silence timeout is the engine giving up politely; keep the\n // words we have instead of showing an error.\n if (engineError.kind === DICTATION_ENGINE_ERROR_KINDS.noSpeech) return;\n reportEngineError(engineError);\n },\n onEnd: () => {\n if (generationRef.current !== generation) return;\n endSession();\n },\n },\n });\n } catch (error) {\n // A relay engine that cannot build its session must not strand the\n // microphone or leave the composer in `starting`.\n releaseSession();\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n return;\n }\n if (terminated) return;\n sessionRef.current = session;\n dispatch({ type: 'listening' });\n };\n void run();\n }, [captureStream, engine, lang, releaseSession, reportEngineError, settleWaiters]);\n\n const stop = useCallback((): Promise<string> => {\n const session = sessionRef.current;\n if (!session) {\n // Stopped while the microphone prompt was still open: nothing was heard\n // yet, and the pending start must not bring the engine up afterwards.\n if (stateRef.current.status === DICTATION_STATUSES.starting) {\n teardown();\n dispatch({ type: 'cancel' });\n return Promise.resolve('');\n }\n return Promise.resolve(spokenText(transcriptRef.current));\n }\n dispatch({ type: 'stop' });\n const flushed = new Promise<string>((resolve) => {\n stopWaitersRef.current.push(resolve);\n window.setTimeout(() => {\n // Bounded wait: a recognizer that never fires onend must not hold the\n // composer hostage. Force the session closed and hand over what was\n // heard so far.\n if (sessionRef.current === session) {\n session.abort();\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spokenText(transcriptRef.current));\n }\n }, STOP_FLUSH_TIMEOUT_MS);\n });\n session.stop();\n return flushed;\n }, [releaseSession, settleWaiters, teardown]);\n\n const cancel = useCallback(() => {\n teardown();\n dispatch({ type: 'cancel' });\n }, [teardown]);\n\n // Release the microphone with the component.\n useEffect(() => () => teardown(), [teardown]);\n\n const isActive =\n state.status === DICTATION_STATUSES.starting ||\n state.status === DICTATION_STATUSES.listening ||\n state.status === DICTATION_STATUSES.stopping;\n\n return {\n status: state.status,\n isActive,\n transcript: state.transcript,\n text: spokenText(state.transcript),\n error: state.error,\n stream,\n isSupported,\n start,\n stop,\n cancel,\n };\n}\n"],"names":["useMemo","createWebSpeechDictationEngine","useReducer","dictationReducer","INITIAL_DICTATION_STATE","useState","useRef","useEffect","useCallback","athenaDiagnostics","DICTATION_ENGINE_ERROR_KINDS","spokenText","DICTATION_STATUSES","joinSpeech"],"mappings":"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"}
1
+ {"version":3,"file":"use-dictation.cjs","sources":["../../src/dictation/use-dictation.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n createWebSpeechDictationEngine,\n DICTATION_ENGINE_ERROR_KINDS,\n type DictationEngine,\n type DictationEngineError,\n type DictationEngineSession,\n} from './dictation-engine';\nimport {\n DICTATION_STATUSES,\n dictationReducer,\n INITIAL_DICTATION_STATE,\n joinSpeech,\n spokenText,\n type DictationStatus,\n type DictationTranscript,\n} from './dictation-state';\n\nexport interface UseDictationOptions {\n /**\n * Speech engine. Defaults to the browser's Web Speech API; `AthenaChat`\n * passes the record-then-transcribe engine from `useAthenaDictationEngine`.\n */\n engine?: DictationEngine;\n /** BCP-47 language tag. Defaults to the document language, else `en-US`. */\n lang?: string;\n /**\n * Open the microphone through `getUserMedia` so callers get a `stream` for\n * a waveform. Web Speech opens its own capture; this second capture is\n * what the level meter reads. Defaults to true.\n */\n captureStream?: boolean;\n /**\n * The engine ended on its own (silence window, recognizer timeout) while\n * listening, without `stop()` or `cancel()`. Receives the spoken text so\n * the words are not lost; the composer puts them into the draft.\n */\n onAutoEnd?: (spoken: string) => void;\n}\n\nexport interface DictationStopResult {\n /** Spoken text (final plus interim tail) gathered before the engine closed. */\n text: string;\n /**\n * The engine error reported during this session, if any. When set the\n * transcript may be incomplete: a recorded engine whose upload failed has no\n * words to hand over, so callers should not send the draft as if it were.\n */\n error: DictationEngineError | null;\n}\n\nexport interface DictationController {\n status: DictationStatus;\n /** True while starting, listening or stopping. */\n isActive: boolean;\n transcript: DictationTranscript;\n /** `final` plus the interim tail, ready to hand to the composer. */\n text: string;\n error: DictationEngineError | null;\n /** The microphone stream while active (for a waveform), else null. */\n stream: MediaStream | null;\n /** Whether the engine can run here. Hide the control when false. */\n isSupported: boolean;\n /** The engine in use (for its `stoppingLabel` and diagnostics). */\n engine: DictationEngine;\n start: () => void;\n /**\n * Finish recording. Resolves once the engine has flushed (bounded wait), so\n * trailing words are not lost, with the spoken text and any error raised\n * along the way.\n */\n stop: () => Promise<DictationStopResult>;\n /** Discard the session and its transcript. */\n cancel: () => void;\n}\n\n/** How long `stop()` waits for a live engine's final flush before giving up. */\nconst DEFAULT_STOP_FLUSH_TIMEOUT_MS = 1500;\n\ntype StopResolver = (result: DictationStopResult) => void;\n\nconst EMPTY_TRANSCRIPT: DictationTranscript = { final: '', interim: '' };\n\nfunction documentLanguage(): string {\n if (typeof document !== 'undefined' && document.documentElement.lang) {\n return document.documentElement.lang;\n }\n if (typeof navigator !== 'undefined' && navigator.language) return navigator.language;\n return 'en-US';\n}\n\nasync function openMicrophone(): Promise<MediaStream | null> {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) return null;\n return navigator.mediaDevices.getUserMedia({ audio: true });\n}\n\nfunction releaseStream(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) track.stop();\n}\n\n/**\n * Drive a dictation session: microphone, speech engine, and the transcript\n * state machine. The composer renders the waveform from `stream` and the\n * live caption from `transcript`; on `stop()` it appends `text` to the draft.\n */\nexport function useDictation(options: UseDictationOptions = {}): DictationController {\n const engine = useMemo(() => options.engine ?? createWebSpeechDictationEngine(), [options.engine]);\n const { lang, captureStream = true, onAutoEnd } = options;\n const [state, dispatch] = useReducer(dictationReducer, INITIAL_DICTATION_STATE);\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n const sessionRef = useRef<DictationEngineSession | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const stateRef = useRef(state);\n stateRef.current = state;\n // The authoritative transcript, updated synchronously as the engine reports\n // it. The reducer's copy renders the caption; this one is what `stop()` and\n // `onAutoEnd` hand over, so an engine that flushes its last words and ends\n // in the same tick — before React has rendered — loses nothing.\n const transcriptRef = useRef<DictationTranscript>(EMPTY_TRANSCRIPT);\n const onAutoEndRef = useRef(onAutoEnd);\n onAutoEndRef.current = onAutoEnd;\n // stop() callers waiting for the engine's final flush.\n const stopWaitersRef = useRef<StopResolver[]>([]);\n // Last engine error of the current session, handed to stop() callers\n // synchronously (the reducer's copy may not have rendered yet).\n const sessionErrorRef = useRef<DictationEngineError | null>(null);\n // Distinguishes a fresh session from the callbacks of a torn-down one.\n const generationRef = useRef(0);\n\n useEffect(() => {\n setIsSupported(engine.isSupported());\n }, [engine]);\n\n const settleWaiters = useCallback((spoken: string) => {\n const waiters = stopWaitersRef.current;\n stopWaitersRef.current = [];\n const result: DictationStopResult = { text: spoken, error: sessionErrorRef.current };\n for (const resolve of waiters) resolve(result);\n }, []);\n\n const releaseSession = useCallback(() => {\n sessionRef.current = null;\n releaseStream(streamRef.current);\n streamRef.current = null;\n setStream(null);\n }, []);\n\n const teardown = useCallback(() => {\n generationRef.current += 1;\n sessionRef.current?.abort();\n releaseSession();\n transcriptRef.current = EMPTY_TRANSCRIPT;\n settleWaiters('');\n }, [releaseSession, settleWaiters]);\n\n const reportEngineError = useCallback(\n (engineError: DictationEngineError) => {\n athenaDiagnostics.emit('composer.dictation', {\n level: 'warn',\n data: { phase: 'error', engine: engine.id, kind: engineError.kind },\n });\n sessionErrorRef.current = engineError;\n dispatch({ type: 'error', error: engineError });\n },\n [engine.id],\n );\n\n const start = useCallback(() => {\n if (sessionRef.current) return;\n const generation = generationRef.current + 1;\n generationRef.current = generation;\n transcriptRef.current = EMPTY_TRANSCRIPT;\n sessionErrorRef.current = null;\n dispatch({ type: 'start' });\n athenaDiagnostics.emit('composer.dictation', {\n level: 'debug',\n data: { phase: 'start', engine: engine.id },\n });\n\n const run = async () => {\n let microphone: MediaStream | null = null;\n if (captureStream) {\n try {\n microphone = await openMicrophone();\n } catch (error) {\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n dispatch({\n type: 'error',\n error: {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message:\n error instanceof DOMException && error.name === 'NotFoundError'\n ? 'No microphone was found.'\n : 'Microphone access was denied. Allow the microphone for this site and try again.',\n },\n });\n return;\n }\n }\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n streamRef.current = microphone;\n setStream(microphone);\n\n // An engine may fail synchronously inside start() (Web Speech throws\n // when recognition.start() is refused) and run onError/onEnd before we\n // hold the session. Those callbacks flip this flag so a dead session is\n // never stored, which would make every later mic click return early.\n let terminated = false;\n const endSession = () => {\n terminated = true;\n const current = stateRef.current;\n const spoken = spokenText(transcriptRef.current);\n // No stop() is waiting: the engine gave up on its own. Hand the\n // words over so they land in the draft instead of vanishing.\n const endedOnItsOwn =\n stopWaitersRef.current.length === 0 && current.status === DICTATION_STATUSES.listening;\n // The reducer keeps an error status through 'ended', so an error\n // reported just before the engine closed stays on screen.\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spoken);\n if (endedOnItsOwn) onAutoEndRef.current?.(spoken);\n };\n let session: DictationEngineSession;\n try {\n session = engine.start({\n stream: microphone,\n lang: lang ?? documentLanguage(),\n callbacks: {\n onTranscript: (update) => {\n if (generationRef.current !== generation) return;\n transcriptRef.current = {\n final: joinSpeech(transcriptRef.current.final, update.final),\n interim: update.interim,\n };\n dispatch({ type: 'transcript', update });\n },\n onError: (engineError) => {\n if (generationRef.current !== generation) return;\n // A silence timeout is the engine giving up politely; keep the\n // words we have instead of showing an error.\n if (engineError.kind === DICTATION_ENGINE_ERROR_KINDS.noSpeech) return;\n reportEngineError(engineError);\n },\n onEnd: () => {\n if (generationRef.current !== generation) return;\n endSession();\n },\n },\n });\n } catch (error) {\n // A relay engine that cannot build its session must not strand the\n // microphone or leave the composer in `starting`.\n releaseSession();\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n return;\n }\n if (terminated) return;\n sessionRef.current = session;\n dispatch({ type: 'listening' });\n };\n void run();\n }, [captureStream, engine, lang, releaseSession, reportEngineError, settleWaiters]);\n\n const stop = useCallback((): Promise<DictationStopResult> => {\n const session = sessionRef.current;\n if (!session) {\n // Stopped while the microphone prompt was still open: nothing was heard\n // yet, and the pending start must not bring the engine up afterwards.\n if (stateRef.current.status === DICTATION_STATUSES.starting) {\n teardown();\n dispatch({ type: 'cancel' });\n return Promise.resolve({ text: '', error: null });\n }\n return Promise.resolve({ text: spokenText(transcriptRef.current), error: sessionErrorRef.current });\n }\n dispatch({ type: 'stop' });\n const flushed = new Promise<DictationStopResult>((resolve) => {\n stopWaitersRef.current.push(resolve);\n window.setTimeout(() => {\n // Bounded wait: a recognizer that never fires onend must not hold the\n // composer hostage. Force the session closed and hand over what was\n // heard so far. An engine that had nothing to show yet (a recorded\n // clip still transcribing) surfaces that as an error rather than\n // passing off silence as the result.\n if (sessionRef.current === session) {\n // Report before abort(): an engine that ends synchronously on\n // abort settles the waiters, and they must see this error.\n if (!spokenText(transcriptRef.current)) {\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: 'Transcription timed out. Try again.',\n });\n }\n session.abort();\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spokenText(transcriptRef.current));\n }\n }, engine.flushTimeoutMs ?? DEFAULT_STOP_FLUSH_TIMEOUT_MS);\n });\n session.stop();\n return flushed;\n }, [engine.flushTimeoutMs, releaseSession, reportEngineError, settleWaiters, teardown]);\n\n const cancel = useCallback(() => {\n teardown();\n dispatch({ type: 'cancel' });\n }, [teardown]);\n\n // Release the microphone with the component.\n useEffect(() => () => teardown(), [teardown]);\n\n const isActive =\n state.status === DICTATION_STATUSES.starting ||\n state.status === DICTATION_STATUSES.listening ||\n state.status === DICTATION_STATUSES.stopping;\n\n return {\n status: state.status,\n isActive,\n transcript: state.transcript,\n text: spokenText(state.transcript),\n error: state.error,\n stream,\n isSupported,\n engine,\n start,\n stop,\n cancel,\n };\n}\n"],"names":["useMemo","createWebSpeechDictationEngine","useReducer","dictationReducer","INITIAL_DICTATION_STATE","useState","useRef","useEffect","useCallback","athenaDiagnostics","DICTATION_ENGINE_ERROR_KINDS","spokenText","DICTATION_STATUSES","joinSpeech"],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  import { type DictationEngine, type DictationEngineError } from './dictation-engine';
2
2
  import { type DictationStatus, type DictationTranscript } from './dictation-state';
3
3
  export interface UseDictationOptions {
4
- /** Speech engine. Defaults to the browser's Web Speech API. */
4
+ /**
5
+ * Speech engine. Defaults to the browser's Web Speech API; `AthenaChat`
6
+ * passes the record-then-transcribe engine from `useAthenaDictationEngine`.
7
+ */
5
8
  engine?: DictationEngine;
6
9
  /** BCP-47 language tag. Defaults to the document language, else `en-US`. */
7
10
  lang?: string;
@@ -18,6 +21,16 @@ export interface UseDictationOptions {
18
21
  */
19
22
  onAutoEnd?: (spoken: string) => void;
20
23
  }
24
+ export interface DictationStopResult {
25
+ /** Spoken text (final plus interim tail) gathered before the engine closed. */
26
+ text: string;
27
+ /**
28
+ * The engine error reported during this session, if any. When set the
29
+ * transcript may be incomplete: a recorded engine whose upload failed has no
30
+ * words to hand over, so callers should not send the draft as if it were.
31
+ */
32
+ error: DictationEngineError | null;
33
+ }
21
34
  export interface DictationController {
22
35
  status: DictationStatus;
23
36
  /** True while starting, listening or stopping. */
@@ -30,12 +43,15 @@ export interface DictationController {
30
43
  stream: MediaStream | null;
31
44
  /** Whether the engine can run here. Hide the control when false. */
32
45
  isSupported: boolean;
46
+ /** The engine in use (for its `stoppingLabel` and diagnostics). */
47
+ engine: DictationEngine;
33
48
  start: () => void;
34
49
  /**
35
- * Finish recording. Resolves with the spoken text once the engine has
36
- * flushed (bounded wait), so trailing words are not lost.
50
+ * Finish recording. Resolves once the engine has flushed (bounded wait), so
51
+ * trailing words are not lost, with the spoken text and any error raised
52
+ * along the way.
37
53
  */
38
- stop: () => Promise<string>;
54
+ stop: () => Promise<DictationStopResult>;
39
55
  /** Discard the session and its transcript. */
40
56
  cancel: () => void;
41
57
  }
@@ -2,7 +2,7 @@ import { useMemo, useReducer, useState, useRef, useEffect, useCallback } from "r
2
2
  import { athenaDiagnostics } from "../diagnostics/athena-diagnostics.js";
3
3
  import { createWebSpeechDictationEngine, DICTATION_ENGINE_ERROR_KINDS } from "./dictation-engine.js";
4
4
  import { dictationReducer, INITIAL_DICTATION_STATE, DICTATION_STATUSES, spokenText, joinSpeech } from "./dictation-state.js";
5
- const STOP_FLUSH_TIMEOUT_MS = 1500;
5
+ const DEFAULT_STOP_FLUSH_TIMEOUT_MS = 1500;
6
6
  const EMPTY_TRANSCRIPT = { final: "", interim: "" };
7
7
  function documentLanguage() {
8
8
  if (typeof document !== "undefined" && document.documentElement.lang) {
@@ -34,6 +34,7 @@ function useDictation(options = {}) {
34
34
  const onAutoEndRef = useRef(onAutoEnd);
35
35
  onAutoEndRef.current = onAutoEnd;
36
36
  const stopWaitersRef = useRef([]);
37
+ const sessionErrorRef = useRef(null);
37
38
  const generationRef = useRef(0);
38
39
  useEffect(() => {
39
40
  setIsSupported(engine.isSupported());
@@ -41,7 +42,8 @@ function useDictation(options = {}) {
41
42
  const settleWaiters = useCallback((spoken) => {
42
43
  const waiters = stopWaitersRef.current;
43
44
  stopWaitersRef.current = [];
44
- for (const resolve of waiters) resolve(spoken);
45
+ const result = { text: spoken, error: sessionErrorRef.current };
46
+ for (const resolve of waiters) resolve(result);
45
47
  }, []);
46
48
  const releaseSession = useCallback(() => {
47
49
  sessionRef.current = null;
@@ -63,6 +65,7 @@ function useDictation(options = {}) {
63
65
  level: "warn",
64
66
  data: { phase: "error", engine: engine.id, kind: engineError.kind }
65
67
  });
68
+ sessionErrorRef.current = engineError;
66
69
  dispatch({ type: "error", error: engineError });
67
70
  },
68
71
  [engine.id]
@@ -72,6 +75,7 @@ function useDictation(options = {}) {
72
75
  const generation = generationRef.current + 1;
73
76
  generationRef.current = generation;
74
77
  transcriptRef.current = EMPTY_TRANSCRIPT;
78
+ sessionErrorRef.current = null;
75
79
  dispatch({ type: "start" });
76
80
  athenaDiagnostics.emit("composer.dictation", {
77
81
  level: "debug",
@@ -160,25 +164,31 @@ function useDictation(options = {}) {
160
164
  if (stateRef.current.status === DICTATION_STATUSES.starting) {
161
165
  teardown();
162
166
  dispatch({ type: "cancel" });
163
- return Promise.resolve("");
167
+ return Promise.resolve({ text: "", error: null });
164
168
  }
165
- return Promise.resolve(spokenText(transcriptRef.current));
169
+ return Promise.resolve({ text: spokenText(transcriptRef.current), error: sessionErrorRef.current });
166
170
  }
167
171
  dispatch({ type: "stop" });
168
172
  const flushed = new Promise((resolve) => {
169
173
  stopWaitersRef.current.push(resolve);
170
174
  window.setTimeout(() => {
171
175
  if (sessionRef.current === session) {
176
+ if (!spokenText(transcriptRef.current)) {
177
+ reportEngineError({
178
+ kind: DICTATION_ENGINE_ERROR_KINDS.connection,
179
+ message: "Transcription timed out. Try again."
180
+ });
181
+ }
172
182
  session.abort();
173
183
  dispatch({ type: "ended" });
174
184
  releaseSession();
175
185
  settleWaiters(spokenText(transcriptRef.current));
176
186
  }
177
- }, STOP_FLUSH_TIMEOUT_MS);
187
+ }, engine.flushTimeoutMs ?? DEFAULT_STOP_FLUSH_TIMEOUT_MS);
178
188
  });
179
189
  session.stop();
180
190
  return flushed;
181
- }, [releaseSession, settleWaiters, teardown]);
191
+ }, [engine.flushTimeoutMs, releaseSession, reportEngineError, settleWaiters, teardown]);
182
192
  const cancel = useCallback(() => {
183
193
  teardown();
184
194
  dispatch({ type: "cancel" });
@@ -193,6 +203,7 @@ function useDictation(options = {}) {
193
203
  error: state.error,
194
204
  stream,
195
205
  isSupported,
206
+ engine,
196
207
  start,
197
208
  stop,
198
209
  cancel
@@ -1 +1 @@
1
- {"version":3,"file":"use-dictation.js","sources":["../../src/dictation/use-dictation.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n createWebSpeechDictationEngine,\n DICTATION_ENGINE_ERROR_KINDS,\n type DictationEngine,\n type DictationEngineError,\n type DictationEngineSession,\n} from './dictation-engine';\nimport {\n DICTATION_STATUSES,\n dictationReducer,\n INITIAL_DICTATION_STATE,\n joinSpeech,\n spokenText,\n type DictationStatus,\n type DictationTranscript,\n} from './dictation-state';\n\nexport interface UseDictationOptions {\n /** Speech engine. Defaults to the browser's Web Speech API. */\n engine?: DictationEngine;\n /** BCP-47 language tag. Defaults to the document language, else `en-US`. */\n lang?: string;\n /**\n * Open the microphone through `getUserMedia` so callers get a `stream` for\n * a waveform. Web Speech opens its own capture; this second capture is\n * what the level meter reads. Defaults to true.\n */\n captureStream?: boolean;\n /**\n * The engine ended on its own (silence window, recognizer timeout) while\n * listening, without `stop()` or `cancel()`. Receives the spoken text so\n * the words are not lost; the composer puts them into the draft.\n */\n onAutoEnd?: (spoken: string) => void;\n}\n\nexport interface DictationController {\n status: DictationStatus;\n /** True while starting, listening or stopping. */\n isActive: boolean;\n transcript: DictationTranscript;\n /** `final` plus the interim tail, ready to hand to the composer. */\n text: string;\n error: DictationEngineError | null;\n /** The microphone stream while active (for a waveform), else null. */\n stream: MediaStream | null;\n /** Whether the engine can run here. Hide the control when false. */\n isSupported: boolean;\n start: () => void;\n /**\n * Finish recording. Resolves with the spoken text once the engine has\n * flushed (bounded wait), so trailing words are not lost.\n */\n stop: () => Promise<string>;\n /** Discard the session and its transcript. */\n cancel: () => void;\n}\n\n/** How long `stop()` waits for the engine's final flush before giving up. */\nconst STOP_FLUSH_TIMEOUT_MS = 1500;\n\ntype StopResolver = (spoken: string) => void;\n\nconst EMPTY_TRANSCRIPT: DictationTranscript = { final: '', interim: '' };\n\nfunction documentLanguage(): string {\n if (typeof document !== 'undefined' && document.documentElement.lang) {\n return document.documentElement.lang;\n }\n if (typeof navigator !== 'undefined' && navigator.language) return navigator.language;\n return 'en-US';\n}\n\nasync function openMicrophone(): Promise<MediaStream | null> {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) return null;\n return navigator.mediaDevices.getUserMedia({ audio: true });\n}\n\nfunction releaseStream(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) track.stop();\n}\n\n/**\n * Drive a dictation session: microphone, speech engine, and the transcript\n * state machine. The composer renders the waveform from `stream` and the\n * live caption from `transcript`; on `stop()` it appends `text` to the draft.\n */\nexport function useDictation(options: UseDictationOptions = {}): DictationController {\n const engine = useMemo(() => options.engine ?? createWebSpeechDictationEngine(), [options.engine]);\n const { lang, captureStream = true, onAutoEnd } = options;\n const [state, dispatch] = useReducer(dictationReducer, INITIAL_DICTATION_STATE);\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n const sessionRef = useRef<DictationEngineSession | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const stateRef = useRef(state);\n stateRef.current = state;\n // The authoritative transcript, updated synchronously as the engine reports\n // it. The reducer's copy renders the caption; this one is what `stop()` and\n // `onAutoEnd` hand over, so an engine that flushes its last words and ends\n // in the same tick — before React has rendered — loses nothing.\n const transcriptRef = useRef<DictationTranscript>(EMPTY_TRANSCRIPT);\n const onAutoEndRef = useRef(onAutoEnd);\n onAutoEndRef.current = onAutoEnd;\n // stop() callers waiting for the engine's final flush.\n const stopWaitersRef = useRef<StopResolver[]>([]);\n // Distinguishes a fresh session from the callbacks of a torn-down one.\n const generationRef = useRef(0);\n\n useEffect(() => {\n setIsSupported(engine.isSupported());\n }, [engine]);\n\n const settleWaiters = useCallback((spoken: string) => {\n const waiters = stopWaitersRef.current;\n stopWaitersRef.current = [];\n for (const resolve of waiters) resolve(spoken);\n }, []);\n\n const releaseSession = useCallback(() => {\n sessionRef.current = null;\n releaseStream(streamRef.current);\n streamRef.current = null;\n setStream(null);\n }, []);\n\n const teardown = useCallback(() => {\n generationRef.current += 1;\n sessionRef.current?.abort();\n releaseSession();\n transcriptRef.current = EMPTY_TRANSCRIPT;\n settleWaiters('');\n }, [releaseSession, settleWaiters]);\n\n const reportEngineError = useCallback(\n (engineError: DictationEngineError) => {\n athenaDiagnostics.emit('composer.dictation', {\n level: 'warn',\n data: { phase: 'error', engine: engine.id, kind: engineError.kind },\n });\n dispatch({ type: 'error', error: engineError });\n },\n [engine.id],\n );\n\n const start = useCallback(() => {\n if (sessionRef.current) return;\n const generation = generationRef.current + 1;\n generationRef.current = generation;\n transcriptRef.current = EMPTY_TRANSCRIPT;\n dispatch({ type: 'start' });\n athenaDiagnostics.emit('composer.dictation', {\n level: 'debug',\n data: { phase: 'start', engine: engine.id },\n });\n\n const run = async () => {\n let microphone: MediaStream | null = null;\n if (captureStream) {\n try {\n microphone = await openMicrophone();\n } catch (error) {\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n dispatch({\n type: 'error',\n error: {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message:\n error instanceof DOMException && error.name === 'NotFoundError'\n ? 'No microphone was found.'\n : 'Microphone access was denied. Allow the microphone for this site and try again.',\n },\n });\n return;\n }\n }\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n streamRef.current = microphone;\n setStream(microphone);\n\n // An engine may fail synchronously inside start() (Web Speech throws\n // when recognition.start() is refused) and run onError/onEnd before we\n // hold the session. Those callbacks flip this flag so a dead session is\n // never stored, which would make every later mic click return early.\n let terminated = false;\n const endSession = () => {\n terminated = true;\n const current = stateRef.current;\n const spoken = spokenText(transcriptRef.current);\n // No stop() is waiting: the engine gave up on its own. Hand the\n // words over so they land in the draft instead of vanishing.\n const endedOnItsOwn =\n stopWaitersRef.current.length === 0 && current.status === DICTATION_STATUSES.listening;\n // The reducer keeps an error status through 'ended', so an error\n // reported just before the engine closed stays on screen.\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spoken);\n if (endedOnItsOwn) onAutoEndRef.current?.(spoken);\n };\n let session: DictationEngineSession;\n try {\n session = engine.start({\n stream: microphone,\n lang: lang ?? documentLanguage(),\n callbacks: {\n onTranscript: (update) => {\n if (generationRef.current !== generation) return;\n transcriptRef.current = {\n final: joinSpeech(transcriptRef.current.final, update.final),\n interim: update.interim,\n };\n dispatch({ type: 'transcript', update });\n },\n onError: (engineError) => {\n if (generationRef.current !== generation) return;\n // A silence timeout is the engine giving up politely; keep the\n // words we have instead of showing an error.\n if (engineError.kind === DICTATION_ENGINE_ERROR_KINDS.noSpeech) return;\n reportEngineError(engineError);\n },\n onEnd: () => {\n if (generationRef.current !== generation) return;\n endSession();\n },\n },\n });\n } catch (error) {\n // A relay engine that cannot build its session must not strand the\n // microphone or leave the composer in `starting`.\n releaseSession();\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n return;\n }\n if (terminated) return;\n sessionRef.current = session;\n dispatch({ type: 'listening' });\n };\n void run();\n }, [captureStream, engine, lang, releaseSession, reportEngineError, settleWaiters]);\n\n const stop = useCallback((): Promise<string> => {\n const session = sessionRef.current;\n if (!session) {\n // Stopped while the microphone prompt was still open: nothing was heard\n // yet, and the pending start must not bring the engine up afterwards.\n if (stateRef.current.status === DICTATION_STATUSES.starting) {\n teardown();\n dispatch({ type: 'cancel' });\n return Promise.resolve('');\n }\n return Promise.resolve(spokenText(transcriptRef.current));\n }\n dispatch({ type: 'stop' });\n const flushed = new Promise<string>((resolve) => {\n stopWaitersRef.current.push(resolve);\n window.setTimeout(() => {\n // Bounded wait: a recognizer that never fires onend must not hold the\n // composer hostage. Force the session closed and hand over what was\n // heard so far.\n if (sessionRef.current === session) {\n session.abort();\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spokenText(transcriptRef.current));\n }\n }, STOP_FLUSH_TIMEOUT_MS);\n });\n session.stop();\n return flushed;\n }, [releaseSession, settleWaiters, teardown]);\n\n const cancel = useCallback(() => {\n teardown();\n dispatch({ type: 'cancel' });\n }, [teardown]);\n\n // Release the microphone with the component.\n useEffect(() => () => teardown(), [teardown]);\n\n const isActive =\n state.status === DICTATION_STATUSES.starting ||\n state.status === DICTATION_STATUSES.listening ||\n state.status === DICTATION_STATUSES.stopping;\n\n return {\n status: state.status,\n isActive,\n transcript: state.transcript,\n text: spokenText(state.transcript),\n error: state.error,\n stream,\n isSupported,\n start,\n stop,\n cancel,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-dictation.js","sources":["../../src/dictation/use-dictation.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n createWebSpeechDictationEngine,\n DICTATION_ENGINE_ERROR_KINDS,\n type DictationEngine,\n type DictationEngineError,\n type DictationEngineSession,\n} from './dictation-engine';\nimport {\n DICTATION_STATUSES,\n dictationReducer,\n INITIAL_DICTATION_STATE,\n joinSpeech,\n spokenText,\n type DictationStatus,\n type DictationTranscript,\n} from './dictation-state';\n\nexport interface UseDictationOptions {\n /**\n * Speech engine. Defaults to the browser's Web Speech API; `AthenaChat`\n * passes the record-then-transcribe engine from `useAthenaDictationEngine`.\n */\n engine?: DictationEngine;\n /** BCP-47 language tag. Defaults to the document language, else `en-US`. */\n lang?: string;\n /**\n * Open the microphone through `getUserMedia` so callers get a `stream` for\n * a waveform. Web Speech opens its own capture; this second capture is\n * what the level meter reads. Defaults to true.\n */\n captureStream?: boolean;\n /**\n * The engine ended on its own (silence window, recognizer timeout) while\n * listening, without `stop()` or `cancel()`. Receives the spoken text so\n * the words are not lost; the composer puts them into the draft.\n */\n onAutoEnd?: (spoken: string) => void;\n}\n\nexport interface DictationStopResult {\n /** Spoken text (final plus interim tail) gathered before the engine closed. */\n text: string;\n /**\n * The engine error reported during this session, if any. When set the\n * transcript may be incomplete: a recorded engine whose upload failed has no\n * words to hand over, so callers should not send the draft as if it were.\n */\n error: DictationEngineError | null;\n}\n\nexport interface DictationController {\n status: DictationStatus;\n /** True while starting, listening or stopping. */\n isActive: boolean;\n transcript: DictationTranscript;\n /** `final` plus the interim tail, ready to hand to the composer. */\n text: string;\n error: DictationEngineError | null;\n /** The microphone stream while active (for a waveform), else null. */\n stream: MediaStream | null;\n /** Whether the engine can run here. Hide the control when false. */\n isSupported: boolean;\n /** The engine in use (for its `stoppingLabel` and diagnostics). */\n engine: DictationEngine;\n start: () => void;\n /**\n * Finish recording. Resolves once the engine has flushed (bounded wait), so\n * trailing words are not lost, with the spoken text and any error raised\n * along the way.\n */\n stop: () => Promise<DictationStopResult>;\n /** Discard the session and its transcript. */\n cancel: () => void;\n}\n\n/** How long `stop()` waits for a live engine's final flush before giving up. */\nconst DEFAULT_STOP_FLUSH_TIMEOUT_MS = 1500;\n\ntype StopResolver = (result: DictationStopResult) => void;\n\nconst EMPTY_TRANSCRIPT: DictationTranscript = { final: '', interim: '' };\n\nfunction documentLanguage(): string {\n if (typeof document !== 'undefined' && document.documentElement.lang) {\n return document.documentElement.lang;\n }\n if (typeof navigator !== 'undefined' && navigator.language) return navigator.language;\n return 'en-US';\n}\n\nasync function openMicrophone(): Promise<MediaStream | null> {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) return null;\n return navigator.mediaDevices.getUserMedia({ audio: true });\n}\n\nfunction releaseStream(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) track.stop();\n}\n\n/**\n * Drive a dictation session: microphone, speech engine, and the transcript\n * state machine. The composer renders the waveform from `stream` and the\n * live caption from `transcript`; on `stop()` it appends `text` to the draft.\n */\nexport function useDictation(options: UseDictationOptions = {}): DictationController {\n const engine = useMemo(() => options.engine ?? createWebSpeechDictationEngine(), [options.engine]);\n const { lang, captureStream = true, onAutoEnd } = options;\n const [state, dispatch] = useReducer(dictationReducer, INITIAL_DICTATION_STATE);\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n const sessionRef = useRef<DictationEngineSession | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const stateRef = useRef(state);\n stateRef.current = state;\n // The authoritative transcript, updated synchronously as the engine reports\n // it. The reducer's copy renders the caption; this one is what `stop()` and\n // `onAutoEnd` hand over, so an engine that flushes its last words and ends\n // in the same tick — before React has rendered — loses nothing.\n const transcriptRef = useRef<DictationTranscript>(EMPTY_TRANSCRIPT);\n const onAutoEndRef = useRef(onAutoEnd);\n onAutoEndRef.current = onAutoEnd;\n // stop() callers waiting for the engine's final flush.\n const stopWaitersRef = useRef<StopResolver[]>([]);\n // Last engine error of the current session, handed to stop() callers\n // synchronously (the reducer's copy may not have rendered yet).\n const sessionErrorRef = useRef<DictationEngineError | null>(null);\n // Distinguishes a fresh session from the callbacks of a torn-down one.\n const generationRef = useRef(0);\n\n useEffect(() => {\n setIsSupported(engine.isSupported());\n }, [engine]);\n\n const settleWaiters = useCallback((spoken: string) => {\n const waiters = stopWaitersRef.current;\n stopWaitersRef.current = [];\n const result: DictationStopResult = { text: spoken, error: sessionErrorRef.current };\n for (const resolve of waiters) resolve(result);\n }, []);\n\n const releaseSession = useCallback(() => {\n sessionRef.current = null;\n releaseStream(streamRef.current);\n streamRef.current = null;\n setStream(null);\n }, []);\n\n const teardown = useCallback(() => {\n generationRef.current += 1;\n sessionRef.current?.abort();\n releaseSession();\n transcriptRef.current = EMPTY_TRANSCRIPT;\n settleWaiters('');\n }, [releaseSession, settleWaiters]);\n\n const reportEngineError = useCallback(\n (engineError: DictationEngineError) => {\n athenaDiagnostics.emit('composer.dictation', {\n level: 'warn',\n data: { phase: 'error', engine: engine.id, kind: engineError.kind },\n });\n sessionErrorRef.current = engineError;\n dispatch({ type: 'error', error: engineError });\n },\n [engine.id],\n );\n\n const start = useCallback(() => {\n if (sessionRef.current) return;\n const generation = generationRef.current + 1;\n generationRef.current = generation;\n transcriptRef.current = EMPTY_TRANSCRIPT;\n sessionErrorRef.current = null;\n dispatch({ type: 'start' });\n athenaDiagnostics.emit('composer.dictation', {\n level: 'debug',\n data: { phase: 'start', engine: engine.id },\n });\n\n const run = async () => {\n let microphone: MediaStream | null = null;\n if (captureStream) {\n try {\n microphone = await openMicrophone();\n } catch (error) {\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n dispatch({\n type: 'error',\n error: {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message:\n error instanceof DOMException && error.name === 'NotFoundError'\n ? 'No microphone was found.'\n : 'Microphone access was denied. Allow the microphone for this site and try again.',\n },\n });\n return;\n }\n }\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n streamRef.current = microphone;\n setStream(microphone);\n\n // An engine may fail synchronously inside start() (Web Speech throws\n // when recognition.start() is refused) and run onError/onEnd before we\n // hold the session. Those callbacks flip this flag so a dead session is\n // never stored, which would make every later mic click return early.\n let terminated = false;\n const endSession = () => {\n terminated = true;\n const current = stateRef.current;\n const spoken = spokenText(transcriptRef.current);\n // No stop() is waiting: the engine gave up on its own. Hand the\n // words over so they land in the draft instead of vanishing.\n const endedOnItsOwn =\n stopWaitersRef.current.length === 0 && current.status === DICTATION_STATUSES.listening;\n // The reducer keeps an error status through 'ended', so an error\n // reported just before the engine closed stays on screen.\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spoken);\n if (endedOnItsOwn) onAutoEndRef.current?.(spoken);\n };\n let session: DictationEngineSession;\n try {\n session = engine.start({\n stream: microphone,\n lang: lang ?? documentLanguage(),\n callbacks: {\n onTranscript: (update) => {\n if (generationRef.current !== generation) return;\n transcriptRef.current = {\n final: joinSpeech(transcriptRef.current.final, update.final),\n interim: update.interim,\n };\n dispatch({ type: 'transcript', update });\n },\n onError: (engineError) => {\n if (generationRef.current !== generation) return;\n // A silence timeout is the engine giving up politely; keep the\n // words we have instead of showing an error.\n if (engineError.kind === DICTATION_ENGINE_ERROR_KINDS.noSpeech) return;\n reportEngineError(engineError);\n },\n onEnd: () => {\n if (generationRef.current !== generation) return;\n endSession();\n },\n },\n });\n } catch (error) {\n // A relay engine that cannot build its session must not strand the\n // microphone or leave the composer in `starting`.\n releaseSession();\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n return;\n }\n if (terminated) return;\n sessionRef.current = session;\n dispatch({ type: 'listening' });\n };\n void run();\n }, [captureStream, engine, lang, releaseSession, reportEngineError, settleWaiters]);\n\n const stop = useCallback((): Promise<DictationStopResult> => {\n const session = sessionRef.current;\n if (!session) {\n // Stopped while the microphone prompt was still open: nothing was heard\n // yet, and the pending start must not bring the engine up afterwards.\n if (stateRef.current.status === DICTATION_STATUSES.starting) {\n teardown();\n dispatch({ type: 'cancel' });\n return Promise.resolve({ text: '', error: null });\n }\n return Promise.resolve({ text: spokenText(transcriptRef.current), error: sessionErrorRef.current });\n }\n dispatch({ type: 'stop' });\n const flushed = new Promise<DictationStopResult>((resolve) => {\n stopWaitersRef.current.push(resolve);\n window.setTimeout(() => {\n // Bounded wait: a recognizer that never fires onend must not hold the\n // composer hostage. Force the session closed and hand over what was\n // heard so far. An engine that had nothing to show yet (a recorded\n // clip still transcribing) surfaces that as an error rather than\n // passing off silence as the result.\n if (sessionRef.current === session) {\n // Report before abort(): an engine that ends synchronously on\n // abort settles the waiters, and they must see this error.\n if (!spokenText(transcriptRef.current)) {\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: 'Transcription timed out. Try again.',\n });\n }\n session.abort();\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spokenText(transcriptRef.current));\n }\n }, engine.flushTimeoutMs ?? DEFAULT_STOP_FLUSH_TIMEOUT_MS);\n });\n session.stop();\n return flushed;\n }, [engine.flushTimeoutMs, releaseSession, reportEngineError, settleWaiters, teardown]);\n\n const cancel = useCallback(() => {\n teardown();\n dispatch({ type: 'cancel' });\n }, [teardown]);\n\n // Release the microphone with the component.\n useEffect(() => () => teardown(), [teardown]);\n\n const isActive =\n state.status === DICTATION_STATUSES.starting ||\n state.status === DICTATION_STATUSES.listening ||\n state.status === DICTATION_STATUSES.stopping;\n\n return {\n status: state.status,\n isActive,\n transcript: state.transcript,\n text: spokenText(state.transcript),\n error: state.error,\n stream,\n isSupported,\n engine,\n start,\n stop,\n cancel,\n };\n}\n"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -40,27 +40,6 @@ const toolUis = require("./tools/tool-uis.cjs");
40
40
  const primitives = require("./tools/primitives.cjs");
41
41
  const miscToolUis = require("./tools/misc-tool-uis.cjs");
42
42
  const toolIcons = require("./tools/tool-icons.cjs");
43
- require("./packages/agent-runtime/src/generated/runconfig-contract.cjs");
44
- require("react");
45
- require("@assistant-ui/tap");
46
- require("statewire");
47
- require("./packages/agent-runtime/src/statewire-suspended.cjs");
48
- const conversationList = require("./packages/agent-runtime/src/conversation-list.cjs");
49
- const toolMetadata = require("./packages/agent-runtime/src/tool-metadata.cjs");
50
- const toolResult = require("./packages/agent-runtime/src/tool-result.cjs");
51
- require("@tanstack/react-query");
52
- const interrupts = require("./packages/agent-runtime/src/interrupts.cjs");
53
- require("./packages/agent-runtime/src/messages.cjs");
54
- require("./packages/agent-runtime/src/statewire-state.cjs");
55
- const threadExtras = require("./packages/agent-runtime/src/thread-extras.cjs");
56
- require("@assistant-ui/store");
57
- require("./packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
58
- require("./packages/agent-runtime/src/statewire-attach-observer.cjs");
59
- require("./packages/agent-runtime/src/thread-status.cjs");
60
- require("@assistant-ui/react-statewire");
61
- const githubWakeEvent = require("./packages/agent-runtime/src/github-wake-event.cjs");
62
- require("@assistant-ui/core");
63
- require("./packages/agent-runtime/src/thread-handoff.cjs");
64
43
  const writeTodosToolUi = require("./tools/write-todos-tool-ui.cjs");
65
44
  const todoPlanStore = require("./tools/todo-plan-store.cjs");
66
45
  const fileToolUis = require("./tools/file-tool-uis.cjs");
@@ -91,6 +70,8 @@ const useAthenaThreadManager = require("./threads/useAthenaThreadManager.cjs");
91
70
  const useDictation = require("./dictation/use-dictation.cjs");
92
71
  const DictationStrip = require("./dictation/DictationStrip.cjs");
93
72
  const dictationEngine = require("./dictation/dictation-engine.cjs");
73
+ const recordedDictationEngine = require("./dictation/recorded-dictation-engine.cjs");
74
+ const useAthenaDictationEngine = require("./dictation/use-athena-dictation-engine.cjs");
94
75
  const dictationState = require("./dictation/dictation-state.cjs");
95
76
  const useAppendToComposer = require("./composer/useAppendToComposer.cjs");
96
77
  const composerHandoff = require("./composer/composer-handoff.cjs");
@@ -108,7 +89,13 @@ const tooltip = require("./ui/tooltip.cjs");
108
89
  const collapsible = require("./ui/collapsible.cjs");
109
90
  const index = require("./toolkits/index.cjs");
110
91
  const index$1 = require("./theme/index.cjs");
92
+ const interrupts = require("./packages/agent-runtime/src/interrupts.cjs");
93
+ const toolResult = require("./packages/agent-runtime/src/tool-result.cjs");
111
94
  const cnfast = require("cnfast");
95
+ const conversationList = require("./packages/agent-runtime/src/conversation-list.cjs");
96
+ const toolMetadata = require("./packages/agent-runtime/src/tool-metadata.cjs");
97
+ const githubWakeEvent = require("./packages/agent-runtime/src/github-wake-event.cjs");
98
+ const threadExtras = require("./packages/agent-runtime/src/thread-extras.cjs");
112
99
  const toolAppearance = require("./tools/tool-appearance.cjs");
113
100
  exports.ATHENA_REACT_SDK_VERSION = version.ATHENA_REACT_SDK_VERSION;
114
101
  exports.athenaDiagnostics = athenaDiagnostics.athenaDiagnostics;
@@ -218,24 +205,6 @@ exports.truncate = primitives.truncate;
218
205
  exports.InstallPackageToolUI = miscToolUis.InstallPackageToolUI;
219
206
  exports.TOOL_ICONS = toolIcons.TOOL_ICONS;
220
207
  exports.toolIcon = toolIcons.toolIcon;
221
- exports.deriveConversationAttention = conversationList.deriveConversationAttention;
222
- exports.describeToolArgs = toolMetadata.describeToolArgs;
223
- exports.formatToolName = toolMetadata.formatToolName;
224
- exports.getToolDisplayMeta = toolMetadata.getToolDisplayMeta;
225
- exports.toolActiveLabel = toolMetadata.toolActiveLabel;
226
- exports.toolCompletedLabel = toolMetadata.toolCompletedLabel;
227
- exports.classifyToolResult = toolResult.classifyToolResult;
228
- exports.isToolResultSuccess = toolResult.isToolResultSuccess;
229
- exports.resultMessage = toolResult.resultMessage;
230
- exports.HITL_PENDING_RESUME_GRACE_MS = interrupts.HITL_PENDING_RESUME_GRACE_MS;
231
- exports.asHitlApproval = interrupts.asHitlApproval;
232
- exports.isPendingInterrupt = interrupts.isPendingInterrupt;
233
- exports.readInterruptMessage = interrupts.readInterruptMessage;
234
- exports.hasStatewireThreadExtras = threadExtras.hasStatewireThreadExtras;
235
- exports.githubWakeEventLabel = githubWakeEvent.githubWakeEventLabel;
236
- exports.githubWakeMarkerText = githubWakeEvent.githubWakeMarkerText;
237
- exports.isGithubWakeMessage = githubWakeEvent.isGithubWakeMessage;
238
- exports.readGithubWakeEvent = githubWakeEvent.readGithubWakeEvent;
239
208
  exports.HiddenWriteTodosToolUI = writeTodosToolUi.HiddenWriteTodosToolUI;
240
209
  exports.WriteTodosToolUI = writeTodosToolUi.WriteTodosToolUI;
241
210
  exports.parseTodoPlan = writeTodosToolUi.parseTodoPlan;
@@ -278,6 +247,7 @@ exports.ThreadList = ThreadList.ThreadList;
278
247
  exports.SessionList = SessionList.SessionList;
279
248
  exports.useAthenaSessionAttention = useAthenaSessionAttention.useAthenaSessionAttention;
280
249
  exports.groupSessions = sessionListModel.groupSessions;
250
+ exports.orderedPullRequests = sessionListModel.orderedPullRequests;
281
251
  exports.pullRequestChip = sessionListModel.pullRequestChip;
282
252
  exports.pullRequestChipState = sessionListModel.pullRequestChipState;
283
253
  exports.sessionIndicator = sessionListModel.sessionIndicator;
@@ -310,6 +280,8 @@ exports.DictationStrip = DictationStrip.DictationStrip;
310
280
  exports.DictationWaveform = DictationStrip.DictationWaveform;
311
281
  exports.DICTATION_ENGINE_ERROR_KINDS = dictationEngine.DICTATION_ENGINE_ERROR_KINDS;
312
282
  exports.createWebSpeechDictationEngine = dictationEngine.createWebSpeechDictationEngine;
283
+ exports.createRecordedDictationEngine = recordedDictationEngine.createRecordedDictationEngine;
284
+ exports.useAthenaDictationEngine = useAthenaDictationEngine.useAthenaDictationEngine;
313
285
  exports.DICTATION_STATUSES = dictationState.DICTATION_STATUSES;
314
286
  exports.composeDictationDraft = dictationState.composeDictationDraft;
315
287
  exports.useAppendToComposer = useAppendToComposer.useAppendToComposer;
@@ -342,9 +314,27 @@ exports.CollapsibleTrigger = collapsible.CollapsibleTrigger;
342
314
  exports.Toolkits = index.Toolkits;
343
315
  exports.themeToStyleVars = index$1.themeToStyleVars;
344
316
  exports.themes = index$1.themes;
317
+ exports.HITL_PENDING_RESUME_GRACE_MS = interrupts.HITL_PENDING_RESUME_GRACE_MS;
318
+ exports.asHitlApproval = interrupts.asHitlApproval;
319
+ exports.isPendingInterrupt = interrupts.isPendingInterrupt;
320
+ exports.readInterruptMessage = interrupts.readInterruptMessage;
321
+ exports.classifyToolResult = toolResult.classifyToolResult;
322
+ exports.isToolResultSuccess = toolResult.isToolResultSuccess;
323
+ exports.resultMessage = toolResult.resultMessage;
345
324
  Object.defineProperty(exports, "cn", {
346
325
  enumerable: true,
347
326
  get: () => cnfast.cn
348
327
  });
328
+ exports.deriveConversationAttention = conversationList.deriveConversationAttention;
329
+ exports.describeToolArgs = toolMetadata.describeToolArgs;
330
+ exports.formatToolName = toolMetadata.formatToolName;
331
+ exports.getToolDisplayMeta = toolMetadata.getToolDisplayMeta;
332
+ exports.toolActiveLabel = toolMetadata.toolActiveLabel;
333
+ exports.toolCompletedLabel = toolMetadata.toolCompletedLabel;
334
+ exports.githubWakeEventLabel = githubWakeEvent.githubWakeEventLabel;
335
+ exports.githubWakeMarkerText = githubWakeEvent.githubWakeMarkerText;
336
+ exports.isGithubWakeMessage = githubWakeEvent.isGithubWakeMessage;
337
+ exports.readGithubWakeEvent = githubWakeEvent.readGithubWakeEvent;
338
+ exports.hasStatewireThreadExtras = threadExtras.hasStatewireThreadExtras;
349
339
  exports.useToolAppearance = toolAppearance.useToolAppearance;
350
340
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ export type { WelcomeSuggestion } from './chat/athena-chat-welcome';
22
22
  export { AthenaChatErrorBoundary } from './chat/AthenaChatErrorBoundary';
23
23
  export { type ActivityItem, type ActivityPhase, type ActivityTimelineOptions, type ActivityToolState, buildActivityTimeline, CollapsibleGroup, type CollapsibleGroupProps, countActivityTools, createToolBasedGroupingFunction, DEFAULT_TOOLKIT_THEME, defaultGetToolGroupKey, excludeFinalTextFromGroups, type FinalTextPart, getActivityItemLabel, getActivitySummary, getActivityToolState, getFinalText, type GetToolGroupKey, type GroupablePart, type GroupingFunction, type MessagePartGroup, resetRememberedExpansionsForTests, resolveActivityPhase, SuperGroupingCard, type SuperGroupingCardProps, SuperGroupingFinalText, SuperGroupingProvider, TOOLKIT_THEMES, type ToolGroupKeyOptions, type ToolkitTheme, useActivityExpansion, } from './chat/super-grouping';
24
24
  export { StatewireApprovalCard } from './chat/StatewireApprovalCard';
25
- export type { StatewireApprovalCardProps, StatewireApprovalRequestContext, } from './chat/StatewireApprovalCard';
25
+ export type { StatewireApprovalCardProps, StatewireApprovalEditorContext, StatewireApprovalRequestContext, } from './chat/StatewireApprovalCard';
26
26
  export { useStatewireApprovalRequest } from './chat/use-statewire-approval-request';
27
27
  export type { StatewireApprovalRequest, StatewireInterruptPredicate, UseStatewireApprovalRequestOptions, } from './chat/use-statewire-approval-request';
28
28
  export { useAthenaRunLive } from './chat/use-athena-run-live';
@@ -99,7 +99,7 @@ export { SessionList } from './threads/SessionList';
99
99
  export type { SessionListProps } from './threads/SessionList';
100
100
  export { useAthenaSessionAttention } from './threads/use-athena-session-attention';
101
101
  export type { AthenaSessionAttention } from './threads/use-athena-session-attention';
102
- export { groupSessions, pullRequestChip, pullRequestChipState, sessionIndicator, sessionListGroupOf, sessionOriginLabel, sessionSecondaryLine, } from './threads/session-list-model';
102
+ export { groupSessions, orderedPullRequests, pullRequestChip, pullRequestChipState, sessionIndicator, sessionListGroupOf, sessionOriginLabel, sessionSecondaryLine, } from './threads/session-list-model';
103
103
  export type { PullRequestChip, PullRequestChipState, SessionIndicator, SessionLineTone, SessionListGroup, SessionListSection, SessionSecondaryLine, } from './threads/session-list-model';
104
104
  export { deriveConversationAttention, githubWakeEventLabel, githubWakeMarkerText, isGithubWakeMessage, readGithubWakeEvent, } from '@athenaintel/agent-runtime';
105
105
  export type { ConversationAttention, ConversationAttentionSource, ConversationPullRequest, ConversationSummary, ConversationWake, GithubWakeEvent, } from '@athenaintel/agent-runtime';
@@ -115,11 +115,14 @@ export { conversationSummaryFromIndexEntry, useStatewireConversationHistory, typ
115
115
  export { useAthenaThreadManager } from './threads/useAthenaThreadManager';
116
116
  export type { AthenaThreadManagerState } from './threads/useAthenaThreadManager';
117
117
  export { useDictation } from './dictation/use-dictation';
118
- export type { DictationController, UseDictationOptions } from './dictation/use-dictation';
118
+ export type { DictationController, DictationStopResult, UseDictationOptions, } from './dictation/use-dictation';
119
119
  export { DictationStrip, DictationWaveform } from './dictation/DictationStrip';
120
120
  export type { DictationStripProps } from './dictation/DictationStrip';
121
121
  export { createWebSpeechDictationEngine, DICTATION_ENGINE_ERROR_KINDS, } from './dictation/dictation-engine';
122
122
  export type { DictationEngine, DictationEngineCallbacks, DictationEngineError, DictationEngineErrorKind, DictationEngineSession, DictationTranscriptUpdate, } from './dictation/dictation-engine';
123
+ export { createRecordedDictationEngine } from './dictation/recorded-dictation-engine';
124
+ export type { DictationTranscribeContext, DictationTranscriber, RecordedDictationEngineOptions, } from './dictation/recorded-dictation-engine';
125
+ export { useAthenaDictationEngine } from './dictation/use-athena-dictation-engine';
123
126
  export { composeDictationDraft, DICTATION_STATUSES } from './dictation/dictation-state';
124
127
  export type { DictationStatus, DictationTranscript } from './dictation/dictation-state';
125
128
  export type { AthenaChatComposerSlots, AthenaChatDictationOptions } from './chat/AthenaChat';