@athenaintel/react 0.48.0 → 0.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +68 -10
  2. package/dist/agent-runtime/src/call-recap.cjs +319 -0
  3. package/dist/agent-runtime/src/call-recap.cjs.map +1 -0
  4. package/dist/agent-runtime/src/call-recap.d.ts +142 -0
  5. package/dist/agent-runtime/src/call-recap.js +319 -0
  6. package/dist/agent-runtime/src/call-recap.js.map +1 -0
  7. package/dist/agent-runtime/src/chat-outbox-store.cjs +1 -0
  8. package/dist/agent-runtime/src/chat-outbox-store.cjs.map +1 -1
  9. package/dist/agent-runtime/src/chat-outbox-store.d.ts +7 -0
  10. package/dist/agent-runtime/src/chat-outbox-store.js +1 -0
  11. package/dist/agent-runtime/src/chat-outbox-store.js.map +1 -1
  12. package/dist/agent-runtime/src/chat-outbox.cjs +46 -5
  13. package/dist/agent-runtime/src/chat-outbox.cjs.map +1 -1
  14. package/dist/agent-runtime/src/chat-outbox.d.ts +29 -0
  15. package/dist/agent-runtime/src/chat-outbox.js +46 -5
  16. package/dist/agent-runtime/src/chat-outbox.js.map +1 -1
  17. package/dist/agent-runtime/src/conversation-index.cjs.map +1 -1
  18. package/dist/agent-runtime/src/conversation-index.d.ts +7 -0
  19. package/dist/agent-runtime/src/conversation-index.js.map +1 -1
  20. package/dist/agent-runtime/src/conversation-list.cjs +2 -1
  21. package/dist/agent-runtime/src/conversation-list.cjs.map +1 -1
  22. package/dist/agent-runtime/src/conversation-list.d.ts +19 -3
  23. package/dist/agent-runtime/src/conversation-list.js +2 -1
  24. package/dist/agent-runtime/src/conversation-list.js.map +1 -1
  25. package/dist/agent-runtime/src/desktop-handoff.d.ts +10 -0
  26. package/dist/agent-runtime/src/edit-command.cjs +21 -0
  27. package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
  28. package/dist/agent-runtime/src/edit-command.d.ts +8 -0
  29. package/dist/agent-runtime/src/edit-command.js +21 -0
  30. package/dist/agent-runtime/src/edit-command.js.map +1 -1
  31. package/dist/agent-runtime/src/handoff-arrival.cjs +19 -0
  32. package/dist/agent-runtime/src/handoff-arrival.cjs.map +1 -1
  33. package/dist/agent-runtime/src/handoff-arrival.d.ts +18 -3
  34. package/dist/agent-runtime/src/handoff-arrival.js +20 -1
  35. package/dist/agent-runtime/src/handoff-arrival.js.map +1 -1
  36. package/dist/agent-runtime/src/handoff-cursor-id.cjs +6 -0
  37. package/dist/agent-runtime/src/handoff-cursor-id.cjs.map +1 -0
  38. package/dist/agent-runtime/src/handoff-cursor-id.d.ts +12 -0
  39. package/dist/agent-runtime/src/handoff-cursor-id.js +6 -0
  40. package/dist/agent-runtime/src/handoff-cursor-id.js.map +1 -0
  41. package/dist/agent-runtime/src/handoff-cursor.cjs +88 -0
  42. package/dist/agent-runtime/src/handoff-cursor.cjs.map +1 -0
  43. package/dist/agent-runtime/src/handoff-cursor.d.ts +120 -0
  44. package/dist/agent-runtime/src/handoff-cursor.js +88 -0
  45. package/dist/agent-runtime/src/handoff-cursor.js.map +1 -0
  46. package/dist/agent-runtime/src/index.d.ts +9 -7
  47. package/dist/agent-runtime/src/interrupts.cjs +28 -1
  48. package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
  49. package/dist/agent-runtime/src/interrupts.d.ts +21 -1
  50. package/dist/agent-runtime/src/interrupts.js +28 -1
  51. package/dist/agent-runtime/src/interrupts.js.map +1 -1
  52. package/dist/agent-runtime/src/model-context.cjs +3 -2
  53. package/dist/agent-runtime/src/model-context.cjs.map +1 -1
  54. package/dist/agent-runtime/src/model-context.d.ts +6 -1
  55. package/dist/agent-runtime/src/model-context.js +3 -2
  56. package/dist/agent-runtime/src/model-context.js.map +1 -1
  57. package/dist/agent-runtime/src/runtime.d.ts +3 -2
  58. package/dist/agent-runtime/src/send-recovery.cjs +37 -3
  59. package/dist/agent-runtime/src/send-recovery.cjs.map +1 -1
  60. package/dist/agent-runtime/src/send-recovery.d.ts +19 -0
  61. package/dist/agent-runtime/src/send-recovery.js +37 -3
  62. package/dist/agent-runtime/src/send-recovery.js.map +1 -1
  63. package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +14 -8
  64. package/dist/agent-runtime/src/thread-handoff-protocol.d.ts +227 -5
  65. package/dist/agent-runtime/src/thread-handoff.d.ts +1 -1
  66. package/dist/agent-runtime/src/use-conversation-list.cjs +3 -2
  67. package/dist/agent-runtime/src/use-conversation-list.cjs.map +1 -1
  68. package/dist/agent-runtime/src/use-conversation-list.d.ts +6 -0
  69. package/dist/agent-runtime/src/use-conversation-list.js +3 -2
  70. package/dist/agent-runtime/src/use-conversation-list.js.map +1 -1
  71. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs +21 -1
  72. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs.map +1 -1
  73. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.d.ts +8 -2
  74. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js +21 -1
  75. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js.map +1 -1
  76. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +24 -6
  77. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
  78. package/dist/agent-runtime/src/use-deep-agent-runtime.d.ts +17 -0
  79. package/dist/agent-runtime/src/use-deep-agent-runtime.js +24 -6
  80. package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
  81. package/dist/agent-runtime/src/use-deep-agent-voice.cjs +44 -9
  82. package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
  83. package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +34 -1
  84. package/dist/agent-runtime/src/use-deep-agent-voice.js +44 -9
  85. package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -1
  86. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +8 -3
  87. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
  88. package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +7 -0
  89. package/dist/agent-runtime/src/use-deep-agent-window-sync.js +9 -4
  90. package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
  91. package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +13 -2
  92. package/dist/agent-runtime/src/voice-earcon.cjs +139 -0
  93. package/dist/agent-runtime/src/voice-earcon.cjs.map +1 -0
  94. package/dist/agent-runtime/src/voice-earcon.d.ts +38 -0
  95. package/dist/agent-runtime/src/voice-earcon.js +139 -0
  96. package/dist/agent-runtime/src/voice-earcon.js.map +1 -0
  97. package/dist/agent-runtime/src/voice-media.cjs.map +1 -1
  98. package/dist/agent-runtime/src/voice-media.d.ts +14 -0
  99. package/dist/agent-runtime/src/voice-media.js.map +1 -1
  100. package/dist/agent-runtime/src/voice-presentation.cjs +2 -0
  101. package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -1
  102. package/dist/agent-runtime/src/voice-presentation.js +2 -0
  103. package/dist/agent-runtime/src/voice-presentation.js.map +1 -1
  104. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  105. package/dist/assets/AthenaAssetEmbed.d.ts +11 -5
  106. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  107. package/dist/assets/use-asset-embed.cjs.map +1 -1
  108. package/dist/assets/use-asset-embed.d.ts +12 -5
  109. package/dist/assets/use-asset-embed.js.map +1 -1
  110. package/dist/chat/AthenaChat.cjs +27 -12
  111. package/dist/chat/AthenaChat.cjs.map +1 -1
  112. package/dist/chat/AthenaChat.d.ts +13 -0
  113. package/dist/chat/AthenaChat.js +27 -12
  114. package/dist/chat/AthenaChat.js.map +1 -1
  115. package/dist/chat/AthenaPlanStrip.cjs +97 -0
  116. package/dist/chat/AthenaPlanStrip.cjs.map +1 -0
  117. package/dist/chat/AthenaPlanStrip.d.ts +15 -0
  118. package/dist/chat/AthenaPlanStrip.js +97 -0
  119. package/dist/chat/AthenaPlanStrip.js.map +1 -0
  120. package/dist/chat/StatewireApprovalCard.cjs +41 -6
  121. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  122. package/dist/chat/StatewireApprovalCard.d.ts +18 -1
  123. package/dist/chat/StatewireApprovalCard.js +42 -7
  124. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  125. package/dist/chat/StatewireQueuedMessages.cjs +56 -6
  126. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  127. package/dist/chat/StatewireQueuedMessages.d.ts +14 -0
  128. package/dist/chat/StatewireQueuedMessages.js +58 -8
  129. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  130. package/dist/chat/WaitingOnYouBanner.cjs +6 -2
  131. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
  132. package/dist/chat/WaitingOnYouBanner.js +7 -3
  133. package/dist/chat/WaitingOnYouBanner.js.map +1 -1
  134. package/dist/chat/chat-voice-controls.cjs +3 -1
  135. package/dist/chat/chat-voice-controls.cjs.map +1 -1
  136. package/dist/chat/chat-voice-controls.d.ts +6 -1
  137. package/dist/chat/chat-voice-controls.js +3 -1
  138. package/dist/chat/chat-voice-controls.js.map +1 -1
  139. package/dist/chat/super-grouping/SuperGroupingCard.cjs +4 -2
  140. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  141. package/dist/chat/super-grouping/SuperGroupingCard.js +4 -2
  142. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  143. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +4 -2
  144. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  145. package/dist/chat/super-grouping/SuperGroupingTranscript.js +4 -2
  146. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  147. package/dist/chat/super-grouping/activity-timeline.cjs +6 -4
  148. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  149. package/dist/chat/super-grouping/activity-timeline.d.ts +9 -1
  150. package/dist/chat/super-grouping/activity-timeline.js +6 -4
  151. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  152. package/dist/chat/super-grouping/index.d.ts +1 -1
  153. package/dist/chat/super-grouping/use-earlier-steps-unloaded.cjs +17 -0
  154. package/dist/chat/super-grouping/use-earlier-steps-unloaded.cjs.map +1 -0
  155. package/dist/chat/super-grouping/use-earlier-steps-unloaded.d.ts +8 -0
  156. package/dist/chat/super-grouping/use-earlier-steps-unloaded.js +17 -0
  157. package/dist/chat/super-grouping/use-earlier-steps-unloaded.js.map +1 -0
  158. package/dist/chat-ui/index.cjs +11 -0
  159. package/dist/chat-ui/index.cjs.map +1 -1
  160. package/dist/chat-ui/index.d.ts +3 -1
  161. package/dist/chat-ui/index.js +12 -1
  162. package/dist/chat-ui/index.js.map +1 -1
  163. package/dist/chat-ui/messages/from-the-call-card.cjs +124 -0
  164. package/dist/chat-ui/messages/from-the-call-card.cjs.map +1 -0
  165. package/dist/chat-ui/messages/from-the-call-card.d.ts +17 -0
  166. package/dist/chat-ui/messages/from-the-call-card.js +124 -0
  167. package/dist/chat-ui/messages/from-the-call-card.js.map +1 -0
  168. package/dist/chat-ui/rich-text/chat-link.cjs +1 -0
  169. package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -1
  170. package/dist/chat-ui/rich-text/chat-link.d.ts +4 -0
  171. package/dist/chat-ui/rich-text/chat-link.js +4 -3
  172. package/dist/chat-ui/rich-text/chat-link.js.map +1 -1
  173. package/dist/chat-ui/rich-text/chat-message-body.cjs +1 -0
  174. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
  175. package/dist/chat-ui/rich-text/chat-message-body.d.ts +12 -0
  176. package/dist/chat-ui/rich-text/chat-message-body.js +2 -1
  177. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
  178. package/dist/chat-ui/thread/chat-thread-shell.cjs +4 -0
  179. package/dist/chat-ui/thread/chat-thread-shell.cjs.map +1 -1
  180. package/dist/chat-ui/thread/chat-thread-shell.d.ts +4 -0
  181. package/dist/chat-ui/thread/chat-thread-shell.js +4 -0
  182. package/dist/chat-ui/thread/chat-thread-shell.js.map +1 -1
  183. package/dist/chat-ui/thread/handoff-cursor.cjs +216 -0
  184. package/dist/chat-ui/thread/handoff-cursor.cjs.map +1 -0
  185. package/dist/chat-ui/thread/handoff-cursor.d.ts +57 -0
  186. package/dist/chat-ui/thread/handoff-cursor.js +216 -0
  187. package/dist/chat-ui/thread/handoff-cursor.js.map +1 -0
  188. package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -0
  189. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  190. package/dist/chat-ui/thread/statewire-history-edge.d.ts +2 -0
  191. package/dist/chat-ui/thread/statewire-history-edge.js +3 -1
  192. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  193. package/dist/collab/client.cjs +15 -3
  194. package/dist/collab/client.cjs.map +1 -1
  195. package/dist/collab/client.d.ts +4 -2
  196. package/dist/collab/client.js +16 -4
  197. package/dist/collab/client.js.map +1 -1
  198. package/dist/composer/index.d.ts +1 -1
  199. package/dist/composer/useReadComposerDraft.cjs +26 -7
  200. package/dist/composer/useReadComposerDraft.cjs.map +1 -1
  201. package/dist/composer/useReadComposerDraft.d.ts +23 -1
  202. package/dist/composer/useReadComposerDraft.js +27 -8
  203. package/dist/composer/useReadComposerDraft.js.map +1 -1
  204. package/dist/index.cjs +5 -0
  205. package/dist/index.cjs.map +1 -1
  206. package/dist/index.d.ts +5 -2
  207. package/dist/index.js +6 -1
  208. package/dist/index.js.map +1 -1
  209. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs +207 -0
  210. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs.map +1 -0
  211. package/dist/keryx-http/src/unconfirmed-update-attributions.d.ts +141 -0
  212. package/dist/keryx-http/src/unconfirmed-update-attributions.js +207 -0
  213. package/dist/keryx-http/src/unconfirmed-update-attributions.js.map +1 -0
  214. package/dist/keryx-http/src/update-attributions.cjs +34 -4
  215. package/dist/keryx-http/src/update-attributions.cjs.map +1 -1
  216. package/dist/keryx-http/src/update-attributions.d.ts +36 -4
  217. package/dist/keryx-http/src/update-attributions.js +35 -5
  218. package/dist/keryx-http/src/update-attributions.js.map +1 -1
  219. package/dist/provider/AthenaProvider.cjs +4 -0
  220. package/dist/provider/AthenaProvider.cjs.map +1 -1
  221. package/dist/provider/AthenaProvider.d.ts +11 -1
  222. package/dist/provider/AthenaProvider.js +4 -0
  223. package/dist/provider/AthenaProvider.js.map +1 -1
  224. package/dist/react/package.json.cjs +1 -1
  225. package/dist/react/package.json.js +1 -1
  226. package/dist/styles.css +1 -1
  227. package/dist/threads/api.cjs.map +1 -1
  228. package/dist/threads/api.d.ts +15 -0
  229. package/dist/threads/api.js.map +1 -1
  230. package/dist/threads/conversation-list-fetcher.cjs +13 -5
  231. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  232. package/dist/threads/conversation-list-fetcher.d.ts +4 -1
  233. package/dist/threads/conversation-list-fetcher.js +13 -5
  234. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  235. package/dist/threads/statewire-thread-list.cjs +28 -6
  236. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  237. package/dist/threads/statewire-thread-list.d.ts +20 -8
  238. package/dist/threads/statewire-thread-list.js +28 -6
  239. package/dist/threads/statewire-thread-list.js.map +1 -1
  240. package/dist/threads/use-athena-sessions.cjs +27 -5
  241. package/dist/threads/use-athena-sessions.cjs.map +1 -1
  242. package/dist/threads/use-athena-sessions.d.ts +10 -5
  243. package/dist/threads/use-athena-sessions.js +28 -6
  244. package/dist/threads/use-athena-sessions.js.map +1 -1
  245. package/dist/threads/use-rest-conversation-history.d.ts +1 -0
  246. package/dist/tools/write-todos-tool-ui.cjs +8 -0
  247. package/dist/tools/write-todos-tool-ui.cjs.map +1 -1
  248. package/dist/tools/write-todos-tool-ui.d.ts +2 -0
  249. package/dist/tools/write-todos-tool-ui.js +8 -0
  250. package/dist/tools/write-todos-tool-ui.js.map +1 -1
  251. package/dist/workbench/session-workbench-panes.cjs +1 -8
  252. package/dist/workbench/session-workbench-panes.cjs.map +1 -1
  253. package/dist/workbench/session-workbench-panes.js +2 -9
  254. package/dist/workbench/session-workbench-panes.js.map +1 -1
  255. package/package.json +2 -2
