@athenaintel/react 0.23.3 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/README.md +6 -0
  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 +3 -18
  16. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  17. package/dist/chat/StatewireApprovalCard.js +3 -18
  18. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  19. package/dist/chat/StatewireClientToolBridge.cjs +2 -16
  20. package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
  21. package/dist/chat/StatewireClientToolBridge.js +2 -16
  22. package/dist/chat/StatewireClientToolBridge.js.map +1 -1
  23. package/dist/chat/StatewireQueuedMessages.cjs +5 -16
  24. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  25. package/dist/chat/StatewireQueuedMessages.js +5 -16
  26. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  27. package/dist/chat/WaitingOnYouBanner.cjs +3 -18
  28. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
  29. package/dist/chat/WaitingOnYouBanner.js +3 -18
  30. package/dist/chat/WaitingOnYouBanner.js.map +1 -1
  31. package/dist/chat/run-activity.cjs +0 -16
  32. package/dist/chat/run-activity.cjs.map +1 -1
  33. package/dist/chat/run-activity.js +0 -16
  34. package/dist/chat/run-activity.js.map +1 -1
  35. package/dist/chat/statewire-client-tools.cjs +1 -17
  36. package/dist/chat/statewire-client-tools.cjs.map +1 -1
  37. package/dist/chat/statewire-client-tools.js +1 -17
  38. package/dist/chat/statewire-client-tools.js.map +1 -1
  39. package/dist/chat/super-grouping/activity-timeline.cjs +0 -16
  40. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  41. package/dist/chat/super-grouping/activity-timeline.js +0 -16
  42. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  43. package/dist/chat/tiptap-text.cjs +1 -17
  44. package/dist/chat/tiptap-text.cjs.map +1 -1
  45. package/dist/chat/tiptap-text.js +1 -17
  46. package/dist/chat/tiptap-text.js.map +1 -1
  47. package/dist/chat/use-athena-run-live.cjs +0 -15
  48. package/dist/chat/use-athena-run-live.cjs.map +1 -1
  49. package/dist/chat/use-athena-run-live.js +0 -15
  50. package/dist/chat/use-athena-run-live.js.map +1 -1
  51. package/dist/chat/use-statewire-approval-request.cjs +4 -16
  52. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  53. package/dist/chat/use-statewire-approval-request.d.ts +2 -0
  54. package/dist/chat/use-statewire-approval-request.js +4 -16
  55. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  56. package/dist/chat-ui/composer/tiptap-composer.cjs +1 -16
  57. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  58. package/dist/chat-ui/composer/tiptap-composer.js +1 -16
  59. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  60. package/dist/chat-ui/index.cjs +0 -16
  61. package/dist/chat-ui/index.cjs.map +1 -1
  62. package/dist/chat-ui/index.js +0 -16
  63. package/dist/chat-ui/index.js.map +1 -1
  64. package/dist/chat-ui/mentions/mention-chip.cjs +1 -16
  65. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  66. package/dist/chat-ui/mentions/mention-chip.js +1 -16
  67. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  68. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +4 -6
  69. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs.map +1 -1
  70. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js +4 -6
  71. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
  72. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +2 -9
  73. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
  74. package/dist/chat-ui/mentions/suggestions/data/sources/index.js +2 -9
  75. package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
  76. package/dist/chat-ui/messages/message-components.cjs +5 -19
  77. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  78. package/dist/chat-ui/messages/message-components.js +5 -19
  79. package/dist/chat-ui/messages/message-components.js.map +1 -1
  80. package/dist/chat-ui/messages/runtime-message.cjs +1 -16
  81. package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
  82. package/dist/chat-ui/messages/runtime-message.js +1 -16
  83. package/dist/chat-ui/messages/runtime-message.js.map +1 -1
  84. package/dist/chat-ui/messages/statewire-continue-run.cjs +3 -15
  85. package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
  86. package/dist/chat-ui/messages/statewire-continue-run.js +3 -15
  87. package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
  88. package/dist/chat-ui/messages/user-edit-composer.cjs +0 -16
  89. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  90. package/dist/chat-ui/messages/user-edit-composer.js +0 -16
  91. package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
  92. package/dist/chat-ui/messages/wake-marker.cjs +1 -16
  93. package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
  94. package/dist/chat-ui/messages/wake-marker.js +1 -16
  95. package/dist/chat-ui/messages/wake-marker.js.map +1 -1
  96. package/dist/chat-ui/statewire.cjs +0 -16
  97. package/dist/chat-ui/statewire.cjs.map +1 -1
  98. package/dist/chat-ui/statewire.js +0 -16
  99. package/dist/chat-ui/statewire.js.map +1 -1
  100. package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -17
  101. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  102. package/dist/chat-ui/thread/statewire-history-edge.js +2 -17
  103. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  104. package/dist/dictation/DictationStrip.cjs +2 -1
  105. package/dist/dictation/DictationStrip.cjs.map +1 -1
  106. package/dist/dictation/DictationStrip.d.ts +2 -0
  107. package/dist/dictation/DictationStrip.js +2 -1
  108. package/dist/dictation/DictationStrip.js.map +1 -1
  109. package/dist/dictation/dictation-engine.cjs.map +1 -1
  110. package/dist/dictation/dictation-engine.d.ts +8 -0
  111. package/dist/dictation/dictation-engine.js.map +1 -1
  112. package/dist/dictation/recorded-dictation-engine.cjs +153 -0
  113. package/dist/dictation/recorded-dictation-engine.cjs.map +1 -0
  114. package/dist/dictation/recorded-dictation-engine.d.ts +66 -0
  115. package/dist/dictation/recorded-dictation-engine.js +153 -0
  116. package/dist/dictation/recorded-dictation-engine.js.map +1 -0
  117. package/dist/dictation/use-athena-dictation-engine.cjs +64 -0
  118. package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -0
  119. package/dist/dictation/use-athena-dictation-engine.d.ts +25 -0
  120. package/dist/dictation/use-athena-dictation-engine.js +64 -0
  121. package/dist/dictation/use-athena-dictation-engine.js.map +1 -0
  122. package/dist/dictation/use-dictation.cjs +17 -6
  123. package/dist/dictation/use-dictation.cjs.map +1 -1
  124. package/dist/dictation/use-dictation.d.ts +20 -4
  125. package/dist/dictation/use-dictation.js +17 -6
  126. package/dist/dictation/use-dictation.js.map +1 -1
  127. package/dist/index.cjs +28 -39
  128. package/dist/index.cjs.map +1 -1
  129. package/dist/index.d.ts +4 -1
  130. package/dist/index.js +10 -21
  131. package/dist/index.js.map +1 -1
  132. package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
  133. package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
  134. package/dist/packages/agent-runtime/src/errors.cjs +18 -0
  135. package/dist/packages/agent-runtime/src/errors.cjs.map +1 -1
  136. package/dist/packages/agent-runtime/src/errors.js +19 -1
  137. package/dist/packages/agent-runtime/src/errors.js.map +1 -1
  138. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +1 -0
  139. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  140. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +1 -0
  141. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  142. package/dist/packages/agent-runtime/src/interrupts.cjs +61 -12
  143. package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
  144. package/dist/packages/agent-runtime/src/interrupts.js +61 -12
  145. package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
  146. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +49 -0
  147. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  148. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +49 -0
  149. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  150. package/dist/packages/agent-runtime/src/thread-status.js +1 -1
  151. package/dist/packages/agent-runtime/src/tool-metadata.cjs +1 -37
  152. package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
  153. package/dist/packages/agent-runtime/src/tool-metadata.js +1 -37
  154. package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
  155. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +0 -1
  156. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
  157. package/dist/packages/agent-runtime/src/use-deep-agent-config.js +0 -1
  158. package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -1
  159. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  160. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  161. package/dist/packages/react/package.json.cjs +1 -1
  162. package/dist/packages/react/package.json.js +1 -1
  163. package/dist/provider/AthenaProvider.cjs +3 -17
  164. package/dist/provider/AthenaProvider.cjs.map +1 -1
  165. package/dist/provider/AthenaProvider.js +3 -17
  166. package/dist/provider/AthenaProvider.js.map +1 -1
  167. package/dist/runtime/statewire-lifecycle.cjs +0 -16
  168. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  169. package/dist/runtime/statewire-lifecycle.js +0 -16
  170. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  171. package/dist/runtime/useAthenaStatewireRuntime.cjs +3 -17
  172. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  173. package/dist/runtime/useAthenaStatewireRuntime.js +3 -17
  174. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  175. package/dist/threads/statewire-thread-list.cjs +4 -17
  176. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  177. package/dist/threads/statewire-thread-list.js +4 -17
  178. package/dist/threads/statewire-thread-list.js.map +1 -1
  179. package/dist/threads/use-rest-conversation-history.cjs +2 -16
  180. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  181. package/dist/threads/use-rest-conversation-history.js +2 -16
  182. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  183. package/dist/threads/use-statewire-conversation-history.cjs +2 -16
  184. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  185. package/dist/threads/use-statewire-conversation-history.js +2 -16
  186. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  187. package/dist/toolkits/index.cjs +0 -7
  188. package/dist/toolkits/index.cjs.map +1 -1
  189. package/dist/toolkits/index.d.ts +0 -2
  190. package/dist/toolkits/index.js +0 -7
  191. package/dist/toolkits/index.js.map +1 -1
  192. package/dist/tools/index.cjs +4 -20
  193. package/dist/tools/index.cjs.map +1 -1
  194. package/dist/tools/index.js +1 -17
  195. package/dist/tools/index.js.map +1 -1
  196. package/dist/tools/misc-tool-uis.cjs +2 -17
  197. package/dist/tools/misc-tool-uis.cjs.map +1 -1
  198. package/dist/tools/misc-tool-uis.js +2 -17
  199. package/dist/tools/misc-tool-uis.js.map +1 -1
  200. package/dist/tools/primitives.cjs +1 -16
  201. package/dist/tools/primitives.cjs.map +1 -1
  202. package/dist/tools/primitives.js +1 -16
  203. package/dist/tools/primitives.js.map +1 -1
  204. package/dist/tools/tool-fallback.cjs +2 -17
  205. package/dist/tools/tool-fallback.cjs.map +1 -1
  206. package/dist/tools/tool-fallback.js +2 -17
  207. package/dist/tools/tool-fallback.js.map +1 -1
  208. package/dist/tools/voice-update-tool-ui.cjs +1 -17
  209. package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
  210. package/dist/tools/voice-update-tool-ui.js +1 -17
  211. package/dist/tools/voice-update-tool-ui.js.map +1 -1
  212. package/package.json +1 -1
  213. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +0 -32
  214. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +0 -1
  215. package/dist/node_modules/qrcode-generator/dist/qrcode.js +0 -31
  216. package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +0 -1
  217. package/dist/node_modules/zod/v4/classic/errors.cjs +0 -44
  218. package/dist/node_modules/zod/v4/classic/errors.cjs.map +0 -1
  219. package/dist/node_modules/zod/v4/classic/errors.js +0 -44
  220. package/dist/node_modules/zod/v4/classic/errors.js.map +0 -1
  221. package/dist/node_modules/zod/v4/classic/iso.cjs +0 -43
  222. package/dist/node_modules/zod/v4/classic/iso.cjs.map +0 -1
  223. package/dist/node_modules/zod/v4/classic/iso.js +0 -43
  224. package/dist/node_modules/zod/v4/classic/iso.js.map +0 -1
  225. package/dist/node_modules/zod/v4/classic/parse.cjs +0 -29
  226. package/dist/node_modules/zod/v4/classic/parse.cjs.map +0 -1
  227. package/dist/node_modules/zod/v4/classic/parse.js +0 -29
  228. package/dist/node_modules/zod/v4/classic/parse.js.map +0 -1
  229. package/dist/node_modules/zod/v4/classic/schemas.cjs +0 -871
  230. package/dist/node_modules/zod/v4/classic/schemas.cjs.map +0 -1
  231. package/dist/node_modules/zod/v4/classic/schemas.js +0 -871
  232. package/dist/node_modules/zod/v4/classic/schemas.js.map +0 -1
  233. package/dist/node_modules/zod/v4/core/api.cjs +0 -570
  234. package/dist/node_modules/zod/v4/core/api.cjs.map +0 -1
  235. package/dist/node_modules/zod/v4/core/api.js +0 -570
  236. package/dist/node_modules/zod/v4/core/api.js.map +0 -1
  237. package/dist/node_modules/zod/v4/core/checks.cjs +0 -411
  238. package/dist/node_modules/zod/v4/core/checks.cjs.map +0 -1
  239. package/dist/node_modules/zod/v4/core/checks.js +0 -411
  240. package/dist/node_modules/zod/v4/core/checks.js.map +0 -1
  241. package/dist/node_modules/zod/v4/core/core.cjs +0 -77
  242. package/dist/node_modules/zod/v4/core/core.cjs.map +0 -1
  243. package/dist/node_modules/zod/v4/core/core.js +0 -77
  244. package/dist/node_modules/zod/v4/core/core.js.map +0 -1
  245. package/dist/node_modules/zod/v4/core/doc.cjs +0 -38
  246. package/dist/node_modules/zod/v4/core/doc.cjs.map +0 -1
  247. package/dist/node_modules/zod/v4/core/doc.js +0 -38
  248. package/dist/node_modules/zod/v4/core/doc.js.map +0 -1
  249. package/dist/node_modules/zod/v4/core/errors.cjs +0 -76
  250. package/dist/node_modules/zod/v4/core/errors.cjs.map +0 -1
  251. package/dist/node_modules/zod/v4/core/errors.js +0 -76
  252. package/dist/node_modules/zod/v4/core/errors.js.map +0 -1
  253. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +0 -312
  254. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +0 -1
  255. package/dist/node_modules/zod/v4/core/json-schema-processors.js +0 -312
  256. package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +0 -1
  257. package/dist/node_modules/zod/v4/core/parse.cjs +0 -96
  258. package/dist/node_modules/zod/v4/core/parse.cjs.map +0 -1
  259. package/dist/node_modules/zod/v4/core/parse.js +0 -96
  260. package/dist/node_modules/zod/v4/core/parse.js.map +0 -1
  261. package/dist/node_modules/zod/v4/core/regexes.cjs +0 -86
  262. package/dist/node_modules/zod/v4/core/regexes.cjs.map +0 -1
  263. package/dist/node_modules/zod/v4/core/regexes.js +0 -86
  264. package/dist/node_modules/zod/v4/core/regexes.js.map +0 -1
  265. package/dist/node_modules/zod/v4/core/registries.cjs +0 -52
  266. package/dist/node_modules/zod/v4/core/registries.cjs.map +0 -1
  267. package/dist/node_modules/zod/v4/core/registries.js +0 -52
  268. package/dist/node_modules/zod/v4/core/registries.js.map +0 -1
  269. package/dist/node_modules/zod/v4/core/schemas.cjs +0 -1412
  270. package/dist/node_modules/zod/v4/core/schemas.cjs.map +0 -1
  271. package/dist/node_modules/zod/v4/core/schemas.js +0 -1413
  272. package/dist/node_modules/zod/v4/core/schemas.js.map +0 -1
  273. package/dist/node_modules/zod/v4/core/to-json-schema.cjs +0 -370
  274. package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +0 -1
  275. package/dist/node_modules/zod/v4/core/to-json-schema.js +0 -370
  276. package/dist/node_modules/zod/v4/core/to-json-schema.js.map +0 -1
  277. package/dist/node_modules/zod/v4/core/util.cjs +0 -444
  278. package/dist/node_modules/zod/v4/core/util.cjs.map +0 -1
  279. package/dist/node_modules/zod/v4/core/util.js +0 -444
  280. package/dist/node_modules/zod/v4/core/util.js.map +0 -1
  281. package/dist/node_modules/zod/v4/core/versions.cjs +0 -9
  282. package/dist/node_modules/zod/v4/core/versions.cjs.map +0 -1
  283. package/dist/node_modules/zod/v4/core/versions.js +0 -9
  284. package/dist/node_modules/zod/v4/core/versions.js.map +0 -1
  285. package/dist/packages/agent-runtime/src/thread-handoff.cjs +0 -85
  286. package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +0 -1
  287. package/dist/packages/agent-runtime/src/thread-handoff.js +0 -85
  288. package/dist/packages/agent-runtime/src/thread-handoff.js.map +0 -1
