@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
@@ -1,23 +1,8 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useAuiState } from "@assistant-ui/react";
3
- import "../../packages/agent-runtime/src/generated/runconfig-contract.js";
3
+ import { GitPullRequestIcon } from "lucide-react";
4
4
  import { useMemo } from "react";
5
- import "@assistant-ui/tap";
6
- import "statewire";
7
- import "../../packages/agent-runtime/src/statewire-suspended.js";
8
- import "../../packages/agent-runtime/src/conversation-list.js";
9
- import "@tanstack/react-query";
10
- import "../../packages/agent-runtime/src/statewire-state.js";
11
- import "../../packages/agent-runtime/src/messages.js";
12
- import "@assistant-ui/store";
13
- import "../../packages/agent-runtime/src/quota-safe-local-storage-session.js";
14
- import "../../packages/agent-runtime/src/statewire-attach-observer.js";
15
- import "../../packages/agent-runtime/src/thread-status.js";
16
- import "@assistant-ui/react-statewire";
17
5
  import { githubWakeReference, githubWakeEventLabel, readGithubWakeEvent } from "../../packages/agent-runtime/src/github-wake-event.js";
18
- import "@assistant-ui/core";
19
- import "../../packages/agent-runtime/src/thread-handoff.js";
20
- import { GitPullRequestIcon } from "lucide-react";
21
6
  import { cn } from "cnfast";