@@ -53,6 +53,7 @@ function useDeepAgentRuntime(options) {
53
53
  session = false,
54
54
  useClient,
55
55
  runConfig,
56
+ onBeforeSendRetry,
56
57
  onError,
57
58
  onStateChange,
58
59
  onCommandChange,
@@ -69,6 +70,8 @@ function useDeepAgentRuntime(options) {
69
70
  onCommandChangeRef.current = onCommandChange;
70
71
  const diagnosticsRef = useRef(threadOptions.diagnostics);
71
72
  diagnosticsRef.current = threadOptions.diagnostics;
73
+ const onBeforeSendRetryRef = useRef(onBeforeSendRetry);
74
+ onBeforeSendRetryRef.current = onBeforeSendRetry;
72
75
  const clientRef = useRef(null);
73
76
  const failureStampsRef = useRef(
74
77
  void 0
@@ -103,7 +106,11 @@ function useDeepAgentRuntime(options) {
103
106
  var _a;
104
107
  return ((_a = failureStampsRef.current) == null ? void 0 : _a.call(failureStampsRef)) ?? null;
105
108
  },
106
- diagnostics: () => diagnosticsRef.current
109
+ diagnostics: () => diagnosticsRef.current,
110
+ beforeRetry: (messageId) => {
111
+ var _a;
112
+ return (_a = onBeforeSendRetryRef.current) == null ? void 0 : _a.call(onBeforeSendRetryRef, messageId);
113
+ }
107
114
  }),
108
115
  [attachId]
109
116
  );