@@ -2,7 +2,7 @@ import { useMemo, useReducer, useState, useRef, useEffect, useCallback } from "r
2
2
  import { athenaDiagnostics } from "../diagnostics/athena-diagnostics.js";
3
3
  import { createWebSpeechDictationEngine, DICTATION_ENGINE_ERROR_KINDS } from "./dictation-engine.js";
4
4
  import { dictationReducer, INITIAL_DICTATION_STATE, DICTATION_STATUSES, spokenText, joinSpeech } from "./dictation-state.js";
5
- const STOP_FLUSH_TIMEOUT_MS = 1500;
5
+ const DEFAULT_STOP_FLUSH_TIMEOUT_MS = 1500;
6
6
  const EMPTY_TRANSCRIPT = { final: "", interim: "" };
7
7
  function documentLanguage() {
8
8
  if (typeof document !== "undefined" && document.documentElement.lang) {
@@ -34,6 +34,7 @@ function useDictation(options = {}) {
34
34
  const onAutoEndRef = useRef(onAutoEnd);
35
35
  onAutoEndRef.current = onAutoEnd;
36
36
  const stopWaitersRef = useRef([]);
37
+ const sessionErrorRef = useRef(null);
37
38
  const generationRef = useRef(0);
38
39
  useEffect(() => {
39
40
  setIsSupported(engine.isSupported());
@@ -41,7 +42,8 @@ function useDictation(options = {}) {
41
42
  const settleWaiters = useCallback((spoken) => {
42
43
  const waiters = stopWaitersRef.current;
43
44
  stopWaitersRef.current = [];
44
- for (const resolve of waiters) resolve(spoken);
45
+ const result = { text: spoken, error: sessionErrorRef.current };
46
+ for (const resolve of waiters) resolve(result);
45
47
  }, []);
46
48
  const releaseSession = useCallback(() => {
47
49
  sessionRef.current = null;
@@ -63,6 +65,7 @@ function useDictation(options = {}) {
63
65
  level: "warn",
64
66
  data: { phase: "error", engine: engine.id, kind: engineError.kind }
65
67
  });
68
+ sessionErrorRef.current = engineError;
66
69
  dispatch({ type: "error", error: engineError });
67
70
  },
68
71
  [engine.id]
@@ -72,6 +75,7 @@ function useDictation(options = {}) {
72
75
  const generation = generationRef.current + 1;
73
76
  generationRef.current = generation;
74
77
  transcriptRef.current = EMPTY_TRANSCRIPT;
78
+ sessionErrorRef.current = null;
75
79
  dispatch({ type: "start" });
76
80
  athenaDiagnostics.emit("composer.dictation", {
77
81
  level: "debug",
@@ -160,25 +164,31 @@ function useDictation(options = {}) {
160
164
  if (stateRef.current.status === DICTATION_STATUSES.starting) {
161
165
  teardown();
162
166
  dispatch({ type: "cancel" });
163
- return Promise.resolve("");
167
+ return Promise.resolve({ text: "", error: null });
164
168
  }
165
- return Promise.resolve(spokenText(transcriptRef.current));
169
+ return Promise.resolve({ text: spokenText(transcriptRef.current), error: sessionErrorRef.current });
166
170
  }
167
171
  dispatch({ type: "stop" });
168
172
  const flushed = new Promise((resolve) => {
169
173
  stopWaitersRef.current.push(resolve);
170
174
  window.setTimeout(() => {
171
175
  if (sessionRef.current === session) {
176
+ if (!spokenText(transcriptRef.current)) {
177
+ reportEngineError({
178
+ kind: DICTATION_ENGINE_ERROR_KINDS.connection,
179
+ message: "Transcription timed out. Try again."
180
+ });
181
+ }
172
182
  session.abort();
173
183
  dispatch({ type: "ended" });
174
184
  releaseSession();
175
185
  settleWaiters(spokenText(transcriptRef.current));
176
186
  }
177
- }, STOP_FLUSH_TIMEOUT_MS);
187
+ }, engine.flushTimeoutMs ?? DEFAULT_STOP_FLUSH_TIMEOUT_MS);
178
188
  });
179
189
  session.stop();
180
190
  return flushed;
181
- }, [releaseSession, settleWaiters, teardown]);
191
+ }, [engine.flushTimeoutMs, releaseSession, reportEngineError, settleWaiters, teardown]);
182
192
  const cancel = useCallback(() => {
183
193
  teardown();
184
194
  dispatch({ type: "cancel" });
@@ -193,6 +203,7 @@ function useDictation(options = {}) {
193
203
  error: state.error,
194
204
  stream,
195
205
  isSupported,
206
+ engine,
196
207
  start,
197
208
  stop,
198
209
  cancel
@@ -1 +1 @@
1
- {"version":3,"file":"use-dictation.js","sources":["../../src/dictation/use-dictation.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n createWebSpeechDictationEngine,\n DICTATION_ENGINE_ERROR_KINDS,\n type DictationEngine,\n type DictationEngineError,\n type DictationEngineSession,\n} from './dictation-engine';\nimport {\n DICTATION_STATUSES,\n dictationReducer,\n INITIAL_DICTATION_STATE,\n joinSpeech,\n spokenText,\n type DictationStatus,\n type DictationTranscript,\n} from './dictation-state';\n\nexport interface UseDictationOptions {\n /** Speech engine. Defaults to the browser's Web Speech API. */\n engine?: DictationEngine;\n /** BCP-47 language tag. Defaults to the document language, else `en-US`. */\n lang?: string;\n /**\n * Open the microphone through `getUserMedia` so callers get a `stream` for\n * a waveform. Web Speech opens its own capture; this second capture is\n * what the level meter reads. Defaults to true.\n */\n captureStream?: boolean;\n /**\n * The engine ended on its own (silence window, recognizer timeout) while\n * listening, without `stop()` or `cancel()`. Receives the spoken text so\n * the words are not lost; the composer puts them into the draft.\n */\n onAutoEnd?: (spoken: string) => void;\n}\n\nexport interface DictationController {\n status: DictationStatus;\n /** True while starting, listening or stopping. */\n isActive: boolean;\n transcript: DictationTranscript;\n /** `final` plus the interim tail, ready to hand to the composer. */\n text: string;\n error: DictationEngineError | null;\n /** The microphone stream while active (for a waveform), else null. */\n stream: MediaStream | null;\n /** Whether the engine can run here. Hide the control when false. */\n isSupported: boolean;\n start: () => void;\n /**\n * Finish recording. Resolves with the spoken text once the engine has\n * flushed (bounded wait), so trailing words are not lost.\n */\n stop: () => Promise<string>;\n /** Discard the session and its transcript. */\n cancel: () => void;\n}\n\n/** How long `stop()` waits for the engine's final flush before giving up. */\nconst STOP_FLUSH_TIMEOUT_MS = 1500;\n\ntype StopResolver = (spoken: string) => void;\n\nconst EMPTY_TRANSCRIPT: DictationTranscript = { final: '', interim: '' };\n\nfunction documentLanguage(): string {\n if (typeof document !== 'undefined' && document.documentElement.lang) {\n return document.documentElement.lang;\n }\n if (typeof navigator !== 'undefined' && navigator.language) return navigator.language;\n return 'en-US';\n}\n\nasync function openMicrophone(): Promise<MediaStream | null> {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) return null;\n return navigator.mediaDevices.getUserMedia({ audio: true });\n}\n\nfunction releaseStream(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) track.stop();\n}\n\n/**\n * Drive a dictation session: microphone, speech engine, and the transcript\n * state machine. The composer renders the waveform from `stream` and the\n * live caption from `transcript`; on `stop()` it appends `text` to the draft.\n */\nexport function useDictation(options: UseDictationOptions = {}): DictationController {\n const engine = useMemo(() => options.engine ?? createWebSpeechDictationEngine(), [options.engine]);\n const { lang, captureStream = true, onAutoEnd } = options;\n const [state, dispatch] = useReducer(dictationReducer, INITIAL_DICTATION_STATE);\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n const sessionRef = useRef<DictationEngineSession | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const stateRef = useRef(state);\n stateRef.current = state;\n // The authoritative transcript, updated synchronously as the engine reports\n // it. The reducer's copy renders the caption; this one is what `stop()` and\n // `onAutoEnd` hand over, so an engine that flushes its last words and ends\n // in the same tick — before React has rendered — loses nothing.\n const transcriptRef = useRef<DictationTranscript>(EMPTY_TRANSCRIPT);\n const onAutoEndRef = useRef(onAutoEnd);\n onAutoEndRef.current = onAutoEnd;\n // stop() callers waiting for the engine's final flush.\n const stopWaitersRef = useRef<StopResolver[]>([]);\n // Distinguishes a fresh session from the callbacks of a torn-down one.\n const generationRef = useRef(0);\n\n useEffect(() => {\n setIsSupported(engine.isSupported());\n }, [engine]);\n\n const settleWaiters = useCallback((spoken: string) => {\n const waiters = stopWaitersRef.current;\n stopWaitersRef.current = [];\n for (const resolve of waiters) resolve(spoken);\n }, []);\n\n const releaseSession = useCallback(() => {\n sessionRef.current = null;\n releaseStream(streamRef.current);\n streamRef.current = null;\n setStream(null);\n }, []);\n\n const teardown = useCallback(() => {\n generationRef.current += 1;\n sessionRef.current?.abort();\n releaseSession();\n transcriptRef.current = EMPTY_TRANSCRIPT;\n settleWaiters('');\n }, [releaseSession, settleWaiters]);\n\n const reportEngineError = useCallback(\n (engineError: DictationEngineError) => {\n athenaDiagnostics.emit('composer.dictation', {\n level: 'warn',\n data: { phase: 'error', engine: engine.id, kind: engineError.kind },\n });\n dispatch({ type: 'error', error: engineError });\n },\n [engine.id],\n );\n\n const start = useCallback(() => {\n if (sessionRef.current) return;\n const generation = generationRef.current + 1;\n generationRef.current = generation;\n transcriptRef.current = EMPTY_TRANSCRIPT;\n dispatch({ type: 'start' });\n athenaDiagnostics.emit('composer.dictation', {\n level: 'debug',\n data: { phase: 'start', engine: engine.id },\n });\n\n const run = async () => {\n let microphone: MediaStream | null = null;\n if (captureStream) {\n try {\n microphone = await openMicrophone();\n } catch (error) {\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n dispatch({\n type: 'error',\n error: {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message:\n error instanceof DOMException && error.name === 'NotFoundError'\n ? 'No microphone was found.'\n : 'Microphone access was denied. Allow the microphone for this site and try again.',\n },\n });\n return;\n }\n }\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n streamRef.current = microphone;\n setStream(microphone);\n\n // An engine may fail synchronously inside start() (Web Speech throws\n // when recognition.start() is refused) and run onError/onEnd before we\n // hold the session. Those callbacks flip this flag so a dead session is\n // never stored, which would make every later mic click return early.\n let terminated = false;\n const endSession = () => {\n terminated = true;\n const current = stateRef.current;\n const spoken = spokenText(transcriptRef.current);\n // No stop() is waiting: the engine gave up on its own. Hand the\n // words over so they land in the draft instead of vanishing.\n const endedOnItsOwn =\n stopWaitersRef.current.length === 0 && current.status === DICTATION_STATUSES.listening;\n // The reducer keeps an error status through 'ended', so an error\n // reported just before the engine closed stays on screen.\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spoken);\n if (endedOnItsOwn) onAutoEndRef.current?.(spoken);\n };\n let session: DictationEngineSession;\n try {\n session = engine.start({\n stream: microphone,\n lang: lang ?? documentLanguage(),\n callbacks: {\n onTranscript: (update) => {\n if (generationRef.current !== generation) return;\n transcriptRef.current = {\n final: joinSpeech(transcriptRef.current.final, update.final),\n interim: update.interim,\n };\n dispatch({ type: 'transcript', update });\n },\n onError: (engineError) => {\n if (generationRef.current !== generation) return;\n // A silence timeout is the engine giving up politely; keep the\n // words we have instead of showing an error.\n if (engineError.kind === DICTATION_ENGINE_ERROR_KINDS.noSpeech) return;\n reportEngineError(engineError);\n },\n onEnd: () => {\n if (generationRef.current !== generation) return;\n endSession();\n },\n },\n });\n } catch (error) {\n // A relay engine that cannot build its session must not strand the\n // microphone or leave the composer in `starting`.\n releaseSession();\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n return;\n }\n if (terminated) return;\n sessionRef.current = session;\n dispatch({ type: 'listening' });\n };\n void run();\n }, [captureStream, engine, lang, releaseSession, reportEngineError, settleWaiters]);\n\n const stop = useCallback((): Promise<string> => {\n const session = sessionRef.current;\n if (!session) {\n // Stopped while the microphone prompt was still open: nothing was heard\n // yet, and the pending start must not bring the engine up afterwards.\n if (stateRef.current.status === DICTATION_STATUSES.starting) {\n teardown();\n dispatch({ type: 'cancel' });\n return Promise.resolve('');\n }\n return Promise.resolve(spokenText(transcriptRef.current));\n }\n dispatch({ type: 'stop' });\n const flushed = new Promise<string>((resolve) => {\n stopWaitersRef.current.push(resolve);\n window.setTimeout(() => {\n // Bounded wait: a recognizer that never fires onend must not hold the\n // composer hostage. Force the session closed and hand over what was\n // heard so far.\n if (sessionRef.current === session) {\n session.abort();\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spokenText(transcriptRef.current));\n }\n }, STOP_FLUSH_TIMEOUT_MS);\n });\n session.stop();\n return flushed;\n }, [releaseSession, settleWaiters, teardown]);\n\n const cancel = useCallback(() => {\n teardown();\n dispatch({ type: 'cancel' });\n }, [teardown]);\n\n // Release the microphone with the component.\n useEffect(() => () => teardown(), [teardown]);\n\n const isActive =\n state.status === DICTATION_STATUSES.starting ||\n state.status === DICTATION_STATUSES.listening ||\n state.status === DICTATION_STATUSES.stopping;\n\n return {\n status: state.status,\n isActive,\n transcript: state.transcript,\n text: spokenText(state.transcript),\n error: state.error,\n stream,\n isSupported,\n start,\n stop,\n cancel,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-dictation.js","sources":["../../src/dictation/use-dictation.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n createWebSpeechDictationEngine,\n DICTATION_ENGINE_ERROR_KINDS,\n type DictationEngine,\n type DictationEngineError,\n type DictationEngineSession,\n} from './dictation-engine';\nimport {\n DICTATION_STATUSES,\n dictationReducer,\n INITIAL_DICTATION_STATE,\n joinSpeech,\n spokenText,\n type DictationStatus,\n type DictationTranscript,\n} from './dictation-state';\n\nexport interface UseDictationOptions {\n /**\n * Speech engine. Defaults to the browser's Web Speech API; `AthenaChat`\n * passes the record-then-transcribe engine from `useAthenaDictationEngine`.\n */\n engine?: DictationEngine;\n /** BCP-47 language tag. Defaults to the document language, else `en-US`. */\n lang?: string;\n /**\n * Open the microphone through `getUserMedia` so callers get a `stream` for\n * a waveform. Web Speech opens its own capture; this second capture is\n * what the level meter reads. Defaults to true.\n */\n captureStream?: boolean;\n /**\n * The engine ended on its own (silence window, recognizer timeout) while\n * listening, without `stop()` or `cancel()`. Receives the spoken text so\n * the words are not lost; the composer puts them into the draft.\n */\n onAutoEnd?: (spoken: string) => void;\n}\n\nexport interface DictationStopResult {\n /** Spoken text (final plus interim tail) gathered before the engine closed. */\n text: string;\n /**\n * The engine error reported during this session, if any. When set the\n * transcript may be incomplete: a recorded engine whose upload failed has no\n * words to hand over, so callers should not send the draft as if it were.\n */\n error: DictationEngineError | null;\n}\n\nexport interface DictationController {\n status: DictationStatus;\n /** True while starting, listening or stopping. */\n isActive: boolean;\n transcript: DictationTranscript;\n /** `final` plus the interim tail, ready to hand to the composer. */\n text: string;\n error: DictationEngineError | null;\n /** The microphone stream while active (for a waveform), else null. */\n stream: MediaStream | null;\n /** Whether the engine can run here. Hide the control when false. */\n isSupported: boolean;\n /** The engine in use (for its `stoppingLabel` and diagnostics). */\n engine: DictationEngine;\n start: () => void;\n /**\n * Finish recording. Resolves once the engine has flushed (bounded wait), so\n * trailing words are not lost, with the spoken text and any error raised\n * along the way.\n */\n stop: () => Promise<DictationStopResult>;\n /** Discard the session and its transcript. */\n cancel: () => void;\n}\n\n/** How long `stop()` waits for a live engine's final flush before giving up. */\nconst DEFAULT_STOP_FLUSH_TIMEOUT_MS = 1500;\n\ntype StopResolver = (result: DictationStopResult) => void;\n\nconst EMPTY_TRANSCRIPT: DictationTranscript = { final: '', interim: '' };\n\nfunction documentLanguage(): string {\n if (typeof document !== 'undefined' && document.documentElement.lang) {\n return document.documentElement.lang;\n }\n if (typeof navigator !== 'undefined' && navigator.language) return navigator.language;\n return 'en-US';\n}\n\nasync function openMicrophone(): Promise<MediaStream | null> {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) return null;\n return navigator.mediaDevices.getUserMedia({ audio: true });\n}\n\nfunction releaseStream(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) track.stop();\n}\n\n/**\n * Drive a dictation session: microphone, speech engine, and the transcript\n * state machine. The composer renders the waveform from `stream` and the\n * live caption from `transcript`; on `stop()` it appends `text` to the draft.\n */\nexport function useDictation(options: UseDictationOptions = {}): DictationController {\n const engine = useMemo(() => options.engine ?? createWebSpeechDictationEngine(), [options.engine]);\n const { lang, captureStream = true, onAutoEnd } = options;\n const [state, dispatch] = useReducer(dictationReducer, INITIAL_DICTATION_STATE);\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n const sessionRef = useRef<DictationEngineSession | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const stateRef = useRef(state);\n stateRef.current = state;\n // The authoritative transcript, updated synchronously as the engine reports\n // it. The reducer's copy renders the caption; this one is what `stop()` and\n // `onAutoEnd` hand over, so an engine that flushes its last words and ends\n // in the same tick — before React has rendered — loses nothing.\n const transcriptRef = useRef<DictationTranscript>(EMPTY_TRANSCRIPT);\n const onAutoEndRef = useRef(onAutoEnd);\n onAutoEndRef.current = onAutoEnd;\n // stop() callers waiting for the engine's final flush.\n const stopWaitersRef = useRef<StopResolver[]>([]);\n // Last engine error of the current session, handed to stop() callers\n // synchronously (the reducer's copy may not have rendered yet).\n const sessionErrorRef = useRef<DictationEngineError | null>(null);\n // Distinguishes a fresh session from the callbacks of a torn-down one.\n const generationRef = useRef(0);\n\n useEffect(() => {\n setIsSupported(engine.isSupported());\n }, [engine]);\n\n const settleWaiters = useCallback((spoken: string) => {\n const waiters = stopWaitersRef.current;\n stopWaitersRef.current = [];\n const result: DictationStopResult = { text: spoken, error: sessionErrorRef.current };\n for (const resolve of waiters) resolve(result);\n }, []);\n\n const releaseSession = useCallback(() => {\n sessionRef.current = null;\n releaseStream(streamRef.current);\n streamRef.current = null;\n setStream(null);\n }, []);\n\n const teardown = useCallback(() => {\n generationRef.current += 1;\n sessionRef.current?.abort();\n releaseSession();\n transcriptRef.current = EMPTY_TRANSCRIPT;\n settleWaiters('');\n }, [releaseSession, settleWaiters]);\n\n const reportEngineError = useCallback(\n (engineError: DictationEngineError) => {\n athenaDiagnostics.emit('composer.dictation', {\n level: 'warn',\n data: { phase: 'error', engine: engine.id, kind: engineError.kind },\n });\n sessionErrorRef.current = engineError;\n dispatch({ type: 'error', error: engineError });\n },\n [engine.id],\n );\n\n const start = useCallback(() => {\n if (sessionRef.current) return;\n const generation = generationRef.current + 1;\n generationRef.current = generation;\n transcriptRef.current = EMPTY_TRANSCRIPT;\n sessionErrorRef.current = null;\n dispatch({ type: 'start' });\n athenaDiagnostics.emit('composer.dictation', {\n level: 'debug',\n data: { phase: 'start', engine: engine.id },\n });\n\n const run = async () => {\n let microphone: MediaStream | null = null;\n if (captureStream) {\n try {\n microphone = await openMicrophone();\n } catch (error) {\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n dispatch({\n type: 'error',\n error: {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message:\n error instanceof DOMException && error.name === 'NotFoundError'\n ? 'No microphone was found.'\n : 'Microphone access was denied. Allow the microphone for this site and try again.',\n },\n });\n return;\n }\n }\n if (generationRef.current !== generation) {\n releaseStream(microphone);\n return;\n }\n streamRef.current = microphone;\n setStream(microphone);\n\n // An engine may fail synchronously inside start() (Web Speech throws\n // when recognition.start() is refused) and run onError/onEnd before we\n // hold the session. Those callbacks flip this flag so a dead session is\n // never stored, which would make every later mic click return early.\n let terminated = false;\n const endSession = () => {\n terminated = true;\n const current = stateRef.current;\n const spoken = spokenText(transcriptRef.current);\n // No stop() is waiting: the engine gave up on its own. Hand the\n // words over so they land in the draft instead of vanishing.\n const endedOnItsOwn =\n stopWaitersRef.current.length === 0 && current.status === DICTATION_STATUSES.listening;\n // The reducer keeps an error status through 'ended', so an error\n // reported just before the engine closed stays on screen.\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spoken);\n if (endedOnItsOwn) onAutoEndRef.current?.(spoken);\n };\n let session: DictationEngineSession;\n try {\n session = engine.start({\n stream: microphone,\n lang: lang ?? documentLanguage(),\n callbacks: {\n onTranscript: (update) => {\n if (generationRef.current !== generation) return;\n transcriptRef.current = {\n final: joinSpeech(transcriptRef.current.final, update.final),\n interim: update.interim,\n };\n dispatch({ type: 'transcript', update });\n },\n onError: (engineError) => {\n if (generationRef.current !== generation) return;\n // A silence timeout is the engine giving up politely; keep the\n // words we have instead of showing an error.\n if (engineError.kind === DICTATION_ENGINE_ERROR_KINDS.noSpeech) return;\n reportEngineError(engineError);\n },\n onEnd: () => {\n if (generationRef.current !== generation) return;\n endSession();\n },\n },\n });\n } catch (error) {\n // A relay engine that cannot build its session must not strand the\n // microphone or leave the composer in `starting`.\n releaseSession();\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n return;\n }\n if (terminated) return;\n sessionRef.current = session;\n dispatch({ type: 'listening' });\n };\n void run();\n }, [captureStream, engine, lang, releaseSession, reportEngineError, settleWaiters]);\n\n const stop = useCallback((): Promise<DictationStopResult> => {\n const session = sessionRef.current;\n if (!session) {\n // Stopped while the microphone prompt was still open: nothing was heard\n // yet, and the pending start must not bring the engine up afterwards.\n if (stateRef.current.status === DICTATION_STATUSES.starting) {\n teardown();\n dispatch({ type: 'cancel' });\n return Promise.resolve({ text: '', error: null });\n }\n return Promise.resolve({ text: spokenText(transcriptRef.current), error: sessionErrorRef.current });\n }\n dispatch({ type: 'stop' });\n const flushed = new Promise<DictationStopResult>((resolve) => {\n stopWaitersRef.current.push(resolve);\n window.setTimeout(() => {\n // Bounded wait: a recognizer that never fires onend must not hold the\n // composer hostage. Force the session closed and hand over what was\n // heard so far. An engine that had nothing to show yet (a recorded\n // clip still transcribing) surfaces that as an error rather than\n // passing off silence as the result.\n if (sessionRef.current === session) {\n // Report before abort(): an engine that ends synchronously on\n // abort settles the waiters, and they must see this error.\n if (!spokenText(transcriptRef.current)) {\n reportEngineError({\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: 'Transcription timed out. Try again.',\n });\n }\n session.abort();\n dispatch({ type: 'ended' });\n releaseSession();\n settleWaiters(spokenText(transcriptRef.current));\n }\n }, engine.flushTimeoutMs ?? DEFAULT_STOP_FLUSH_TIMEOUT_MS);\n });\n session.stop();\n return flushed;\n }, [engine.flushTimeoutMs, releaseSession, reportEngineError, settleWaiters, teardown]);\n\n const cancel = useCallback(() => {\n teardown();\n dispatch({ type: 'cancel' });\n }, [teardown]);\n\n // Release the microphone with the component.\n useEffect(() => () => teardown(), [teardown]);\n\n const isActive =\n state.status === DICTATION_STATUSES.starting ||\n state.status === DICTATION_STATUSES.listening ||\n state.status === DICTATION_STATUSES.stopping;\n\n return {\n status: state.status,\n isActive,\n transcript: state.transcript,\n text: spokenText(state.transcript),\n error: state.error,\n stream,\n isSupported,\n engine,\n start,\n stop,\n cancel,\n };\n}\n"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -40,27 +40,6 @@ const toolUis = require("./tools/tool-uis.cjs");
40
40
  const primitives = require("./tools/primitives.cjs");
41
41
  const miscToolUis = require("./tools/misc-tool-uis.cjs");
42
42
  const toolIcons = require("./tools/tool-icons.cjs");
43
- require("./packages/agent-runtime/src/generated/runconfig-contract.cjs");
44
- require("react");
45
- require("@assistant-ui/tap");
46
- require("statewire");
47
- require("./packages/agent-runtime/src/statewire-suspended.cjs");
48
- const conversationList = require("./packages/agent-runtime/src/conversation-list.cjs");
49
- const toolMetadata = require("./packages/agent-runtime/src/tool-metadata.cjs");
50
- const toolResult = require("./packages/agent-runtime/src/tool-result.cjs");
51
- require("@tanstack/react-query");
52
- const interrupts = require("./packages/agent-runtime/src/interrupts.cjs");
53
- require("./packages/agent-runtime/src/messages.cjs");
54
- require("./packages/agent-runtime/src/statewire-state.cjs");
55
- const threadExtras = require("./packages/agent-runtime/src/thread-extras.cjs");
56
- require("@assistant-ui/store");
57
- require("./packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
58
- require("./packages/agent-runtime/src/statewire-attach-observer.cjs");
59
- require("./packages/agent-runtime/src/thread-status.cjs");
60
- require("@assistant-ui/react-statewire");
61
- const githubWakeEvent = require("./packages/agent-runtime/src/github-wake-event.cjs");
62
- require("@assistant-ui/core");
63
- require("./packages/agent-runtime/src/thread-handoff.cjs");
64
43
  const writeTodosToolUi = require("./tools/write-todos-tool-ui.cjs");
65
44
  const todoPlanStore = require("./tools/todo-plan-store.cjs");
66
45
  const fileToolUis = require("./tools/file-tool-uis.cjs");
@@ -91,6 +70,8 @@ const useAthenaThreadManager = require("./threads/useAthenaThreadManager.cjs");
91
70
  const useDictation = require("./dictation/use-dictation.cjs");
92
71
  const DictationStrip = require("./dictation/DictationStrip.cjs");
93
72
  const dictationEngine = require("./dictation/dictation-engine.cjs");
73
+ const recordedDictationEngine = require("./dictation/recorded-dictation-engine.cjs");
74
+ const useAthenaDictationEngine = require("./dictation/use-athena-dictation-engine.cjs");
94
75
  const dictationState = require("./dictation/dictation-state.cjs");
95
76
  const useAppendToComposer = require("./composer/useAppendToComposer.cjs");
96
77
  const composerHandoff = require("./composer/composer-handoff.cjs");
@@ -108,7 +89,13 @@ const tooltip = require("./ui/tooltip.cjs");
108
89
  const collapsible = require("./ui/collapsible.cjs");
109
90
  const index = require("./toolkits/index.cjs");
110
91
  const index$1 = require("./theme/index.cjs");
92
+ const interrupts = require("./packages/agent-runtime/src/interrupts.cjs");
93
+ const toolResult = require("./packages/agent-runtime/src/tool-result.cjs");
111
94
  const cnfast = require("cnfast");
95
+ const conversationList = require("./packages/agent-runtime/src/conversation-list.cjs");
96
+ const toolMetadata = require("./packages/agent-runtime/src/tool-metadata.cjs");
97
+ const githubWakeEvent = require("./packages/agent-runtime/src/github-wake-event.cjs");
98
+ const threadExtras = require("./packages/agent-runtime/src/thread-extras.cjs");
112
99
  const toolAppearance = require("./tools/tool-appearance.cjs");
113
100
  exports.ATHENA_REACT_SDK_VERSION = version.ATHENA_REACT_SDK_VERSION;
114
101
  exports.athenaDiagnostics = athenaDiagnostics.athenaDiagnostics;
@@ -218,24 +205,6 @@ exports.truncate = primitives.truncate;
218
205
  exports.InstallPackageToolUI = miscToolUis.InstallPackageToolUI;
219
206
  exports.TOOL_ICONS = toolIcons.TOOL_ICONS;
220
207
  exports.toolIcon = toolIcons.toolIcon;
221
- exports.deriveConversationAttention = conversationList.deriveConversationAttention;
222
- exports.describeToolArgs = toolMetadata.describeToolArgs;
223
- exports.formatToolName = toolMetadata.formatToolName;
224
- exports.getToolDisplayMeta = toolMetadata.getToolDisplayMeta;
225
- exports.toolActiveLabel = toolMetadata.toolActiveLabel;
226
- exports.toolCompletedLabel = toolMetadata.toolCompletedLabel;
227
- exports.classifyToolResult = toolResult.classifyToolResult;
228
- exports.isToolResultSuccess = toolResult.isToolResultSuccess;
229
- exports.resultMessage = toolResult.resultMessage;
230
- exports.HITL_PENDING_RESUME_GRACE_MS = interrupts.HITL_PENDING_RESUME_GRACE_MS;
231
- exports.asHitlApproval = interrupts.asHitlApproval;
232
- exports.isPendingInterrupt = interrupts.isPendingInterrupt;
233
- exports.readInterruptMessage = interrupts.readInterruptMessage;
234
- exports.hasStatewireThreadExtras = threadExtras.hasStatewireThreadExtras;
235
- exports.githubWakeEventLabel = githubWakeEvent.githubWakeEventLabel;
236
- exports.githubWakeMarkerText = githubWakeEvent.githubWakeMarkerText;
237
- exports.isGithubWakeMessage = githubWakeEvent.isGithubWakeMessage;
238
- exports.readGithubWakeEvent = githubWakeEvent.readGithubWakeEvent;
239
208
  exports.HiddenWriteTodosToolUI = writeTodosToolUi.HiddenWriteTodosToolUI;
240
209
  exports.WriteTodosToolUI = writeTodosToolUi.WriteTodosToolUI;
241
210
  exports.parseTodoPlan = writeTodosToolUi.parseTodoPlan;
@@ -310,6 +279,8 @@ exports.DictationStrip = DictationStrip.DictationStrip;
310
279
  exports.DictationWaveform = DictationStrip.DictationWaveform;
311
280
  exports.DICTATION_ENGINE_ERROR_KINDS = dictationEngine.DICTATION_ENGINE_ERROR_KINDS;
312
281
  exports.createWebSpeechDictationEngine = dictationEngine.createWebSpeechDictationEngine;
282
+ exports.createRecordedDictationEngine = recordedDictationEngine.createRecordedDictationEngine;
283
+ exports.useAthenaDictationEngine = useAthenaDictationEngine.useAthenaDictationEngine;
313
284
  exports.DICTATION_STATUSES = dictationState.DICTATION_STATUSES;
314
285
  exports.composeDictationDraft = dictationState.composeDictationDraft;
315
286
  exports.useAppendToComposer = useAppendToComposer.useAppendToComposer;
@@ -342,9 +313,27 @@ exports.CollapsibleTrigger = collapsible.CollapsibleTrigger;
342
313
  exports.Toolkits = index.Toolkits;
343
314
  exports.themeToStyleVars = index$1.themeToStyleVars;
344
315
  exports.themes = index$1.themes;
316
+ exports.HITL_PENDING_RESUME_GRACE_MS = interrupts.HITL_PENDING_RESUME_GRACE_MS;
317
+ exports.asHitlApproval = interrupts.asHitlApproval;
318
+ exports.isPendingInterrupt = interrupts.isPendingInterrupt;
319
+ exports.readInterruptMessage = interrupts.readInterruptMessage;
320
+ exports.classifyToolResult = toolResult.classifyToolResult;
321
+ exports.isToolResultSuccess = toolResult.isToolResultSuccess;
322
+ exports.resultMessage = toolResult.resultMessage;
345
323
  Object.defineProperty(exports, "cn", {
346
324
  enumerable: true,
347
325
  get: () => cnfast.cn
348
326
  });
327
+ exports.deriveConversationAttention = conversationList.deriveConversationAttention;
328
+ exports.describeToolArgs = toolMetadata.describeToolArgs;
329
+ exports.formatToolName = toolMetadata.formatToolName;
330
+ exports.getToolDisplayMeta = toolMetadata.getToolDisplayMeta;
331
+ exports.toolActiveLabel = toolMetadata.toolActiveLabel;
332
+ exports.toolCompletedLabel = toolMetadata.toolCompletedLabel;
333
+ exports.githubWakeEventLabel = githubWakeEvent.githubWakeEventLabel;
334
+ exports.githubWakeMarkerText = githubWakeEvent.githubWakeMarkerText;
335
+ exports.isGithubWakeMessage = githubWakeEvent.isGithubWakeMessage;
336
+ exports.readGithubWakeEvent = githubWakeEvent.readGithubWakeEvent;
337
+ exports.hasStatewireThreadExtras = threadExtras.hasStatewireThreadExtras;
349
338
  exports.useToolAppearance = toolAppearance.useToolAppearance;
350
339
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -115,11 +115,14 @@ export { conversationSummaryFromIndexEntry, useStatewireConversationHistory, typ
115
115
  export { useAthenaThreadManager } from './threads/useAthenaThreadManager';
116
116
  export type { AthenaThreadManagerState } from './threads/useAthenaThreadManager';
117
117
  export { useDictation } from './dictation/use-dictation';
118
- export type { DictationController, UseDictationOptions } from './dictation/use-dictation';
118
+ export type { DictationController, DictationStopResult, UseDictationOptions, } from './dictation/use-dictation';
119
119
  export { DictationStrip, DictationWaveform } from './dictation/DictationStrip';
120
120
  export type { DictationStripProps } from './dictation/DictationStrip';
121
121
  export { createWebSpeechDictationEngine, DICTATION_ENGINE_ERROR_KINDS, } from './dictation/dictation-engine';
122
122
  export type { DictationEngine, DictationEngineCallbacks, DictationEngineError, DictationEngineErrorKind, DictationEngineSession, DictationTranscriptUpdate, } from './dictation/dictation-engine';
123
+ export { createRecordedDictationEngine } from './dictation/recorded-dictation-engine';
124
+ export type { DictationTranscribeContext, DictationTranscriber, RecordedDictationEngineOptions, } from './dictation/recorded-dictation-engine';
125
+ export { useAthenaDictationEngine } from './dictation/use-athena-dictation-engine';
123
126
  export { composeDictationDraft, DICTATION_STATUSES } from './dictation/dictation-state';
124
127
  export type { DictationStatus, DictationTranscript } from './dictation/dictation-state';
125
128
  export type { AthenaChatComposerSlots, AthenaChatDictationOptions } from './chat/AthenaChat';
package/dist/index.js CHANGED
@@ -38,27 +38,6 @@ import { BrowseToolUI, CreateDocumentToolUI, CreateEmailDraftToolUI, CreateNoteb
38
38
  import { CopyToolSpecButton, ExpandableSection, ToolCard, normalizeResult, truncate } from "./tools/primitives.js";
39
39
  import { InstallPackageToolUI } from "./tools/misc-tool-uis.js";
40
40
  import { TOOL_ICONS, toolIcon } from "./tools/tool-icons.js";
41
- import "./packages/agent-runtime/src/generated/runconfig-contract.js";
42
- import "react";
43
- import "@assistant-ui/tap";
44
- import "statewire";
45
- import "./packages/agent-runtime/src/statewire-suspended.js";
46
- import { deriveConversationAttention } from "./packages/agent-runtime/src/conversation-list.js";
47
- import { describeToolArgs, formatToolName, getToolDisplayMeta, toolActiveLabel, toolCompletedLabel } from "./packages/agent-runtime/src/tool-metadata.js";
48
- import { classifyToolResult, isToolResultSuccess, resultMessage } from "./packages/agent-runtime/src/tool-result.js";
49
- import "@tanstack/react-query";
50
- import { HITL_PENDING_RESUME_GRACE_MS, asHitlApproval, isPendingInterrupt, readInterruptMessage } from "./packages/agent-runtime/src/interrupts.js";
51
- import "./packages/agent-runtime/src/messages.js";
52
- import "./packages/agent-runtime/src/statewire-state.js";
53
- import { hasStatewireThreadExtras } from "./packages/agent-runtime/src/thread-extras.js";
54
- import "@assistant-ui/store";
55
- import "./packages/agent-runtime/src/quota-safe-local-storage-session.js";
56
- import "./packages/agent-runtime/src/statewire-attach-observer.js";
57
- import "./packages/agent-runtime/src/thread-status.js";
58
- import "@assistant-ui/react-statewire";
59
- import { githubWakeEventLabel, githubWakeMarkerText, isGithubWakeMessage, readGithubWakeEvent } from "./packages/agent-runtime/src/github-wake-event.js";
60
- import "@assistant-ui/core";
61
- import "./packages/agent-runtime/src/thread-handoff.js";
62
41
  import { HiddenWriteTodosToolUI, WriteTodosToolUI, parseTodoPlan } from "./tools/write-todos-tool-ui.js";
63
42
  import { useLatestTodoPlan } from "./tools/todo-plan-store.js";
64
43
  import { EditFileToolUI, ListFilesToolUI, ReadFileToolUI, SearchFilesToolUI, WriteFileToolUI, friendlyWorkspacePath } from "./tools/file-tool-uis.js";
@@ -89,6 +68,8 @@ import { useAthenaThreadManager } from "./threads/useAthenaThreadManager.js";
89
68
  import { useDictation } from "./dictation/use-dictation.js";
90
69
  import { DictationStrip, DictationWaveform } from "./dictation/DictationStrip.js";
91
70
  import { DICTATION_ENGINE_ERROR_KINDS, createWebSpeechDictationEngine } from "./dictation/dictation-engine.js";
71
+ import { createRecordedDictationEngine } from "./dictation/recorded-dictation-engine.js";
72
+ import { useAthenaDictationEngine } from "./dictation/use-athena-dictation-engine.js";
92
73
  import { DICTATION_STATUSES, composeDictationDraft } from "./dictation/dictation-state.js";
93
74
  import { useAppendToComposer } from "./composer/useAppendToComposer.js";
94
75
  import { useComposerReady } from "./composer/composer-handoff.js";
@@ -106,7 +87,13 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/t
106
87
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible.js";
107
88
  import { Toolkits } from "./toolkits/index.js";
108
89
  import { themeToStyleVars, themes } from "./theme/index.js";
90
+ import { HITL_PENDING_RESUME_GRACE_MS, asHitlApproval, isPendingInterrupt, readInterruptMessage } from "./packages/agent-runtime/src/interrupts.js";
91
+ import { classifyToolResult, isToolResultSuccess, resultMessage } from "./packages/agent-runtime/src/tool-result.js";
109
92
  import { cn } from "cnfast";
93
+ import { deriveConversationAttention } from "./packages/agent-runtime/src/conversation-list.js";
94
+ import { describeToolArgs, formatToolName, getToolDisplayMeta, toolActiveLabel, toolCompletedLabel } from "./packages/agent-runtime/src/tool-metadata.js";
95
+ import { githubWakeEventLabel, githubWakeMarkerText, isGithubWakeMessage, readGithubWakeEvent } from "./packages/agent-runtime/src/github-wake-event.js";
96
+ import { hasStatewireThreadExtras } from "./packages/agent-runtime/src/thread-extras.js";
110
97
  import { useToolAppearance } from "./tools/tool-appearance.js";
111
98
  export {
112
99
  ATHENA_REACT_SDK_VERSION,
@@ -238,6 +225,7 @@ export {
238
225
  countActivityTools,
239
226
  createAssetToolUI,
240
227
  createAthenaSpacesUrl,
228
+ createRecordedDictationEngine,
241
229
  createThread,
242
230
  createToolBasedGroupingFunction,
243
231
  createWebSpeechDictationEngine,
@@ -315,6 +303,7 @@ export {
315
303
  useAthenaCitationLinkHandler,
316
304
  useAthenaConfig,
317
305
  useAthenaDiagnostics,
306
+ useAthenaDictationEngine,
318
307
  useAthenaLinkClickHandler,
319
308
  useAthenaRepoSearch,
320
309
  useAthenaRunLive,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"edit-command.cjs","sources":["../../../../../agent-runtime/src/edit-command.ts"],"sourcesContent":["import type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { DEEP_AGENT_INTEREST_PROTOCOL } from './generated/runconfig-contract';\nimport {\n type DeepAgentMessageWindow,\n pageInterestRequests,\n readDeepAgentMessageWindow,\n} from './message-window';\nimport { hasStatewireThreadExtras } from './thread-extras';\n\n/**\n * Wire-accurate `run/edit` construction for deep-agent retry/edit, shared by\n * every surface that resubmits a user message.\n *\n * assistant-ui builds the command's `runAnchorMessageId` from the CONVERTED\n * thread messages (`resolveEditCommand`: `messages[index - 1].id`), but the\n * deep-agent host validates that anchor against the edited message's parent in\n * its RAW message list (`harness_sdk.linear_thread`: the entry before it in\n * `state[\"messages\"]`). Those two lists do not line up:\n *\n * - the converter chunks an assistant turn and its tool messages into ONE\n * thread message carrying only the FIRST wire id, so the anchor names the\n * head of the previous turn instead of its last tool message;\n * - `sanitizeLangChainMessages` drops hidden-from-frontend and unconvertible\n * entries, so a dropped tail message is invisible to the anchor too.\n *\n * Either way the host rejects the command with `wrong-anchor`\n * (`runAnchorMessageId … is not the source's parent`) AFTER the composer's\n * synchronous `send()` returned, so the retry/edit silently does nothing.\n *\n * Building the command here off the replicated raw state makes the anchor the\n * same value the host computes.\n *\n * The raw list is the loaded interest window, so the first loaded message's\n * parent is not in it while older messages exist — an edit there must not\n * anchor on `null` (the root, rejected `wrong-anchor`); a message below the\n * window has no parent on this client at all. {@link resolveDeepAgentEditCommand}\n * states older page interests until the parent is loaded.\n */\n\n/** The pieces of the statewire thread extras the edit path reads. */\nexport interface DeepAgentStatewireExtras {\n /** The library's dispatch; it resolves with the host's reply to the command. */\n sendCommand: (\n command: StatewireThread.Command,\n options?: StatewireThread.SendOptions,\n ) => Promise<unknown> | void;\n /** Optional on older hosts; capture before loading an edit's history anchor. */\n prepareMessageMetadata?: StatewireThread.Extras['prepareMessageMetadata'];\n discardPreparedMessageMetadata?: StatewireThread.Extras['discardPreparedMessageMetadata'];\n sendPreparedEdit?: StatewireThread.Extras['sendPreparedEdit'];\n isPreparingSubmission?: boolean;\n /** The composed thread state — its `messages` are the raw wire messages of the loaded window. */\n state: { messages?: readonly { id?: unknown }[] } | null;\n /** The mounted documents (the interest window), absent on a wire without documents. */\n documents?: StatewireThread.Extras['documents'];\n invoke?: StatewireThread.Extras['invoke'];\n /** The live run, when one is in flight; `epoch` advances on each dispatch. */\n runs: { runId?: string | null; epoch?: number };\n /** The host's replicated run status; absent on a surface that predates it. */\n status?: StatewireThread.ThreadStatus;\n}\n\n/**\n * The statewire extras of the nearest aui thread, or `null` when the thread is\n * not a statewire thread (e.g. a read-only subagent transcript runtime).\n * Gated on {@link hasStatewireThreadExtras} for the same reason the approval\n * card is: the library's own hooks throw on a non-statewire thread.\n */\nexport function readDeepAgentStatewireExtras(extras: unknown): DeepAgentStatewireExtras | null {\n if (!hasStatewireThreadExtras(extras)) return null;\n return extras as DeepAgentStatewireExtras;\n}\n\n/**\n * The host's parent of `sourceId`: the raw wire message directly before it\n * (`null` when it is the first of the whole transcript). Returns `undefined`\n * when `sourceId` is not in the raw list at all — an optimistic bubble the\n * host has not settled yet, a message below the loaded window, or a surface\n * whose state this module cannot read — or when it is the first loaded\n * message while older ones exist (`hasOlder`), so the caller can load more or\n * fall back to the library's own path instead of asserting a wrong anchor.\n */\nexport function resolveWireAnchorMessageId({\n wireMessages,\n sourceId,\n hasOlder = false,\n}: {\n wireMessages: readonly { id?: unknown }[] | undefined;\n sourceId: string;\n hasOlder?: boolean;\n}): string | null | undefined {\n if (!wireMessages) return undefined;\n const index = wireMessages.findIndex((message) => message?.id === sourceId);\n if (index < 0) return undefined;\n if (index === 0) return hasOlder ? undefined : null;\n const parentId = wireMessages[index - 1]?.id;\n return typeof parentId === 'string' ? parentId : undefined;\n}\n\n/**\n * Whether `sourceId`'s parent lies below the loaded window: the message is\n * absent from the raw list, or its first entry, while older messages exist.\n * The caller loads older history before an edit can be anchored.\n */\nexport function isSourceBelowLoadedWindow({\n wireMessages,\n sourceId,\n window,\n}: {\n wireMessages: readonly { id?: unknown }[] | undefined;\n sourceId: string;\n window: DeepAgentMessageWindow | null;\n}): boolean {\n if (!wireMessages || window === null || !window.hasOlder) return false;\n const index = wireMessages.findIndex((message) => message?.id === sourceId);\n return index <= 0;\n}\n\nconst readWindow = (extras: DeepAgentStatewireExtras) =>\n readDeepAgentMessageWindow(extras.documents?.list());\n\n/**\n * A fresh command / message id (uniqueness, not secrecy). `crypto.randomUUID`\n * where the host runtime has it — every browser, Bun, Node and Hermes with\n * webcrypto — and a v4-shaped fallback elsewhere, so the shared path never\n * throws on a runtime without full webcrypto.\n */\nfunction mintCommandId(): string {\n const webcrypto = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (typeof webcrypto?.randomUUID === 'function') return webcrypto.randomUUID();\n const hex = (): string => Math.floor(Math.random() * 0x10000).toString(16).padStart(4, '0');\n return `${hex()}${hex()}-${hex()}-4${hex().slice(1)}-${hex()}-${hex()}${hex()}${hex()}`;\n}\n\n/**\n * The `run/edit` command for resubmitting `sourceId` with `text`, anchored on\n * the host's own parent. `null` when the anchor cannot be resolved from the\n * replicated state.\n */\nexport function buildDeepAgentEditCommand({\n extras,\n sourceId,\n text,\n}: {\n extras: DeepAgentStatewireExtras;\n sourceId: string;\n text: string;\n}): StatewireThread.RunEditCommand | null {\n const runAnchorMessageId = resolveWireAnchorMessageId({\n wireMessages: extras.state?.messages,\n sourceId,\n hasOlder: readWindow(extras)?.hasOlder ?? false,\n });\n if (runAnchorMessageId === undefined) return null;\n return {\n type: 'run/edit',\n runId: extras.runs.runId ?? mintCommandId(),\n message: { id: mintCommandId(), role: 'user', parts: [{ type: 'text', text }] },\n sourceId,\n runAnchorMessageId,\n };\n}\n\n/**\n * Pages of older history stated while looking for an edited message's host\n * parent. Each page is up to a window's worth of messages, so an edit reaches\n * this many windows below what was loaded before it gives up.\n */\nexport const MAX_EDIT_ANCHOR_HISTORY_LOADS = 5;\n\n/**\n * The anchored `run/edit` for `sourceId`, stating older page interests one at\n * a time when the message's parent lies below the loaded window. Every page\n * is read back from the live documents (`readExtras` — the host mounts them\n * before it answers the statement), so the anchor is always the host's own\n * parent id.\n *\n * Resolves `null` — the caller's cue for the library's composer path, whose\n * failure it reports — when the thread is not a statewire thread, the host has\n * not settled the message, the history is exhausted without it, or the page\n * budget ({@link MAX_EDIT_ANCHOR_HISTORY_LOADS}) is spent. A synchronous\n * resolution never awaits: the first read either anchors at once or decides\n * that pages are needed.\n */\nexport async function resolveDeepAgentEditCommand({\n readExtras,\n sourceId,\n text,\n pageSize,\n maxHistoryLoads = MAX_EDIT_ANCHOR_HISTORY_LOADS,\n}: {\n /** The live thread extras — re-read per page so each anchor attempt sees the widened window. */\n readExtras: () => DeepAgentStatewireExtras | null;\n sourceId: string;\n text: string;\n pageSize?: number;\n maxHistoryLoads?: number;\n}): Promise<StatewireThread.RunEditCommand | null> {\n for (let page = 0; page <= maxHistoryLoads; page += 1) {\n const extras = readExtras();\n if (!extras) return null;\n const command = buildDeepAgentEditCommand({ extras, sourceId, text });\n if (command) return command;\n if (page === maxHistoryLoads || !extras.invoke) return null;\n const window = readWindow(extras);\n if (!isSourceBelowLoadedWindow({ wireMessages: extras.state?.messages, sourceId, window })) {\n return null;\n }\n const requests = window === null ? null : pageInterestRequests(window, pageSize);\n if (requests === null) return null;\n await extras.invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [requests]);\n }\n return null;\n}\n"],"names":["hasStatewireThreadExtras","readDeepAgentMessageWindow","pageInterestRequests","DEEP_AGENT_INTEREST_PROTOCOL"],"mappings":";;;;;AAoEO,SAAS,6BAA6B,QAAkD;AAC7F,MAAI,CAACA,aAAAA,yBAAyB,MAAM,EAAG,QAAO;AAC9C,SAAO;AACT;AAWO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAI8B;;AAC5B,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,QAAQ,aAAa,UAAU,CAAC,aAAY,mCAAS,QAAO,QAAQ;AAC1E,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,UAAU,EAAG,QAAO,WAAW,SAAY;AAC/C,QAAM,YAAW,kBAAa,QAAQ,CAAC,MAAtB,mBAAyB;AAC1C,SAAO,OAAO,aAAa,WAAW,WAAW;AACnD;AAOO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,GAIY;AACV,MAAI,CAAC,gBAAgB,WAAW,QAAQ,CAAC,OAAO,SAAU,QAAO;AACjE,QAAM,QAAQ,aAAa,UAAU,CAAC,aAAY,mCAAS,QAAO,QAAQ;AAC1E,SAAO,SAAS;AAClB;AAEA,MAAM,aAAa,CAAC,WAAA;;AAClBC,uBAAAA,4BAA2B,YAAO,cAAP,mBAAkB,MAAM;AAAA;AAQrD,SAAS,gBAAwB;AAC/B,QAAM,YAAa,WAA0D;AAC7E,MAAI,QAAO,uCAAW,gBAAe,WAAY,QAAO,UAAU,WAAA;AAClE,QAAM,MAAM,MAAc,KAAK,MAAM,KAAK,OAAA,IAAW,KAAO,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1F,SAAO,GAAG,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,IAAA,CAAK,KAAK,MAAM,MAAM,CAAC,CAAC,IAAI,IAAA,CAAK,IAAI,KAAK,GAAG,IAAA,CAAK,GAAG,IAAA,CAAK;AACvF;AAOO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,GAI0C;;AACxC,QAAM,qBAAqB,2BAA2B;AAAA,IACpD,eAAc,YAAO,UAAP,mBAAc;AAAA,IAC5B;AAAA,IACA,YAAU,gBAAW,MAAM,MAAjB,mBAAoB,aAAY;AAAA,EAAA,CAC3C;AACD,MAAI,uBAAuB,OAAW,QAAO;AAC7C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,OAAO,KAAK,SAAS,cAAA;AAAA,IAC5B,SAAS,EAAE,IAAI,cAAA,GAAiB,MAAM,QAAQ,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAA,CAAM,EAAA;AAAA,IAC5E;AAAA,IACA;AAAA,EAAA;AAEJ;AAOO,MAAM,gCAAgC;AAgB7C,eAAsB,4BAA4B;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAOmD;;AACjD,WAAS,OAAO,GAAG,QAAQ,iBAAiB,QAAQ,GAAG;AACrD,UAAM,SAAS,WAAA;AACf,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,UAAU,0BAA0B,EAAE,QAAQ,UAAU,MAAM;AACpE,QAAI,QAAS,QAAO;AACpB,QAAI,SAAS,mBAAmB,CAAC,OAAO,OAAQ,QAAO;AACvD,UAAM,SAAS,WAAW,MAAM;AAChC,QAAI,CAAC,0BAA0B,EAAE,eAAc,YAAO,UAAP,mBAAc,UAAU,UAAU,OAAA,CAAQ,GAAG;AAC1F,aAAO;AAAA,IACT;AACA,UAAM,WAAW,WAAW,OAAO,OAAOC,cAAAA,qBAAqB,QAAQ,QAAQ;AAC/E,QAAI,aAAa,KAAM,QAAO;AAC9B,UAAM,OAAO,OAAOC,kBAAAA,8BAA8B,OAAO,CAAC,QAAQ,CAAC;AAAA,EACrE;AACA,SAAO;AACT;;;;;;;"}
1
+ {"version":3,"file":"edit-command.cjs","sources":["../../../../../agent-runtime/src/edit-command.ts"],"sourcesContent":["import type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { DEEP_AGENT_INTEREST_PROTOCOL } from './generated/runconfig-contract';\nimport {\n type DeepAgentMessageWindow,\n pageInterestRequests,\n readDeepAgentMessageWindow,\n} from './message-window';\nimport { hasStatewireThreadExtras } from './thread-extras';\n\n/**\n * Wire-accurate `run/edit` construction for deep-agent retry/edit, shared by\n * every surface that resubmits a user message.\n *\n * assistant-ui builds the command's `runAnchorMessageId` from the CONVERTED\n * thread messages (`resolveEditCommand`: `messages[index - 1].id`), but the\n * deep-agent host validates that anchor against the edited message's parent in\n * its RAW message list (`harness_sdk.linear_thread`: the entry before it in\n * `state[\"messages\"]`). Those two lists do not line up:\n *\n * - the converter chunks an assistant turn and its tool messages into ONE\n * thread message carrying only the FIRST wire id, so the anchor names the\n * head of the previous turn instead of its last tool message;\n * - `sanitizeLangChainMessages` drops hidden-from-frontend and unconvertible\n * entries, so a dropped tail message is invisible to the anchor too.\n *\n * Either way the host rejects the command with `wrong-anchor`\n * (`runAnchorMessageId … is not the source's parent`) AFTER the composer's\n * synchronous `send()` returned, so the retry/edit silently does nothing.\n *\n * Building the command here off the replicated raw state makes the anchor the\n * same value the host computes.\n *\n * The raw list is the loaded interest window, so the first loaded message's\n * parent is not in it while older messages exist — an edit there must not\n * anchor on `null` (the root, rejected `wrong-anchor`); a message below the\n * window has no parent on this client at all. {@link resolveDeepAgentEditCommand}\n * states older page interests until the parent is loaded.\n */\n\n/**\n * Host metadata frozen at submission, settling once the async capture does.\n * The message-metadata preparation surface — `prepareMessageMetadata`,\n * `discardPreparedMessageMetadata`, `sendPreparedEdit` and `sendCommand`'s\n * `preparedMetadata` option — is what the monorepo patches into\n * `@assistant-ui/react-statewire@0.29.1` (`patches/`). It is declared\n * structurally here rather than as `StatewireThread.Extras[...]` so that the\n * published sources also compile against the unpatched upstream typings, where\n * those members are absent and the edit path's optional calls never run.\n */\nexport type DeepAgentPreparedMessageMetadata = Promise<Record<string, unknown> | undefined>;\n\n/** The pieces of the statewire thread extras the edit path reads. */\nexport interface DeepAgentStatewireExtras {\n /** The library's dispatch; it resolves with the host's reply to the command. */\n sendCommand: (\n command: StatewireThread.Command,\n options?: { preparedMetadata?: DeepAgentPreparedMessageMetadata | undefined },\n ) => Promise<unknown> | void;\n /** Optional on older hosts; capture before loading an edit's history anchor. */\n prepareMessageMetadata?: (() => DeepAgentPreparedMessageMetadata) | undefined;\n discardPreparedMessageMetadata?:\n | ((prepared: DeepAgentPreparedMessageMetadata) => void)\n | undefined;\n sendPreparedEdit?:\n | ((sourceId: string, prepared: DeepAgentPreparedMessageMetadata) => void)\n | undefined;\n isPreparingSubmission?: boolean;\n /** The composed thread state — its `messages` are the raw wire messages of the loaded window. */\n state: { messages?: readonly { id?: unknown }[] } | null;\n /** The mounted documents (the interest window), absent on a wire without documents. */\n documents?: StatewireThread.Extras['documents'];\n invoke?: StatewireThread.Extras['invoke'];\n /** The live run, when one is in flight; `epoch` advances on each dispatch. */\n runs: { runId?: string | null; epoch?: number };\n /** The host's replicated run status; absent on a surface that predates it. */\n status?: StatewireThread.ThreadStatus;\n}\n\n/**\n * The statewire extras of the nearest aui thread, or `null` when the thread is\n * not a statewire thread (e.g. a read-only subagent transcript runtime).\n * Gated on {@link hasStatewireThreadExtras} for the same reason the approval\n * card is: the library's own hooks throw on a non-statewire thread.\n */\nexport function readDeepAgentStatewireExtras(extras: unknown): DeepAgentStatewireExtras | null {\n if (!hasStatewireThreadExtras(extras)) return null;\n return extras as DeepAgentStatewireExtras;\n}\n\n/**\n * The host's parent of `sourceId`: the raw wire message directly before it\n * (`null` when it is the first of the whole transcript). Returns `undefined`\n * when `sourceId` is not in the raw list at all — an optimistic bubble the\n * host has not settled yet, a message below the loaded window, or a surface\n * whose state this module cannot read — or when it is the first loaded\n * message while older ones exist (`hasOlder`), so the caller can load more or\n * fall back to the library's own path instead of asserting a wrong anchor.\n */\nexport function resolveWireAnchorMessageId({\n wireMessages,\n sourceId,\n hasOlder = false,\n}: {\n wireMessages: readonly { id?: unknown }[] | undefined;\n sourceId: string;\n hasOlder?: boolean;\n}): string | null | undefined {\n if (!wireMessages) return undefined;\n const index = wireMessages.findIndex((message) => message?.id === sourceId);\n if (index < 0) return undefined;\n if (index === 0) return hasOlder ? undefined : null;\n const parentId = wireMessages[index - 1]?.id;\n return typeof parentId === 'string' ? parentId : undefined;\n}\n\n/**\n * Whether `sourceId`'s parent lies below the loaded window: the message is\n * absent from the raw list, or its first entry, while older messages exist.\n * The caller loads older history before an edit can be anchored.\n */\nexport function isSourceBelowLoadedWindow({\n wireMessages,\n sourceId,\n window,\n}: {\n wireMessages: readonly { id?: unknown }[] | undefined;\n sourceId: string;\n window: DeepAgentMessageWindow | null;\n}): boolean {\n if (!wireMessages || window === null || !window.hasOlder) return false;\n const index = wireMessages.findIndex((message) => message?.id === sourceId);\n return index <= 0;\n}\n\nconst readWindow = (extras: DeepAgentStatewireExtras) =>\n readDeepAgentMessageWindow(extras.documents?.list());\n\n/**\n * A fresh command / message id (uniqueness, not secrecy). `crypto.randomUUID`\n * where the host runtime has it — every browser, Bun, Node and Hermes with\n * webcrypto — and a v4-shaped fallback elsewhere, so the shared path never\n * throws on a runtime without full webcrypto.\n */\nfunction mintCommandId(): string {\n const webcrypto = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (typeof webcrypto?.randomUUID === 'function') return webcrypto.randomUUID();\n const hex = (): string => Math.floor(Math.random() * 0x10000).toString(16).padStart(4, '0');\n return `${hex()}${hex()}-${hex()}-4${hex().slice(1)}-${hex()}-${hex()}${hex()}${hex()}`;\n}\n\n/**\n * The `run/edit` command for resubmitting `sourceId` with `text`, anchored on\n * the host's own parent. `null` when the anchor cannot be resolved from the\n * replicated state.\n */\nexport function buildDeepAgentEditCommand({\n extras,\n sourceId,\n text,\n}: {\n extras: DeepAgentStatewireExtras;\n sourceId: string;\n text: string;\n}): StatewireThread.RunEditCommand | null {\n const runAnchorMessageId = resolveWireAnchorMessageId({\n wireMessages: extras.state?.messages,\n sourceId,\n hasOlder: readWindow(extras)?.hasOlder ?? false,\n });\n if (runAnchorMessageId === undefined) return null;\n return {\n type: 'run/edit',\n runId: extras.runs.runId ?? mintCommandId(),\n message: { id: mintCommandId(), role: 'user', parts: [{ type: 'text', text }] },\n sourceId,\n runAnchorMessageId,\n };\n}\n\n/**\n * Pages of older history stated while looking for an edited message's host\n * parent. Each page is up to a window's worth of messages, so an edit reaches\n * this many windows below what was loaded before it gives up.\n */\nexport const MAX_EDIT_ANCHOR_HISTORY_LOADS = 5;\n\n/**\n * The anchored `run/edit` for `sourceId`, stating older page interests one at\n * a time when the message's parent lies below the loaded window. Every page\n * is read back from the live documents (`readExtras` — the host mounts them\n * before it answers the statement), so the anchor is always the host's own\n * parent id.\n *\n * Resolves `null` — the caller's cue for the library's composer path, whose\n * failure it reports — when the thread is not a statewire thread, the host has\n * not settled the message, the history is exhausted without it, or the page\n * budget ({@link MAX_EDIT_ANCHOR_HISTORY_LOADS}) is spent. A synchronous\n * resolution never awaits: the first read either anchors at once or decides\n * that pages are needed.\n */\nexport async function resolveDeepAgentEditCommand({\n readExtras,\n sourceId,\n text,\n pageSize,\n maxHistoryLoads = MAX_EDIT_ANCHOR_HISTORY_LOADS,\n}: {\n /** The live thread extras — re-read per page so each anchor attempt sees the widened window. */\n readExtras: () => DeepAgentStatewireExtras | null;\n sourceId: string;\n text: string;\n pageSize?: number;\n maxHistoryLoads?: number;\n}): Promise<StatewireThread.RunEditCommand | null> {\n for (let page = 0; page <= maxHistoryLoads; page += 1) {\n const extras = readExtras();\n if (!extras) return null;\n const command = buildDeepAgentEditCommand({ extras, sourceId, text });\n if (command) return command;\n if (page === maxHistoryLoads || !extras.invoke) return null;\n const window = readWindow(extras);\n if (!isSourceBelowLoadedWindow({ wireMessages: extras.state?.messages, sourceId, window })) {\n return null;\n }\n const requests = window === null ? null : pageInterestRequests(window, pageSize);\n if (requests === null) return null;\n await extras.invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [requests]);\n }\n return null;\n}\n"],"names":["hasStatewireThreadExtras","readDeepAgentMessageWindow","pageInterestRequests","DEEP_AGENT_INTEREST_PROTOCOL"],"mappings":";;;;;AAoFO,SAAS,6BAA6B,QAAkD;AAC7F,MAAI,CAACA,aAAAA,yBAAyB,MAAM,EAAG,QAAO;AAC9C,SAAO;AACT;AAWO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAI8B;;AAC5B,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,QAAQ,aAAa,UAAU,CAAC,aAAY,mCAAS,QAAO,QAAQ;AAC1E,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,UAAU,EAAG,QAAO,WAAW,SAAY;AAC/C,QAAM,YAAW,kBAAa,QAAQ,CAAC,MAAtB,mBAAyB;AAC1C,SAAO,OAAO,aAAa,WAAW,WAAW;AACnD;AAOO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,GAIY;AACV,MAAI,CAAC,gBAAgB,WAAW,QAAQ,CAAC,OAAO,SAAU,QAAO;AACjE,QAAM,QAAQ,aAAa,UAAU,CAAC,aAAY,mCAAS,QAAO,QAAQ;AAC1E,SAAO,SAAS;AAClB;AAEA,MAAM,aAAa,CAAC,WAAA;;AAClBC,uBAAAA,4BAA2B,YAAO,cAAP,mBAAkB,MAAM;AAAA;AAQrD,SAAS,gBAAwB;AAC/B,QAAM,YAAa,WAA0D;AAC7E,MAAI,QAAO,uCAAW,gBAAe,WAAY,QAAO,UAAU,WAAA;AAClE,QAAM,MAAM,MAAc,KAAK,MAAM,KAAK,OAAA,IAAW,KAAO,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1F,SAAO,GAAG,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,IAAA,CAAK,KAAK,MAAM,MAAM,CAAC,CAAC,IAAI,IAAA,CAAK,IAAI,KAAK,GAAG,IAAA,CAAK,GAAG,IAAA,CAAK;AACvF;AAOO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,GAI0C;;AACxC,QAAM,qBAAqB,2BAA2B;AAAA,IACpD,eAAc,YAAO,UAAP,mBAAc;AAAA,IAC5B;AAAA,IACA,YAAU,gBAAW,MAAM,MAAjB,mBAAoB,aAAY;AAAA,EAAA,CAC3C;AACD,MAAI,uBAAuB,OAAW,QAAO;AAC7C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,OAAO,KAAK,SAAS,cAAA;AAAA,IAC5B,SAAS,EAAE,IAAI,cAAA,GAAiB,MAAM,QAAQ,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAA,CAAM,EAAA;AAAA,IAC5E;AAAA,IACA;AAAA,EAAA;AAEJ;AAOO,MAAM,gCAAgC;AAgB7C,eAAsB,4BAA4B;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAOmD;;AACjD,WAAS,OAAO,GAAG,QAAQ,iBAAiB,QAAQ,GAAG;AACrD,UAAM,SAAS,WAAA;AACf,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,UAAU,0BAA0B,EAAE,QAAQ,UAAU,MAAM;AACpE,QAAI,QAAS,QAAO;AACpB,QAAI,SAAS,mBAAmB,CAAC,OAAO,OAAQ,QAAO;AACvD,UAAM,SAAS,WAAW,MAAM;AAChC,QAAI,CAAC,0BAA0B,EAAE,eAAc,YAAO,UAAP,mBAAc,UAAU,UAAU,OAAA,CAAQ,GAAG;AAC1F,aAAO;AAAA,IACT;AACA,UAAM,WAAW,WAAW,OAAO,OAAOC,cAAAA,qBAAqB,QAAQ,QAAQ;AAC/E,QAAI,aAAa,KAAM,QAAO;AAC9B,UAAM,OAAO,OAAOC,kBAAAA,8BAA8B,OAAO,CAAC,QAAQ,CAAC;AAAA,EACrE;AACA,SAAO;AACT;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"edit-command.js","sources":["../../../../../agent-runtime/src/edit-command.ts"],"sourcesContent":["import type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { DEEP_AGENT_INTEREST_PROTOCOL } from './generated/runconfig-contract';\nimport {\n type DeepAgentMessageWindow,\n pageInterestRequests,\n readDeepAgentMessageWindow,\n} from './message-window';\nimport { hasStatewireThreadExtras } from './thread-extras';\n\n/**\n * Wire-accurate `run/edit` construction for deep-agent retry/edit, shared by\n * every surface that resubmits a user message.\n *\n * assistant-ui builds the command's `runAnchorMessageId` from the CONVERTED\n * thread messages (`resolveEditCommand`: `messages[index - 1].id`), but the\n * deep-agent host validates that anchor against the edited message's parent in\n * its RAW message list (`harness_sdk.linear_thread`: the entry before it in\n * `state[\"messages\"]`). Those two lists do not line up:\n *\n * - the converter chunks an assistant turn and its tool messages into ONE\n * thread message carrying only the FIRST wire id, so the anchor names the\n * head of the previous turn instead of its last tool message;\n * - `sanitizeLangChainMessages` drops hidden-from-frontend and unconvertible\n * entries, so a dropped tail message is invisible to the anchor too.\n *\n * Either way the host rejects the command with `wrong-anchor`\n * (`runAnchorMessageId … is not the source's parent`) AFTER the composer's\n * synchronous `send()` returned, so the retry/edit silently does nothing.\n *\n * Building the command here off the replicated raw state makes the anchor the\n * same value the host computes.\n *\n * The raw list is the loaded interest window, so the first loaded message's\n * parent is not in it while older messages exist — an edit there must not\n * anchor on `null` (the root, rejected `wrong-anchor`); a message below the\n * window has no parent on this client at all. {@link resolveDeepAgentEditCommand}\n * states older page interests until the parent is loaded.\n */\n\n/** The pieces of the statewire thread extras the edit path reads. */\nexport interface DeepAgentStatewireExtras {\n /** The library's dispatch; it resolves with the host's reply to the command. */\n sendCommand: (\n command: StatewireThread.Command,\n options?: StatewireThread.SendOptions,\n ) => Promise<unknown> | void;\n /** Optional on older hosts; capture before loading an edit's history anchor. */\n prepareMessageMetadata?: StatewireThread.Extras['prepareMessageMetadata'];\n discardPreparedMessageMetadata?: StatewireThread.Extras['discardPreparedMessageMetadata'];\n sendPreparedEdit?: StatewireThread.Extras['sendPreparedEdit'];\n isPreparingSubmission?: boolean;\n /** The composed thread state — its `messages` are the raw wire messages of the loaded window. */\n state: { messages?: readonly { id?: unknown }[] } | null;\n /** The mounted documents (the interest window), absent on a wire without documents. */\n documents?: StatewireThread.Extras['documents'];\n invoke?: StatewireThread.Extras['invoke'];\n /** The live run, when one is in flight; `epoch` advances on each dispatch. */\n runs: { runId?: string | null; epoch?: number };\n /** The host's replicated run status; absent on a surface that predates it. */\n status?: StatewireThread.ThreadStatus;\n}\n\n/**\n * The statewire extras of the nearest aui thread, or `null` when the thread is\n * not a statewire thread (e.g. a read-only subagent transcript runtime).\n * Gated on {@link hasStatewireThreadExtras} for the same reason the approval\n * card is: the library's own hooks throw on a non-statewire thread.\n */\nexport function readDeepAgentStatewireExtras(extras: unknown): DeepAgentStatewireExtras | null {\n if (!hasStatewireThreadExtras(extras)) return null;\n return extras as DeepAgentStatewireExtras;\n}\n\n/**\n * The host's parent of `sourceId`: the raw wire message directly before it\n * (`null` when it is the first of the whole transcript). Returns `undefined`\n * when `sourceId` is not in the raw list at all — an optimistic bubble the\n * host has not settled yet, a message below the loaded window, or a surface\n * whose state this module cannot read — or when it is the first loaded\n * message while older ones exist (`hasOlder`), so the caller can load more or\n * fall back to the library's own path instead of asserting a wrong anchor.\n */\nexport function resolveWireAnchorMessageId({\n wireMessages,\n sourceId,\n hasOlder = false,\n}: {\n wireMessages: readonly { id?: unknown }[] | undefined;\n sourceId: string;\n hasOlder?: boolean;\n}): string | null | undefined {\n if (!wireMessages) return undefined;\n const index = wireMessages.findIndex((message) => message?.id === sourceId);\n if (index < 0) return undefined;\n if (index === 0) return hasOlder ? undefined : null;\n const parentId = wireMessages[index - 1]?.id;\n return typeof parentId === 'string' ? parentId : undefined;\n}\n\n/**\n * Whether `sourceId`'s parent lies below the loaded window: the message is\n * absent from the raw list, or its first entry, while older messages exist.\n * The caller loads older history before an edit can be anchored.\n */\nexport function isSourceBelowLoadedWindow({\n wireMessages,\n sourceId,\n window,\n}: {\n wireMessages: readonly { id?: unknown }[] | undefined;\n sourceId: string;\n window: DeepAgentMessageWindow | null;\n}): boolean {\n if (!wireMessages || window === null || !window.hasOlder) return false;\n const index = wireMessages.findIndex((message) => message?.id === sourceId);\n return index <= 0;\n}\n\nconst readWindow = (extras: DeepAgentStatewireExtras) =>\n readDeepAgentMessageWindow(extras.documents?.list());\n\n/**\n * A fresh command / message id (uniqueness, not secrecy). `crypto.randomUUID`\n * where the host runtime has it — every browser, Bun, Node and Hermes with\n * webcrypto — and a v4-shaped fallback elsewhere, so the shared path never\n * throws on a runtime without full webcrypto.\n */\nfunction mintCommandId(): string {\n const webcrypto = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (typeof webcrypto?.randomUUID === 'function') return webcrypto.randomUUID();\n const hex = (): string => Math.floor(Math.random() * 0x10000).toString(16).padStart(4, '0');\n return `${hex()}${hex()}-${hex()}-4${hex().slice(1)}-${hex()}-${hex()}${hex()}${hex()}`;\n}\n\n/**\n * The `run/edit` command for resubmitting `sourceId` with `text`, anchored on\n * the host's own parent. `null` when the anchor cannot be resolved from the\n * replicated state.\n */\nexport function buildDeepAgentEditCommand({\n extras,\n sourceId,\n text,\n}: {\n extras: DeepAgentStatewireExtras;\n sourceId: string;\n text: string;\n}): StatewireThread.RunEditCommand | null {\n const runAnchorMessageId = resolveWireAnchorMessageId({\n wireMessages: extras.state?.messages,\n sourceId,\n hasOlder: readWindow(extras)?.hasOlder ?? false,\n });\n if (runAnchorMessageId === undefined) return null;\n return {\n type: 'run/edit',\n runId: extras.runs.runId ?? mintCommandId(),\n message: { id: mintCommandId(), role: 'user', parts: [{ type: 'text', text }] },\n sourceId,\n runAnchorMessageId,\n };\n}\n\n/**\n * Pages of older history stated while looking for an edited message's host\n * parent. Each page is up to a window's worth of messages, so an edit reaches\n * this many windows below what was loaded before it gives up.\n */\nexport const MAX_EDIT_ANCHOR_HISTORY_LOADS = 5;\n\n/**\n * The anchored `run/edit` for `sourceId`, stating older page interests one at\n * a time when the message's parent lies below the loaded window. Every page\n * is read back from the live documents (`readExtras` — the host mounts them\n * before it answers the statement), so the anchor is always the host's own\n * parent id.\n *\n * Resolves `null` — the caller's cue for the library's composer path, whose\n * failure it reports — when the thread is not a statewire thread, the host has\n * not settled the message, the history is exhausted without it, or the page\n * budget ({@link MAX_EDIT_ANCHOR_HISTORY_LOADS}) is spent. A synchronous\n * resolution never awaits: the first read either anchors at once or decides\n * that pages are needed.\n */\nexport async function resolveDeepAgentEditCommand({\n readExtras,\n sourceId,\n text,\n pageSize,\n maxHistoryLoads = MAX_EDIT_ANCHOR_HISTORY_LOADS,\n}: {\n /** The live thread extras — re-read per page so each anchor attempt sees the widened window. */\n readExtras: () => DeepAgentStatewireExtras | null;\n sourceId: string;\n text: string;\n pageSize?: number;\n maxHistoryLoads?: number;\n}): Promise<StatewireThread.RunEditCommand | null> {\n for (let page = 0; page <= maxHistoryLoads; page += 1) {\n const extras = readExtras();\n if (!extras) return null;\n const command = buildDeepAgentEditCommand({ extras, sourceId, text });\n if (command) return command;\n if (page === maxHistoryLoads || !extras.invoke) return null;\n const window = readWindow(extras);\n if (!isSourceBelowLoadedWindow({ wireMessages: extras.state?.messages, sourceId, window })) {\n return null;\n }\n const requests = window === null ? null : pageInterestRequests(window, pageSize);\n if (requests === null) return null;\n await extras.invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [requests]);\n }\n return null;\n}\n"],"names":[],"mappings":";;;AAoEO,SAAS,6BAA6B,QAAkD;AAC7F,MAAI,CAAC,yBAAyB,MAAM,EAAG,QAAO;AAC9C,SAAO;AACT;AAWO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAI8B;;AAC5B,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,QAAQ,aAAa,UAAU,CAAC,aAAY,mCAAS,QAAO,QAAQ;AAC1E,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,UAAU,EAAG,QAAO,WAAW,SAAY;AAC/C,QAAM,YAAW,kBAAa,QAAQ,CAAC,MAAtB,mBAAyB;AAC1C,SAAO,OAAO,aAAa,WAAW,WAAW;AACnD;AAOO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,GAIY;AACV,MAAI,CAAC,gBAAgB,WAAW,QAAQ,CAAC,OAAO,SAAU,QAAO;AACjE,QAAM,QAAQ,aAAa,UAAU,CAAC,aAAY,mCAAS,QAAO,QAAQ;AAC1E,SAAO,SAAS;AAClB;AAEA,MAAM,aAAa,CAAC,WAAA;;AAClB,qCAA2B,YAAO,cAAP,mBAAkB,MAAM;AAAA;AAQrD,SAAS,gBAAwB;AAC/B,QAAM,YAAa,WAA0D;AAC7E,MAAI,QAAO,uCAAW,gBAAe,WAAY,QAAO,UAAU,WAAA;AAClE,QAAM,MAAM,MAAc,KAAK,MAAM,KAAK,OAAA,IAAW,KAAO,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1F,SAAO,GAAG,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,IAAA,CAAK,KAAK,MAAM,MAAM,CAAC,CAAC,IAAI,IAAA,CAAK,IAAI,KAAK,GAAG,IAAA,CAAK,GAAG,IAAA,CAAK;AACvF;AAOO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,GAI0C;;AACxC,QAAM,qBAAqB,2BAA2B;AAAA,IACpD,eAAc,YAAO,UAAP,mBAAc;AAAA,IAC5B;AAAA,IACA,YAAU,gBAAW,MAAM,MAAjB,mBAAoB,aAAY;AAAA,EAAA,CAC3C;AACD,MAAI,uBAAuB,OAAW,QAAO;AAC7C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,OAAO,KAAK,SAAS,cAAA;AAAA,IAC5B,SAAS,EAAE,IAAI,cAAA,GAAiB,MAAM,QAAQ,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAA,CAAM,EAAA;AAAA,IAC5E;AAAA,IACA;AAAA,EAAA;AAEJ;AAOO,MAAM,gCAAgC;AAgB7C,eAAsB,4BAA4B;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAOmD;;AACjD,WAAS,OAAO,GAAG,QAAQ,iBAAiB,QAAQ,GAAG;AACrD,UAAM,SAAS,WAAA;AACf,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,UAAU,0BAA0B,EAAE,QAAQ,UAAU,MAAM;AACpE,QAAI,QAAS,QAAO;AACpB,QAAI,SAAS,mBAAmB,CAAC,OAAO,OAAQ,QAAO;AACvD,UAAM,SAAS,WAAW,MAAM;AAChC,QAAI,CAAC,0BAA0B,EAAE,eAAc,YAAO,UAAP,mBAAc,UAAU,UAAU,OAAA,CAAQ,GAAG;AAC1F,aAAO;AAAA,IACT;AACA,UAAM,WAAW,WAAW,OAAO,OAAO,qBAAqB,QAAQ,QAAQ;AAC/E,QAAI,aAAa,KAAM,QAAO;AAC9B,UAAM,OAAO,OAAO,8BAA8B,OAAO,CAAC,QAAQ,CAAC;AAAA,EACrE;AACA,SAAO;AACT;"}
1
+ {"version":3,"file":"edit-command.js","sources":["../../../../../agent-runtime/src/edit-command.ts"],"sourcesContent":["import type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { DEEP_AGENT_INTEREST_PROTOCOL } from './generated/runconfig-contract';\nimport {\n type DeepAgentMessageWindow,\n pageInterestRequests,\n readDeepAgentMessageWindow,\n} from './message-window';\nimport { hasStatewireThreadExtras } from './thread-extras';\n\n/**\n * Wire-accurate `run/edit` construction for deep-agent retry/edit, shared by\n * every surface that resubmits a user message.\n *\n * assistant-ui builds the command's `runAnchorMessageId` from the CONVERTED\n * thread messages (`resolveEditCommand`: `messages[index - 1].id`), but the\n * deep-agent host validates that anchor against the edited message's parent in\n * its RAW message list (`harness_sdk.linear_thread`: the entry before it in\n * `state[\"messages\"]`). Those two lists do not line up:\n *\n * - the converter chunks an assistant turn and its tool messages into ONE\n * thread message carrying only the FIRST wire id, so the anchor names the\n * head of the previous turn instead of its last tool message;\n * - `sanitizeLangChainMessages` drops hidden-from-frontend and unconvertible\n * entries, so a dropped tail message is invisible to the anchor too.\n *\n * Either way the host rejects the command with `wrong-anchor`\n * (`runAnchorMessageId … is not the source's parent`) AFTER the composer's\n * synchronous `send()` returned, so the retry/edit silently does nothing.\n *\n * Building the command here off the replicated raw state makes the anchor the\n * same value the host computes.\n *\n * The raw list is the loaded interest window, so the first loaded message's\n * parent is not in it while older messages exist — an edit there must not\n * anchor on `null` (the root, rejected `wrong-anchor`); a message below the\n * window has no parent on this client at all. {@link resolveDeepAgentEditCommand}\n * states older page interests until the parent is loaded.\n */\n\n/**\n * Host metadata frozen at submission, settling once the async capture does.\n * The message-metadata preparation surface — `prepareMessageMetadata`,\n * `discardPreparedMessageMetadata`, `sendPreparedEdit` and `sendCommand`'s\n * `preparedMetadata` option — is what the monorepo patches into\n * `@assistant-ui/react-statewire@0.29.1` (`patches/`). It is declared\n * structurally here rather than as `StatewireThread.Extras[...]` so that the\n * published sources also compile against the unpatched upstream typings, where\n * those members are absent and the edit path's optional calls never run.\n */\nexport type DeepAgentPreparedMessageMetadata = Promise<Record<string, unknown> | undefined>;\n\n/** The pieces of the statewire thread extras the edit path reads. */\nexport interface DeepAgentStatewireExtras {\n /** The library's dispatch; it resolves with the host's reply to the command. */\n sendCommand: (\n command: StatewireThread.Command,\n options?: { preparedMetadata?: DeepAgentPreparedMessageMetadata | undefined },\n ) => Promise<unknown> | void;\n /** Optional on older hosts; capture before loading an edit's history anchor. */\n prepareMessageMetadata?: (() => DeepAgentPreparedMessageMetadata) | undefined;\n discardPreparedMessageMetadata?:\n | ((prepared: DeepAgentPreparedMessageMetadata) => void)\n | undefined;\n sendPreparedEdit?:\n | ((sourceId: string, prepared: DeepAgentPreparedMessageMetadata) => void)\n | undefined;\n isPreparingSubmission?: boolean;\n /** The composed thread state — its `messages` are the raw wire messages of the loaded window. */\n state: { messages?: readonly { id?: unknown }[] } | null;\n /** The mounted documents (the interest window), absent on a wire without documents. */\n documents?: StatewireThread.Extras['documents'];\n invoke?: StatewireThread.Extras['invoke'];\n /** The live run, when one is in flight; `epoch` advances on each dispatch. */\n runs: { runId?: string | null; epoch?: number };\n /** The host's replicated run status; absent on a surface that predates it. */\n status?: StatewireThread.ThreadStatus;\n}\n\n/**\n * The statewire extras of the nearest aui thread, or `null` when the thread is\n * not a statewire thread (e.g. a read-only subagent transcript runtime).\n * Gated on {@link hasStatewireThreadExtras} for the same reason the approval\n * card is: the library's own hooks throw on a non-statewire thread.\n */\nexport function readDeepAgentStatewireExtras(extras: unknown): DeepAgentStatewireExtras | null {\n if (!hasStatewireThreadExtras(extras)) return null;\n return extras as DeepAgentStatewireExtras;\n}\n\n/**\n * The host's parent of `sourceId`: the raw wire message directly before it\n * (`null` when it is the first of the whole transcript). Returns `undefined`\n * when `sourceId` is not in the raw list at all — an optimistic bubble the\n * host has not settled yet, a message below the loaded window, or a surface\n * whose state this module cannot read — or when it is the first loaded\n * message while older ones exist (`hasOlder`), so the caller can load more or\n * fall back to the library's own path instead of asserting a wrong anchor.\n */\nexport function resolveWireAnchorMessageId({\n wireMessages,\n sourceId,\n hasOlder = false,\n}: {\n wireMessages: readonly { id?: unknown }[] | undefined;\n sourceId: string;\n hasOlder?: boolean;\n}): string | null | undefined {\n if (!wireMessages) return undefined;\n const index = wireMessages.findIndex((message) => message?.id === sourceId);\n if (index < 0) return undefined;\n if (index === 0) return hasOlder ? undefined : null;\n const parentId = wireMessages[index - 1]?.id;\n return typeof parentId === 'string' ? parentId : undefined;\n}\n\n/**\n * Whether `sourceId`'s parent lies below the loaded window: the message is\n * absent from the raw list, or its first entry, while older messages exist.\n * The caller loads older history before an edit can be anchored.\n */\nexport function isSourceBelowLoadedWindow({\n wireMessages,\n sourceId,\n window,\n}: {\n wireMessages: readonly { id?: unknown }[] | undefined;\n sourceId: string;\n window: DeepAgentMessageWindow | null;\n}): boolean {\n if (!wireMessages || window === null || !window.hasOlder) return false;\n const index = wireMessages.findIndex((message) => message?.id === sourceId);\n return index <= 0;\n}\n\nconst readWindow = (extras: DeepAgentStatewireExtras) =>\n readDeepAgentMessageWindow(extras.documents?.list());\n\n/**\n * A fresh command / message id (uniqueness, not secrecy). `crypto.randomUUID`\n * where the host runtime has it — every browser, Bun, Node and Hermes with\n * webcrypto — and a v4-shaped fallback elsewhere, so the shared path never\n * throws on a runtime without full webcrypto.\n */\nfunction mintCommandId(): string {\n const webcrypto = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (typeof webcrypto?.randomUUID === 'function') return webcrypto.randomUUID();\n const hex = (): string => Math.floor(Math.random() * 0x10000).toString(16).padStart(4, '0');\n return `${hex()}${hex()}-${hex()}-4${hex().slice(1)}-${hex()}-${hex()}${hex()}${hex()}`;\n}\n\n/**\n * The `run/edit` command for resubmitting `sourceId` with `text`, anchored on\n * the host's own parent. `null` when the anchor cannot be resolved from the\n * replicated state.\n */\nexport function buildDeepAgentEditCommand({\n extras,\n sourceId,\n text,\n}: {\n extras: DeepAgentStatewireExtras;\n sourceId: string;\n text: string;\n}): StatewireThread.RunEditCommand | null {\n const runAnchorMessageId = resolveWireAnchorMessageId({\n wireMessages: extras.state?.messages,\n sourceId,\n hasOlder: readWindow(extras)?.hasOlder ?? false,\n });\n if (runAnchorMessageId === undefined) return null;\n return {\n type: 'run/edit',\n runId: extras.runs.runId ?? mintCommandId(),\n message: { id: mintCommandId(), role: 'user', parts: [{ type: 'text', text }] },\n sourceId,\n runAnchorMessageId,\n };\n}\n\n/**\n * Pages of older history stated while looking for an edited message's host\n * parent. Each page is up to a window's worth of messages, so an edit reaches\n * this many windows below what was loaded before it gives up.\n */\nexport const MAX_EDIT_ANCHOR_HISTORY_LOADS = 5;\n\n/**\n * The anchored `run/edit` for `sourceId`, stating older page interests one at\n * a time when the message's parent lies below the loaded window. Every page\n * is read back from the live documents (`readExtras` — the host mounts them\n * before it answers the statement), so the anchor is always the host's own\n * parent id.\n *\n * Resolves `null` — the caller's cue for the library's composer path, whose\n * failure it reports — when the thread is not a statewire thread, the host has\n * not settled the message, the history is exhausted without it, or the page\n * budget ({@link MAX_EDIT_ANCHOR_HISTORY_LOADS}) is spent. A synchronous\n * resolution never awaits: the first read either anchors at once or decides\n * that pages are needed.\n */\nexport async function resolveDeepAgentEditCommand({\n readExtras,\n sourceId,\n text,\n pageSize,\n maxHistoryLoads = MAX_EDIT_ANCHOR_HISTORY_LOADS,\n}: {\n /** The live thread extras — re-read per page so each anchor attempt sees the widened window. */\n readExtras: () => DeepAgentStatewireExtras | null;\n sourceId: string;\n text: string;\n pageSize?: number;\n maxHistoryLoads?: number;\n}): Promise<StatewireThread.RunEditCommand | null> {\n for (let page = 0; page <= maxHistoryLoads; page += 1) {\n const extras = readExtras();\n if (!extras) return null;\n const command = buildDeepAgentEditCommand({ extras, sourceId, text });\n if (command) return command;\n if (page === maxHistoryLoads || !extras.invoke) return null;\n const window = readWindow(extras);\n if (!isSourceBelowLoadedWindow({ wireMessages: extras.state?.messages, sourceId, window })) {\n return null;\n }\n const requests = window === null ? null : pageInterestRequests(window, pageSize);\n if (requests === null) return null;\n await extras.invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [requests]);\n }\n return null;\n}\n"],"names":[],"mappings":";;;AAoFO,SAAS,6BAA6B,QAAkD;AAC7F,MAAI,CAAC,yBAAyB,MAAM,EAAG,QAAO;AAC9C,SAAO;AACT;AAWO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAI8B;;AAC5B,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,QAAQ,aAAa,UAAU,CAAC,aAAY,mCAAS,QAAO,QAAQ;AAC1E,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,UAAU,EAAG,QAAO,WAAW,SAAY;AAC/C,QAAM,YAAW,kBAAa,QAAQ,CAAC,MAAtB,mBAAyB;AAC1C,SAAO,OAAO,aAAa,WAAW,WAAW;AACnD;AAOO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,GAIY;AACV,MAAI,CAAC,gBAAgB,WAAW,QAAQ,CAAC,OAAO,SAAU,QAAO;AACjE,QAAM,QAAQ,aAAa,UAAU,CAAC,aAAY,mCAAS,QAAO,QAAQ;AAC1E,SAAO,SAAS;AAClB;AAEA,MAAM,aAAa,CAAC,WAAA;;AAClB,qCAA2B,YAAO,cAAP,mBAAkB,MAAM;AAAA;AAQrD,SAAS,gBAAwB;AAC/B,QAAM,YAAa,WAA0D;AAC7E,MAAI,QAAO,uCAAW,gBAAe,WAAY,QAAO,UAAU,WAAA;AAClE,QAAM,MAAM,MAAc,KAAK,MAAM,KAAK,OAAA,IAAW,KAAO,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1F,SAAO,GAAG,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,IAAA,CAAK,KAAK,MAAM,MAAM,CAAC,CAAC,IAAI,IAAA,CAAK,IAAI,KAAK,GAAG,IAAA,CAAK,GAAG,IAAA,CAAK;AACvF;AAOO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF,GAI0C;;AACxC,QAAM,qBAAqB,2BAA2B;AAAA,IACpD,eAAc,YAAO,UAAP,mBAAc;AAAA,IAC5B;AAAA,IACA,YAAU,gBAAW,MAAM,MAAjB,mBAAoB,aAAY;AAAA,EAAA,CAC3C;AACD,MAAI,uBAAuB,OAAW,QAAO;AAC7C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,OAAO,KAAK,SAAS,cAAA;AAAA,IAC5B,SAAS,EAAE,IAAI,cAAA,GAAiB,MAAM,QAAQ,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAA,CAAM,EAAA;AAAA,IAC5E;AAAA,IACA;AAAA,EAAA;AAEJ;AAOO,MAAM,gCAAgC;AAgB7C,eAAsB,4BAA4B;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAOmD;;AACjD,WAAS,OAAO,GAAG,QAAQ,iBAAiB,QAAQ,GAAG;AACrD,UAAM,SAAS,WAAA;AACf,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,UAAU,0BAA0B,EAAE,QAAQ,UAAU,MAAM;AACpE,QAAI,QAAS,QAAO;AACpB,QAAI,SAAS,mBAAmB,CAAC,OAAO,OAAQ,QAAO;AACvD,UAAM,SAAS,WAAW,MAAM;AAChC,QAAI,CAAC,0BAA0B,EAAE,eAAc,YAAO,UAAP,mBAAc,UAAU,UAAU,OAAA,CAAQ,GAAG;AAC1F,aAAO;AAAA,IACT;AACA,UAAM,WAAW,WAAW,OAAO,OAAO,qBAAqB,QAAQ,QAAQ;AAC/E,QAAI,aAAa,KAAM,QAAO;AAC9B,UAAM,OAAO,OAAO,8BAA8B,OAAO,CAAC,QAAQ,CAAC;AAAA,EACrE;AACA,SAAO;AACT;"}
@@ -25,17 +25,32 @@ function readRetryableRunRefusal(error) {
25
25
  if (error === null || typeof error !== "object" || Array.isArray(error)) return null;
26
26
  return readRetryableDetail(error.detail);
27
27
  }
28
+ function readRetryableCommandRejection(error) {
29
+ const payload = statewireRejectionPayload(error);
30
+ return payload === null ? null : readRetryableDetail(payload.detail);
31
+ }
28
32
  function isLegacyThreadReadOnlyRejection(error) {
29
33
  const payload = statewireRejectionPayload(error);
30
34
  if ((payload == null ? void 0 : payload.status) !== 409) return false;
31
35
  const detail = payload.detail;
32
36
  return (detail == null ? void 0 : detail.code) === "legacy_thread_read_only";
33
37
  }
38
+ function isStatewireExpiredVerdict(error) {
39
+ const failure = error == null ? void 0 : error.failure;
40
+ return (failure == null ? void 0 : failure.fate) === "expired" && failure.cause === "server";
41
+ }
34
42
  function isStatewireChannelTeardown(error) {
35
43
  var _a;
36
44
  const cause = (_a = error == null ? void 0 : error.failure) == null ? void 0 : _a.cause;
37
45
  return typeof cause === "string" && cause !== "server";
38
46
  }
47
+ function statewireTransportOutcome(error) {
48
+ var _a, _b;
49
+ const cause = (_a = error == null ? void 0 : error.failure) == null ? void 0 : _a.cause;
50
+ if (typeof cause === "string" && cause !== "server") return cause;
51
+ if (isStatewireExpiredVerdict(error)) return "expired";
52
+ return ((_b = readRetryableCommandRejection(error)) == null ? void 0 : _b.code) ?? null;
53
+ }
39
54
  const sanitizeConnectionMessage = (message) => {
40
55
  const printable = Array.from(message, (character) => {
41
56
  const code = character.charCodeAt(0);
@@ -88,8 +103,11 @@ function unknownConnectionStatus(connection) {
88
103
  }
89
104
  exports.isLegacyThreadReadOnlyRejection = isLegacyThreadReadOnlyRejection;
90
105
  exports.isStatewireChannelTeardown = isStatewireChannelTeardown;
106
+ exports.isStatewireExpiredVerdict = isStatewireExpiredVerdict;
91
107
  exports.projectDeepAgentConnection = projectDeepAgentConnection;
108
+ exports.readRetryableCommandRejection = readRetryableCommandRejection;
92
109
  exports.readRetryableRunRefusal = readRetryableRunRefusal;
93
110
  exports.statewireRejectionDetailMessage = statewireRejectionDetailMessage;
94
111
  exports.statewireRejectionPayload = statewireRejectionPayload;
112
+ exports.statewireTransportOutcome = statewireTransportOutcome;
95
113
  //# sourceMappingURL=errors.cjs.map