22
7
  const WakeMarkerCard = ({ event, className, ...rest }) => {
23
8
  const target = event.number !== null ? `#${event.number}` : event.repo;
@@ -1 +1 @@
1
- {"version":3,"file":"wake-marker.js","sources":["../../../src/chat-ui/messages/wake-marker.tsx"],"sourcesContent":["/**\n * The runtime marker for a wake turn: a GitHub webhook woke the session and\n * injected a user message (see `@athenaintel/agent-runtime`'s\n * `github-wake-event.ts`). It renders in the same visual language as the\n * runtime-ending card (`RuntimeMessageCard`) — a compact status row, not a\n * user bubble quoting the whole wake text.\n */\nimport { useAuiState } from \"@assistant-ui/react\";\nimport {\n type GithubWakeEvent,\n githubWakeEventLabel,\n githubWakeReference,\n readGithubWakeEvent,\n} from \"@athenaintel/agent-runtime\";\nimport { GitPullRequestIcon } from \"lucide-react\";\nimport { type ComponentPropsWithoutRef, type FC, useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport interface WakeMarkerCardProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"role\"> {\n event: GithubWakeEvent;\n}\n\n/** The \"Woke: review comment on #36337\" row; pure presenter. */\nexport const WakeMarkerCard: FC<WakeMarkerCardProps> = ({ event, className, ...rest }) => {\n const target = event.number !== null ? `#${event.number}` : event.repo;\n const reference = githubWakeReference(event);\n return (\n <div\n {...rest}\n role=\"status\"\n data-runtime-message-kind=\"wake\"\n data-testid=\"wake-marker\"\n className={cn(\n \"my-2 flex w-full items-start gap-2 rounded-lg border border-border bg-muted/40 px-3 py-2 text-muted-foreground text-sm\",\n className,\n )}\n >\n <GitPullRequestIcon className=\"mt-0.5 size-4 shrink-0\" aria-hidden=\"true\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5 break-words [overflow-wrap:anywhere]\">\n <span className=\"font-medium\">\n Woke: {githubWakeEventLabel(event)} on{\" \"}\n {event.url ? (\n <a\n href={event.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n title={event.title ?? reference}\n className=\"underline-offset-2 hover:underline\"\n >\n {target}\n </a>\n ) : (\n <span title={event.title ?? reference}>{target}</span>\n )}\n </span>\n {event.sender || event.title ? (\n <span className=\"text-xs opacity-90\">\n {event.sender ? `by ${event.sender}` : null}\n {event.sender && event.title ? \" · \" : null}\n {event.title}\n </span>\n ) : null}\n {event.bodyExcerpt ? (\n <span className=\"truncate text-xs italic opacity-90\" title={event.bodyExcerpt}>\n “{event.bodyExcerpt}”\n </span>\n ) : null}\n </div>\n </div>\n );\n};\n\n/**\n * {@link WakeMarkerCard} bound to the assistant-ui message in scope. Selects\n * the message's `metadata.custom` (a stable store reference) and derives the\n * envelope from it: the reader allocates a fresh object per call, so selecting\n * its result directly would re-render on every store update.\n */\nexport const WakeMarkerMessage: FC<{ className?: string }> = ({ className }) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const event = useMemo(() => readGithubWakeEvent(custom), [custom]);\n if (!event) return null;\n return <WakeMarkerCard event={event} className={className} />;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBO,MAAM,iBAA0C,CAAC,EAAE,OAAO,WAAW,GAAG,WAAW;AACxF,QAAM,SAAS,MAAM,WAAW,OAAO,IAAI,MAAM,MAAM,KAAK,MAAM;AAClE,QAAM,YAAY,oBAAoB,KAAK;AAC3C,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAG;AAAA,MACJ,MAAK;AAAA,MACL,6BAA0B;AAAA,MAC1B,eAAY;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAA,oBAAC,oBAAA,EAAmB,WAAU,0BAAyB,eAAY,QAAO;AAAA,QAC1E,qBAAC,OAAA,EAAI,WAAU,6EACb,UAAA;AAAA,UAAA,qBAAC,QAAA,EAAK,WAAU,eAAc,UAAA;AAAA,YAAA;AAAA,YACrB,qBAAqB,KAAK;AAAA,YAAE;AAAA,YAAI;AAAA,YACtC,MAAM,MACL;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAM,MAAM;AAAA,gBACZ,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,OAAO,MAAM,SAAS;AAAA,gBACtB,WAAU;AAAA,gBAET,UAAA;AAAA,cAAA;AAAA,YAAA,IAGH,oBAAC,QAAA,EAAK,OAAO,MAAM,SAAS,WAAY,UAAA,OAAA,CAAO;AAAA,UAAA,GAEnD;AAAA,UACC,MAAM,UAAU,MAAM,QACrB,qBAAC,QAAA,EAAK,WAAU,sBACb,UAAA;AAAA,YAAA,MAAM,SAAS,MAAM,MAAM,MAAM,KAAK;AAAA,YACtC,MAAM,UAAU,MAAM,QAAQ,QAAQ;AAAA,YACtC,MAAM;AAAA,UAAA,EAAA,CACT,IACE;AAAA,UACH,MAAM,cACL,qBAAC,QAAA,EAAK,WAAU,sCAAqC,OAAO,MAAM,aAAa,UAAA;AAAA,YAAA;AAAA,YAC3E,MAAM;AAAA,YAAY;AAAA,UAAA,EAAA,CACtB,IACE;AAAA,QAAA,EAAA,CACN;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;AAQO,MAAM,oBAAgD,CAAC,EAAE,gBAAgB;AAC9E,QAAM,SAAS,YAAY,CAAC;;AAAM,mBAAE,QAAQ,aAAV,mBAAoB;AAAA,GAAM;AAC5D,QAAM,QAAQ,QAAQ,MAAM,oBAAoB,MAAM,GAAG,CAAC,MAAM,CAAC;AACjE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,oBAAC,gBAAA,EAAe,OAAc,UAAA,CAAsB;AAC7D;"}
1
+ {"version":3,"file":"wake-marker.js","sources":["../../../src/chat-ui/messages/wake-marker.tsx"],"sourcesContent":["/**\n * The runtime marker for a wake turn: a GitHub webhook woke the session and\n * injected a user message (see `@athenaintel/agent-runtime`'s\n * `github-wake-event.ts`). It renders in the same visual language as the\n * runtime-ending card (`RuntimeMessageCard`) — a compact status row, not a\n * user bubble quoting the whole wake text.\n */\nimport { useAuiState } from \"@assistant-ui/react\";\nimport {\n type GithubWakeEvent,\n githubWakeEventLabel,\n githubWakeReference,\n readGithubWakeEvent,\n} from \"@athenaintel/agent-runtime\";\nimport { GitPullRequestIcon } from \"lucide-react\";\nimport { type ComponentPropsWithoutRef, type FC, useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport interface WakeMarkerCardProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"role\"> {\n event: GithubWakeEvent;\n}\n\n/** The \"Woke: review comment on #36337\" row; pure presenter. */\nexport const WakeMarkerCard: FC<WakeMarkerCardProps> = ({ event, className, ...rest }) => {\n const target = event.number !== null ? `#${event.number}` : event.repo;\n const reference = githubWakeReference(event);\n return (\n <div\n {...rest}\n role=\"status\"\n data-runtime-message-kind=\"wake\"\n data-testid=\"wake-marker\"\n className={cn(\n \"my-2 flex w-full items-start gap-2 rounded-lg border border-border bg-muted/40 px-3 py-2 text-muted-foreground text-sm\",\n className,\n )}\n >\n <GitPullRequestIcon className=\"mt-0.5 size-4 shrink-0\" aria-hidden=\"true\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5 break-words [overflow-wrap:anywhere]\">\n <span className=\"font-medium\">\n Woke: {githubWakeEventLabel(event)} on{\" \"}\n {event.url ? (\n <a\n href={event.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n title={event.title ?? reference}\n className=\"underline-offset-2 hover:underline\"\n >\n {target}\n </a>\n ) : (\n <span title={event.title ?? reference}>{target}</span>\n )}\n </span>\n {event.sender || event.title ? (\n <span className=\"text-xs opacity-90\">\n {event.sender ? `by ${event.sender}` : null}\n {event.sender && event.title ? \" · \" : null}\n {event.title}\n </span>\n ) : null}\n {event.bodyExcerpt ? (\n <span className=\"truncate text-xs italic opacity-90\" title={event.bodyExcerpt}>\n “{event.bodyExcerpt}”\n </span>\n ) : null}\n </div>\n </div>\n );\n};\n\n/**\n * {@link WakeMarkerCard} bound to the assistant-ui message in scope. Selects\n * the message's `metadata.custom` (a stable store reference) and derives the\n * envelope from it: the reader allocates a fresh object per call, so selecting\n * its result directly would re-render on every store update.\n */\nexport const WakeMarkerMessage: FC<{ className?: string }> = ({ className }) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const event = useMemo(() => readGithubWakeEvent(custom), [custom]);\n if (!event) return null;\n return <WakeMarkerCard event={event} className={className} />;\n};\n"],"names":[],"mappings":";;;;;;AAwBO,MAAM,iBAA0C,CAAC,EAAE,OAAO,WAAW,GAAG,WAAW;AACxF,QAAM,SAAS,MAAM,WAAW,OAAO,IAAI,MAAM,MAAM,KAAK,MAAM;AAClE,QAAM,YAAY,oBAAoB,KAAK;AAC3C,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAG;AAAA,MACJ,MAAK;AAAA,MACL,6BAA0B;AAAA,MAC1B,eAAY;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAA,oBAAC,oBAAA,EAAmB,WAAU,0BAAyB,eAAY,QAAO;AAAA,QAC1E,qBAAC,OAAA,EAAI,WAAU,6EACb,UAAA;AAAA,UAAA,qBAAC,QAAA,EAAK,WAAU,eAAc,UAAA;AAAA,YAAA;AAAA,YACrB,qBAAqB,KAAK;AAAA,YAAE;AAAA,YAAI;AAAA,YACtC,MAAM,MACL;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAM,MAAM;AAAA,gBACZ,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,OAAO,MAAM,SAAS;AAAA,gBACtB,WAAU;AAAA,gBAET,UAAA;AAAA,cAAA;AAAA,YAAA,IAGH,oBAAC,QAAA,EAAK,OAAO,MAAM,SAAS,WAAY,UAAA,OAAA,CAAO;AAAA,UAAA,GAEnD;AAAA,UACC,MAAM,UAAU,MAAM,QACrB,qBAAC,QAAA,EAAK,WAAU,sBACb,UAAA;AAAA,YAAA,MAAM,SAAS,MAAM,MAAM,MAAM,KAAK;AAAA,YACtC,MAAM,UAAU,MAAM,QAAQ,QAAQ;AAAA,YACtC,MAAM;AAAA,UAAA,EAAA,CACT,IACE;AAAA,UACH,MAAM,cACL,qBAAC,QAAA,EAAK,WAAU,sCAAqC,OAAO,MAAM,aAAa,UAAA;AAAA,YAAA;AAAA,YAC3E,MAAM;AAAA,YAAY;AAAA,UAAA,EAAA,CACtB,IACE;AAAA,QAAA,EAAA,CACN;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;AAQO,MAAM,oBAAgD,CAAC,EAAE,gBAAgB;AAC9E,QAAM,SAAS,YAAY,CAAC;;AAAM,mBAAE,QAAQ,aAAV,mBAAoB;AAAA,GAAM;AAC5D,QAAM,QAAQ,QAAQ,MAAM,oBAAoB,MAAM,GAAG,CAAC,MAAM,CAAC;AACjE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,oBAAC,gBAAA,EAAe,OAAc,UAAA,CAAsB;AAC7D;"}
@@ -10,23 +10,7 @@ const runtimeMessage = require("./messages/runtime-message.cjs");
10
10
  const wakeMarker = require("./messages/wake-marker.cjs");
11
11
  const WaitingOnYouBanner = require("../chat/WaitingOnYouBanner.cjs");
12
12
  const statewireContinueRun = require("./messages/statewire-continue-run.cjs");
13
- require("../packages/agent-runtime/src/generated/runconfig-contract.cjs");
14
- require("react");
15
- require("@assistant-ui/tap");
16
- require("statewire");
17
- require("../packages/agent-runtime/src/statewire-suspended.cjs");
18
- require("../packages/agent-runtime/src/conversation-list.cjs");
19
- require("@tanstack/react-query");
20
13
  const interrupts = require("../packages/agent-runtime/src/interrupts.cjs");
21
- require("../packages/agent-runtime/src/messages.cjs");
22
- require("../packages/agent-runtime/src/statewire-state.cjs");
23
- require("@assistant-ui/store");
24
- require("../packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
25
- require("../packages/agent-runtime/src/statewire-attach-observer.cjs");
26
- require("../packages/agent-runtime/src/thread-status.cjs");
27
- require("@assistant-ui/react-statewire");
28
- require("@assistant-ui/core");
29
- require("../packages/agent-runtime/src/thread-handoff.cjs");
30
14
  exports.StatewireApprovalCard = StatewireApprovalCard.StatewireApprovalCard;
31
15
  exports.useStatewireApprovalRequest = useStatewireApprovalRequest.useStatewireApprovalRequest;
32
16
  exports.StatewireQueuedMessages = StatewireQueuedMessages.StatewireQueuedMessages;
@@ -1 +1 @@
1
- {"version":3,"file":"statewire.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"statewire.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Published as the `@athenaintel/react/chat-ui/statewire` sub-path.
10
10
  */
11
- export { StatewireApprovalCard, type StatewireApprovalCardProps, type StatewireApprovalRequestContext, } from "../chat/StatewireApprovalCard";
11
+ export { StatewireApprovalCard, type StatewireApprovalCardProps, type StatewireApprovalEditorContext, type StatewireApprovalRequestContext, } from "../chat/StatewireApprovalCard";
12
12
  export { type StatewireApprovalRequest, type StatewireInterruptPredicate, type UseStatewireApprovalRequestOptions, useStatewireApprovalRequest, } from "../chat/use-statewire-approval-request";
13
13
  export { StatewireQueuedMessages, type StatewireQueueEditorContext, type StatewireQueueEvent, type StatewireQueueRowContentContext, type StatewireQueuedMessagesProps, } from "../chat/StatewireQueuedMessages";
14
14
  export { HistoryEdgeRow, type HistoryEdgeRowProps, StatewireHistoryEdge, type StatewireHistoryEdgeProps, } from "./thread/statewire-history-edge";
@@ -8,23 +8,7 @@ import { RuntimeMessageCard } from "./messages/runtime-message.js";
8
8
  import { WakeMarkerCard, WakeMarkerMessage } from "./messages/wake-marker.js";
9
9
  import { WaitingOnYouBanner, WaitingOnYouBannerView } from "../chat/WaitingOnYouBanner.js";
10
10
  import { StatewireContinueRunAffordance, StatewireContinueRunButton, StatewireRunEndedFooter } from "./messages/statewire-continue-run.js";
11
- import "../packages/agent-runtime/src/generated/runconfig-contract.js";
12
- import "react";
13
- import "@assistant-ui/tap";
14
- import "statewire";
15
- import "../packages/agent-runtime/src/statewire-suspended.js";
16
- import "../packages/agent-runtime/src/conversation-list.js";
17
- import "@tanstack/react-query";
18
11
  import { HITL_PENDING_RESUME_GRACE_MS } from "../packages/agent-runtime/src/interrupts.js";
19
- import "../packages/agent-runtime/src/messages.js";
20
- import "../packages/agent-runtime/src/statewire-state.js";
21
- import "@assistant-ui/store";
22
- import "../packages/agent-runtime/src/quota-safe-local-storage-session.js";
23
- import "../packages/agent-runtime/src/statewire-attach-observer.js";
24
- import "../packages/agent-runtime/src/thread-status.js";
25
- import "@assistant-ui/react-statewire";
26
- import "@assistant-ui/core";
27
- import "../packages/agent-runtime/src/thread-handoff.js";
28
12
  export {
29
13
  ContinuePill,
30
14
  HITL_PENDING_RESUME_GRACE_MS,
@@ -1 +1 @@
1
- {"version":3,"file":"statewire.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"statewire.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;"}
@@ -2,25 +2,10 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const react = require("@assistant-ui/react");
5
- require("../../packages/agent-runtime/src/generated/runconfig-contract.cjs");
6
5
  const react$1 = require("react");
7
- require("@assistant-ui/tap");
8
- require("statewire");
9
- const errors = require("../../packages/agent-runtime/src/errors.cjs");
10
- require("../../packages/agent-runtime/src/statewire-suspended.cjs");
11
- require("../../packages/agent-runtime/src/conversation-list.cjs");
12
- require("@tanstack/react-query");
13
- require("../../packages/agent-runtime/src/statewire-state.cjs");
14
- require("../../packages/agent-runtime/src/messages.cjs");
15
- const useDeepAgentWindowSync = require("../../packages/agent-runtime/src/use-deep-agent-window-sync.cjs");
16
- require("@assistant-ui/store");
17
- require("../../packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
18
- require("../../packages/agent-runtime/src/statewire-attach-observer.cjs");
19
- require("../../packages/agent-runtime/src/thread-status.cjs");
20
- require("@assistant-ui/react-statewire");
21
- require("@assistant-ui/core");
22
- require("../../packages/agent-runtime/src/thread-handoff.cjs");
23
6
  const statewireWindowContext = require("../../provider/statewire-window-context.cjs");
7
+ const useDeepAgentWindowSync = require("../../packages/agent-runtime/src/use-deep-agent-window-sync.cjs");
8
+ const errors = require("../../packages/agent-runtime/src/errors.cjs");
24
9
  const cnfast = require("cnfast");
25
10
  const THREAD_VIEWPORT_ATTR = "data-aui-thread-viewport";
26
11
  const HISTORY_EDGE_NEAR_MARGIN_PX = 1500;
@@ -1 +1 @@
1
- {"version":3,"file":"statewire-history-edge.cjs","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":["jsx","cn","useStatewireToolOutputs","useAuiState","useState","useRef","useDeepAgentWindowSync","isStatewireChannelTeardown","useEffect","useLayoutEffect"],"mappings":"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"}
1
+ {"version":3,"file":"statewire-history-edge.cjs","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":["jsx","cn","useStatewireToolOutputs","useAuiState","useState","useRef","useDeepAgentWindowSync","isStatewireChannelTeardown","useEffect","useLayoutEffect"],"mappings":"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"}
@@ -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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"}
@@ -34,11 +34,12 @@ const DictationStrip = ({
34
34
  transcript,
35
35
  stream,
36
36
  bars = 48,
37
+ stoppingLabel = "Finishing…",
37
38
  className
38
39
  }) => {
39
40
  const history = audioLevelHistory.useAudioLevelHistory(stream, { size: bars });
40
41
  const caption = [transcript.final, transcript.interim].filter(Boolean).join(" ");
41
- const statusLabel = status === "starting" ? "Starting microphone…" : status === "stopping" ? "Finishing…" : "Listening…";
42
+ const statusLabel = status === "starting" ? "Starting microphone…" : status === "stopping" ? stoppingLabel : "Listening…";
42
43
  return /* @__PURE__ */ jsxRuntime.jsxs(
43
44
  "div",
44
45
  {
@@ -1 +1 @@
1
- {"version":3,"file":"DictationStrip.cjs","sources":["../../src/dictation/DictationStrip.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { cn } from '../lib/utils';\nimport { type AudioLevelHistory, useAudioLevelHistory } from './audio-level-history';\nimport type { DictationStatus, DictationTranscript } from './dictation-state';\n\nexport interface DictationStripProps {\n status: DictationStatus;\n transcript: DictationTranscript;\n /** Microphone stream to visualize; null draws the resting baseline. */\n stream: MediaStream | null;\n /** Number of bars across the strip. Defaults to 48. */\n bars?: number;\n className?: string;\n}\n\nconst MIN_BAR_HEIGHT_PX = 3;\nconst MAX_BAR_HEIGHT_PX = 24;\n\nfunction barHeight(level: number): number {\n return Math.round(MIN_BAR_HEIGHT_PX + level * (MAX_BAR_HEIGHT_PX - MIN_BAR_HEIGHT_PX));\n}\n\n/**\n * The bars. A rolling level history renders newest-right: unfilled slots are\n * the dotted baseline, filled slots grow with the microphone level.\n */\nexport const DictationWaveform: FC<{ history: AudioLevelHistory; className?: string }> = ({\n history,\n className,\n}) => (\n <div\n className={cn('aui-dictation-waveform flex h-6 items-center gap-[3px]', className)}\n aria-hidden=\"true\"\n >\n {history.map((level, index) => (\n <span\n // Slots are positional; the history is a fixed-size window.\n key={index}\n className={cn(\n 'aui-dictation-bar w-[3px] shrink-0 rounded-full transition-[height] duration-75',\n level === null ? 'bg-muted-foreground/30' : 'bg-foreground/80',\n )}\n style={{ height: level === null ? MIN_BAR_HEIGHT_PX : barHeight(level) }}\n />\n ))}\n </div>\n);\n\n/**\n * The composer's recording mode: a live waveform in place of the text editor\n * and the interim transcript as a caption under it.\n */\nexport const DictationStrip: FC<DictationStripProps> = ({\n status,\n transcript,\n stream,\n bars = 48,\n className,\n}) => {\n const history = useAudioLevelHistory(stream, { size: bars });\n const caption = [transcript.final, transcript.interim].filter(Boolean).join(' ');\n const statusLabel =\n status === 'starting' ? 'Starting microphone…' : status === 'stopping' ? 'Finishing…' : 'Listening…';\n\n return (\n <div\n className={cn('aui-dictation-strip flex min-h-10 flex-col gap-1 px-1.5 py-2', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"aui-dictation-status text-muted-foreground shrink-0 text-xs\">{statusLabel}</span>\n <DictationWaveform history={history} className=\"min-w-0 flex-1 justify-end overflow-hidden\" />\n </div>\n {caption && (\n <p className=\"aui-dictation-caption text-muted-foreground line-clamp-2 text-sm\">{caption}</p>\n )}\n </div>\n );\n};\n"],"names":["jsx","cn","useAudioLevelHistory","jsxs"],"mappings":";;;;;AAeA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAE1B,SAAS,UAAU,OAAuB;AACxC,SAAO,KAAK,MAAM,oBAAoB,SAAS,oBAAoB,kBAAkB;AACvF;AAMO,MAAM,oBAA4E,CAAC;AAAA,EACxF;AAAA,EACA;AACF,MACEA,2BAAAA;AAAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAWC,OAAAA,GAAG,0DAA0D,SAAS;AAAA,IACjF,eAAY;AAAA,IAEX,UAAA,QAAQ,IAAI,CAAC,OAAO,UACnBD,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QAGC,WAAWC,OAAAA;AAAAA,UACT;AAAA,UACA,UAAU,OAAO,2BAA2B;AAAA,QAAA;AAAA,QAE9C,OAAO,EAAE,QAAQ,UAAU,OAAO,oBAAoB,UAAU,KAAK,EAAA;AAAA,MAAE;AAAA,MALlE;AAAA,IAAA,CAOR;AAAA,EAAA;AACH;AAOK,MAAM,iBAA0C,CAAC;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AACF,MAAM;AACJ,QAAM,UAAUC,kBAAAA,qBAAqB,QAAQ,EAAE,MAAM,MAAM;AAC3D,QAAM,UAAU,CAAC,WAAW,OAAO,WAAW,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC/E,QAAM,cACJ,WAAW,aAAa,yBAAyB,WAAW,aAAa,eAAe;AAE1F,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWF,OAAAA,GAAG,gEAAgE,SAAS;AAAA,MACvF,MAAK;AAAA,MACL,aAAU;AAAA,MAEV,UAAA;AAAA,QAAAE,2BAAAA,KAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,UAAAH,2BAAAA,IAAC,QAAA,EAAK,WAAU,+DAA+D,UAAA,aAAY;AAAA,UAC3FA,2BAAAA,IAAC,mBAAA,EAAkB,SAAkB,WAAU,6CAAA,CAA6C;AAAA,QAAA,GAC9F;AAAA,QACC,WACCA,2BAAAA,IAAC,KAAA,EAAE,WAAU,oEAAoE,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIjG;;;"}
1
+ {"version":3,"file":"DictationStrip.cjs","sources":["../../src/dictation/DictationStrip.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { cn } from '../lib/utils';\nimport { type AudioLevelHistory, useAudioLevelHistory } from './audio-level-history';\nimport type { DictationStatus, DictationTranscript } from './dictation-state';\n\nexport interface DictationStripProps {\n status: DictationStatus;\n transcript: DictationTranscript;\n /** Microphone stream to visualize; null draws the resting baseline. */\n stream: MediaStream | null;\n /** Number of bars across the strip. Defaults to 48. */\n bars?: number;\n /** Status while the engine flushes. Defaults to `Finishing…`. */\n stoppingLabel?: string;\n className?: string;\n}\n\nconst MIN_BAR_HEIGHT_PX = 3;\nconst MAX_BAR_HEIGHT_PX = 24;\n\nfunction barHeight(level: number): number {\n return Math.round(MIN_BAR_HEIGHT_PX + level * (MAX_BAR_HEIGHT_PX - MIN_BAR_HEIGHT_PX));\n}\n\n/**\n * The bars. A rolling level history renders newest-right: unfilled slots are\n * the dotted baseline, filled slots grow with the microphone level.\n */\nexport const DictationWaveform: FC<{ history: AudioLevelHistory; className?: string }> = ({\n history,\n className,\n}) => (\n <div\n className={cn('aui-dictation-waveform flex h-6 items-center gap-[3px]', className)}\n aria-hidden=\"true\"\n >\n {history.map((level, index) => (\n <span\n // Slots are positional; the history is a fixed-size window.\n key={index}\n className={cn(\n 'aui-dictation-bar w-[3px] shrink-0 rounded-full transition-[height] duration-75',\n level === null ? 'bg-muted-foreground/30' : 'bg-foreground/80',\n )}\n style={{ height: level === null ? MIN_BAR_HEIGHT_PX : barHeight(level) }}\n />\n ))}\n </div>\n);\n\n/**\n * The composer's recording mode: a live waveform in place of the text editor\n * and the interim transcript as a caption under it.\n */\nexport const DictationStrip: FC<DictationStripProps> = ({\n status,\n transcript,\n stream,\n bars = 48,\n stoppingLabel = 'Finishing…',\n className,\n}) => {\n const history = useAudioLevelHistory(stream, { size: bars });\n const caption = [transcript.final, transcript.interim].filter(Boolean).join(' ');\n const statusLabel =\n status === 'starting' ? 'Starting microphone…' : status === 'stopping' ? stoppingLabel : 'Listening…';\n\n return (\n <div\n className={cn('aui-dictation-strip flex min-h-10 flex-col gap-1 px-1.5 py-2', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"aui-dictation-status text-muted-foreground shrink-0 text-xs\">{statusLabel}</span>\n <DictationWaveform history={history} className=\"min-w-0 flex-1 justify-end overflow-hidden\" />\n </div>\n {caption && (\n <p className=\"aui-dictation-caption text-muted-foreground line-clamp-2 text-sm\">{caption}</p>\n )}\n </div>\n );\n};\n"],"names":["jsx","cn","useAudioLevelHistory","jsxs"],"mappings":";;;;;AAiBA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAE1B,SAAS,UAAU,OAAuB;AACxC,SAAO,KAAK,MAAM,oBAAoB,SAAS,oBAAoB,kBAAkB;AACvF;AAMO,MAAM,oBAA4E,CAAC;AAAA,EACxF;AAAA,EACA;AACF,MACEA,2BAAAA;AAAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAWC,OAAAA,GAAG,0DAA0D,SAAS;AAAA,IACjF,eAAY;AAAA,IAEX,UAAA,QAAQ,IAAI,CAAC,OAAO,UACnBD,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QAGC,WAAWC,OAAAA;AAAAA,UACT;AAAA,UACA,UAAU,OAAO,2BAA2B;AAAA,QAAA;AAAA,QAE9C,OAAO,EAAE,QAAQ,UAAU,OAAO,oBAAoB,UAAU,KAAK,EAAA;AAAA,MAAE;AAAA,MALlE;AAAA,IAAA,CAOR;AAAA,EAAA;AACH;AAOK,MAAM,iBAA0C,CAAC;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB;AACF,MAAM;AACJ,QAAM,UAAUC,kBAAAA,qBAAqB,QAAQ,EAAE,MAAM,MAAM;AAC3D,QAAM,UAAU,CAAC,WAAW,OAAO,WAAW,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC/E,QAAM,cACJ,WAAW,aAAa,yBAAyB,WAAW,aAAa,gBAAgB;AAE3F,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWF,OAAAA,GAAG,gEAAgE,SAAS;AAAA,MACvF,MAAK;AAAA,MACL,aAAU;AAAA,MAEV,UAAA;AAAA,QAAAE,2BAAAA,KAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,UAAAH,2BAAAA,IAAC,QAAA,EAAK,WAAU,+DAA+D,UAAA,aAAY;AAAA,UAC3FA,2BAAAA,IAAC,mBAAA,EAAkB,SAAkB,WAAU,6CAAA,CAA6C;AAAA,QAAA,GAC9F;AAAA,QACC,WACCA,2BAAAA,IAAC,KAAA,EAAE,WAAU,oEAAoE,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIjG;;;"}
@@ -8,6 +8,8 @@ export interface DictationStripProps {
8
8
  stream: MediaStream | null;
9
9
  /** Number of bars across the strip. Defaults to 48. */
10
10
  bars?: number;
11
+ /** Status while the engine flushes. Defaults to `Finishing…`. */
12
+ stoppingLabel?: string;
11
13
  className?: string;
12
14
  }
13
15
  /**
@@ -32,11 +32,12 @@ const DictationStrip = ({
32
32
  transcript,
33
33
  stream,
34
34
  bars = 48,
35
+ stoppingLabel = "Finishing…",
35
36
  className
36
37
  }) => {
37
38
  const history = useAudioLevelHistory(stream, { size: bars });
38
39
  const caption = [transcript.final, transcript.interim].filter(Boolean).join(" ");
39
- const statusLabel = status === "starting" ? "Starting microphone…" : status === "stopping" ? "Finishing…" : "Listening…";
40
+ const statusLabel = status === "starting" ? "Starting microphone…" : status === "stopping" ? stoppingLabel : "Listening…";
40
41
  return /* @__PURE__ */ jsxs(
41
42
  "div",
42
43
  {
@@ -1 +1 @@
1
- {"version":3,"file":"DictationStrip.js","sources":["../../src/dictation/DictationStrip.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { cn } from '../lib/utils';\nimport { type AudioLevelHistory, useAudioLevelHistory } from './audio-level-history';\nimport type { DictationStatus, DictationTranscript } from './dictation-state';\n\nexport interface DictationStripProps {\n status: DictationStatus;\n transcript: DictationTranscript;\n /** Microphone stream to visualize; null draws the resting baseline. */\n stream: MediaStream | null;\n /** Number of bars across the strip. Defaults to 48. */\n bars?: number;\n className?: string;\n}\n\nconst MIN_BAR_HEIGHT_PX = 3;\nconst MAX_BAR_HEIGHT_PX = 24;\n\nfunction barHeight(level: number): number {\n return Math.round(MIN_BAR_HEIGHT_PX + level * (MAX_BAR_HEIGHT_PX - MIN_BAR_HEIGHT_PX));\n}\n\n/**\n * The bars. A rolling level history renders newest-right: unfilled slots are\n * the dotted baseline, filled slots grow with the microphone level.\n */\nexport const DictationWaveform: FC<{ history: AudioLevelHistory; className?: string }> = ({\n history,\n className,\n}) => (\n <div\n className={cn('aui-dictation-waveform flex h-6 items-center gap-[3px]', className)}\n aria-hidden=\"true\"\n >\n {history.map((level, index) => (\n <span\n // Slots are positional; the history is a fixed-size window.\n key={index}\n className={cn(\n 'aui-dictation-bar w-[3px] shrink-0 rounded-full transition-[height] duration-75',\n level === null ? 'bg-muted-foreground/30' : 'bg-foreground/80',\n )}\n style={{ height: level === null ? MIN_BAR_HEIGHT_PX : barHeight(level) }}\n />\n ))}\n </div>\n);\n\n/**\n * The composer's recording mode: a live waveform in place of the text editor\n * and the interim transcript as a caption under it.\n */\nexport const DictationStrip: FC<DictationStripProps> = ({\n status,\n transcript,\n stream,\n bars = 48,\n className,\n}) => {\n const history = useAudioLevelHistory(stream, { size: bars });\n const caption = [transcript.final, transcript.interim].filter(Boolean).join(' ');\n const statusLabel =\n status === 'starting' ? 'Starting microphone…' : status === 'stopping' ? 'Finishing…' : 'Listening…';\n\n return (\n <div\n className={cn('aui-dictation-strip flex min-h-10 flex-col gap-1 px-1.5 py-2', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"aui-dictation-status text-muted-foreground shrink-0 text-xs\">{statusLabel}</span>\n <DictationWaveform history={history} className=\"min-w-0 flex-1 justify-end overflow-hidden\" />\n </div>\n {caption && (\n <p className=\"aui-dictation-caption text-muted-foreground line-clamp-2 text-sm\">{caption}</p>\n )}\n </div>\n );\n};\n"],"names":[],"mappings":";;;AAeA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAE1B,SAAS,UAAU,OAAuB;AACxC,SAAO,KAAK,MAAM,oBAAoB,SAAS,oBAAoB,kBAAkB;AACvF;AAMO,MAAM,oBAA4E,CAAC;AAAA,EACxF;AAAA,EACA;AACF,MACE;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAW,GAAG,0DAA0D,SAAS;AAAA,IACjF,eAAY;AAAA,IAEX,UAAA,QAAQ,IAAI,CAAC,OAAO,UACnB;AAAA,MAAC;AAAA,MAAA;AAAA,QAGC,WAAW;AAAA,UACT;AAAA,UACA,UAAU,OAAO,2BAA2B;AAAA,QAAA;AAAA,QAE9C,OAAO,EAAE,QAAQ,UAAU,OAAO,oBAAoB,UAAU,KAAK,EAAA;AAAA,MAAE;AAAA,MALlE;AAAA,IAAA,CAOR;AAAA,EAAA;AACH;AAOK,MAAM,iBAA0C,CAAC;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AACF,MAAM;AACJ,QAAM,UAAU,qBAAqB,QAAQ,EAAE,MAAM,MAAM;AAC3D,QAAM,UAAU,CAAC,WAAW,OAAO,WAAW,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC/E,QAAM,cACJ,WAAW,aAAa,yBAAyB,WAAW,aAAa,eAAe;AAE1F,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,GAAG,gEAAgE,SAAS;AAAA,MACvF,MAAK;AAAA,MACL,aAAU;AAAA,MAEV,UAAA;AAAA,QAAA,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,+DAA+D,UAAA,aAAY;AAAA,UAC3F,oBAAC,mBAAA,EAAkB,SAAkB,WAAU,6CAAA,CAA6C;AAAA,QAAA,GAC9F;AAAA,QACC,WACC,oBAAC,KAAA,EAAE,WAAU,oEAAoE,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIjG;"}
1
+ {"version":3,"file":"DictationStrip.js","sources":["../../src/dictation/DictationStrip.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { cn } from '../lib/utils';\nimport { type AudioLevelHistory, useAudioLevelHistory } from './audio-level-history';\nimport type { DictationStatus, DictationTranscript } from './dictation-state';\n\nexport interface DictationStripProps {\n status: DictationStatus;\n transcript: DictationTranscript;\n /** Microphone stream to visualize; null draws the resting baseline. */\n stream: MediaStream | null;\n /** Number of bars across the strip. Defaults to 48. */\n bars?: number;\n /** Status while the engine flushes. Defaults to `Finishing…`. */\n stoppingLabel?: string;\n className?: string;\n}\n\nconst MIN_BAR_HEIGHT_PX = 3;\nconst MAX_BAR_HEIGHT_PX = 24;\n\nfunction barHeight(level: number): number {\n return Math.round(MIN_BAR_HEIGHT_PX + level * (MAX_BAR_HEIGHT_PX - MIN_BAR_HEIGHT_PX));\n}\n\n/**\n * The bars. A rolling level history renders newest-right: unfilled slots are\n * the dotted baseline, filled slots grow with the microphone level.\n */\nexport const DictationWaveform: FC<{ history: AudioLevelHistory; className?: string }> = ({\n history,\n className,\n}) => (\n <div\n className={cn('aui-dictation-waveform flex h-6 items-center gap-[3px]', className)}\n aria-hidden=\"true\"\n >\n {history.map((level, index) => (\n <span\n // Slots are positional; the history is a fixed-size window.\n key={index}\n className={cn(\n 'aui-dictation-bar w-[3px] shrink-0 rounded-full transition-[height] duration-75',\n level === null ? 'bg-muted-foreground/30' : 'bg-foreground/80',\n )}\n style={{ height: level === null ? MIN_BAR_HEIGHT_PX : barHeight(level) }}\n />\n ))}\n </div>\n);\n\n/**\n * The composer's recording mode: a live waveform in place of the text editor\n * and the interim transcript as a caption under it.\n */\nexport const DictationStrip: FC<DictationStripProps> = ({\n status,\n transcript,\n stream,\n bars = 48,\n stoppingLabel = 'Finishing…',\n className,\n}) => {\n const history = useAudioLevelHistory(stream, { size: bars });\n const caption = [transcript.final, transcript.interim].filter(Boolean).join(' ');\n const statusLabel =\n status === 'starting' ? 'Starting microphone…' : status === 'stopping' ? stoppingLabel : 'Listening…';\n\n return (\n <div\n className={cn('aui-dictation-strip flex min-h-10 flex-col gap-1 px-1.5 py-2', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"aui-dictation-status text-muted-foreground shrink-0 text-xs\">{statusLabel}</span>\n <DictationWaveform history={history} className=\"min-w-0 flex-1 justify-end overflow-hidden\" />\n </div>\n {caption && (\n <p className=\"aui-dictation-caption text-muted-foreground line-clamp-2 text-sm\">{caption}</p>\n )}\n </div>\n );\n};\n"],"names":[],"mappings":";;;AAiBA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAE1B,SAAS,UAAU,OAAuB;AACxC,SAAO,KAAK,MAAM,oBAAoB,SAAS,oBAAoB,kBAAkB;AACvF;AAMO,MAAM,oBAA4E,CAAC;AAAA,EACxF;AAAA,EACA;AACF,MACE;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAW,GAAG,0DAA0D,SAAS;AAAA,IACjF,eAAY;AAAA,IAEX,UAAA,QAAQ,IAAI,CAAC,OAAO,UACnB;AAAA,MAAC;AAAA,MAAA;AAAA,QAGC,WAAW;AAAA,UACT;AAAA,UACA,UAAU,OAAO,2BAA2B;AAAA,QAAA;AAAA,QAE9C,OAAO,EAAE,QAAQ,UAAU,OAAO,oBAAoB,UAAU,KAAK,EAAA;AAAA,MAAE;AAAA,MALlE;AAAA,IAAA,CAOR;AAAA,EAAA;AACH;AAOK,MAAM,iBAA0C,CAAC;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB;AACF,MAAM;AACJ,QAAM,UAAU,qBAAqB,QAAQ,EAAE,MAAM,MAAM;AAC3D,QAAM,UAAU,CAAC,WAAW,OAAO,WAAW,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC/E,QAAM,cACJ,WAAW,aAAa,yBAAyB,WAAW,aAAa,gBAAgB;AAE3F,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,GAAG,gEAAgE,SAAS;AAAA,MACvF,MAAK;AAAA,MACL,aAAU;AAAA,MAEV,UAAA;AAAA,QAAA,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,+DAA+D,UAAA,aAAY;AAAA,UAC3F,oBAAC,mBAAA,EAAkB,SAAkB,WAAU,6CAAA,CAA6C;AAAA,QAAA,GAC9F;AAAA,QACC,WACC,oBAAC,KAAA,EAAE,WAAU,oEAAoE,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIjG;"}
@@ -1 +1 @@
1
- {"version":3,"file":"dictation-engine.cjs","sources":["../../src/dictation/dictation-engine.ts"],"sourcesContent":["/**\n * Speech-to-text engines behind the composer's dictation mode.\n *\n * An engine turns microphone audio into a stream of transcript updates. The\n * SDK ships the browser's own `SpeechRecognition` engine (Chrome, Edge,\n * Safari), which needs no backend. A host can plug in another engine (for\n * example a relay to a server-side recognizer) through the `engine` option\n * of `useDictation` / `<AthenaChat dictation>`; the composer UI is engine\n * agnostic.\n */\n\nexport interface DictationTranscriptUpdate {\n /** Text the engine has committed. Appended to earlier committed text. */\n final: string;\n /** Text still being recognized; replaced on the next update. */\n interim: string;\n}\n\nexport interface DictationEngineCallbacks {\n onTranscript: (update: DictationTranscriptUpdate) => void;\n /** The engine stopped on its own (silence timeout, network, permission). */\n onEnd: () => void;\n onError: (error: DictationEngineError) => void;\n}\n\nexport interface DictationEngineSession {\n /** Ask the engine to finish. `onEnd` fires once it has flushed. */\n stop: () => void;\n /** Tear the engine down without waiting for a flush. */\n abort: () => void;\n}\n\nexport interface DictationEngine {\n /** Stable identifier surfaced in diagnostics (e.g. `web-speech`). */\n readonly id: string;\n /**\n * Whether this engine can run in the current browser. When false the\n * composer hides the mic instead of rendering a control that cannot work.\n */\n isSupported: () => boolean;\n /**\n * Start recognizing. `stream` is the microphone stream the composer already\n * opened for the waveform; engines that read audio themselves (Web Speech)\n * ignore it, relay engines encode it.\n */\n start: (options: {\n stream: MediaStream | null;\n lang: string;\n callbacks: DictationEngineCallbacks;\n }) => DictationEngineSession;\n}\n\nexport const DICTATION_ENGINE_ERROR_KINDS = {\n /** The user denied the microphone, or the browser blocked it. */\n permission: 'permission',\n /** No speech engine is available in this browser. */\n unsupported: 'unsupported',\n /** The engine could not reach its recognizer. */\n connection: 'connection',\n /** The engine heard nothing for its silence window. */\n noSpeech: 'no-speech',\n unknown: 'unknown',\n} as const;\n\nexport type DictationEngineErrorKind =\n (typeof DICTATION_ENGINE_ERROR_KINDS)[keyof typeof DICTATION_ENGINE_ERROR_KINDS];\n\nexport interface DictationEngineError {\n kind: DictationEngineErrorKind;\n /** One line the composer can show the user. */\n message: string;\n}\n\n// ─── Web Speech API engine ──────────────────────────────────────────\n\n// The Web Speech API has no lib.dom entry in every TypeScript version the SDK\n// supports, and browsers still vendor-prefix the constructor.\ninterface SpeechRecognitionAlternativeLike {\n transcript: string;\n}\ninterface SpeechRecognitionResultLike {\n isFinal: boolean;\n readonly length: number;\n [index: number]: SpeechRecognitionAlternativeLike;\n}\ninterface SpeechRecognitionEventLike extends Event {\n resultIndex: number;\n results: ArrayLike<SpeechRecognitionResultLike>;\n}\ninterface SpeechRecognitionErrorEventLike extends Event {\n error: string;\n message?: string;\n}\ninterface SpeechRecognitionLike extends EventTarget {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n start: () => void;\n stop: () => void;\n abort: () => void;\n onresult: ((event: SpeechRecognitionEventLike) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEventLike) => void) | null;\n onend: (() => void) | null;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\nfunction resolveSpeechRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === 'undefined') return null;\n const scope = window as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n };\n return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;\n}\n\n/** Map a Web Speech `error` code to the engine's error vocabulary. */\nexport function classifyWebSpeechError(code: string): DictationEngineError {\n switch (code) {\n case 'not-allowed':\n case 'service-not-allowed':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'Microphone access was denied. Allow the microphone for this site and try again.',\n };\n case 'network':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: 'Speech recognition could not connect. Check your connection and try again.',\n };\n case 'no-speech':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.noSpeech,\n message: 'No speech was heard.',\n };\n case 'audio-capture':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'No microphone was found.',\n };\n case 'aborted':\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation was interrupted.' };\n default:\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation stopped unexpectedly.' };\n }\n}\n\n/**\n * Fold one Web Speech result event into `{ final, interim }`. Finalized\n * alternatives from `resultIndex` onward are joined with a space; the rest is\n * the interim tail. Pure so it can be tested without a browser.\n */\nexport function foldWebSpeechResults(\n results: ArrayLike<SpeechRecognitionResultLike>,\n resultIndex: number,\n): DictationTranscriptUpdate {\n let final = '';\n let interim = '';\n for (let i = resultIndex; i < results.length; i += 1) {\n const result = results[i];\n const transcript = result?.[0]?.transcript ?? '';\n if (!transcript) continue;\n if (result.isFinal) final = final ? `${final} ${transcript.trim()}` : transcript.trim();\n else interim = interim ? `${interim} ${transcript.trim()}` : transcript.trim();\n }\n return { final, interim };\n}\n\n/**\n * The browser's own recognizer. Zero dependencies and zero backend; Chrome\n * and Edge stream partial results, Safari returns results in chunks, Firefox\n * has no implementation (`isSupported()` is false there).\n */\nexport function createWebSpeechDictationEngine(): DictationEngine {\n return {\n id: 'web-speech',\n isSupported: () => resolveSpeechRecognitionCtor() !== null,\n start: ({ lang, callbacks }) => {\n const Ctor = resolveSpeechRecognitionCtor();\n if (!Ctor) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unsupported,\n message: 'This browser has no speech recognition.',\n });\n callbacks.onEnd();\n return { stop: () => undefined, abort: () => undefined };\n }\n const recognition = new Ctor();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = lang;\n let ended = false;\n const end = () => {\n if (ended) return;\n ended = true;\n recognition.onresult = null;\n recognition.onerror = null;\n recognition.onend = null;\n callbacks.onEnd();\n };\n recognition.onresult = (event) => {\n callbacks.onTranscript(foldWebSpeechResults(event.results, event.resultIndex));\n };\n recognition.onerror = (event) => {\n // `aborted` follows our own abort(); the session already ended.\n if (event.error === 'aborted') return;\n callbacks.onError(classifyWebSpeechError(event.error));\n };\n recognition.onend = end;\n try {\n recognition.start();\n } catch (error) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unknown,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n end();\n }\n return {\n stop: () => {\n try {\n recognition.stop();\n } catch {\n end();\n }\n },\n abort: () => {\n try {\n recognition.abort();\n } finally {\n end();\n }\n },\n };\n },\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dictation-engine.cjs","sources":["../../src/dictation/dictation-engine.ts"],"sourcesContent":["/**\n * Speech-to-text engines behind the composer's dictation mode.\n *\n * An engine turns microphone audio into a stream of transcript updates. The\n * SDK ships the browser's own `SpeechRecognition` engine (Chrome, Edge,\n * Safari), which needs no backend. A host can plug in another engine (for\n * example a relay to a server-side recognizer) through the `engine` option\n * of `useDictation` / `<AthenaChat dictation>`; the composer UI is engine\n * agnostic.\n */\n\nexport interface DictationTranscriptUpdate {\n /** Text the engine has committed. Appended to earlier committed text. */\n final: string;\n /** Text still being recognized; replaced on the next update. */\n interim: string;\n}\n\nexport interface DictationEngineCallbacks {\n onTranscript: (update: DictationTranscriptUpdate) => void;\n /** The engine stopped on its own (silence timeout, network, permission). */\n onEnd: () => void;\n onError: (error: DictationEngineError) => void;\n}\n\nexport interface DictationEngineSession {\n /** Ask the engine to finish. `onEnd` fires once it has flushed. */\n stop: () => void;\n /** Tear the engine down without waiting for a flush. */\n abort: () => void;\n}\n\nexport interface DictationEngine {\n /** Stable identifier surfaced in diagnostics (e.g. `web-speech`). */\n readonly id: string;\n /**\n * Whether this engine can run in the current browser. When false the\n * composer hides the mic instead of rendering a control that cannot work.\n */\n isSupported: () => boolean;\n /**\n * How long `stop()` may wait for the final transcript before the session\n * is forced closed. Engines that transcribe after recording need more than\n * the live default (1.5s).\n */\n readonly flushTimeoutMs?: number;\n /** Status shown while flushing (e.g. `Transcribing…`); defaults to `Finishing…`. */\n readonly stoppingLabel?: string;\n /**\n * Start recognizing. `stream` is the microphone stream the composer already\n * opened for the waveform; engines that read audio themselves (Web Speech)\n * ignore it, relay engines encode it.\n */\n start: (options: {\n stream: MediaStream | null;\n lang: string;\n callbacks: DictationEngineCallbacks;\n }) => DictationEngineSession;\n}\n\nexport const DICTATION_ENGINE_ERROR_KINDS = {\n /** The user denied the microphone, or the browser blocked it. */\n permission: 'permission',\n /** No speech engine is available in this browser. */\n unsupported: 'unsupported',\n /** The engine could not reach its recognizer. */\n connection: 'connection',\n /** The engine heard nothing for its silence window. */\n noSpeech: 'no-speech',\n unknown: 'unknown',\n} as const;\n\nexport type DictationEngineErrorKind =\n (typeof DICTATION_ENGINE_ERROR_KINDS)[keyof typeof DICTATION_ENGINE_ERROR_KINDS];\n\nexport interface DictationEngineError {\n kind: DictationEngineErrorKind;\n /** One line the composer can show the user. */\n message: string;\n}\n\n// ─── Web Speech API engine ──────────────────────────────────────────\n\n// The Web Speech API has no lib.dom entry in every TypeScript version the SDK\n// supports, and browsers still vendor-prefix the constructor.\ninterface SpeechRecognitionAlternativeLike {\n transcript: string;\n}\ninterface SpeechRecognitionResultLike {\n isFinal: boolean;\n readonly length: number;\n [index: number]: SpeechRecognitionAlternativeLike;\n}\ninterface SpeechRecognitionEventLike extends Event {\n resultIndex: number;\n results: ArrayLike<SpeechRecognitionResultLike>;\n}\ninterface SpeechRecognitionErrorEventLike extends Event {\n error: string;\n message?: string;\n}\ninterface SpeechRecognitionLike extends EventTarget {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n start: () => void;\n stop: () => void;\n abort: () => void;\n onresult: ((event: SpeechRecognitionEventLike) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEventLike) => void) | null;\n onend: (() => void) | null;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\nfunction resolveSpeechRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === 'undefined') return null;\n const scope = window as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n };\n return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;\n}\n\n/** Map a Web Speech `error` code to the engine's error vocabulary. */\nexport function classifyWebSpeechError(code: string): DictationEngineError {\n switch (code) {\n case 'not-allowed':\n case 'service-not-allowed':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'Microphone access was denied. Allow the microphone for this site and try again.',\n };\n case 'network':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: 'Speech recognition could not connect. Check your connection and try again.',\n };\n case 'no-speech':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.noSpeech,\n message: 'No speech was heard.',\n };\n case 'audio-capture':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'No microphone was found.',\n };\n case 'aborted':\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation was interrupted.' };\n default:\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation stopped unexpectedly.' };\n }\n}\n\n/**\n * Fold one Web Speech result event into `{ final, interim }`. Finalized\n * alternatives from `resultIndex` onward are joined with a space; the rest is\n * the interim tail. Pure so it can be tested without a browser.\n */\nexport function foldWebSpeechResults(\n results: ArrayLike<SpeechRecognitionResultLike>,\n resultIndex: number,\n): DictationTranscriptUpdate {\n let final = '';\n let interim = '';\n for (let i = resultIndex; i < results.length; i += 1) {\n const result = results[i];\n const transcript = result?.[0]?.transcript ?? '';\n if (!transcript) continue;\n if (result.isFinal) final = final ? `${final} ${transcript.trim()}` : transcript.trim();\n else interim = interim ? `${interim} ${transcript.trim()}` : transcript.trim();\n }\n return { final, interim };\n}\n\n/**\n * The browser's own recognizer. Zero dependencies and zero backend; Chrome\n * and Edge stream partial results, Safari returns results in chunks, Firefox\n * has no implementation (`isSupported()` is false there).\n */\nexport function createWebSpeechDictationEngine(): DictationEngine {\n return {\n id: 'web-speech',\n isSupported: () => resolveSpeechRecognitionCtor() !== null,\n start: ({ lang, callbacks }) => {\n const Ctor = resolveSpeechRecognitionCtor();\n if (!Ctor) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unsupported,\n message: 'This browser has no speech recognition.',\n });\n callbacks.onEnd();\n return { stop: () => undefined, abort: () => undefined };\n }\n const recognition = new Ctor();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = lang;\n let ended = false;\n const end = () => {\n if (ended) return;\n ended = true;\n recognition.onresult = null;\n recognition.onerror = null;\n recognition.onend = null;\n callbacks.onEnd();\n };\n recognition.onresult = (event) => {\n callbacks.onTranscript(foldWebSpeechResults(event.results, event.resultIndex));\n };\n recognition.onerror = (event) => {\n // `aborted` follows our own abort(); the session already ended.\n if (event.error === 'aborted') return;\n callbacks.onError(classifyWebSpeechError(event.error));\n };\n recognition.onend = end;\n try {\n recognition.start();\n } catch (error) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unknown,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n end();\n }\n return {\n stop: () => {\n try {\n recognition.stop();\n } catch {\n end();\n }\n },\n abort: () => {\n try {\n recognition.abort();\n } finally {\n end();\n }\n },\n };\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -34,6 +34,14 @@ export interface DictationEngine {
34
34
  * composer hides the mic instead of rendering a control that cannot work.
35
35
  */
36
36
  isSupported: () => boolean;
37
+ /**
38
+ * How long `stop()` may wait for the final transcript before the session
39
+ * is forced closed. Engines that transcribe after recording need more than
40
+ * the live default (1.5s).
41
+ */
42
+ readonly flushTimeoutMs?: number;
43
+ /** Status shown while flushing (e.g. `Transcribing…`); defaults to `Finishing…`. */
44
+ readonly stoppingLabel?: string;
37
45
  /**
38
46
  * Start recognizing. `stream` is the microphone stream the composer already
39
47
  * opened for the waveform; engines that read audio themselves (Web Speech)
@@ -1 +1 @@
1
- {"version":3,"file":"dictation-engine.js","sources":["../../src/dictation/dictation-engine.ts"],"sourcesContent":["/**\n * Speech-to-text engines behind the composer's dictation mode.\n *\n * An engine turns microphone audio into a stream of transcript updates. The\n * SDK ships the browser's own `SpeechRecognition` engine (Chrome, Edge,\n * Safari), which needs no backend. A host can plug in another engine (for\n * example a relay to a server-side recognizer) through the `engine` option\n * of `useDictation` / `<AthenaChat dictation>`; the composer UI is engine\n * agnostic.\n */\n\nexport interface DictationTranscriptUpdate {\n /** Text the engine has committed. Appended to earlier committed text. */\n final: string;\n /** Text still being recognized; replaced on the next update. */\n interim: string;\n}\n\nexport interface DictationEngineCallbacks {\n onTranscript: (update: DictationTranscriptUpdate) => void;\n /** The engine stopped on its own (silence timeout, network, permission). */\n onEnd: () => void;\n onError: (error: DictationEngineError) => void;\n}\n\nexport interface DictationEngineSession {\n /** Ask the engine to finish. `onEnd` fires once it has flushed. */\n stop: () => void;\n /** Tear the engine down without waiting for a flush. */\n abort: () => void;\n}\n\nexport interface DictationEngine {\n /** Stable identifier surfaced in diagnostics (e.g. `web-speech`). */\n readonly id: string;\n /**\n * Whether this engine can run in the current browser. When false the\n * composer hides the mic instead of rendering a control that cannot work.\n */\n isSupported: () => boolean;\n /**\n * Start recognizing. `stream` is the microphone stream the composer already\n * opened for the waveform; engines that read audio themselves (Web Speech)\n * ignore it, relay engines encode it.\n */\n start: (options: {\n stream: MediaStream | null;\n lang: string;\n callbacks: DictationEngineCallbacks;\n }) => DictationEngineSession;\n}\n\nexport const DICTATION_ENGINE_ERROR_KINDS = {\n /** The user denied the microphone, or the browser blocked it. */\n permission: 'permission',\n /** No speech engine is available in this browser. */\n unsupported: 'unsupported',\n /** The engine could not reach its recognizer. */\n connection: 'connection',\n /** The engine heard nothing for its silence window. */\n noSpeech: 'no-speech',\n unknown: 'unknown',\n} as const;\n\nexport type DictationEngineErrorKind =\n (typeof DICTATION_ENGINE_ERROR_KINDS)[keyof typeof DICTATION_ENGINE_ERROR_KINDS];\n\nexport interface DictationEngineError {\n kind: DictationEngineErrorKind;\n /** One line the composer can show the user. */\n message: string;\n}\n\n// ─── Web Speech API engine ──────────────────────────────────────────\n\n// The Web Speech API has no lib.dom entry in every TypeScript version the SDK\n// supports, and browsers still vendor-prefix the constructor.\ninterface SpeechRecognitionAlternativeLike {\n transcript: string;\n}\ninterface SpeechRecognitionResultLike {\n isFinal: boolean;\n readonly length: number;\n [index: number]: SpeechRecognitionAlternativeLike;\n}\ninterface SpeechRecognitionEventLike extends Event {\n resultIndex: number;\n results: ArrayLike<SpeechRecognitionResultLike>;\n}\ninterface SpeechRecognitionErrorEventLike extends Event {\n error: string;\n message?: string;\n}\ninterface SpeechRecognitionLike extends EventTarget {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n start: () => void;\n stop: () => void;\n abort: () => void;\n onresult: ((event: SpeechRecognitionEventLike) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEventLike) => void) | null;\n onend: (() => void) | null;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\nfunction resolveSpeechRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === 'undefined') return null;\n const scope = window as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n };\n return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;\n}\n\n/** Map a Web Speech `error` code to the engine's error vocabulary. */\nexport function classifyWebSpeechError(code: string): DictationEngineError {\n switch (code) {\n case 'not-allowed':\n case 'service-not-allowed':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'Microphone access was denied. Allow the microphone for this site and try again.',\n };\n case 'network':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: 'Speech recognition could not connect. Check your connection and try again.',\n };\n case 'no-speech':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.noSpeech,\n message: 'No speech was heard.',\n };\n case 'audio-capture':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'No microphone was found.',\n };\n case 'aborted':\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation was interrupted.' };\n default:\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation stopped unexpectedly.' };\n }\n}\n\n/**\n * Fold one Web Speech result event into `{ final, interim }`. Finalized\n * alternatives from `resultIndex` onward are joined with a space; the rest is\n * the interim tail. Pure so it can be tested without a browser.\n */\nexport function foldWebSpeechResults(\n results: ArrayLike<SpeechRecognitionResultLike>,\n resultIndex: number,\n): DictationTranscriptUpdate {\n let final = '';\n let interim = '';\n for (let i = resultIndex; i < results.length; i += 1) {\n const result = results[i];\n const transcript = result?.[0]?.transcript ?? '';\n if (!transcript) continue;\n if (result.isFinal) final = final ? `${final} ${transcript.trim()}` : transcript.trim();\n else interim = interim ? `${interim} ${transcript.trim()}` : transcript.trim();\n }\n return { final, interim };\n}\n\n/**\n * The browser's own recognizer. Zero dependencies and zero backend; Chrome\n * and Edge stream partial results, Safari returns results in chunks, Firefox\n * has no implementation (`isSupported()` is false there).\n */\nexport function createWebSpeechDictationEngine(): DictationEngine {\n return {\n id: 'web-speech',\n isSupported: () => resolveSpeechRecognitionCtor() !== null,\n start: ({ lang, callbacks }) => {\n const Ctor = resolveSpeechRecognitionCtor();\n if (!Ctor) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unsupported,\n message: 'This browser has no speech recognition.',\n });\n callbacks.onEnd();\n return { stop: () => undefined, abort: () => undefined };\n }\n const recognition = new Ctor();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = lang;\n let ended = false;\n const end = () => {\n if (ended) return;\n ended = true;\n recognition.onresult = null;\n recognition.onerror = null;\n recognition.onend = null;\n callbacks.onEnd();\n };\n recognition.onresult = (event) => {\n callbacks.onTranscript(foldWebSpeechResults(event.results, event.resultIndex));\n };\n recognition.onerror = (event) => {\n // `aborted` follows our own abort(); the session already ended.\n if (event.error === 'aborted') return;\n callbacks.onError(classifyWebSpeechError(event.error));\n };\n recognition.onend = end;\n try {\n recognition.start();\n } catch (error) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unknown,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n end();\n }\n return {\n stop: () => {\n try {\n recognition.stop();\n } catch {\n end();\n }\n },\n abort: () => {\n try {\n recognition.abort();\n } finally {\n end();\n }\n },\n };\n },\n };\n}\n"],"names":[],"mappings":"AAoDO,MAAM,+BAA+B;AAAA;AAAA,EAE1C,YAAY;AAAA;AAAA,EAEZ,aAAa;AAAA;AAAA,EAEb,YAAY;AAAA;AAAA,EAEZ,UAAU;AAAA,EACV,SAAS;AACX;AA4CA,SAAS,+BAA6D;AACpE,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,QAAQ;AAId,SAAO,MAAM,qBAAqB,MAAM,2BAA2B;AACrE;AAGO,SAAS,uBAAuB,MAAoC;AACzE,UAAQ,MAAA;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO,EAAE,MAAM,6BAA6B,SAAS,SAAS,6BAAA;AAAA,IAChE;AACE,aAAO,EAAE,MAAM,6BAA6B,SAAS,SAAS,kCAAA;AAAA,EAAkC;AAEtG;AAOO,SAAS,qBACd,SACA,aAC2B;AAtGtB;AAuGL,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,WAAS,IAAI,aAAa,IAAI,QAAQ,QAAQ,KAAK,GAAG;AACpD,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,eAAa,sCAAS,OAAT,mBAAa,eAAc;AAC9C,QAAI,CAAC,WAAY;AACjB,QAAI,OAAO,QAAS,SAAQ,QAAQ,GAAG,KAAK,IAAI,WAAW,KAAA,CAAM,KAAK,WAAW,KAAA;AAAA,QAC5E,WAAU,UAAU,GAAG,OAAO,IAAI,WAAW,MAAM,KAAK,WAAW,KAAA;AAAA,EAC1E;AACA,SAAO,EAAE,OAAO,QAAA;AAClB;AAOO,SAAS,iCAAkD;AAChE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,aAAa,MAAM,6BAAA,MAAmC;AAAA,IACtD,OAAO,CAAC,EAAE,MAAM,gBAAgB;AAC9B,YAAM,OAAO,6BAAA;AACb,UAAI,CAAC,MAAM;AACT,kBAAU,QAAQ;AAAA,UAChB,MAAM,6BAA6B;AAAA,UACnC,SAAS;AAAA,QAAA,CACV;AACD,kBAAU,MAAA;AACV,eAAO,EAAE,MAAM,MAAM,QAAW,OAAO,MAAM,OAAA;AAAA,MAC/C;AACA,YAAM,cAAc,IAAI,KAAA;AACxB,kBAAY,aAAa;AACzB,kBAAY,iBAAiB;AAC7B,kBAAY,OAAO;AACnB,UAAI,QAAQ;AACZ,YAAM,MAAM,MAAM;AAChB,YAAI,MAAO;AACX,gBAAQ;AACR,oBAAY,WAAW;AACvB,oBAAY,UAAU;AACtB,oBAAY,QAAQ;AACpB,kBAAU,MAAA;AAAA,MACZ;AACA,kBAAY,WAAW,CAAC,UAAU;AAChC,kBAAU,aAAa,qBAAqB,MAAM,SAAS,MAAM,WAAW,CAAC;AAAA,MAC/E;AACA,kBAAY,UAAU,CAAC,UAAU;AAE/B,YAAI,MAAM,UAAU,UAAW;AAC/B,kBAAU,QAAQ,uBAAuB,MAAM,KAAK,CAAC;AAAA,MACvD;AACA,kBAAY,QAAQ;AACpB,UAAI;AACF,oBAAY,MAAA;AAAA,MACd,SAAS,OAAO;AACd,kBAAU,QAAQ;AAAA,UAChB,MAAM,6BAA6B;AAAA,UACnC,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAAA,CACnD;AACD,YAAA;AAAA,MACF;AACA,aAAO;AAAA,QACL,MAAM,MAAM;AACV,cAAI;AACF,wBAAY,KAAA;AAAA,UACd,QAAQ;AACN,gBAAA;AAAA,UACF;AAAA,QACF;AAAA,QACA,OAAO,MAAM;AACX,cAAI;AACF,wBAAY,MAAA;AAAA,UACd,UAAA;AACE,gBAAA;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"dictation-engine.js","sources":["../../src/dictation/dictation-engine.ts"],"sourcesContent":["/**\n * Speech-to-text engines behind the composer's dictation mode.\n *\n * An engine turns microphone audio into a stream of transcript updates. The\n * SDK ships the browser's own `SpeechRecognition` engine (Chrome, Edge,\n * Safari), which needs no backend. A host can plug in another engine (for\n * example a relay to a server-side recognizer) through the `engine` option\n * of `useDictation` / `<AthenaChat dictation>`; the composer UI is engine\n * agnostic.\n */\n\nexport interface DictationTranscriptUpdate {\n /** Text the engine has committed. Appended to earlier committed text. */\n final: string;\n /** Text still being recognized; replaced on the next update. */\n interim: string;\n}\n\nexport interface DictationEngineCallbacks {\n onTranscript: (update: DictationTranscriptUpdate) => void;\n /** The engine stopped on its own (silence timeout, network, permission). */\n onEnd: () => void;\n onError: (error: DictationEngineError) => void;\n}\n\nexport interface DictationEngineSession {\n /** Ask the engine to finish. `onEnd` fires once it has flushed. */\n stop: () => void;\n /** Tear the engine down without waiting for a flush. */\n abort: () => void;\n}\n\nexport interface DictationEngine {\n /** Stable identifier surfaced in diagnostics (e.g. `web-speech`). */\n readonly id: string;\n /**\n * Whether this engine can run in the current browser. When false the\n * composer hides the mic instead of rendering a control that cannot work.\n */\n isSupported: () => boolean;\n /**\n * How long `stop()` may wait for the final transcript before the session\n * is forced closed. Engines that transcribe after recording need more than\n * the live default (1.5s).\n */\n readonly flushTimeoutMs?: number;\n /** Status shown while flushing (e.g. `Transcribing…`); defaults to `Finishing…`. */\n readonly stoppingLabel?: string;\n /**\n * Start recognizing. `stream` is the microphone stream the composer already\n * opened for the waveform; engines that read audio themselves (Web Speech)\n * ignore it, relay engines encode it.\n */\n start: (options: {\n stream: MediaStream | null;\n lang: string;\n callbacks: DictationEngineCallbacks;\n }) => DictationEngineSession;\n}\n\nexport const DICTATION_ENGINE_ERROR_KINDS = {\n /** The user denied the microphone, or the browser blocked it. */\n permission: 'permission',\n /** No speech engine is available in this browser. */\n unsupported: 'unsupported',\n /** The engine could not reach its recognizer. */\n connection: 'connection',\n /** The engine heard nothing for its silence window. */\n noSpeech: 'no-speech',\n unknown: 'unknown',\n} as const;\n\nexport type DictationEngineErrorKind =\n (typeof DICTATION_ENGINE_ERROR_KINDS)[keyof typeof DICTATION_ENGINE_ERROR_KINDS];\n\nexport interface DictationEngineError {\n kind: DictationEngineErrorKind;\n /** One line the composer can show the user. */\n message: string;\n}\n\n// ─── Web Speech API engine ──────────────────────────────────────────\n\n// The Web Speech API has no lib.dom entry in every TypeScript version the SDK\n// supports, and browsers still vendor-prefix the constructor.\ninterface SpeechRecognitionAlternativeLike {\n transcript: string;\n}\ninterface SpeechRecognitionResultLike {\n isFinal: boolean;\n readonly length: number;\n [index: number]: SpeechRecognitionAlternativeLike;\n}\ninterface SpeechRecognitionEventLike extends Event {\n resultIndex: number;\n results: ArrayLike<SpeechRecognitionResultLike>;\n}\ninterface SpeechRecognitionErrorEventLike extends Event {\n error: string;\n message?: string;\n}\ninterface SpeechRecognitionLike extends EventTarget {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n start: () => void;\n stop: () => void;\n abort: () => void;\n onresult: ((event: SpeechRecognitionEventLike) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEventLike) => void) | null;\n onend: (() => void) | null;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\nfunction resolveSpeechRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === 'undefined') return null;\n const scope = window as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n };\n return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;\n}\n\n/** Map a Web Speech `error` code to the engine's error vocabulary. */\nexport function classifyWebSpeechError(code: string): DictationEngineError {\n switch (code) {\n case 'not-allowed':\n case 'service-not-allowed':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'Microphone access was denied. Allow the microphone for this site and try again.',\n };\n case 'network':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: 'Speech recognition could not connect. Check your connection and try again.',\n };\n case 'no-speech':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.noSpeech,\n message: 'No speech was heard.',\n };\n case 'audio-capture':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'No microphone was found.',\n };\n case 'aborted':\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation was interrupted.' };\n default:\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation stopped unexpectedly.' };\n }\n}\n\n/**\n * Fold one Web Speech result event into `{ final, interim }`. Finalized\n * alternatives from `resultIndex` onward are joined with a space; the rest is\n * the interim tail. Pure so it can be tested without a browser.\n */\nexport function foldWebSpeechResults(\n results: ArrayLike<SpeechRecognitionResultLike>,\n resultIndex: number,\n): DictationTranscriptUpdate {\n let final = '';\n let interim = '';\n for (let i = resultIndex; i < results.length; i += 1) {\n const result = results[i];\n const transcript = result?.[0]?.transcript ?? '';\n if (!transcript) continue;\n if (result.isFinal) final = final ? `${final} ${transcript.trim()}` : transcript.trim();\n else interim = interim ? `${interim} ${transcript.trim()}` : transcript.trim();\n }\n return { final, interim };\n}\n\n/**\n * The browser's own recognizer. Zero dependencies and zero backend; Chrome\n * and Edge stream partial results, Safari returns results in chunks, Firefox\n * has no implementation (`isSupported()` is false there).\n */\nexport function createWebSpeechDictationEngine(): DictationEngine {\n return {\n id: 'web-speech',\n isSupported: () => resolveSpeechRecognitionCtor() !== null,\n start: ({ lang, callbacks }) => {\n const Ctor = resolveSpeechRecognitionCtor();\n if (!Ctor) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unsupported,\n message: 'This browser has no speech recognition.',\n });\n callbacks.onEnd();\n return { stop: () => undefined, abort: () => undefined };\n }\n const recognition = new Ctor();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = lang;\n let ended = false;\n const end = () => {\n if (ended) return;\n ended = true;\n recognition.onresult = null;\n recognition.onerror = null;\n recognition.onend = null;\n callbacks.onEnd();\n };\n recognition.onresult = (event) => {\n callbacks.onTranscript(foldWebSpeechResults(event.results, event.resultIndex));\n };\n recognition.onerror = (event) => {\n // `aborted` follows our own abort(); the session already ended.\n if (event.error === 'aborted') return;\n callbacks.onError(classifyWebSpeechError(event.error));\n };\n recognition.onend = end;\n try {\n recognition.start();\n } catch (error) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unknown,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n end();\n }\n return {\n stop: () => {\n try {\n recognition.stop();\n } catch {\n end();\n }\n },\n abort: () => {\n try {\n recognition.abort();\n } finally {\n end();\n }\n },\n };\n },\n };\n}\n"],"names":[],"mappings":"AA4DO,MAAM,+BAA+B;AAAA;AAAA,EAE1C,YAAY;AAAA;AAAA,EAEZ,aAAa;AAAA;AAAA,EAEb,YAAY;AAAA;AAAA,EAEZ,UAAU;AAAA,EACV,SAAS;AACX;AA4CA,SAAS,+BAA6D;AACpE,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,QAAQ;AAId,SAAO,MAAM,qBAAqB,MAAM,2BAA2B;AACrE;AAGO,SAAS,uBAAuB,MAAoC;AACzE,UAAQ,MAAA;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO,EAAE,MAAM,6BAA6B,SAAS,SAAS,6BAAA;AAAA,IAChE;AACE,aAAO,EAAE,MAAM,6BAA6B,SAAS,SAAS,kCAAA;AAAA,EAAkC;AAEtG;AAOO,SAAS,qBACd,SACA,aAC2B;AAtGtB;AAuGL,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,WAAS,IAAI,aAAa,IAAI,QAAQ,QAAQ,KAAK,GAAG;AACpD,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,eAAa,sCAAS,OAAT,mBAAa,eAAc;AAC9C,QAAI,CAAC,WAAY;AACjB,QAAI,OAAO,QAAS,SAAQ,QAAQ,GAAG,KAAK,IAAI,WAAW,KAAA,CAAM,KAAK,WAAW,KAAA;AAAA,QAC5E,WAAU,UAAU,GAAG,OAAO,IAAI,WAAW,MAAM,KAAK,WAAW,KAAA;AAAA,EAC1E;AACA,SAAO,EAAE,OAAO,QAAA;AAClB;AAOO,SAAS,iCAAkD;AAChE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,aAAa,MAAM,6BAAA,MAAmC;AAAA,IACtD,OAAO,CAAC,EAAE,MAAM,gBAAgB;AAC9B,YAAM,OAAO,6BAAA;AACb,UAAI,CAAC,MAAM;AACT,kBAAU,QAAQ;AAAA,UAChB,MAAM,6BAA6B;AAAA,UACnC,SAAS;AAAA,QAAA,CACV;AACD,kBAAU,MAAA;AACV,eAAO,EAAE,MAAM,MAAM,QAAW,OAAO,MAAM,OAAA;AAAA,MAC/C;AACA,YAAM,cAAc,IAAI,KAAA;AACxB,kBAAY,aAAa;AACzB,kBAAY,iBAAiB;AAC7B,kBAAY,OAAO;AACnB,UAAI,QAAQ;AACZ,YAAM,MAAM,MAAM;AAChB,YAAI,MAAO;AACX,gBAAQ;AACR,oBAAY,WAAW;AACvB,oBAAY,UAAU;AACtB,oBAAY,QAAQ;AACpB,kBAAU,MAAA;AAAA,MACZ;AACA,kBAAY,WAAW,CAAC,UAAU;AAChC,kBAAU,aAAa,qBAAqB,MAAM,SAAS,MAAM,WAAW,CAAC;AAAA,MAC/E;AACA,kBAAY,UAAU,CAAC,UAAU;AAE/B,YAAI,MAAM,UAAU,UAAW;AAC/B,kBAAU,QAAQ,uBAAuB,MAAM,KAAK,CAAC;AAAA,MACvD;AACA,kBAAY,QAAQ;AACpB,UAAI;AACF,oBAAY,MAAA;AAAA,MACd,SAAS,OAAO;AACd,kBAAU,QAAQ;AAAA,UAChB,MAAM,6BAA6B;AAAA,UACnC,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAAA,CACnD;AACD,YAAA;AAAA,MACF;AACA,aAAO;AAAA,QACL,MAAM,MAAM;AACV,cAAI;AACF,wBAAY,KAAA;AAAA,UACd,QAAQ;AACN,gBAAA;AAAA,UACF;AAAA,QACF;AAAA,QACA,OAAO,MAAM;AACX,cAAI;AACF,wBAAY,MAAA;AAAA,UACd,UAAA;AACE,gBAAA;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ;AAAA,EAAA;AAEJ;"}