@@ -165,13 +172,24 @@ function useDeepAgentRuntime(options) {
165
172
  getRunConfigRef.current = runConfig == null ? void 0 : runConfig.get;
166
173
  const hasRunConfig = runConfig !== void 0;
167
174
  const runConfigKey = runConfig == null ? void 0 : runConfig.key;
175
+ const subscribeRunConfigRef = useRef(runConfig == null ? void 0 : runConfig.subscribe);
176
+ subscribeRunConfigRef.current = runConfig == null ? void 0 : runConfig.subscribe;
177
+ const hasRunConfigSubscribe = (runConfig == null ? void 0 : runConfig.subscribe) !== void 0;
168
178
  useEffect(() => {
169
179
  if (!hasRunConfig) return void 0;
170
- return registerDeepAgentRunConfig(client, () => {
171
- var _a;
172
- return ((_a = getRunConfigRef.current) == null ? void 0 : _a.call(getRunConfigRef)) ?? {};
173
- });
174
- }, [client, hasRunConfig, runConfigKey]);
180
+ return registerDeepAgentRunConfig(
181
+ client,
182
+ () => {
183
+ var _a;
184
+ return ((_a = getRunConfigRef.current) == null ? void 0 : _a.call(getRunConfigRef)) ?? {};
185
+ },
186
+ hasRunConfigSubscribe ? (listener) => {
187
+ var _a;
188
+ return ((_a = subscribeRunConfigRef.current) == null ? void 0 : _a.call(subscribeRunConfigRef, listener)) ?? (() => {
189
+ });
190
+ } : void 0
191
+ );
192
+ }, [client, hasRunConfig, runConfigKey, hasRunConfigSubscribe]);
175
193
  return client;
176
194
  }
177
195
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"use-deep-agent-runtime.js","sources":["../../../../agent-runtime/src/use-deep-agent-runtime.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { isStatewireChannelTeardown } from './errors';\nimport { type DeepAgentModelContextHost, registerDeepAgentRunConfig } from './model-context';\nimport {\n isStorageQuotaError,\n QuotaSafeLocalStorageSession,\n readFailedSendStampsFromRecord,\n readPersistedFailedSendStamps,\n type SessionStorageDegradation,\n} from './quota-safe-local-storage-session';\nimport { createDeepAgentSendRecovery } from './send-recovery';\nimport { INITIAL_SESSION_LANE_EPOCH, nextSessionLaneEpoch } from './session-lane-scope';\nimport type { StatewireTransportOptions } from './statewire-transport';\nimport { readStatewireFailedSendLane } from './thread-extras';\nimport { isConfigSyncUnavailableRejection } from './use-deep-agent-config';\nimport {\n deepAgentAttachId,\n useDeepAgentThread,\n type UseDeepAgentThreadOptions,\n} from './use-deep-agent-thread';\n\n/** The thread element `useDeepAgentThread` produces — what the host's `useAui`\n * consumes (`useAui({ thread })`). */\nexport type DeepAgentThreadElement = ReturnType<typeof useDeepAgentThread>;\n\n/**\n * Durable command-lane persistence for the thread — where the lane identity and\n * the commands still awaiting a durable verdict live, so a reload resumes the\n * lane instead of minting a new client.\n *\n * - `local-storage`: browser hosts. Backed by {@link QuotaSafeLocalStorageSession}\n * (the upstream `LocalStorageSession` contract with Athena keys, legacy\n * metadata, and retention). A storage failure after mounting — the origin\n * out of quota after statewire's own sweep, a lockdown-mode write refusal —\n * degrades the session to memory-only persistence and is reported through\n * `onStorageDegraded` (default: one console warning); it never stops the\n * connection. Guarded by a shared availability probe: SSR, storage-blocked\n * iframes, and locked-down browsers keep the session process-local, exactly\n * as before the seam existed, while an origin that is merely full mounts the\n * session and lets it degrade. `key` scopes the stored lane — a thread id is\n * only meaningful against the backend (and, where relevant, principal) that\n * minted it, so include the sync-host URL.\n * - `session-store`: React Native hosts pass a hydrated library store backed\n * by AsyncStorage. Its synchronous `load()` is\n * also the send-recovery freshness gate's view of the persisted failed lane,\n * so a store that persists `failed` gets the same 24 h staleness rule as the\n * browser hosts.\n * - `pending`: the principal the lane is scoped by is not known yet (the\n * host's signed-in user is still loading). See {@link DeepAgentPendingSession}.\n * - `false`: process-local session (a reload mints a new client).\n *\n * A `local-storage` `key` names one principal's lanes (and one backend's). The\n * statewire session that mounts on a lane keeps the client id, lane clocks and\n * journal it loaded there, and everything in that journal is sent under\n * whatever credential the transport holds at its next attach. So a mounted\n * session never moves to another key: when the key starts naming another\n * principal's lanes for the same thread (another account signed in under a\n * mounted chat), the runtime starts a new lane epoch\n * (`UseDeepAgentThreadOptions['laneEpoch']`). The whole statewire thread\n * remounts on the new principal's lane and attaches afresh, as that lane's own\n * client, with nothing remembered or pooled from before. The previous\n * principal's record stays where it is, with its client id and whatever it had\n * journaled and not yet delivered: that principal's next mount on this browser\n * resends it as them, and the retention sweep retires it otherwise. It is never\n * sent as anyone else. A key for the same principal (a token refresh, a fresh\n * `key` function) and a thread switch keep the lane as it is. A host whose\n * principal arrives after mount passes `pending` until it does, never a\n * stand-in key: moving off a stand-in would be a principal change, a second\n * attach.\n */\nexport type DeepAgentSessionPersistence =\n | {\n kind: 'local-storage';\n key: (threadId: string) => string;\n onStorageDegraded?: (degradation: SessionStorageDegradation) => void;\n }\n | { kind: 'session-store'; store: NonNullable<StatewireTransportOptions['sessionStore']> }\n | DeepAgentPendingSession\n | false;\n\n/**\n * The durable lane's principal is not known yet. A lane belongs to one\n * principal and a mounted session cannot move to another lane (see\n * {@link DeepAgentSessionPersistence}), so nothing is mounted, nothing is\n * journaled and the transport stays parked — no dial — until the host passes\n * the real persistence. The park is an attach identity of its own: the attach\n * that follows makes every per-attach decision afresh (the remembered-snapshot\n * seed, the pooled hello, which the park leaves in the pool) and dials the real\n * lane once, as the thread's first attach. The park carries no connection\n * message, so it reads like a never-started chat rather than a paused one; a\n * host suspension, when one applies, wins.\n */\nexport type DeepAgentPendingSession = { kind: 'pending' };\n\n/**\n * The attach key a pending-lane park runs under. It differs from every key the\n * host passes, so the real attach after it is a new attach identity.\n */\nexport function pendingSessionAttachKey(attachKey: string | number | undefined): string {\n return `session-pending:${attachKey ?? ''}`;\n}\n\n/** Structural view of the browser storage global — this package compiles\n * without the DOM lib (React Native honesty; see `globals.d.ts`). */\ntype StorageGlobals = {\n localStorage?: {\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n };\n navigator?: { locks?: unknown };\n};\n\n/**\n * Whether a localStorage-backed session can persist here. A write probe, not a\n * `typeof` check: storage-blocked iframes (SDK embeds under third-party-storage\n * blocking) throw from the `localStorage` accessor itself, and lockdown-mode\n * origins throw from `setItem` — each of those must mean \"stay process-local\",\n * never a crash at mount. An origin that is merely out of quota still reads\n * and takes locks, so it mounts the session: {@link QuotaSafeLocalStorageSession}\n * degrades that to memory-only persistence and recovers when room frees,\n * where staying process-local would give up cross-tab sharing for the whole\n * page load over a condition that clears.\n *\n * The Web Locks API is probed too: the session storage serializes the shared\n * record across tabs through `navigator.locks` and refuses to mount without it\n * (the same precondition as the vendor `LocalStorageSession`), so an origin\n * with localStorage but no Web Locks — older WebViews, Safari before 15.4 —\n * must also stay process-local instead of throwing at mount.\n */\nexport function canPersistDeepAgentSession(): boolean {\n try {\n const globals = globalThis as StorageGlobals;\n if (!globals.navigator?.locks) return false;\n const storage = globals.localStorage;\n if (!storage) return false;\n const probeKey = '__athena_statewire_session_probe__';\n try {\n storage.setItem(probeKey, probeKey);\n storage.removeItem(probeKey);\n } catch (error) {\n return isStorageQuotaError(error);\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** A host session store's current record, or `null` when the read throws —\n * the recovery gate must never take the runtime down over a storage fault. */\nfunction loadSessionRecord(\n store: NonNullable<StatewireTransportOptions['sessionStore']>\n): ReturnType<NonNullable<StatewireTransportOptions['sessionStore']>['load']> | null {\n try {\n return store.load();\n } catch {\n return null;\n }\n}\n\n/**\n * The shared tail of every surface's transport error policy: channel teardown\n * is lifecycle noise (logged at debug), as is `config_sync_unavailable` — only\n * `config/*` writes emit it, and the host's config sync already handles it\n * through its own command promise (regionally-pinned workspaces run on\n * local-only config); everything else is a real transport error. Hosts with their own error chain\n * (paywall, telemetry, send-failure stores) end it with this so the fallback\n * logging stays uniform per surface.\n */\nexport function logUnhandledStatewireError(surface: string, error: Error): void {\n if (isStatewireChannelTeardown(error)) {\n console.debug(`[${surface}] statewire channel teardown:`, error);\n return;\n }\n if (isConfigSyncUnavailableRejection(error)) {\n console.debug(`[${surface}] config sync unavailable (handled by config sync):`, error);\n return;\n }\n console.error(`[${surface}] statewire transport error:`, error);\n}\n\n/**\n * The imperative thread-state slice of an assistant-ui client — structural so\n * web and native `useAui` results both satisfy it. The send-recovery layer\n * reads the statewire thread extras through it (the sanctioned imperative\n * pattern: `aui.thread().getState()` inside callbacks/effects, never render).\n */\nexport interface DeepAgentThreadStateClientHost {\n thread: () => { getState: () => { extras?: unknown } };\n subscribe: (listener: () => void) => () => void;\n}\n\n/** Deep-agent runConfig registration on the ambient model context (see\n * {@link registerDeepAgentRunConfig}). */\nexport interface DeepAgentRunConfigRegistration {\n /**\n * Builds the `runConfig` that drives the run. The core pulls it when the\n * model context notifies (mount and each key change); the server reads that\n * synced value at command admission. The per-render ref makes each pull use\n * the latest closure but does not trigger a pull itself.\n */\n get: () => Record<string, unknown>;\n /**\n * Re-registers the provider and notifies model-context sync when this value\n * changes (reference equality). Include getter inputs that can change while\n * mounted; they reach the server only after a key change triggers a pull.\n */\n key?: unknown;\n}\n\nexport interface UseDeepAgentRuntimeOptions<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n> extends Omit<UseDeepAgentThreadOptions, 'storage' | 'sessionStore' | 'laneEpoch'> {\n /** Host surface tag for the shared fallback error logging (e.g.\n * `'AthenaDeepAgent'`, `'AthenaSDK'`, `'ChromeStatewire'`). */\n surface: string;\n /** Durable command-lane persistence; defaults to process-local. */\n session?: DeepAgentSessionPersistence;\n /**\n * Builds the host's assistant-ui client from the thread element — pass the\n * platform `useAui` (web hosts from `@assistant-ui/react`, React Native\n * hosts from `@assistant-ui/react-native`; this package imports neither).\n * Called unconditionally on every render, so it MUST be (or call) a hook\n * following the rules of hooks — define it at module scope with a `use*`\n * name: `const useHostClient = (thread: DeepAgentThreadElement) =>\n * useAui({ thread })`.\n */\n useClient: (thread: DeepAgentThreadElement) => TClient;\n /** Registers the deep-agent runConfig on the client's model context. Hosts\n * that register elsewhere (e.g. a bridge component mounted under the\n * provider) omit it. */\n runConfig?: DeepAgentRunConfigRegistration;\n}\n\n/**\n * The configurable deep-agent statewire runtime — the one hook every Athena\n * chat surface (Olympus, `@athenaintel/react` SDK, Chrome extension, Office\n * add-in, mobile) mounts its assistant-ui client through.\n *\n * Composes the pieces every surface previously wired by hand:\n * `useDeepAgentThread` (attach lifecycle, auth parking, lane projection,\n * connection/running observers), durable session persistence with the shared\n * availability guard, transport-level send recovery (failed sends the server\n * never judged auto-retry under their original message id — `send-recovery.ts`),\n * the platform client via `useClient`, and the\n * runConfig model-context registration on provider notifications. Everything\n * host-specific — auth headers, run-config building, error policy, adapters,\n * diagnostics recorders — stays an option, so per-surface behavior is\n * configuration, not a fork.\n */\nexport function useDeepAgentRuntime<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n>(options: UseDeepAgentRuntimeOptions<TClient>): TClient {\n const {\n surface,\n session = false,\n useClient,\n runConfig,\n onError,\n onStateChange,\n onCommandChange,\n ...threadOptions\n } = options;\n\n // Probed once per mount, like every browser host did individually: an origin\n // that cannot persist stays process-local for the mount's lifetime.\n const [canPersistLocalSession] = useState(canPersistDeepAgentSession);\n\n // Per-render refs so the transport never remounts on a new closure identity\n // and always reads the latest policy.\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const surfaceRef = useRef(surface);\n surfaceRef.current = surface;\n const onStateChangeRef = useRef(onStateChange);\n onStateChangeRef.current = onStateChange;\n const onCommandChangeRef = useRef(onCommandChange);\n onCommandChangeRef.current = onCommandChange;\n const diagnosticsRef = useRef(threadOptions.diagnostics);\n diagnosticsRef.current = threadOptions.diagnostics;\n const clientRef = useRef<TClient | null>(null);\n\n // Where the persisted session record lives, for the recovery freshness gate\n // (read per sweep — undefined means \"no persistence, everything is fresh\").\n const failureStampsRef = useRef<(() => ReadonlyMap<string, number> | null) | undefined>(\n undefined\n );\n failureStampsRef.current =\n session === false || session.kind === 'pending'\n ? undefined\n : session.kind === 'local-storage'\n ? canPersistLocalSession\n ? () => readPersistedFailedSendStamps(session.key(threadOptions.threadId))\n : undefined\n : () => readFailedSendStampsFromRecord(loadSessionRecord(session.store));\n\n // A pending lane parks the thread under an attach identity of its own (see\n // `DeepAgentPendingSession`), so the attach once the lane is known is new.\n const sessionPending = session !== false && session.kind === 'pending';\n const attachKey = sessionPending\n ? pendingSessionAttachKey(threadOptions.attachKey)\n : threadOptions.attachKey;\n\n // The lane's scope, as the host's `key` names it: whose lanes this mount\n // runs on. A key that starts naming another principal's lanes starts a new\n // lane epoch, which remounts the whole thread on the new lane instead of\n // moving the mounted session there (see `DeepAgentSessionPersistence`).\n // Tracked whether or not this origin can persist: a process-local session\n // holds the previous principal's journal in memory just the same. A pending\n // lane keeps the last known scope, so a sign-out and a sign-in as someone\n // else with a gap between them is still a change.\n const laneKey = session !== false && session.kind === 'local-storage' ? session.key : null;\n const [laneEpochState, setLaneEpochState] = useState(() =>\n nextSessionLaneEpoch({\n previous: INITIAL_SESSION_LANE_EPOCH,\n threadId: threadOptions.threadId,\n key: laneKey,\n })\n );\n const nextLaneEpochState = nextSessionLaneEpoch({\n previous: laneEpochState,\n threadId: threadOptions.threadId,\n key: laneKey,\n });\n // React's derived-state update: re-renders at once with the new epoch, and\n // this render already uses it, so no render pairs the new key with the\n // mounted thread.\n if (nextLaneEpochState !== laneEpochState) setLaneEpochState(nextLaneEpochState);\n const laneEpoch = nextLaneEpochState.epoch;\n\n // Send recovery, keyed by attach identity exactly like the thread's session\n // (a switch, re-attach or new lane epoch gets fresh hydration gating and\n // retry budgets, so it never re-dispatches a previous principal's failed\n // sends). The controller re-dispatches failed sends the server never judged;\n // see `send-recovery.ts` for the contract.\n const attachId = deepAgentAttachId({ threadId: threadOptions.threadId, attachKey, laneEpoch });\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by attach identity on purpose; every other input is read through a per-render ref.\n const sendRecovery = useMemo(\n () =>\n createDeepAgentSendRecovery({\n lane: () => {\n const client = clientRef.current;\n if (!client) return null;\n return readStatewireFailedSendLane(client.thread().getState().extras);\n },\n failureStamps: () => failureStampsRef.current?.() ?? null,\n diagnostics: () => diagnosticsRef.current,\n }),\n [attachId]\n );\n useEffect(() => () => sendRecovery.dispose(), [sendRecovery]);\n const sendRecoveryRef = useRef(sendRecovery);\n sendRecoveryRef.current = sendRecovery;\n\n const thread = useDeepAgentThread({\n ...threadOptions,\n ...(attachKey !== undefined && { attachKey }),\n laneEpoch,\n // Parked without a message (a host suspension, when one applies, wins):\n // nothing dials and nothing journals until the lane is known. What paints\n // the first frame is the real attach's to report, not the park's.\n ...(sessionPending && {\n suspended: threadOptions.suspended || {},\n onAttachSource: undefined,\n }),\n // A pooled dial for the thread is the real attach's to take: a park that\n // only waits for the lane neither takes nor discards it. A host suspension\n // still discards it, as it does without a pending lane.\n ...(sessionPending && !threadOptions.suspended && { prewarmPool: undefined }),\n ...(session !== false &&\n session.kind === 'local-storage' &&\n canPersistLocalSession && {\n storage: {\n session: QuotaSafeLocalStorageSession({\n threadId: threadOptions.threadId,\n key: session.key,\n ...(session.onStorageDegraded === undefined\n ? {}\n : { onStorageDegraded: session.onStorageDegraded }),\n }),\n },\n }),\n ...(session !== false && session.kind === 'session-store' && { sessionStore: session.store }),\n onStateChange: (state, ctx) => {\n sendRecoveryRef.current.observeStateChange(state);\n onStateChangeRef.current?.(state, ctx);\n },\n onCommandChange: (update, ctx) => {\n sendRecoveryRef.current.observeCommand(update);\n onCommandChangeRef.current?.(update, ctx);\n },\n onError: (error) => {\n const handler = onErrorRef.current;\n if (handler) {\n handler(error);\n return;\n }\n logUnhandledStatewireError(surfaceRef.current, error);\n },\n });\n\n const client = useClient(thread);\n clientRef.current = client;\n\n // A locked shared journal can publish failed history after onCommandChange,\n // with no canonical state change to wake recovery. Observe the client that\n // owns the visible failed lane; the controller guards in-flight retries.\n useEffect(() => {\n const unsubscribe = client.subscribe(() => sendRecovery.observeFailedLaneChange());\n sendRecovery.observeFailedLaneChange();\n return unsubscribe;\n }, [client, sendRecovery]);\n\n // Commands travel bare, so the runConfig that drives the run rides the\n // ambient model context: `contextSync: 'command-send'` mirrors it to the\n // server, which reads `config.runConfig` at command admission. The core pulls\n // on model-context notifications; the per-render ref ensures each pull uses\n // the latest closure but does not trigger a pull itself.\n const getRunConfigRef = useRef(runConfig?.get);\n getRunConfigRef.current = runConfig?.get;\n const hasRunConfig = runConfig !== undefined;\n const runConfigKey = runConfig?.key;\n useEffect(() => {\n if (!hasRunConfig) return undefined;\n return registerDeepAgentRunConfig(client, () => getRunConfigRef.current?.() ?? {});\n }, [client, hasRunConfig, runConfigKey]);\n\n return client;\n}\n\n"],"names":["client"],"mappings":";;;;;;;;;AAkGO,SAAS,wBAAwB,WAAgD;AACtF,SAAO,mBAAmB,aAAa,EAAE;AAC3C;AA6BO,SAAS,6BAAsC;;AACpD,MAAI;AACF,UAAM,UAAU;AAChB,QAAI,GAAC,aAAQ,cAAR,mBAAmB,OAAO,QAAO;AACtC,UAAM,UAAU,QAAQ;AACxB,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,WAAW;AACjB,QAAI;AACF,cAAQ,QAAQ,UAAU,QAAQ;AAClC,cAAQ,WAAW,QAAQ;AAAA,IAC7B,SAAS,OAAO;AACd,aAAO,oBAAoB,KAAK;AAAA,IAClC;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIA,SAAS,kBACP,OACmF;AACnF,MAAI;AACF,WAAO,MAAM,KAAA;AAAA,EACf,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAWO,SAAS,2BAA2B,SAAiB,OAAoB;AAC9E,MAAI,2BAA2B,KAAK,GAAG;AACrC,YAAQ,MAAM,IAAI,OAAO,iCAAiC,KAAK;AAC/D;AAAA,EACF;AACA,MAAI,iCAAiC,KAAK,GAAG;AAC3C,YAAQ,MAAM,IAAI,OAAO,uDAAuD,KAAK;AACrF;AAAA,EACF;AACA,UAAQ,MAAM,IAAI,OAAO,gCAAgC,KAAK;AAChE;AAuEO,SAAS,oBAEd,SAAuD;AACvD,QAAM;AAAA,IACJ;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,IACD;AAIJ,QAAM,CAAC,sBAAsB,IAAI,SAAS,0BAA0B;AAIpE,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,mBAAmB,OAAO,aAAa;AAC7C,mBAAiB,UAAU;AAC3B,QAAM,qBAAqB,OAAO,eAAe;AACjD,qBAAmB,UAAU;AAC7B,QAAM,iBAAiB,OAAO,cAAc,WAAW;AACvD,iBAAe,UAAU,cAAc;AACvC,QAAM,YAAY,OAAuB,IAAI;AAI7C,QAAM,mBAAmB;AAAA,IACvB;AAAA,EAAA;AAEF,mBAAiB,UACf,YAAY,SAAS,QAAQ,SAAS,YAClC,SACA,QAAQ,SAAS,kBACf,yBACE,MAAM,8BAA8B,QAAQ,IAAI,cAAc,QAAQ,CAAC,IACvE,SACF,MAAM,+BAA+B,kBAAkB,QAAQ,KAAK,CAAC;AAI7E,QAAM,iBAAiB,YAAY,SAAS,QAAQ,SAAS;AAC7D,QAAM,YAAY,iBACd,wBAAwB,cAAc,SAAS,IAC/C,cAAc;AAUlB,QAAM,UAAU,YAAY,SAAS,QAAQ,SAAS,kBAAkB,QAAQ,MAAM;AACtF,QAAM,CAAC,gBAAgB,iBAAiB,IAAI;AAAA,IAAS,MACnD,qBAAqB;AAAA,MACnB,UAAU;AAAA,MACV,UAAU,cAAc;AAAA,MACxB,KAAK;AAAA,IAAA,CACN;AAAA,EAAA;AAEH,QAAM,qBAAqB,qBAAqB;AAAA,IAC9C,UAAU;AAAA,IACV,UAAU,cAAc;AAAA,IACxB,KAAK;AAAA,EAAA,CACN;AAID,MAAI,uBAAuB,eAAgB,mBAAkB,kBAAkB;AAC/E,QAAM,YAAY,mBAAmB;AAOrC,QAAM,WAAW,kBAAkB,EAAE,UAAU,cAAc,UAAU,WAAW,WAAW;AAE7F,QAAM,eAAe;AAAA,IACnB,MACE,4BAA4B;AAAA,MAC1B,MAAM,MAAM;AACV,cAAMA,UAAS,UAAU;AACzB,YAAI,CAACA,QAAQ,QAAO;AACpB,eAAO,4BAA4BA,QAAO,OAAA,EAAS,SAAA,EAAW,MAAM;AAAA,MACtE;AAAA,MACA,eAAe,MAAA;;AAAM,uCAAiB,YAAjB,8CAAgC;AAAA;AAAA,MACrD,aAAa,MAAM,eAAe;AAAA,IAAA,CACnC;AAAA,IACH,CAAC,QAAQ;AAAA,EAAA;AAEX,YAAU,MAAM,MAAM,aAAa,WAAW,CAAC,YAAY,CAAC;AAC5D,QAAM,kBAAkB,OAAO,YAAY;AAC3C,kBAAgB,UAAU;AAE1B,QAAM,SAAS,mBAAmB;AAAA,IAChC,GAAG;AAAA,IACH,GAAI,cAAc,UAAa,EAAE,UAAA;AAAA,IACjC;AAAA;AAAA;AAAA;AAAA,IAIA,GAAI,kBAAkB;AAAA,MACpB,WAAW,cAAc,aAAa,CAAA;AAAA,MACtC,gBAAgB;AAAA,IAAA;AAAA;AAAA;AAAA;AAAA,IAKlB,GAAI,kBAAkB,CAAC,cAAc,aAAa,EAAE,aAAa,OAAA;AAAA,IACjE,GAAI,YAAY,SACd,QAAQ,SAAS,mBACjB,0BAA0B;AAAA,MACxB,SAAS;AAAA,QACP,SAAS,6BAA6B;AAAA,UACpC,UAAU,cAAc;AAAA,UACxB,KAAK,QAAQ;AAAA,UACb,GAAI,QAAQ,sBAAsB,SAC9B,CAAA,IACA,EAAE,mBAAmB,QAAQ,kBAAA;AAAA,QAAkB,CACpD;AAAA,MAAA;AAAA,IACH;AAAA,IAEJ,GAAI,YAAY,SAAS,QAAQ,SAAS,mBAAmB,EAAE,cAAc,QAAQ,MAAA;AAAA,IACrF,eAAe,CAAC,OAAO,QAAQ;;AAC7B,sBAAgB,QAAQ,mBAAmB,KAAK;AAChD,6BAAiB,YAAjB,0CAA2B,OAAO;AAAA,IACpC;AAAA,IACA,iBAAiB,CAAC,QAAQ,QAAQ;;AAChC,sBAAgB,QAAQ,eAAe,MAAM;AAC7C,+BAAmB,YAAnB,4CAA6B,QAAQ;AAAA,IACvC;AAAA,IACA,SAAS,CAAC,UAAU;AAClB,YAAM,UAAU,WAAW;AAC3B,UAAI,SAAS;AACX,gBAAQ,KAAK;AACb;AAAA,MACF;AACA,iCAA2B,WAAW,SAAS,KAAK;AAAA,IACtD;AAAA,EAAA,CACD;AAED,QAAM,SAAS,UAAU,MAAM;AAC/B,YAAU,UAAU;AAKpB,YAAU,MAAM;AACd,UAAM,cAAc,OAAO,UAAU,MAAM,aAAa,yBAAyB;AACjF,iBAAa,wBAAA;AACb,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,YAAY,CAAC;AAOzB,QAAM,kBAAkB,OAAO,uCAAW,GAAG;AAC7C,kBAAgB,UAAU,uCAAW;AACrC,QAAM,eAAe,cAAc;AACnC,QAAM,eAAe,uCAAW;AAChC,YAAU,MAAM;AACd,QAAI,CAAC,aAAc,QAAO;AAC1B,WAAO,2BAA2B,QAAQ,MAAA;;AAAM,oCAAgB,YAAhB,6CAA+B,CAAA;AAAA,KAAE;AAAA,EACnF,GAAG,CAAC,QAAQ,cAAc,YAAY,CAAC;AAEvC,SAAO;AACT;"}
1
+ {"version":3,"file":"use-deep-agent-runtime.js","sources":["../../../../agent-runtime/src/use-deep-agent-runtime.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { isStatewireChannelTeardown } from './errors';\nimport { type DeepAgentModelContextHost, registerDeepAgentRunConfig } from './model-context';\nimport {\n isStorageQuotaError,\n QuotaSafeLocalStorageSession,\n readFailedSendStampsFromRecord,\n readPersistedFailedSendStamps,\n type SessionStorageDegradation,\n} from './quota-safe-local-storage-session';\nimport { createDeepAgentSendRecovery } from './send-recovery';\nimport { INITIAL_SESSION_LANE_EPOCH, nextSessionLaneEpoch } from './session-lane-scope';\nimport type { StatewireTransportOptions } from './statewire-transport';\nimport { readStatewireFailedSendLane } from './thread-extras';\nimport { isConfigSyncUnavailableRejection } from './use-deep-agent-config';\nimport {\n deepAgentAttachId,\n useDeepAgentThread,\n type UseDeepAgentThreadOptions,\n} from './use-deep-agent-thread';\n\n/** The thread element `useDeepAgentThread` produces — what the host's `useAui`\n * consumes (`useAui({ thread })`). */\nexport type DeepAgentThreadElement = ReturnType<typeof useDeepAgentThread>;\n\n/**\n * Durable command-lane persistence for the thread — where the lane identity and\n * the commands still awaiting a durable verdict live, so a reload resumes the\n * lane instead of minting a new client.\n *\n * - `local-storage`: browser hosts. Backed by {@link QuotaSafeLocalStorageSession}\n * (the upstream `LocalStorageSession` contract with Athena keys, legacy\n * metadata, and retention). A storage failure after mounting — the origin\n * out of quota after statewire's own sweep, a lockdown-mode write refusal —\n * degrades the session to memory-only persistence and is reported through\n * `onStorageDegraded` (default: one console warning); it never stops the\n * connection. Guarded by a shared availability probe: SSR, storage-blocked\n * iframes, and locked-down browsers keep the session process-local, exactly\n * as before the seam existed, while an origin that is merely full mounts the\n * session and lets it degrade. `key` scopes the stored lane — a thread id is\n * only meaningful against the backend (and, where relevant, principal) that\n * minted it, so include the sync-host URL.\n * - `session-store`: React Native hosts pass a hydrated library store backed\n * by AsyncStorage. Its synchronous `load()` is\n * also the send-recovery freshness gate's view of the persisted failed lane,\n * so a store that persists `failed` gets the same 24 h staleness rule as the\n * browser hosts.\n * - `pending`: the principal the lane is scoped by is not known yet (the\n * host's signed-in user is still loading). See {@link DeepAgentPendingSession}.\n * - `false`: process-local session (a reload mints a new client).\n *\n * A `local-storage` `key` names one principal's lanes (and one backend's). The\n * statewire session that mounts on a lane keeps the client id, lane clocks and\n * journal it loaded there, and everything in that journal is sent under\n * whatever credential the transport holds at its next attach. So a mounted\n * session never moves to another key: when the key starts naming another\n * principal's lanes for the same thread (another account signed in under a\n * mounted chat), the runtime starts a new lane epoch\n * (`UseDeepAgentThreadOptions['laneEpoch']`). The whole statewire thread\n * remounts on the new principal's lane and attaches afresh, as that lane's own\n * client, with nothing remembered or pooled from before. The previous\n * principal's record stays where it is, with its client id and whatever it had\n * journaled and not yet delivered: that principal's next mount on this browser\n * resends it as them, and the retention sweep retires it otherwise. It is never\n * sent as anyone else. A key for the same principal (a token refresh, a fresh\n * `key` function) and a thread switch keep the lane as it is. A host whose\n * principal arrives after mount passes `pending` until it does, never a\n * stand-in key: moving off a stand-in would be a principal change, a second\n * attach.\n */\nexport type DeepAgentSessionPersistence =\n | {\n kind: 'local-storage';\n key: (threadId: string) => string;\n onStorageDegraded?: (degradation: SessionStorageDegradation) => void;\n }\n | { kind: 'session-store'; store: NonNullable<StatewireTransportOptions['sessionStore']> }\n | DeepAgentPendingSession\n | false;\n\n/**\n * The durable lane's principal is not known yet. A lane belongs to one\n * principal and a mounted session cannot move to another lane (see\n * {@link DeepAgentSessionPersistence}), so nothing is mounted, nothing is\n * journaled and the transport stays parked — no dial — until the host passes\n * the real persistence. The park is an attach identity of its own: the attach\n * that follows makes every per-attach decision afresh (the remembered-snapshot\n * seed, the pooled hello, which the park leaves in the pool) and dials the real\n * lane once, as the thread's first attach. The park carries no connection\n * message, so it reads like a never-started chat rather than a paused one; a\n * host suspension, when one applies, wins.\n */\nexport type DeepAgentPendingSession = { kind: 'pending' };\n\n/**\n * The attach key a pending-lane park runs under. It differs from every key the\n * host passes, so the real attach after it is a new attach identity.\n */\nexport function pendingSessionAttachKey(attachKey: string | number | undefined): string {\n return `session-pending:${attachKey ?? ''}`;\n}\n\n/** Structural view of the browser storage global — this package compiles\n * without the DOM lib (React Native honesty; see `globals.d.ts`). */\ntype StorageGlobals = {\n localStorage?: {\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n };\n navigator?: { locks?: unknown };\n};\n\n/**\n * Whether a localStorage-backed session can persist here. A write probe, not a\n * `typeof` check: storage-blocked iframes (SDK embeds under third-party-storage\n * blocking) throw from the `localStorage` accessor itself, and lockdown-mode\n * origins throw from `setItem` — each of those must mean \"stay process-local\",\n * never a crash at mount. An origin that is merely out of quota still reads\n * and takes locks, so it mounts the session: {@link QuotaSafeLocalStorageSession}\n * degrades that to memory-only persistence and recovers when room frees,\n * where staying process-local would give up cross-tab sharing for the whole\n * page load over a condition that clears.\n *\n * The Web Locks API is probed too: the session storage serializes the shared\n * record across tabs through `navigator.locks` and refuses to mount without it\n * (the same precondition as the vendor `LocalStorageSession`), so an origin\n * with localStorage but no Web Locks — older WebViews, Safari before 15.4 —\n * must also stay process-local instead of throwing at mount.\n */\nexport function canPersistDeepAgentSession(): boolean {\n try {\n const globals = globalThis as StorageGlobals;\n if (!globals.navigator?.locks) return false;\n const storage = globals.localStorage;\n if (!storage) return false;\n const probeKey = '__athena_statewire_session_probe__';\n try {\n storage.setItem(probeKey, probeKey);\n storage.removeItem(probeKey);\n } catch (error) {\n return isStorageQuotaError(error);\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** A host session store's current record, or `null` when the read throws —\n * the recovery gate must never take the runtime down over a storage fault. */\nfunction loadSessionRecord(\n store: NonNullable<StatewireTransportOptions['sessionStore']>\n): ReturnType<NonNullable<StatewireTransportOptions['sessionStore']>['load']> | null {\n try {\n return store.load();\n } catch {\n return null;\n }\n}\n\n/**\n * The shared tail of every surface's transport error policy: channel teardown\n * is lifecycle noise (logged at debug), as is `config_sync_unavailable` — only\n * `config/*` writes emit it, and the host's config sync already handles it\n * through its own command promise (regionally-pinned workspaces run on\n * local-only config); everything else is a real transport error. Hosts with their own error chain\n * (paywall, telemetry, send-failure stores) end it with this so the fallback\n * logging stays uniform per surface.\n */\nexport function logUnhandledStatewireError(surface: string, error: Error): void {\n if (isStatewireChannelTeardown(error)) {\n console.debug(`[${surface}] statewire channel teardown:`, error);\n return;\n }\n if (isConfigSyncUnavailableRejection(error)) {\n console.debug(`[${surface}] config sync unavailable (handled by config sync):`, error);\n return;\n }\n console.error(`[${surface}] statewire transport error:`, error);\n}\n\n/**\n * The imperative thread-state slice of an assistant-ui client — structural so\n * web and native `useAui` results both satisfy it. The send-recovery layer\n * reads the statewire thread extras through it (the sanctioned imperative\n * pattern: `aui.thread().getState()` inside callbacks/effects, never render).\n */\nexport interface DeepAgentThreadStateClientHost {\n thread: () => { getState: () => { extras?: unknown } };\n subscribe: (listener: () => void) => () => void;\n}\n\n/** Deep-agent runConfig registration on the ambient model context (see\n * {@link registerDeepAgentRunConfig}). */\nexport interface DeepAgentRunConfigRegistration {\n /**\n * Builds the `runConfig` that drives the run. The core pulls it when the\n * model context notifies (mount and each key change); the server reads that\n * synced value at command admission. The per-render ref makes each pull use\n * the latest closure but does not trigger a pull itself.\n */\n get: () => Record<string, unknown>;\n /**\n * Re-registers the provider and notifies model-context sync when this value\n * changes (reference equality). Include getter inputs that can change while\n * mounted; they reach the server only after a key change triggers a pull.\n */\n key?: unknown;\n /**\n * Notifies when a getter input changes outside React, so model-context sync\n * pulls `get` at once, before a command sent right after leaves (Olympus\n * stages a send's named agents right before `composer.send`). Subscribed\n * when the provider registers; a key change subscribes again.\n */\n subscribe?: (listener: () => void) => () => void;\n}\n\nexport interface UseDeepAgentRuntimeOptions<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n> extends Omit<UseDeepAgentThreadOptions, 'storage' | 'sessionStore' | 'laneEpoch'> {\n /** Host surface tag for the shared fallback error logging (e.g.\n * `'AthenaDeepAgent'`, `'AthenaSDK'`, `'ChromeStatewire'`). */\n surface: string;\n /** Durable command-lane persistence; defaults to process-local. */\n session?: DeepAgentSessionPersistence;\n /**\n * Builds the host's assistant-ui client from the thread element — pass the\n * platform `useAui` (web hosts from `@assistant-ui/react`, React Native\n * hosts from `@assistant-ui/react-native`; this package imports neither).\n * Called unconditionally on every render, so it MUST be (or call) a hook\n * following the rules of hooks — define it at module scope with a `use*`\n * name: `const useHostClient = (thread: DeepAgentThreadElement) =>\n * useAui({ thread })`.\n */\n useClient: (thread: DeepAgentThreadElement) => TClient;\n /** Registers the deep-agent runConfig on the client's model context. Hosts\n * that register elsewhere (e.g. a bridge component mounted under the\n * provider) omit it. */\n runConfig?: DeepAgentRunConfigRegistration;\n /**\n * Called right before send recovery dispatches a failed send again, with its\n * message id: the host puts back the per-send context that message was sent\n * with (Olympus stages the agents it named) before its command leaves. A\n * promise holds that retry (and the ones behind it) until it settles, then\n * the host is asked again: it can't put the context back while a send with\n * another one is still on its way (see `DeepAgentSendRecoveryOptions.beforeRetry`).\n * A function returned is called when the retry does not go after all.\n */\n onBeforeSendRetry?: (messageId: string) => void | PromiseLike<void> | (() => void);\n}\n\n/**\n * The configurable deep-agent statewire runtime — the one hook every Athena\n * chat surface (Olympus, `@athenaintel/react` SDK, Chrome extension, Office\n * add-in, mobile) mounts its assistant-ui client through.\n *\n * Composes the pieces every surface previously wired by hand:\n * `useDeepAgentThread` (attach lifecycle, auth parking, lane projection,\n * connection/running observers), durable session persistence with the shared\n * availability guard, transport-level send recovery (failed sends the server\n * never judged auto-retry under their original message id — `send-recovery.ts`),\n * the platform client via `useClient`, and the\n * runConfig model-context registration on provider notifications. Everything\n * host-specific — auth headers, run-config building, error policy, adapters,\n * diagnostics recorders — stays an option, so per-surface behavior is\n * configuration, not a fork.\n */\nexport function useDeepAgentRuntime<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n>(options: UseDeepAgentRuntimeOptions<TClient>): TClient {\n const {\n surface,\n session = false,\n useClient,\n runConfig,\n onBeforeSendRetry,\n onError,\n onStateChange,\n onCommandChange,\n ...threadOptions\n } = options;\n\n // Probed once per mount, like every browser host did individually: an origin\n // that cannot persist stays process-local for the mount's lifetime.\n const [canPersistLocalSession] = useState(canPersistDeepAgentSession);\n\n // Per-render refs so the transport never remounts on a new closure identity\n // and always reads the latest policy.\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const surfaceRef = useRef(surface);\n surfaceRef.current = surface;\n const onStateChangeRef = useRef(onStateChange);\n onStateChangeRef.current = onStateChange;\n const onCommandChangeRef = useRef(onCommandChange);\n onCommandChangeRef.current = onCommandChange;\n const diagnosticsRef = useRef(threadOptions.diagnostics);\n diagnosticsRef.current = threadOptions.diagnostics;\n const onBeforeSendRetryRef = useRef(onBeforeSendRetry);\n onBeforeSendRetryRef.current = onBeforeSendRetry;\n const clientRef = useRef<TClient | null>(null);\n\n // Where the persisted session record lives, for the recovery freshness gate\n // (read per sweep — undefined means \"no persistence, everything is fresh\").\n const failureStampsRef = useRef<(() => ReadonlyMap<string, number> | null) | undefined>(\n undefined\n );\n failureStampsRef.current =\n session === false || session.kind === 'pending'\n ? undefined\n : session.kind === 'local-storage'\n ? canPersistLocalSession\n ? () => readPersistedFailedSendStamps(session.key(threadOptions.threadId))\n : undefined\n : () => readFailedSendStampsFromRecord(loadSessionRecord(session.store));\n\n // A pending lane parks the thread under an attach identity of its own (see\n // `DeepAgentPendingSession`), so the attach once the lane is known is new.\n const sessionPending = session !== false && session.kind === 'pending';\n const attachKey = sessionPending\n ? pendingSessionAttachKey(threadOptions.attachKey)\n : threadOptions.attachKey;\n\n // The lane's scope, as the host's `key` names it: whose lanes this mount\n // runs on. A key that starts naming another principal's lanes starts a new\n // lane epoch, which remounts the whole thread on the new lane instead of\n // moving the mounted session there (see `DeepAgentSessionPersistence`).\n // Tracked whether or not this origin can persist: a process-local session\n // holds the previous principal's journal in memory just the same. A pending\n // lane keeps the last known scope, so a sign-out and a sign-in as someone\n // else with a gap between them is still a change.\n const laneKey = session !== false && session.kind === 'local-storage' ? session.key : null;\n const [laneEpochState, setLaneEpochState] = useState(() =>\n nextSessionLaneEpoch({\n previous: INITIAL_SESSION_LANE_EPOCH,\n threadId: threadOptions.threadId,\n key: laneKey,\n })\n );\n const nextLaneEpochState = nextSessionLaneEpoch({\n previous: laneEpochState,\n threadId: threadOptions.threadId,\n key: laneKey,\n });\n // React's derived-state update: re-renders at once with the new epoch, and\n // this render already uses it, so no render pairs the new key with the\n // mounted thread.\n if (nextLaneEpochState !== laneEpochState) setLaneEpochState(nextLaneEpochState);\n const laneEpoch = nextLaneEpochState.epoch;\n\n // Send recovery, keyed by attach identity exactly like the thread's session\n // (a switch, re-attach or new lane epoch gets fresh hydration gating and\n // retry budgets, so it never re-dispatches a previous principal's failed\n // sends). The controller re-dispatches failed sends the server never judged;\n // see `send-recovery.ts` for the contract.\n const attachId = deepAgentAttachId({ threadId: threadOptions.threadId, attachKey, laneEpoch });\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by attach identity on purpose; every other input is read through a per-render ref.\n const sendRecovery = useMemo(\n () =>\n createDeepAgentSendRecovery({\n lane: () => {\n const client = clientRef.current;\n if (!client) return null;\n return readStatewireFailedSendLane(client.thread().getState().extras);\n },\n failureStamps: () => failureStampsRef.current?.() ?? null,\n diagnostics: () => diagnosticsRef.current,\n beforeRetry: (messageId) => onBeforeSendRetryRef.current?.(messageId),\n }),\n [attachId]\n );\n useEffect(() => () => sendRecovery.dispose(), [sendRecovery]);\n const sendRecoveryRef = useRef(sendRecovery);\n sendRecoveryRef.current = sendRecovery;\n\n const thread = useDeepAgentThread({\n ...threadOptions,\n ...(attachKey !== undefined && { attachKey }),\n laneEpoch,\n // Parked without a message (a host suspension, when one applies, wins):\n // nothing dials and nothing journals until the lane is known. What paints\n // the first frame is the real attach's to report, not the park's.\n ...(sessionPending && {\n suspended: threadOptions.suspended || {},\n onAttachSource: undefined,\n }),\n // A pooled dial for the thread is the real attach's to take: a park that\n // only waits for the lane neither takes nor discards it. A host suspension\n // still discards it, as it does without a pending lane.\n ...(sessionPending && !threadOptions.suspended && { prewarmPool: undefined }),\n ...(session !== false &&\n session.kind === 'local-storage' &&\n canPersistLocalSession && {\n storage: {\n session: QuotaSafeLocalStorageSession({\n threadId: threadOptions.threadId,\n key: session.key,\n ...(session.onStorageDegraded === undefined\n ? {}\n : { onStorageDegraded: session.onStorageDegraded }),\n }),\n },\n }),\n ...(session !== false && session.kind === 'session-store' && { sessionStore: session.store }),\n onStateChange: (state, ctx) => {\n sendRecoveryRef.current.observeStateChange(state);\n onStateChangeRef.current?.(state, ctx);\n },\n onCommandChange: (update, ctx) => {\n sendRecoveryRef.current.observeCommand(update);\n onCommandChangeRef.current?.(update, ctx);\n },\n onError: (error) => {\n const handler = onErrorRef.current;\n if (handler) {\n handler(error);\n return;\n }\n logUnhandledStatewireError(surfaceRef.current, error);\n },\n });\n\n const client = useClient(thread);\n clientRef.current = client;\n\n // A locked shared journal can publish failed history after onCommandChange,\n // with no canonical state change to wake recovery. Observe the client that\n // owns the visible failed lane; the controller guards in-flight retries.\n useEffect(() => {\n const unsubscribe = client.subscribe(() => sendRecovery.observeFailedLaneChange());\n sendRecovery.observeFailedLaneChange();\n return unsubscribe;\n }, [client, sendRecovery]);\n\n // Commands travel bare, so the runConfig that drives the run rides the\n // ambient model context: `contextSync: 'command-send'` mirrors it to the\n // server, which reads `config.runConfig` at command admission. The core pulls\n // on model-context notifications; the per-render ref ensures each pull uses\n // the latest closure but does not trigger a pull itself.\n const getRunConfigRef = useRef(runConfig?.get);\n getRunConfigRef.current = runConfig?.get;\n const hasRunConfig = runConfig !== undefined;\n const runConfigKey = runConfig?.key;\n // A host's own notifications (inputs that change outside React) pull too.\n const subscribeRunConfigRef = useRef(runConfig?.subscribe);\n subscribeRunConfigRef.current = runConfig?.subscribe;\n const hasRunConfigSubscribe = runConfig?.subscribe !== undefined;\n useEffect(() => {\n if (!hasRunConfig) return undefined;\n return registerDeepAgentRunConfig(\n client,\n () => getRunConfigRef.current?.() ?? {},\n hasRunConfigSubscribe\n ? (listener) => subscribeRunConfigRef.current?.(listener) ?? (() => {})\n : undefined\n );\n }, [client, hasRunConfig, runConfigKey, hasRunConfigSubscribe]);\n\n return client;\n}\n\n"],"names":["client"],"mappings":"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"}
@@ -421,6 +421,8 @@ const useDeepAgentVoice = (options = {}) => {
421
421
  modelRef.current = options.model;
422
422
  const onDiagnosticRef = require$$0.useRef(options.onDiagnostic);
423
423
  onDiagnosticRef.current = options.onDiagnostic;
424
+ const onCallMovedRef = require$$0.useRef(options.onCallMoved);
425
+ onCallMovedRef.current = options.onCallMoved;
424
426
  const timeoutsRef = require$$0.useRef(options.timeouts);
425
427
  timeoutsRef.current = options.timeouts;
426
428
  const phaseRef = require$$0.useRef("idle");
@@ -497,7 +499,15 @@ const useDeepAgentVoice = (options = {}) => {
497
499
  activeAudioSessionRef.current = null;
498
500
  audioSessionIdleRef.current = audioSessionIdleRef.current.then(() => session.stop()).catch(() => void 0);
499
501
  }, []);
500
- const teardown = require$$0.useCallback(() => {
502
+ const notifyCallMoved = require$$0.useCallback((move) => {
503
+ const observer = onCallMovedRef.current;
504
+ if (!observer) return;
505
+ try {
506
+ void Promise.resolve(observer(move)).catch(() => void 0);
507
+ } catch {
508
+ }
509
+ }, []);
510
+ const teardown = require$$0.useCallback((options2) => {
501
511
  attemptRef.current += 1;
502
512
  const livekitAbort = livekitAbortRef.current;
503
513
  livekitAbortRef.current = null;
@@ -506,7 +516,7 @@ const useDeepAgentVoice = (options = {}) => {
506
516
  var _a, _b;
507
517
  return (_b = (_a = startingAudioSessionRef.current) == null ? void 0 : _a.cancelStart) == null ? void 0 : _b.call(_a);
508
518
  });
509
- releaseAudioSession();
519
+ if ((options2 == null ? void 0 : options2.keepAudioSession) !== true) releaseAudioSession();
510
520
  const releasePeerListeners = releasePeerListenersRef.current;
511
521
  releasePeerListenersRef.current = null;
512
522
  releaseSafely(() => releasePeerListeners == null ? void 0 : releasePeerListeners());
@@ -558,6 +568,7 @@ const useDeepAgentVoice = (options = {}) => {
558
568
  var _a;
559
569
  const surface = ((_a = voiceOwnerRef.current) == null ? void 0 : _a.surface) ?? "web";
560
570
  report("moved", attemptRef.current, { toSurface: surface });
571
+ notifyCallMoved({ direction: "away", surface });
561
572
  teardown();
562
573
  ownClientIdRef.current = null;
563
574
  micMutedRef.current = false;
@@ -566,7 +577,7 @@ const useDeepAgentVoice = (options = {}) => {
566
577
  setFailure(null);
567
578
  setPhase("idle");
568
579
  setMovedTo({ surface });
569
- }, [report, teardown]);
580
+ }, [notifyCallMoved, report, teardown]);
570
581
  const ownerMovedAway = require$$0.useCallback(() => {
571
582
  const own = ownClientIdRef.current;
572
583
  const current = wireOwnerIdRef.current;
@@ -577,11 +588,17 @@ const useDeepAgentVoice = (options = {}) => {
577
588
  attempt,
578
589
  audioSession
579
590
  }) => {
591
+ const held = activeAudioSessionRef.current;
592
+ if (held !== null && held !== audioSession) releaseAudioSession();
580
593
  await audioSessionIdleRef.current;
581
594
  if (attemptRef.current !== attempt) {
582
595
  report("stale-attempt", attempt, { stage: "audio-session" });
583
596
  return false;
584
597
  }
598
+ if (activeAudioSessionRef.current === audioSession) {
599
+ report("audio-session-ready", attempt, { reused: true });
600
+ return true;
601
+ }
585
602
  startingAudioSessionRef.current = audioSession;
586
603
  try {
587
604
  await audioSession.start();
@@ -620,6 +637,11 @@ const useDeepAgentVoice = (options = {}) => {
620
637
  return;
621
638
  }
622
639
  onDisconnectedAfterJoin(reason);
640
+ },
641
+ // Telemetry about this attempt's room stays this attempt's even after
642
+ // it ended: the room's closing summary arrives after teardown.
643
+ onEvent: (event) => {
644
+ report("media-event", attempt, { ...event.data, type: event.type });
623
645
  }
624
646
  });
625
647
  if (attemptRef.current !== attempt) {
@@ -997,13 +1019,14 @@ const useDeepAgentVoice = (options = {}) => {
997
1019
  handleMovedAway();
998
1020
  return;
999
1021
  }
1022
+ const restarting = !autoRestartedRef.current;
1000
1023
  report("media-loss", attempt, {
1001
1024
  reason,
1002
- restartBudgetLeft: !autoRestartedRef.current
1025
+ restartBudgetLeft: restarting
1003
1026
  });
1004
1027
  if (lastActiveTaskRef.current) setHandoffTask(lastActiveTaskRef.current);
1005
1028
  const ending = sendCommand({ type: "voice/end", attemptId: callAttemptIdRef.current }).catch(() => void 0);
1006
- teardown();
1029
+ teardown({ keepAudioSession: restarting });
1007
1030
  const recovery = attemptRef.current;
1008
1031
  setPhase("idle");
1009
1032
  let endTimer;
@@ -1059,6 +1082,8 @@ const useDeepAgentVoice = (options = {}) => {
1059
1082
  await startSession((options2 == null ? void 0 : options2.opening) === "recap" ? { opening: "recap" } : {});
1060
1083
  }, [startSession]);
1061
1084
  const runTransferHere = require$$0.useCallback(async () => {
1085
+ var _a;
1086
+ const fromSurface = ((_a = liveElsewhereRef.current) == null ? void 0 : _a.surface) ?? "web";
1062
1087
  const attempt = attemptRef.current += 1;
1063
1088
  const attemptId = mintVoiceId({ kind: "attempt" });
1064
1089
  callAttemptIdRef.current = attemptId;
@@ -1185,6 +1210,7 @@ const useDeepAgentVoice = (options = {}) => {
1185
1210
  report("live", attempt, { transport: "livekit", transfer: true });
1186
1211
  setPhase("live");
1187
1212
  setLiveSince(Date.now());
1213
+ notifyCallMoved({ direction: "here", surface: fromSurface });
1188
1214
  } catch (transferError) {
1189
1215
  if (attemptRef.current !== attempt) {
1190
1216
  stale();
@@ -1199,7 +1225,15 @@ const useDeepAgentVoice = (options = {}) => {
1199
1225
  } finally {
1200
1226
  startingRef.current = false;
1201
1227
  }
1202
- }, [acquireAudioSession, fail, joinVoiceRoom, releaseAudioSession, report, sendCommand]);
1228
+ }, [
1229
+ acquireAudioSession,
1230
+ fail,
1231
+ joinVoiceRoom,
1232
+ notifyCallMoved,
1233
+ releaseAudioSession,
1234
+ report,
1235
+ sendCommand
1236
+ ]);
1203
1237
  const transferHere = require$$0.useCallback(async () => {
1204
1238
  if (pcRef.current || wsRef.current || livekitRef.current || startingRef.current) return;
1205
1239
  if (liveElsewhereRef.current === null) return;
@@ -1287,13 +1321,14 @@ const useDeepAgentVoice = (options = {}) => {
1287
1321
  require$$0.useEffect(() => {
1288
1322
  var _a;
1289
1323
  if (phase !== "live" || !sessionGone) return;
1324
+ const restarting = !autoRestartedRef.current;
1290
1325
  report("server-ended", attemptRef.current, {
1291
- restartBudgetLeft: !autoRestartedRef.current
1326
+ restartBudgetLeft: restarting
1292
1327
  });
1293
1328
  if (lastActiveTaskRef.current) setHandoffTask(lastActiveTaskRef.current);
1294
- teardown();
1329
+ teardown({ keepAudioSession: restarting });
1295
1330
  setPhase("idle");
1296
- if (!autoRestartedRef.current) {
1331
+ if (restarting) {
1297
1332
  autoRestartedRef.current = true;
1298
1333
  report("auto-restart", attemptRef.current, { trigger: "server-ended" });
1299
1334
  void ((_a = startRef.current) == null ? void 0 : _a.call(startRef));