@athenaintel/react 0.28.0 → 0.30.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 (761) hide show
  1. package/README.md +418 -10
  2. package/dist/agent-runtime/src/asset-attachment.cjs.map +1 -0
  3. package/dist/agent-runtime/src/asset-attachment.js.map +1 -0
  4. package/dist/agent-runtime/src/auth-denial.cjs.map +1 -0
  5. package/dist/agent-runtime/src/auth-denial.js.map +1 -0
  6. package/dist/agent-runtime/src/client-tool-bridge.cjs.map +1 -0
  7. package/dist/agent-runtime/src/client-tool-bridge.js.map +1 -0
  8. package/dist/agent-runtime/src/client-tools.cjs.map +1 -0
  9. package/dist/agent-runtime/src/client-tools.js.map +1 -0
  10. package/dist/agent-runtime/src/composer-markdown.cjs +124 -0
  11. package/dist/agent-runtime/src/composer-markdown.cjs.map +1 -0
  12. package/dist/agent-runtime/src/composer-markdown.js +124 -0
  13. package/dist/agent-runtime/src/composer-markdown.js.map +1 -0
  14. package/dist/agent-runtime/src/conversation-index.cjs.map +1 -0
  15. package/dist/agent-runtime/src/conversation-index.js.map +1 -0
  16. package/dist/{packages/agent-runtime → agent-runtime}/src/conversation-list.cjs +36 -0
  17. package/dist/agent-runtime/src/conversation-list.cjs.map +1 -0
  18. package/dist/{packages/agent-runtime → agent-runtime}/src/conversation-list.js +36 -0
  19. package/dist/agent-runtime/src/conversation-list.js.map +1 -0
  20. package/dist/{packages/agent-runtime → agent-runtime}/src/deep-agent-transport-recipe.cjs +5 -3
  21. package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -0
  22. package/dist/{packages/agent-runtime → agent-runtime}/src/deep-agent-transport-recipe.js +6 -4
  23. package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -0
  24. package/dist/agent-runtime/src/edit-command.cjs.map +1 -0
  25. package/dist/agent-runtime/src/edit-command.js.map +1 -0
  26. package/dist/{packages/agent-runtime → agent-runtime}/src/errors.cjs +23 -0
  27. package/dist/agent-runtime/src/errors.cjs.map +1 -0
  28. package/dist/{packages/agent-runtime → agent-runtime}/src/errors.js +23 -0
  29. package/dist/agent-runtime/src/errors.js.map +1 -0
  30. package/dist/agent-runtime/src/execute-output.cjs +41 -0
  31. package/dist/agent-runtime/src/execute-output.cjs.map +1 -0
  32. package/dist/agent-runtime/src/execute-output.js +41 -0
  33. package/dist/agent-runtime/src/execute-output.js.map +1 -0
  34. package/dist/{packages/agent-runtime → agent-runtime}/src/generated/runconfig-contract.cjs +12 -2
  35. package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -0
  36. package/dist/{packages/agent-runtime → agent-runtime}/src/generated/runconfig-contract.js +12 -2
  37. package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -0
  38. package/dist/agent-runtime/src/github-wake-event.cjs.map +1 -0
  39. package/dist/agent-runtime/src/github-wake-event.js.map +1 -0
  40. package/dist/{packages/agent-runtime → agent-runtime}/src/interrupts.cjs +24 -1
  41. package/dist/agent-runtime/src/interrupts.cjs.map +1 -0
  42. package/dist/{packages/agent-runtime → agent-runtime}/src/interrupts.js +24 -1
  43. package/dist/agent-runtime/src/interrupts.js.map +1 -0
  44. package/dist/agent-runtime/src/message-author.cjs.map +1 -0
  45. package/dist/agent-runtime/src/message-author.js.map +1 -0
  46. package/dist/agent-runtime/src/message-window.cjs.map +1 -0
  47. package/dist/agent-runtime/src/message-window.js.map +1 -0
  48. package/dist/{packages/agent-runtime → agent-runtime}/src/messages.cjs +8 -4
  49. package/dist/agent-runtime/src/messages.cjs.map +1 -0
  50. package/dist/{packages/agent-runtime → agent-runtime}/src/messages.js +6 -2
  51. package/dist/agent-runtime/src/messages.js.map +1 -0
  52. package/dist/agent-runtime/src/model-context.cjs.map +1 -0
  53. package/dist/agent-runtime/src/model-context.js.map +1 -0
  54. package/dist/agent-runtime/src/optimistic-content.cjs.map +1 -0
  55. package/dist/agent-runtime/src/optimistic-content.js.map +1 -0
  56. package/dist/agent-runtime/src/protocol.cjs.map +1 -0
  57. package/dist/agent-runtime/src/protocol.js.map +1 -0
  58. package/dist/{packages/agent-runtime → agent-runtime}/src/queue.cjs +23 -2
  59. package/dist/agent-runtime/src/queue.cjs.map +1 -0
  60. package/dist/{packages/agent-runtime → agent-runtime}/src/queue.js +23 -2
  61. package/dist/agent-runtime/src/queue.js.map +1 -0
  62. package/dist/{packages/agent-runtime → agent-runtime}/src/quota-safe-local-storage-session.cjs +23 -13
  63. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -0
  64. package/dist/{packages/agent-runtime → agent-runtime}/src/quota-safe-local-storage-session.js +24 -14
  65. package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -0
  66. package/dist/agent-runtime/src/runtime-messages.cjs.map +1 -0
  67. package/dist/agent-runtime/src/runtime-messages.js.map +1 -0
  68. package/dist/agent-runtime/src/screen-point.cjs.map +1 -0
  69. package/dist/agent-runtime/src/screen-point.js.map +1 -0
  70. package/dist/agent-runtime/src/send-recovery.cjs.map +1 -0
  71. package/dist/agent-runtime/src/send-recovery.js.map +1 -0
  72. package/dist/agent-runtime/src/statewire-attach-observer.cjs.map +1 -0
  73. package/dist/agent-runtime/src/statewire-attach-observer.js.map +1 -0
  74. package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-state.cjs +8 -0
  75. package/dist/agent-runtime/src/statewire-state.cjs.map +1 -0
  76. package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-state.js +8 -0
  77. package/dist/agent-runtime/src/statewire-state.js.map +1 -0
  78. package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-suspended.cjs +4 -0
  79. package/dist/agent-runtime/src/statewire-suspended.cjs.map +1 -0
  80. package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-suspended.js +4 -0
  81. package/dist/agent-runtime/src/statewire-suspended.js.map +1 -0
  82. package/dist/agent-runtime/src/statewire-transport.cjs +43 -0
  83. package/dist/agent-runtime/src/statewire-transport.cjs.map +1 -0
  84. package/dist/agent-runtime/src/statewire-transport.js +43 -0
  85. package/dist/agent-runtime/src/statewire-transport.js.map +1 -0
  86. package/dist/agent-runtime/src/streaming-markdown.cjs.map +1 -0
  87. package/dist/agent-runtime/src/streaming-markdown.js.map +1 -0
  88. package/dist/agent-runtime/src/subscription-wake-event.cjs +252 -0
  89. package/dist/agent-runtime/src/subscription-wake-event.cjs.map +1 -0
  90. package/dist/agent-runtime/src/subscription-wake-event.js +252 -0
  91. package/dist/agent-runtime/src/subscription-wake-event.js.map +1 -0
  92. package/dist/agent-runtime/src/thread-extras.cjs.map +1 -0
  93. package/dist/agent-runtime/src/thread-extras.js.map +1 -0
  94. package/dist/agent-runtime/src/thread-snapshot-cache.cjs.map +1 -0
  95. package/dist/agent-runtime/src/thread-snapshot-cache.js.map +1 -0
  96. package/dist/agent-runtime/src/thread-status.cjs.map +1 -0
  97. package/dist/agent-runtime/src/thread-status.js.map +1 -0
  98. package/dist/{packages/agent-runtime → agent-runtime}/src/tool-metadata.cjs +365 -3
  99. package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -0
  100. package/dist/{packages/agent-runtime → agent-runtime}/src/tool-metadata.js +365 -3
  101. package/dist/agent-runtime/src/tool-metadata.js.map +1 -0
  102. package/dist/agent-runtime/src/tool-names.cjs +5 -0
  103. package/dist/agent-runtime/src/tool-names.cjs.map +1 -0
  104. package/dist/agent-runtime/src/tool-names.js +5 -0
  105. package/dist/agent-runtime/src/tool-names.js.map +1 -0
  106. package/dist/agent-runtime/src/tool-outputs.cjs.map +1 -0
  107. package/dist/agent-runtime/src/tool-outputs.js.map +1 -0
  108. package/dist/agent-runtime/src/tool-result.cjs.map +1 -0
  109. package/dist/agent-runtime/src/tool-result.js.map +1 -0
  110. package/dist/{packages/agent-runtime → agent-runtime}/src/use-conversation-list.cjs +33 -22
  111. package/dist/agent-runtime/src/use-conversation-list.cjs.map +1 -0
  112. package/dist/{packages/agent-runtime → agent-runtime}/src/use-conversation-list.js +34 -23
  113. package/dist/agent-runtime/src/use-conversation-list.js.map +1 -0
  114. package/dist/agent-runtime/src/use-deep-agent-config.cjs.map +1 -0
  115. package/dist/agent-runtime/src/use-deep-agent-config.js.map +1 -0
  116. package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-edit-submit.cjs +2 -0
  117. package/dist/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -0
  118. package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-edit-submit.js +2 -0
  119. package/dist/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -0
  120. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -0
  121. package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -0
  122. package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-thread.cjs +1 -0
  123. package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -0
  124. package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-thread.js +2 -1
  125. package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -0
  126. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -0
  127. package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -0
  128. package/dist/agent-runtime/src/use-historical-edit-attachments.cjs +52 -0
  129. package/dist/agent-runtime/src/use-historical-edit-attachments.cjs.map +1 -0
  130. package/dist/agent-runtime/src/use-historical-edit-attachments.js +52 -0
  131. package/dist/agent-runtime/src/use-historical-edit-attachments.js.map +1 -0
  132. package/dist/{packages/agent-runtime → agent-runtime}/src/voice-presentation.cjs +14 -0
  133. package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -0
  134. package/dist/{packages/agent-runtime → agent-runtime}/src/voice-presentation.js +14 -0
  135. package/dist/agent-runtime/src/voice-presentation.js.map +1 -0
  136. package/dist/agent-runtime/src/voice-turn-grouping.cjs.map +1 -0
  137. package/dist/agent-runtime/src/voice-turn-grouping.js.map +1 -0
  138. package/dist/assets/AthenaAssetEmbed.cjs +8 -2
  139. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  140. package/dist/assets/AthenaAssetEmbed.d.ts +8 -0
  141. package/dist/assets/AthenaAssetEmbed.js +8 -2
  142. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  143. package/dist/assets/AthenaLayout.cjs +97 -31
  144. package/dist/assets/AthenaLayout.cjs.map +1 -1
  145. package/dist/assets/AthenaLayout.d.ts +13 -0
  146. package/dist/assets/AthenaLayout.js +98 -32
  147. package/dist/assets/AthenaLayout.js.map +1 -1
  148. package/dist/assets/asset-embed-messages.cjs.map +1 -1
  149. package/dist/assets/asset-embed-messages.d.ts +6 -0
  150. package/dist/assets/asset-embed-messages.js.map +1 -1
  151. package/dist/assets/asset-panel-store.cjs +68 -32
  152. package/dist/assets/asset-panel-store.cjs.map +1 -1
  153. package/dist/assets/asset-panel-store.d.ts +47 -1
  154. package/dist/assets/asset-panel-store.js +68 -32
  155. package/dist/assets/asset-panel-store.js.map +1 -1
  156. package/dist/assets/use-asset-embed.cjs +29 -8
  157. package/dist/assets/use-asset-embed.cjs.map +1 -1
  158. package/dist/assets/use-asset-embed.d.ts +7 -0
  159. package/dist/assets/use-asset-embed.js +29 -8
  160. package/dist/assets/use-asset-embed.js.map +1 -1
  161. package/dist/catalog/chat-pickers.cjs +152 -0
  162. package/dist/catalog/chat-pickers.cjs.map +1 -0
  163. package/dist/catalog/chat-pickers.d.ts +22 -0
  164. package/dist/catalog/chat-pickers.js +152 -0
  165. package/dist/catalog/chat-pickers.js.map +1 -0
  166. package/dist/catalog/runtime-catalog.cjs +181 -0
  167. package/dist/catalog/runtime-catalog.cjs.map +1 -0
  168. package/dist/catalog/runtime-catalog.d.ts +100 -0
  169. package/dist/catalog/runtime-catalog.js +181 -0
  170. package/dist/catalog/runtime-catalog.js.map +1 -0
  171. package/dist/chat/AthenaChat.cjs +86 -46
  172. package/dist/chat/AthenaChat.cjs.map +1 -1
  173. package/dist/chat/AthenaChat.d.ts +21 -4
  174. package/dist/chat/AthenaChat.js +88 -48
  175. package/dist/chat/AthenaChat.js.map +1 -1
  176. package/dist/chat/StatewireApprovalCard.cjs +38 -37
  177. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  178. package/dist/chat/StatewireApprovalCard.d.ts +2 -0
  179. package/dist/chat/StatewireApprovalCard.js +39 -38
  180. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  181. package/dist/chat/StatewireClientToolBridge.cjs +2 -2
  182. package/dist/chat/StatewireClientToolBridge.js +2 -2
  183. package/dist/chat/StatewireQueuedMessages.cjs +15 -12
  184. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  185. package/dist/chat/StatewireQueuedMessages.d.ts +22 -2
  186. package/dist/chat/StatewireQueuedMessages.js +15 -12
  187. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  188. package/dist/chat/StatewireRunRefusalNotice.cjs +48 -0
  189. package/dist/chat/StatewireRunRefusalNotice.cjs.map +1 -0
  190. package/dist/chat/StatewireRunRefusalNotice.d.ts +25 -0
  191. package/dist/chat/StatewireRunRefusalNotice.js +48 -0
  192. package/dist/chat/StatewireRunRefusalNotice.js.map +1 -0
  193. package/dist/chat/WaitingOnYouBanner.cjs +2 -2
  194. package/dist/chat/WaitingOnYouBanner.js +2 -2
  195. package/dist/chat/athena-citation-links.cjs +19 -15
  196. package/dist/chat/athena-citation-links.cjs.map +1 -1
  197. package/dist/chat/athena-citation-links.js +19 -15
  198. package/dist/chat/athena-citation-links.js.map +1 -1
  199. package/dist/chat/run-activity.cjs +9 -1
  200. package/dist/chat/run-activity.cjs.map +1 -1
  201. package/dist/chat/run-activity.d.ts +6 -0
  202. package/dist/chat/run-activity.js +10 -2
  203. package/dist/chat/run-activity.js.map +1 -1
  204. package/dist/chat/statewire-banners.cjs +30 -1
  205. package/dist/chat/statewire-banners.cjs.map +1 -1
  206. package/dist/chat/statewire-banners.d.ts +20 -1
  207. package/dist/chat/statewire-banners.js +32 -3
  208. package/dist/chat/statewire-banners.js.map +1 -1
  209. package/dist/chat/statewire-client-tools.cjs +1 -1
  210. package/dist/chat/statewire-client-tools.js +1 -1
  211. package/dist/chat/super-grouping/CollapsibleGroup.cjs +1 -1
  212. package/dist/chat/super-grouping/CollapsibleGroup.cjs.map +1 -1
  213. package/dist/chat/super-grouping/CollapsibleGroup.js +1 -1
  214. package/dist/chat/super-grouping/CollapsibleGroup.js.map +1 -1
  215. package/dist/chat/super-grouping/SuperGroupingCard.cjs +7 -5
  216. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  217. package/dist/chat/super-grouping/SuperGroupingCard.d.ts +8 -2
  218. package/dist/chat/super-grouping/SuperGroupingCard.js +7 -5
  219. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  220. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +350 -0
  221. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -0
  222. package/dist/chat/super-grouping/SuperGroupingTranscript.d.ts +31 -0
  223. package/dist/chat/super-grouping/SuperGroupingTranscript.js +350 -0
  224. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -0
  225. package/dist/chat/super-grouping/ToolTimeline.cjs +13 -2
  226. package/dist/chat/super-grouping/ToolTimeline.cjs.map +1 -1
  227. package/dist/chat/super-grouping/ToolTimeline.d.ts +10 -3
  228. package/dist/chat/super-grouping/ToolTimeline.js +13 -2
  229. package/dist/chat/super-grouping/ToolTimeline.js.map +1 -1
  230. package/dist/chat/super-grouping/activity-timeline.cjs +51 -26
  231. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  232. package/dist/chat/super-grouping/activity-timeline.d.ts +40 -8
  233. package/dist/chat/super-grouping/activity-timeline.js +52 -27
  234. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  235. package/dist/chat/super-grouping/index.cjs +12 -0
  236. package/dist/chat/super-grouping/index.cjs.map +1 -1
  237. package/dist/chat/super-grouping/index.d.ts +7 -2
  238. package/dist/chat/super-grouping/index.js +14 -2
  239. package/dist/chat/super-grouping/index.js.map +1 -1
  240. package/dist/chat/super-grouping/timeline-step.cjs +14 -5
  241. package/dist/chat/super-grouping/timeline-step.cjs.map +1 -1
  242. package/dist/chat/super-grouping/timeline-step.d.ts +10 -1
  243. package/dist/chat/super-grouping/timeline-step.js +15 -6
  244. package/dist/chat/super-grouping/timeline-step.js.map +1 -1
  245. package/dist/chat/super-grouping/turn-segments.cjs +82 -0
  246. package/dist/chat/super-grouping/turn-segments.cjs.map +1 -0
  247. package/dist/chat/super-grouping/turn-segments.d.ts +69 -0
  248. package/dist/chat/super-grouping/turn-segments.js +82 -0
  249. package/dist/chat/super-grouping/turn-segments.js.map +1 -0
  250. package/dist/chat/super-grouping/turn-timing.cjs +110 -0
  251. package/dist/chat/super-grouping/turn-timing.cjs.map +1 -0
  252. package/dist/chat/super-grouping/turn-timing.d.ts +52 -0
  253. package/dist/chat/super-grouping/turn-timing.js +110 -0
  254. package/dist/chat/super-grouping/turn-timing.js.map +1 -0
  255. package/dist/chat/tiptap-text.cjs +1 -1
  256. package/dist/chat/tiptap-text.js +1 -1
  257. package/dist/chat/use-athena-run-live.cjs +1 -1
  258. package/dist/chat/use-athena-run-live.js +1 -1
  259. package/dist/chat/use-statewire-approval-request.cjs +73 -4
  260. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  261. package/dist/chat/use-statewire-approval-request.js +74 -5
  262. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  263. package/dist/chat-ui/composer/composer-message.cjs +284 -0
  264. package/dist/chat-ui/composer/composer-message.cjs.map +1 -0
  265. package/dist/chat-ui/composer/composer-message.d.ts +152 -0
  266. package/dist/chat-ui/composer/composer-message.js +284 -0
  267. package/dist/chat-ui/composer/composer-message.js.map +1 -0
  268. package/dist/chat-ui/composer/tiptap-composer.cjs +57 -11
  269. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  270. package/dist/chat-ui/composer/tiptap-composer.d.ts +34 -6
  271. package/dist/chat-ui/composer/tiptap-composer.js +57 -11
  272. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  273. package/dist/chat-ui/index.cjs +4 -1
  274. package/dist/chat-ui/index.cjs.map +1 -1
  275. package/dist/chat-ui/index.d.ts +3 -2
  276. package/dist/chat-ui/index.js +5 -2
  277. package/dist/chat-ui/index.js.map +1 -1
  278. package/dist/chat-ui/mentions/mention-chip.cjs +3 -2
  279. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  280. package/dist/chat-ui/mentions/mention-chip.js +4 -3
  281. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  282. package/dist/chat-ui/mentions/mention-extension.cjs +8 -2
  283. package/dist/chat-ui/mentions/mention-extension.cjs.map +1 -1
  284. package/dist/chat-ui/mentions/mention-extension.js +8 -2
  285. package/dist/chat-ui/mentions/mention-extension.js.map +1 -1
  286. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.cjs +3 -1
  287. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.cjs.map +1 -1
  288. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.js +3 -1
  289. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.js.map +1 -1
  290. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +65 -19
  291. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
  292. package/dist/chat-ui/mentions/suggestions/data/sources/index.d.ts +15 -0
  293. package/dist/chat-ui/mentions/suggestions/data/sources/index.js +67 -21
  294. package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
  295. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.cjs +4 -2
  296. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.cjs.map +1 -1
  297. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.d.ts +2 -1
  298. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.js +5 -3
  299. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.js.map +1 -1
  300. package/dist/chat-ui/messages/message-components.cjs +30 -10
  301. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  302. package/dist/chat-ui/messages/message-components.d.ts +21 -1
  303. package/dist/chat-ui/messages/message-components.js +30 -10
  304. package/dist/chat-ui/messages/message-components.js.map +1 -1
  305. package/dist/chat-ui/messages/runtime-message.cjs +1 -1
  306. package/dist/chat-ui/messages/runtime-message.js +1 -1
  307. package/dist/chat-ui/messages/statewire-continue-run.cjs +5 -5
  308. package/dist/chat-ui/messages/statewire-continue-run.js +5 -5
  309. package/dist/chat-ui/messages/user-edit-composer.cjs +1 -1
  310. package/dist/chat-ui/messages/user-edit-composer.js +1 -1
  311. package/dist/chat-ui/messages/wake-marker.cjs +39 -4
  312. package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
  313. package/dist/chat-ui/messages/wake-marker.d.ts +16 -5
  314. package/dist/chat-ui/messages/wake-marker.js +40 -5
  315. package/dist/chat-ui/messages/wake-marker.js.map +1 -1
  316. package/dist/chat-ui/rich-text/chat-message-body.cjs +12 -8
  317. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
  318. package/dist/chat-ui/rich-text/chat-message-body.d.ts +6 -1
  319. package/dist/chat-ui/rich-text/chat-message-body.js +13 -9
  320. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
  321. package/dist/chat-ui/rich-text/chat-text-schema.cjs +64 -6
  322. package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -1
  323. package/dist/chat-ui/rich-text/chat-text-schema.d.ts +5 -0
  324. package/dist/chat-ui/rich-text/chat-text-schema.js +66 -8
  325. package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -1
  326. package/dist/chat-ui/statewire.cjs +1 -1
  327. package/dist/chat-ui/statewire.d.ts +1 -1
  328. package/dist/chat-ui/statewire.js +1 -1
  329. package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -2
  330. package/dist/chat-ui/thread/statewire-history-edge.js +2 -2
  331. package/dist/collab/client.cjs +218 -13
  332. package/dist/collab/client.cjs.map +1 -1
  333. package/dist/collab/client.d.ts +105 -2
  334. package/dist/collab/client.js +221 -17
  335. package/dist/collab/client.js.map +1 -1
  336. package/dist/collab/index.cjs +2 -0
  337. package/dist/collab/index.cjs.map +1 -1
  338. package/dist/collab/index.d.ts +1 -1
  339. package/dist/collab/index.js +3 -1
  340. package/dist/composer/AttachmentContext.cjs +38 -12
  341. package/dist/composer/AttachmentContext.cjs.map +1 -1
  342. package/dist/composer/AttachmentContext.d.ts +22 -1
  343. package/dist/composer/AttachmentContext.js +40 -14
  344. package/dist/composer/AttachmentContext.js.map +1 -1
  345. package/dist/composer/QuoteContext.cjs +80 -28
  346. package/dist/composer/QuoteContext.cjs.map +1 -1
  347. package/dist/composer/QuoteContext.d.ts +35 -6
  348. package/dist/composer/QuoteContext.js +82 -30
  349. package/dist/composer/QuoteContext.js.map +1 -1
  350. package/dist/composer/build-composed-message.cjs +27 -0
  351. package/dist/composer/build-composed-message.cjs.map +1 -1
  352. package/dist/composer/build-composed-message.d.ts +21 -0
  353. package/dist/composer/build-composed-message.js +28 -1
  354. package/dist/composer/build-composed-message.js.map +1 -1
  355. package/dist/composer/composer-handoff.cjs +13 -1
  356. package/dist/composer/composer-handoff.cjs.map +1 -1
  357. package/dist/composer/composer-handoff.d.ts +12 -0
  358. package/dist/composer/composer-handoff.js +15 -3
  359. package/dist/composer/composer-handoff.js.map +1 -1
  360. package/dist/composer/index.d.ts +6 -3
  361. package/dist/composer/useReadComposerDraft.cjs +16 -0
  362. package/dist/composer/useReadComposerDraft.cjs.map +1 -0
  363. package/dist/composer/useReadComposerDraft.d.ts +6 -0
  364. package/dist/composer/useReadComposerDraft.js +16 -0
  365. package/dist/composer/useReadComposerDraft.js.map +1 -0
  366. package/dist/diagnostics/errors.cjs +6 -0
  367. package/dist/diagnostics/errors.cjs.map +1 -1
  368. package/dist/diagnostics/errors.d.ts +6 -0
  369. package/dist/diagnostics/errors.js +6 -0
  370. package/dist/diagnostics/errors.js.map +1 -1
  371. package/dist/index.cjs +159 -7
  372. package/dist/index.cjs.map +1 -1
  373. package/dist/index.d.ts +53 -12
  374. package/dist/index.js +166 -14
  375. package/dist/index.js.map +1 -1
  376. package/dist/lib/owner-document.cjs +32 -0
  377. package/dist/lib/owner-document.cjs.map +1 -0
  378. package/dist/lib/owner-document.d.ts +17 -0
  379. package/dist/lib/owner-document.js +32 -0
  380. package/dist/lib/owner-document.js.map +1 -0
  381. package/dist/lib/portal-surface.cjs +55 -0
  382. package/dist/lib/portal-surface.cjs.map +1 -0
  383. package/dist/lib/portal-surface.d.ts +37 -0
  384. package/dist/lib/portal-surface.js +55 -0
  385. package/dist/lib/portal-surface.js.map +1 -0
  386. package/dist/lib/use-athena-rest-query.cjs +34 -0
  387. package/dist/lib/use-athena-rest-query.cjs.map +1 -0
  388. package/dist/lib/use-athena-rest-query.d.ts +31 -0
  389. package/dist/lib/use-athena-rest-query.js +34 -0
  390. package/dist/lib/use-athena-rest-query.js.map +1 -0
  391. package/dist/provider/AthenaProvider.cjs +36 -6
  392. package/dist/provider/AthenaProvider.cjs.map +1 -1
  393. package/dist/provider/AthenaProvider.d.ts +20 -1
  394. package/dist/provider/AthenaProvider.js +38 -8
  395. package/dist/provider/AthenaProvider.js.map +1 -1
  396. package/dist/provider/citation-links.d.ts +10 -2
  397. package/dist/{packages/react → react}/package.json.cjs +1 -1
  398. package/dist/{packages/react → react}/package.json.js +1 -1
  399. package/dist/runtime/statewire-lifecycle.cjs +15 -3
  400. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  401. package/dist/runtime/statewire-lifecycle.d.ts +6 -0
  402. package/dist/runtime/statewire-lifecycle.js +15 -3
  403. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  404. package/dist/runtime/useAthenaStatewireRuntime.cjs +11 -7
  405. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  406. package/dist/runtime/useAthenaStatewireRuntime.js +11 -7
  407. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  408. package/dist/search/useAthenaRepoSearch.cjs +5 -13
  409. package/dist/search/useAthenaRepoSearch.cjs.map +1 -1
  410. package/dist/search/useAthenaRepoSearch.d.ts +3 -3
  411. package/dist/search/useAthenaRepoSearch.js +6 -14
  412. package/dist/search/useAthenaRepoSearch.js.map +1 -1
  413. package/dist/sheet-range/a1.cjs +162 -0
  414. package/dist/sheet-range/a1.cjs.map +1 -0
  415. package/dist/sheet-range/a1.d.ts +91 -0
  416. package/dist/sheet-range/a1.js +162 -0
  417. package/dist/sheet-range/a1.js.map +1 -0
  418. package/dist/sheet-range/index.cjs +22 -0
  419. package/dist/sheet-range/index.cjs.map +1 -0
  420. package/dist/sheet-range/index.d.ts +12 -0
  421. package/dist/sheet-range/index.js +22 -0
  422. package/dist/sheet-range/index.js.map +1 -0
  423. package/dist/sheet-range/range-preview-table.cjs +75 -0
  424. package/dist/sheet-range/range-preview-table.cjs.map +1 -0
  425. package/dist/sheet-range/range-preview-table.d.ts +20 -0
  426. package/dist/sheet-range/range-preview-table.js +75 -0
  427. package/dist/sheet-range/range-preview-table.js.map +1 -0
  428. package/dist/sheet-range/range-preview.cjs +116 -0
  429. package/dist/sheet-range/range-preview.cjs.map +1 -0
  430. package/dist/sheet-range/range-preview.d.ts +67 -0
  431. package/dist/sheet-range/range-preview.js +116 -0
  432. package/dist/sheet-range/range-preview.js.map +1 -0
  433. package/dist/styles.css +1 -1
  434. package/dist/threads/Inbox.cjs +289 -0
  435. package/dist/threads/Inbox.cjs.map +1 -0
  436. package/dist/threads/Inbox.d.ts +46 -0
  437. package/dist/threads/Inbox.js +289 -0
  438. package/dist/threads/Inbox.js.map +1 -0
  439. package/dist/threads/SessionList.cjs +51 -126
  440. package/dist/threads/SessionList.cjs.map +1 -1
  441. package/dist/threads/SessionList.d.ts +26 -1
  442. package/dist/threads/SessionList.js +45 -120
  443. package/dist/threads/SessionList.js.map +1 -1
  444. package/dist/threads/SessionsPage.cjs +753 -0
  445. package/dist/threads/SessionsPage.cjs.map +1 -0
  446. package/dist/threads/SessionsPage.d.ts +35 -0
  447. package/dist/threads/SessionsPage.js +753 -0
  448. package/dist/threads/SessionsPage.js.map +1 -0
  449. package/dist/threads/api.cjs +21 -1
  450. package/dist/threads/api.cjs.map +1 -1
  451. package/dist/threads/api.d.ts +59 -0
  452. package/dist/threads/api.js +21 -1
  453. package/dist/threads/api.js.map +1 -1
  454. package/dist/threads/conversation-list-fetcher.cjs +9 -2
  455. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  456. package/dist/threads/conversation-list-fetcher.d.ts +14 -1
  457. package/dist/threads/conversation-list-fetcher.js +9 -2
  458. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  459. package/dist/threads/index.d.ts +2 -1
  460. package/dist/threads/pull-request-display.cjs +25 -0
  461. package/dist/threads/pull-request-display.cjs.map +1 -0
  462. package/dist/threads/pull-request-display.d.ts +15 -0
  463. package/dist/threads/pull-request-display.js +25 -0
  464. package/dist/threads/pull-request-display.js.map +1 -0
  465. package/dist/threads/session-list-model.cjs +16 -2
  466. package/dist/threads/session-list-model.cjs.map +1 -1
  467. package/dist/threads/session-list-model.d.ts +10 -0
  468. package/dist/threads/session-list-model.js +16 -2
  469. package/dist/threads/session-list-model.js.map +1 -1
  470. package/dist/threads/session-row-parts.cjs +152 -0
  471. package/dist/threads/session-row-parts.cjs.map +1 -0
  472. package/dist/threads/session-row-parts.d.ts +50 -0
  473. package/dist/threads/session-row-parts.js +152 -0
  474. package/dist/threads/session-row-parts.js.map +1 -0
  475. package/dist/threads/sessions-page-model.cjs +305 -0
  476. package/dist/threads/sessions-page-model.cjs.map +1 -0
  477. package/dist/threads/sessions-page-model.d.ts +86 -0
  478. package/dist/threads/sessions-page-model.js +305 -0
  479. package/dist/threads/sessions-page-model.js.map +1 -0
  480. package/dist/threads/sessions-page-storage.cjs +93 -0
  481. package/dist/threads/sessions-page-storage.cjs.map +1 -0
  482. package/dist/threads/sessions-page-storage.d.ts +23 -0
  483. package/dist/threads/sessions-page-storage.js +93 -0
  484. package/dist/threads/sessions-page-storage.js.map +1 -0
  485. package/dist/threads/sessions-query.cjs +235 -0
  486. package/dist/threads/sessions-query.cjs.map +1 -0
  487. package/dist/threads/sessions-query.d.ts +119 -0
  488. package/dist/threads/sessions-query.js +235 -0
  489. package/dist/threads/sessions-query.js.map +1 -0
  490. package/dist/threads/statewire-thread-list.cjs +133 -40
  491. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  492. package/dist/threads/statewire-thread-list.d.ts +83 -2
  493. package/dist/threads/statewire-thread-list.js +134 -41
  494. package/dist/threads/statewire-thread-list.js.map +1 -1
  495. package/dist/threads/use-athena-sessions.cjs +198 -0
  496. package/dist/threads/use-athena-sessions.cjs.map +1 -0
  497. package/dist/threads/use-athena-sessions.d.ts +81 -0
  498. package/dist/threads/use-athena-sessions.js +198 -0
  499. package/dist/threads/use-athena-sessions.js.map +1 -0
  500. package/dist/threads/use-rest-conversation-history.cjs +1 -1
  501. package/dist/threads/use-rest-conversation-history.js +1 -1
  502. package/dist/threads/use-statewire-conversation-history.cjs +1 -1
  503. package/dist/threads/use-statewire-conversation-history.d.ts +3 -0
  504. package/dist/threads/use-statewire-conversation-history.js +1 -1
  505. package/dist/threads/use-thread-read-state.cjs +73 -0
  506. package/dist/threads/use-thread-read-state.cjs.map +1 -0
  507. package/dist/threads/use-thread-read-state.d.ts +51 -0
  508. package/dist/threads/use-thread-read-state.js +73 -0
  509. package/dist/threads/use-thread-read-state.js.map +1 -0
  510. package/dist/tiptap-markdown-table/src/markdown-table-serializer.cjs.map +1 -0
  511. package/dist/tiptap-markdown-table/src/markdown-table-serializer.js.map +1 -0
  512. package/dist/tools/auto-open-tools.cjs +2 -0
  513. package/dist/tools/auto-open-tools.cjs.map +1 -1
  514. package/dist/tools/auto-open-tools.js +2 -0
  515. package/dist/tools/auto-open-tools.js.map +1 -1
  516. package/dist/tools/file-tool-uis.cjs +18 -43
  517. package/dist/tools/file-tool-uis.cjs.map +1 -1
  518. package/dist/tools/file-tool-uis.d.ts +1 -2
  519. package/dist/tools/file-tool-uis.js +18 -42
  520. package/dist/tools/file-tool-uis.js.map +1 -1
  521. package/dist/tools/index.cjs +6 -2
  522. package/dist/tools/index.cjs.map +1 -1
  523. package/dist/tools/index.d.ts +1 -0
  524. package/dist/tools/index.js +6 -2
  525. package/dist/tools/index.js.map +1 -1
  526. package/dist/tools/misc-tool-uis.cjs +2 -2
  527. package/dist/tools/misc-tool-uis.js +2 -2
  528. package/dist/tools/primitives.cjs +1 -1
  529. package/dist/tools/primitives.js +1 -1
  530. package/dist/tools/read-asset-tool-ui.cjs +21 -70
  531. package/dist/tools/read-asset-tool-ui.cjs.map +1 -1
  532. package/dist/tools/read-asset-tool-ui.js +21 -70
  533. package/dist/tools/read-asset-tool-ui.js.map +1 -1
  534. package/dist/tools/step-text.cjs +21 -0
  535. package/dist/tools/step-text.cjs.map +1 -0
  536. package/dist/tools/step-text.d.ts +10 -0
  537. package/dist/tools/step-text.js +21 -0
  538. package/dist/tools/step-text.js.map +1 -0
  539. package/dist/tools/tool-fallback.cjs +24 -5
  540. package/dist/tools/tool-fallback.cjs.map +1 -1
  541. package/dist/tools/tool-fallback.d.ts +3 -1
  542. package/dist/tools/tool-fallback.js +24 -5
  543. package/dist/tools/tool-fallback.js.map +1 -1
  544. package/dist/tools/tool-summary.cjs +661 -0
  545. package/dist/tools/tool-summary.cjs.map +1 -0
  546. package/dist/tools/tool-summary.d.ts +99 -0
  547. package/dist/tools/tool-summary.js +661 -0
  548. package/dist/tools/tool-summary.js.map +1 -0
  549. package/dist/tools/tool-uis.cjs +39 -97
  550. package/dist/tools/tool-uis.cjs.map +1 -1
  551. package/dist/tools/tool-uis.js +39 -97
  552. package/dist/tools/tool-uis.js.map +1 -1
  553. package/dist/tools/voice-update-tool-ui.cjs +1 -1
  554. package/dist/tools/voice-update-tool-ui.js +1 -1
  555. package/dist/tools/write-todos-tool-ui.cjs +3 -2
  556. package/dist/tools/write-todos-tool-ui.cjs.map +1 -1
  557. package/dist/tools/write-todos-tool-ui.d.ts +5 -0
  558. package/dist/tools/write-todos-tool-ui.js +5 -4
  559. package/dist/tools/write-todos-tool-ui.js.map +1 -1
  560. package/dist/ui/popover.cjs +6 -6
  561. package/dist/ui/popover.cjs.map +1 -1
  562. package/dist/ui/popover.js +6 -6
  563. package/dist/ui/popover.js.map +1 -1
  564. package/dist/ui/tooltip.cjs +16 -2
  565. package/dist/ui/tooltip.cjs.map +1 -1
  566. package/dist/ui/tooltip.d.ts +1 -1
  567. package/dist/ui/tooltip.js +16 -2
  568. package/dist/ui/tooltip.js.map +1 -1
  569. package/dist/version.cjs +1 -1
  570. package/dist/version.js +1 -1
  571. package/dist/voice/use-athena-voice-session.cjs.map +1 -1
  572. package/dist/voice/use-athena-voice-session.d.ts +3 -3
  573. package/dist/voice/use-athena-voice-session.js.map +1 -1
  574. package/dist/workbench/SessionWorkbench.cjs +378 -0
  575. package/dist/workbench/SessionWorkbench.cjs.map +1 -0
  576. package/dist/workbench/SessionWorkbench.d.ts +30 -0
  577. package/dist/workbench/SessionWorkbench.js +378 -0
  578. package/dist/workbench/SessionWorkbench.js.map +1 -0
  579. package/dist/workbench/session-workbench-context.cjs +26 -0
  580. package/dist/workbench/session-workbench-context.cjs.map +1 -0
  581. package/dist/workbench/session-workbench-context.d.ts +55 -0
  582. package/dist/workbench/session-workbench-context.js +26 -0
  583. package/dist/workbench/session-workbench-context.js.map +1 -0
  584. package/dist/workbench/session-workbench-model.cjs +288 -0
  585. package/dist/workbench/session-workbench-model.cjs.map +1 -0
  586. package/dist/workbench/session-workbench-model.d.ts +168 -0
  587. package/dist/workbench/session-workbench-model.js +288 -0
  588. package/dist/workbench/session-workbench-model.js.map +1 -0
  589. package/dist/workbench/session-workbench-panes.cjs +580 -0
  590. package/dist/workbench/session-workbench-panes.cjs.map +1 -0
  591. package/dist/workbench/session-workbench-panes.d.ts +16 -0
  592. package/dist/workbench/session-workbench-panes.js +580 -0
  593. package/dist/workbench/session-workbench-panes.js.map +1 -0
  594. package/dist/workbench/session-workspace-api.cjs +157 -0
  595. package/dist/workbench/session-workspace-api.cjs.map +1 -0
  596. package/dist/workbench/session-workspace-api.d.ts +70 -0
  597. package/dist/workbench/session-workspace-api.js +157 -0
  598. package/dist/workbench/session-workspace-api.js.map +1 -0
  599. package/dist/workbench/use-session-workspace.cjs +100 -0
  600. package/dist/workbench/use-session-workspace.cjs.map +1 -0
  601. package/dist/workbench/use-session-workspace.d.ts +45 -0
  602. package/dist/workbench/use-session-workspace.js +100 -0
  603. package/dist/workbench/use-session-workspace.js.map +1 -0
  604. package/dist/workbench/workbench-layout-context.cjs +10 -0
  605. package/dist/workbench/workbench-layout-context.cjs.map +1 -0
  606. package/dist/workbench/workbench-layout-context.d.ts +17 -0
  607. package/dist/workbench/workbench-layout-context.js +10 -0
  608. package/dist/workbench/workbench-layout-context.js.map +1 -0
  609. package/package.json +20 -9
  610. package/dist/node_modules/@assistant-ui/react-langchain/dist/converter.cjs +0 -77
  611. package/dist/node_modules/@assistant-ui/react-langchain/dist/converter.cjs.map +0 -1
  612. package/dist/node_modules/@assistant-ui/react-langchain/dist/converter.js +0 -77
  613. package/dist/node_modules/@assistant-ui/react-langchain/dist/converter.js.map +0 -1
  614. package/dist/node_modules/@assistant-ui/react-langgraph/dist/convertLangChainMessages.cjs +0 -181
  615. package/dist/node_modules/@assistant-ui/react-langgraph/dist/convertLangChainMessages.cjs.map +0 -1
  616. package/dist/node_modules/@assistant-ui/react-langgraph/dist/convertLangChainMessages.js +0 -181
  617. package/dist/node_modules/@assistant-ui/react-langgraph/dist/convertLangChainMessages.js.map +0 -1
  618. package/dist/packages/agent-runtime/src/asset-attachment.cjs.map +0 -1
  619. package/dist/packages/agent-runtime/src/asset-attachment.js.map +0 -1
  620. package/dist/packages/agent-runtime/src/auth-denial.cjs.map +0 -1
  621. package/dist/packages/agent-runtime/src/auth-denial.js.map +0 -1
  622. package/dist/packages/agent-runtime/src/client-tool-bridge.cjs.map +0 -1
  623. package/dist/packages/agent-runtime/src/client-tool-bridge.js.map +0 -1
  624. package/dist/packages/agent-runtime/src/client-tools.cjs.map +0 -1
  625. package/dist/packages/agent-runtime/src/client-tools.js.map +0 -1
  626. package/dist/packages/agent-runtime/src/conversation-index.cjs.map +0 -1
  627. package/dist/packages/agent-runtime/src/conversation-index.js.map +0 -1
  628. package/dist/packages/agent-runtime/src/conversation-list.cjs.map +0 -1
  629. package/dist/packages/agent-runtime/src/conversation-list.js.map +0 -1
  630. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.cjs.map +0 -1
  631. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.js.map +0 -1
  632. package/dist/packages/agent-runtime/src/edit-command.cjs.map +0 -1
  633. package/dist/packages/agent-runtime/src/edit-command.js.map +0 -1
  634. package/dist/packages/agent-runtime/src/errors.cjs.map +0 -1
  635. package/dist/packages/agent-runtime/src/errors.js.map +0 -1
  636. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +0 -1
  637. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +0 -1
  638. package/dist/packages/agent-runtime/src/github-wake-event.cjs.map +0 -1
  639. package/dist/packages/agent-runtime/src/github-wake-event.js.map +0 -1
  640. package/dist/packages/agent-runtime/src/interrupts.cjs.map +0 -1
  641. package/dist/packages/agent-runtime/src/interrupts.js.map +0 -1
  642. package/dist/packages/agent-runtime/src/message-author.cjs.map +0 -1
  643. package/dist/packages/agent-runtime/src/message-author.js.map +0 -1
  644. package/dist/packages/agent-runtime/src/message-window.cjs.map +0 -1
  645. package/dist/packages/agent-runtime/src/message-window.js.map +0 -1
  646. package/dist/packages/agent-runtime/src/messages.cjs.map +0 -1
  647. package/dist/packages/agent-runtime/src/messages.js.map +0 -1
  648. package/dist/packages/agent-runtime/src/model-context.cjs.map +0 -1
  649. package/dist/packages/agent-runtime/src/model-context.js.map +0 -1
  650. package/dist/packages/agent-runtime/src/optimistic-content.cjs.map +0 -1
  651. package/dist/packages/agent-runtime/src/optimistic-content.js.map +0 -1
  652. package/dist/packages/agent-runtime/src/protocol.cjs.map +0 -1
  653. package/dist/packages/agent-runtime/src/protocol.js.map +0 -1
  654. package/dist/packages/agent-runtime/src/queue.cjs.map +0 -1
  655. package/dist/packages/agent-runtime/src/queue.js.map +0 -1
  656. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +0 -1
  657. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +0 -1
  658. package/dist/packages/agent-runtime/src/runtime-messages.cjs.map +0 -1
  659. package/dist/packages/agent-runtime/src/runtime-messages.js.map +0 -1
  660. package/dist/packages/agent-runtime/src/screen-point.cjs.map +0 -1
  661. package/dist/packages/agent-runtime/src/screen-point.js.map +0 -1
  662. package/dist/packages/agent-runtime/src/send-recovery.cjs.map +0 -1
  663. package/dist/packages/agent-runtime/src/send-recovery.js.map +0 -1
  664. package/dist/packages/agent-runtime/src/statewire-attach-observer.cjs.map +0 -1
  665. package/dist/packages/agent-runtime/src/statewire-attach-observer.js.map +0 -1
  666. package/dist/packages/agent-runtime/src/statewire-state.cjs.map +0 -1
  667. package/dist/packages/agent-runtime/src/statewire-state.js.map +0 -1
  668. package/dist/packages/agent-runtime/src/statewire-suspended.cjs.map +0 -1
  669. package/dist/packages/agent-runtime/src/statewire-suspended.js.map +0 -1
  670. package/dist/packages/agent-runtime/src/statewire-transport.cjs +0 -11
  671. package/dist/packages/agent-runtime/src/statewire-transport.cjs.map +0 -1
  672. package/dist/packages/agent-runtime/src/statewire-transport.js +0 -11
  673. package/dist/packages/agent-runtime/src/statewire-transport.js.map +0 -1
  674. package/dist/packages/agent-runtime/src/streaming-markdown.cjs.map +0 -1
  675. package/dist/packages/agent-runtime/src/streaming-markdown.js.map +0 -1
  676. package/dist/packages/agent-runtime/src/thread-extras.cjs.map +0 -1
  677. package/dist/packages/agent-runtime/src/thread-extras.js.map +0 -1
  678. package/dist/packages/agent-runtime/src/thread-snapshot-cache.cjs.map +0 -1
  679. package/dist/packages/agent-runtime/src/thread-snapshot-cache.js.map +0 -1
  680. package/dist/packages/agent-runtime/src/thread-status.cjs.map +0 -1
  681. package/dist/packages/agent-runtime/src/thread-status.js.map +0 -1
  682. package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +0 -1
  683. package/dist/packages/agent-runtime/src/tool-metadata.js.map +0 -1
  684. package/dist/packages/agent-runtime/src/tool-outputs.cjs.map +0 -1
  685. package/dist/packages/agent-runtime/src/tool-outputs.js.map +0 -1
  686. package/dist/packages/agent-runtime/src/tool-result.cjs.map +0 -1
  687. package/dist/packages/agent-runtime/src/tool-result.js.map +0 -1
  688. package/dist/packages/agent-runtime/src/use-conversation-list.cjs.map +0 -1
  689. package/dist/packages/agent-runtime/src/use-conversation-list.js.map +0 -1
  690. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +0 -1
  691. package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +0 -1
  692. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +0 -1
  693. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js.map +0 -1
  694. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.cjs.map +0 -1
  695. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.js.map +0 -1
  696. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +0 -1
  697. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +0 -1
  698. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.cjs.map +0 -1
  699. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.js.map +0 -1
  700. package/dist/packages/agent-runtime/src/voice-presentation.cjs.map +0 -1
  701. package/dist/packages/agent-runtime/src/voice-presentation.js.map +0 -1
  702. package/dist/packages/agent-runtime/src/voice-turn-grouping.cjs.map +0 -1
  703. package/dist/packages/agent-runtime/src/voice-turn-grouping.js.map +0 -1
  704. package/dist/packages/tiptap-markdown-table/src/markdown-table-serializer.cjs.map +0 -1
  705. package/dist/packages/tiptap-markdown-table/src/markdown-table-serializer.js.map +0 -1
  706. /package/dist/{packages/agent-runtime → agent-runtime}/src/asset-attachment.cjs +0 -0
  707. /package/dist/{packages/agent-runtime → agent-runtime}/src/asset-attachment.js +0 -0
  708. /package/dist/{packages/agent-runtime → agent-runtime}/src/auth-denial.cjs +0 -0
  709. /package/dist/{packages/agent-runtime → agent-runtime}/src/auth-denial.js +0 -0
  710. /package/dist/{packages/agent-runtime → agent-runtime}/src/client-tool-bridge.cjs +0 -0
  711. /package/dist/{packages/agent-runtime → agent-runtime}/src/client-tool-bridge.js +0 -0
  712. /package/dist/{packages/agent-runtime → agent-runtime}/src/client-tools.cjs +0 -0
  713. /package/dist/{packages/agent-runtime → agent-runtime}/src/client-tools.js +0 -0
  714. /package/dist/{packages/agent-runtime → agent-runtime}/src/conversation-index.cjs +0 -0
  715. /package/dist/{packages/agent-runtime → agent-runtime}/src/conversation-index.js +0 -0
  716. /package/dist/{packages/agent-runtime → agent-runtime}/src/edit-command.cjs +0 -0
  717. /package/dist/{packages/agent-runtime → agent-runtime}/src/edit-command.js +0 -0
  718. /package/dist/{packages/agent-runtime → agent-runtime}/src/github-wake-event.cjs +0 -0
  719. /package/dist/{packages/agent-runtime → agent-runtime}/src/github-wake-event.js +0 -0
  720. /package/dist/{packages/agent-runtime → agent-runtime}/src/message-author.cjs +0 -0
  721. /package/dist/{packages/agent-runtime → agent-runtime}/src/message-author.js +0 -0
  722. /package/dist/{packages/agent-runtime → agent-runtime}/src/message-window.cjs +0 -0
  723. /package/dist/{packages/agent-runtime → agent-runtime}/src/message-window.js +0 -0
  724. /package/dist/{packages/agent-runtime → agent-runtime}/src/model-context.cjs +0 -0
  725. /package/dist/{packages/agent-runtime → agent-runtime}/src/model-context.js +0 -0
  726. /package/dist/{packages/agent-runtime → agent-runtime}/src/optimistic-content.cjs +0 -0
  727. /package/dist/{packages/agent-runtime → agent-runtime}/src/optimistic-content.js +0 -0
  728. /package/dist/{packages/agent-runtime → agent-runtime}/src/protocol.cjs +0 -0
  729. /package/dist/{packages/agent-runtime → agent-runtime}/src/protocol.js +0 -0
  730. /package/dist/{packages/agent-runtime → agent-runtime}/src/runtime-messages.cjs +0 -0
  731. /package/dist/{packages/agent-runtime → agent-runtime}/src/runtime-messages.js +0 -0
  732. /package/dist/{packages/agent-runtime → agent-runtime}/src/screen-point.cjs +0 -0
  733. /package/dist/{packages/agent-runtime → agent-runtime}/src/screen-point.js +0 -0
  734. /package/dist/{packages/agent-runtime → agent-runtime}/src/send-recovery.cjs +0 -0
  735. /package/dist/{packages/agent-runtime → agent-runtime}/src/send-recovery.js +0 -0
  736. /package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-attach-observer.cjs +0 -0
  737. /package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-attach-observer.js +0 -0
  738. /package/dist/{packages/agent-runtime → agent-runtime}/src/streaming-markdown.cjs +0 -0
  739. /package/dist/{packages/agent-runtime → agent-runtime}/src/streaming-markdown.js +0 -0
  740. /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-extras.cjs +0 -0
  741. /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-extras.js +0 -0
  742. /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-snapshot-cache.cjs +0 -0
  743. /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-snapshot-cache.js +0 -0
  744. /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-status.cjs +0 -0
  745. /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-status.js +0 -0
  746. /package/dist/{packages/agent-runtime → agent-runtime}/src/tool-outputs.cjs +0 -0
  747. /package/dist/{packages/agent-runtime → agent-runtime}/src/tool-outputs.js +0 -0
  748. /package/dist/{packages/agent-runtime → agent-runtime}/src/tool-result.cjs +0 -0
  749. /package/dist/{packages/agent-runtime → agent-runtime}/src/tool-result.js +0 -0
  750. /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-config.cjs +0 -0
  751. /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-config.js +0 -0
  752. /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-runtime.cjs +0 -0
  753. /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-runtime.js +0 -0
  754. /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-window-sync.cjs +0 -0
  755. /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-window-sync.js +0 -0
  756. /package/dist/{packages/agent-runtime → agent-runtime}/src/voice-turn-grouping.cjs +0 -0
  757. /package/dist/{packages/agent-runtime → agent-runtime}/src/voice-turn-grouping.js +0 -0
  758. /package/dist/{packages/react → react}/package.json.cjs.map +0 -0
  759. /package/dist/{packages/react → react}/package.json.js.map +0 -0
  760. /package/dist/{packages/tiptap-markdown-table → tiptap-markdown-table}/src/markdown-table-serializer.cjs +0 -0
  761. /package/dist/{packages/tiptap-markdown-table → tiptap-markdown-table}/src/markdown-table-serializer.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-tool-bridge.cjs","sources":["../../../../agent-runtime/src/client-tool-bridge.ts"],"sourcesContent":["/**\n * The statewire client-tool bridge loop, shared by every surface that executes\n * frontend tool calls (Olympus, the React SDK, the Office add-in, mobile).\n *\n * Tools declared through `runConfig.custom.client_tools` have no backend\n * implementation: a call parks the run on an interrupt that arrives as a\n * statewire input request. A bridge with handlers for the whole batch claims\n * it (exactly-once per ownership domain), runs those handlers, and resumes\n * with per-call result envelopes. Other attached tabs leave it for the\n * executing surface; a thrown handler still returns an actionable error.\n *\n * Wire-adjacent correctness invariants this module owns, so they can never\n * drift apart per surface again:\n * - exactly-once claim per request, with un-claim on every path that did NOT\n * run any handler (abandoned before execution, consent dismissed, unmount\n * before the first handler) so a re-emitted request is executable instead\n * of stranding the run;\n * - once a batch has STARTED executing handlers, its claim is never released\n * by unmount: handlers cannot be cancelled, so releasing would let another\n * mount re-claim the re-emitted request and re-execute side effects that\n * already ran (double-executing a mutating tool). The completed wire-safe\n * result is cached and may be replayed across reconnects/remounts instead;\n * - liveness checks between awaits (the run can be stopped or answered\n * elsewhere while a handler runs) so a stale request is never resumed;\n * - per-call JSON wire-safety, so one unrepresentable handler result answers\n * that one call with an error instead of failing the whole batch.\n *\n * Like `interrupts.ts`, this is parameterized rather than self-wiring: the\n * host component reads `useStatewireRuns().inputRequests` and\n * `useStatewireSendCommand()` itself (both mockable per surface) and passes\n * them in, together with its ownership tracker and write-consent policy.\n */\n\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useRef } from 'react';\nimport {\n type ClientToolCallRequest,\n type ClientToolRequestTracker,\n type ClientToolResultEnvelope,\n clientToolCallArgs,\n clientToolErrorEnvelope,\n clientToolResultsResumeValue,\n clientToolSuccessEnvelope,\n isClientToolInterrupt,\n} from './client-tools';\n\n/** A locally executable tool, normalized across the per-surface tool shapes. */\nexport interface ClientToolBridgeTool {\n /** Snake_case tool id the model calls (e.g. `update_sheet_range`). */\n name: string;\n /** Mutating tools gate on the surface's write-consent flow (`confirmWrites`). */\n requiresWrite?: boolean;\n run: (\n args: Record<string, unknown>,\n context: { toolCallId: string },\n ) => Promise<unknown>;\n}\n\n/**\n * Batch write-consent outcome. `proceed` runs the write calls, `declined`\n * answers them with the declined message while reads still run, `abandoned`\n * releases the claim without resuming (the request died, or the consent UI was\n * dismissed by the run moving on). Grant persistence (a thread set, a stored\n * preference) is the surface's own policy, applied inside its callback.\n */\nexport type ClientToolWriteConsentDecision = 'proceed' | 'declined' | 'abandoned';\n\nexport interface ClientToolCallSettledEvent {\n toolName: string;\n status: 'success' | 'error' | 'declined';\n errorType?: string;\n}\n\n/** Default resume error for a write call the user declined. */\nexport const CLIENT_TOOL_WRITE_DECLINED_MESSAGE =\n 'The user declined this edit. Do not retry it; ask the user how to proceed.';\n\n/** Default resume error for a call naming a tool this surface does not have. */\nexport function defaultMissingClientToolMessage(toolName: string): string {\n return `This surface has no tool named '${toolName}'.`;\n}\n\n/** Statewire request ids are unique per run, but a thread scope keeps a\n * process-wide claim unambiguous when two conversations are open at once. */\nexport function clientToolClaimKey(threadId: string, requestId: string): string {\n return `${threadId}:${requestId}`;\n}\n\nconst CLIENT_TOOL_RESUME_RETRY_DELAYS_MS = [2_000, 5_000, 10_000, 15_000, 30_000];\nconst MAX_CLIENT_TOOL_RESUME_ATTEMPTS = 25;\n\nfunction clientToolResumeRetryDelayMs(attemptNumber: number): number {\n const index = Math.min(\n Math.max(attemptNumber - 1, 0),\n CLIENT_TOOL_RESUME_RETRY_DELAYS_MS.length - 1,\n );\n return CLIENT_TOOL_RESUME_RETRY_DELAYS_MS[index] ?? 30_000;\n}\n\n/**\n * A wire-safe copy of a handler's return value: handlers can return values\n * JSON cannot represent (DOM nodes, host objects, bigints), which must never\n * reach the transport. Throws for an unrepresentable value so the caller can\n * answer that one call with an error instead of stranding the whole batch.\n */\nexport function jsonSafeClientToolResult(value: unknown): unknown {\n const serialized = JSON.stringify(value);\n return serialized === undefined ? null : JSON.parse(serialized);\n}\n\n/** A parked, unanswered client-tool input request. */\nexport function isPendingClientToolRequest(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n if (request.type !== 'interrupt' || 'response' in request) return false;\n return isClientToolInterrupt((request as { payload?: unknown }).payload);\n}\n\nexport interface ExecuteClientToolBatchOptions {\n calls: readonly ClientToolCallRequest[];\n tools: readonly ClientToolBridgeTool[];\n /** Whether the parked request still exists and this surface may answer it.\n * Checked before consent resolution, before every call, and before the\n * results are handed back for resume. */\n isLive: () => boolean;\n /** Batch write gate; absent means writes run without consent (surfaces that\n * only register read tools). Called once per batch, before any handler. */\n confirmWrites?: (batch: {\n writeToolNames: readonly string[];\n }) => Promise<ClientToolWriteConsentDecision>;\n writeDeclinedMessage?: string;\n missingToolMessage?: (toolName: string) => string;\n /** Per-call telemetry (the Office add-in's tool events). */\n onCallSettled?: (event: ClientToolCallSettledEvent) => void;\n /**\n * Called once, after the write-consent gate resolves and immediately before\n * the first handler runs — the point where side effects begin. There is no\n * await between this callback and the first handler invocation, so a caller\n * marking \"execution started\" here can never miss a handler that ran.\n */\n onExecutionStart?: () => void;\n}\n\n/**\n * Execute one batched client-tool park: consent, per-call execution with\n * wire-safe envelopes, liveness aborts. Returns the per-interrupt result map\n * to resume with, or `null` when the batch was abandoned (the request died,\n * consent was abandoned, or the surface went away) — the caller must release\n * its claim and must NOT resume.\n */\nexport async function executeClientToolBatch(\n options: ExecuteClientToolBatchOptions,\n): Promise<Record<string, ClientToolResultEnvelope> | null> {\n const {\n calls,\n tools,\n isLive,\n confirmWrites,\n writeDeclinedMessage = CLIENT_TOOL_WRITE_DECLINED_MESSAGE,\n missingToolMessage = defaultMissingClientToolMessage,\n onCallSettled,\n onExecutionStart,\n } = options;\n const registry = new Map(tools.map((tool) => [tool.name, tool]));\n\n // One consent covers the whole batch (and whatever scope the surface's\n // callback persists): writes are declined together while reads still run.\n let writesDeclined = false;\n const writeToolNames = calls\n .filter((call) => registry.get(call.tool_name)?.requiresWrite === true)\n .map((call) => call.tool_name);\n if (writeToolNames.length > 0 && confirmWrites) {\n const decision = await confirmWrites({ writeToolNames });\n if (decision === 'abandoned' || !isLive()) {\n // The run was stopped (or answered elsewhere) while consent was\n // pending: abort before any handler mutates the user's asset.\n return null;\n }\n writesDeclined = decision === 'declined';\n }\n\n onExecutionStart?.();\n\n const results: Record<string, ClientToolResultEnvelope> = {};\n for (const call of calls) {\n if (!isLive()) return null;\n const tool = registry.get(call.tool_name);\n let envelope: ClientToolResultEnvelope;\n let status: ClientToolCallSettledEvent['status'] = 'success';\n let errorType: string | undefined;\n if (!tool) {\n envelope = clientToolErrorEnvelope(missingToolMessage(call.tool_name));\n status = 'error';\n errorType = 'UnknownClientTool';\n } else if (tool.requiresWrite === true && writesDeclined) {\n envelope = clientToolErrorEnvelope(writeDeclinedMessage);\n status = 'declined';\n } else {\n try {\n envelope = clientToolSuccessEnvelope(\n jsonSafeClientToolResult(\n await tool.run(clientToolCallArgs(call), {\n toolCallId: call.interrupt_id,\n }),\n ),\n );\n } catch (error) {\n envelope = clientToolErrorEnvelope(error);\n status = 'error';\n errorType = error instanceof Error ? error.name : typeof error;\n }\n }\n results[call.interrupt_id] = envelope;\n onCallSettled?.({\n toolName: call.tool_name,\n status,\n ...(errorType !== undefined ? { errorType } : {}),\n });\n }\n\n // Abandoned mid-flight: resuming a dead request would error, and a\n // re-emitted one must be executable again.\n return isLive() ? results : null;\n}\n\nexport interface UseStatewireClientToolBridgeOptions {\n threadId: string;\n tools: readonly ClientToolBridgeTool[];\n /**\n * Ownership domain for parked-request claims. Pass a module-scoped tracker\n * to make ownership process-wide across every mount of the surface (two\n * mounted surfaces of one conversation execute each request exactly once),\n * or a per-mount tracker to scope ownership to one mounted bridge.\n */\n tracker: ClientToolRequestTracker;\n /** The surface's `useStatewireRuns().inputRequests` read. */\n inputRequests: readonly StatewireThread.InputRequestState[] | undefined;\n /** Dispatch the `run/input` resume on the surface's transport. The value is\n * already wire-safe (per-call JSON round-trip inside the batch engine). */\n /** Dispatch the answer and settle only once the Statewire host accepts or\n * rejects the command. The bridge deliberately keeps replaying while this\n * promise is pending: `run/input` removes the request optimistically before\n * the server has accepted the answer. */\n sendResume: (\n requestId: string,\n value: Record<string, unknown>,\n ) => PromiseLike<unknown>;\n /** Batch write gate, with the parked request's id so the surface can close\n * its consent UI the instant that request disappears. */\n confirmWrites?: (batch: {\n requestId: string;\n writeToolNames: readonly string[];\n }) => Promise<ClientToolWriteConsentDecision>;\n writeDeclinedMessage?: string;\n missingToolMessage?: (toolName: string) => string;\n onCallSettled?: (event: ClientToolCallSettledEvent) => void;\n /** Log prefix for resume-dispatch failures. */\n logLabel?: string;\n}\n\n/**\n * The claim lifecycle around {@link executeClientToolBatch}: claim the pending\n * request once per ownership domain, execute, resume — releasing the claim on\n * every path that ran no handler, so re-emitted requests recover.\n *\n * Unmount splits by whether side effects began:\n * - a claim whose batch has NOT started executing (claimed this tick, or\n * parked on write consent) is released, so another mount — or a remount —\n * can execute the re-emitted request instead of stranding the parked run;\n * - a claim whose batch HAS started stays held: handlers cannot be cancelled,\n * so releasing would let a sibling mount re-claim and re-execute a mutation\n * whose side effects already ran. The detached execution caches its result\n * and attempts one resume through its captured transport; a later mount can\n * replay the same result without repeating the side effect.\n */\nexport function useStatewireClientToolBridge(\n options: UseStatewireClientToolBridgeOptions,\n): void {\n const { threadId, tracker, inputRequests, logLabel = '[AthenaSDK]' } = options;\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const toolNames = new Set(options.tools.map((tool) => tool.name));\n const pendingRequest = inputRequests?.find((request) => {\n if (!isPendingClientToolRequest(request)) return false;\n const payload = (request as StatewireThread.CustomInputRequest).payload;\n if (!isClientToolInterrupt(payload)) return false;\n // Every attached tab sees the same park. An observer without these tools\n // must not claim it and race the executing surface with an unknown-tool\n // error (for example, Olympus watching an Excel conversation).\n // A cached result already belongs to this tracker and can be replayed\n // without loading its handlers again.\n return (\n tracker.getCachedResume(clientToolClaimKey(threadId, request.id)) !== undefined ||\n payload.context.requests.every((call) => toolNames.has(call.tool_name))\n );\n });\n\n // Claims this bridge holds but has not resumed, split by whether their\n // batch has begun running handlers. The ref Set instances are stable (only\n // their contents mutate), so the unmount cleanup sees every claim.\n const ownedClaimsRef = useRef<Set<string>>(new Set());\n const startedClaimsRef = useRef<Set<string>>(new Set());\n const resumeOwnerRef = useRef(Symbol('client-tool-resume-owner'));\n const resumeClaimsRef = useRef<Set<string>>(new Set());\n const resumeTimersRef = useRef<\n Map<string, { requestId: string; timer: ReturnType<typeof setTimeout> }>\n >(new Map());\n const activeRef = useRef(true);\n const releaseClaim = useCallback(\n (claim: string) => {\n // Release the shared tracker entry only while THIS mount still owns it.\n // A detached batch can settle after the unmount cleanup already released\n // (and disowned) its claim; by then the tracker entry may belong to\n // another mount that claimed, executed, and resumed — releasing it here\n // would let a stale re-emission of the same request execute again.\n const owned = ownedClaimsRef.current.delete(claim);\n startedClaimsRef.current.delete(claim);\n if (owned) tracker.release(claim);\n },\n [tracker],\n );\n const stopResumeReplay = useCallback(\n (claim: string) => {\n const timerEntry = resumeTimersRef.current.get(claim);\n if (timerEntry) clearTimeout(timerEntry.timer);\n resumeTimersRef.current.delete(claim);\n if (resumeClaimsRef.current.delete(claim)) {\n tracker.releaseResumeReplay(claim, resumeOwnerRef.current);\n }\n },\n [tracker],\n );\n useEffect(() => {\n activeRef.current = true;\n const ownedClaims = ownedClaimsRef.current;\n const startedClaims = startedClaimsRef.current;\n return () => {\n activeRef.current = false;\n for (const claim of Array.from(resumeClaimsRef.current)) {\n stopResumeReplay(claim);\n }\n for (const claim of ownedClaims) {\n // Started batches keep their claim — their detached execution settles\n // it (resume, or release on a failed dispatch). Releasing here would\n // double-execute the side effects that already ran.\n if (startedClaims.has(claim)) continue;\n ownedClaims.delete(claim);\n tracker.release(claim);\n }\n };\n }, [stopResumeReplay, tracker]);\n\n // Handlers (and consent) are async, so the run can be stopped or the request\n // answered elsewhere while we await. The live ids let us abandon an\n // unfinished local execution when its request is no longer present. Once a\n // result is cached, only command settlement may stop its replay: Statewire\n // removes submitted requests optimistically before the host accepts them.\n const liveRequestIdsRef = useRef<ReadonlySet<string>>(new Set());\n liveRequestIdsRef.current = new Set((inputRequests ?? []).map((request) => request.id));\n\n const startResumeReplay = useCallback(\n (claim: string, requestId: string) => {\n if (!activeRef.current || resumeClaimsRef.current.has(claim)) return;\n if (!tracker.claimResumeReplay(claim, resumeOwnerRef.current)) return;\n resumeClaimsRef.current.add(claim);\n\n const dispatch = (): void => {\n resumeTimersRef.current.delete(claim);\n if (!activeRef.current) {\n stopResumeReplay(claim);\n return;\n }\n const attempt = tracker.takeResumeAttempt(claim, {\n owner: resumeOwnerRef.current,\n maxAttempts: MAX_CLIENT_TOOL_RESUME_ATTEMPTS,\n });\n if (!attempt) {\n stopResumeReplay(claim);\n return;\n }\n\n try {\n const settled = optionsRef.current.sendResume(requestId, attempt.value);\n // A fulfilled command result is the authoritative acknowledgement:\n // the host accepted and applied the answer. The local input request\n // disappearing is NOT an acknowledgement because the Statewire\n // reducer does that optimistically as soon as the command is\n // submitted.\n void Promise.resolve(settled).then(\n () => {\n // Keep the execution claim, but discard the cached result once\n // the host accepts it. A stale pending-request render can no\n // longer replay the command or re-execute the local mutation.\n tracker.settleResume(claim);\n stopResumeReplay(claim);\n },\n (error) => {\n // A rejected attempt may have raced another still-pending\n // replay. Keep the bounded loop alive; any accepted sibling\n // stops it, while the global attempt cap prevents storms.\n console.error(`${logLabel} failed to resume client tool results:`, error);\n },\n );\n } catch (error) {\n // Keep the cached result and retry it. Re-executing a mutating local\n // handler would be unsafe; replaying its id-stamped result is safe.\n console.error(`${logLabel} failed to resume client tool results:`, error);\n }\n\n if (\n attempt.attemptNumber >= MAX_CLIENT_TOOL_RESUME_ATTEMPTS ||\n !activeRef.current ||\n !resumeClaimsRef.current.has(claim)\n ) {\n stopResumeReplay(claim);\n return;\n }\n const timer = setTimeout(dispatch, clientToolResumeRetryDelayMs(attempt.attemptNumber));\n resumeTimersRef.current.set(claim, { requestId, timer });\n };\n\n dispatch();\n },\n [logLabel, stopResumeReplay, tracker],\n );\n\n const executeRequest = useCallback(\n async (request: StatewireThread.CustomInputRequest) => {\n const payload = request.payload;\n if (!isClientToolInterrupt(payload)) return;\n const claim = clientToolClaimKey(threadId, request.id);\n const {\n tools,\n confirmWrites,\n writeDeclinedMessage,\n missingToolMessage,\n onCallSettled,\n sendResume,\n } = optionsRef.current;\n\n const results = await executeClientToolBatch({\n calls: payload.context.requests,\n tools,\n // Live while the request still exists AND this surface may answer it:\n // mounted, or already past the point of no return (handlers started —\n // the detached execution must finish and settle, never re-execute).\n // After unmount the request-id snapshot freezes at its last observed\n // state, which keeps a started batch's own request visible to it.\n isLive: () =>\n liveRequestIdsRef.current.has(request.id) &&\n (activeRef.current || startedClaimsRef.current.has(claim)),\n onExecutionStart: () => {\n startedClaimsRef.current.add(claim);\n },\n ...(confirmWrites\n ? {\n confirmWrites: ({ writeToolNames }: { writeToolNames: readonly string[] }) =>\n confirmWrites({ requestId: request.id, writeToolNames }),\n }\n : {}),\n ...(writeDeclinedMessage !== undefined ? { writeDeclinedMessage } : {}),\n ...(missingToolMessage ? { missingToolMessage } : {}),\n ...(onCallSettled ? { onCallSettled } : {}),\n });\n if (results === null) {\n releaseClaim(claim);\n return;\n }\n\n const resumeValue = clientToolResultsResumeValue(results);\n tracker.cacheResume(claim, resumeValue);\n // Execution is complete, but the exactly-once claim remains. A pending\n // re-emission must replay this cached value, never run the handlers again.\n ownedClaimsRef.current.delete(claim);\n startedClaimsRef.current.delete(claim);\n\n if (!activeRef.current) {\n // The handler crossed an unmount. Preserve the pre-existing best-effort\n // settle through its captured transport, but leave the cached result\n // available for a later mount if this dispatch is lost.\n if (tracker.claimResumeReplay(claim, resumeOwnerRef.current)) {\n const attempt = tracker.takeResumeAttempt(claim, {\n owner: resumeOwnerRef.current,\n maxAttempts: MAX_CLIENT_TOOL_RESUME_ATTEMPTS,\n });\n if (attempt) {\n try {\n void Promise.resolve(sendResume(request.id, attempt.value)).then(\n () => tracker.settleResume(claim),\n (error) => {\n console.error(`${logLabel} failed to resume client tool results:`, error);\n },\n );\n } catch (error) {\n console.error(`${logLabel} failed to resume client tool results:`, error);\n }\n }\n // Ownership only fences this best-effort dispatch; a future mount\n // may claim and replay the cached value if it did not settle.\n tracker.releaseResumeReplay(claim, resumeOwnerRef.current);\n }\n return;\n }\n\n startResumeReplay(claim, request.id);\n },\n [logLabel, releaseClaim, startResumeReplay, threadId, tracker],\n );\n\n useEffect(() => {\n if (!pendingRequest) return;\n const claim = clientToolClaimKey(threadId, pendingRequest.id);\n if (!tracker.claim(claim)) {\n // The local handler already ran. This is the reconnect/remount path:\n // replay its cached answer instead of double-executing the side effect.\n if (tracker.getCachedResume(claim)) {\n startResumeReplay(claim, pendingRequest.id);\n }\n return;\n }\n ownedClaimsRef.current.add(claim);\n void executeRequest(pendingRequest);\n }, [executeRequest, pendingRequest, startResumeReplay, threadId, tracker]);\n}\n"],"names":["isClientToolInterrupt","clientToolErrorEnvelope","clientToolSuccessEnvelope","clientToolCallArgs","useRef","useCallback","useEffect","clientToolResultsResumeValue"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-tool-bridge.js","sources":["../../../../agent-runtime/src/client-tool-bridge.ts"],"sourcesContent":["/**\n * The statewire client-tool bridge loop, shared by every surface that executes\n * frontend tool calls (Olympus, the React SDK, the Office add-in, mobile).\n *\n * Tools declared through `runConfig.custom.client_tools` have no backend\n * implementation: a call parks the run on an interrupt that arrives as a\n * statewire input request. A bridge with handlers for the whole batch claims\n * it (exactly-once per ownership domain), runs those handlers, and resumes\n * with per-call result envelopes. Other attached tabs leave it for the\n * executing surface; a thrown handler still returns an actionable error.\n *\n * Wire-adjacent correctness invariants this module owns, so they can never\n * drift apart per surface again:\n * - exactly-once claim per request, with un-claim on every path that did NOT\n * run any handler (abandoned before execution, consent dismissed, unmount\n * before the first handler) so a re-emitted request is executable instead\n * of stranding the run;\n * - once a batch has STARTED executing handlers, its claim is never released\n * by unmount: handlers cannot be cancelled, so releasing would let another\n * mount re-claim the re-emitted request and re-execute side effects that\n * already ran (double-executing a mutating tool). The completed wire-safe\n * result is cached and may be replayed across reconnects/remounts instead;\n * - liveness checks between awaits (the run can be stopped or answered\n * elsewhere while a handler runs) so a stale request is never resumed;\n * - per-call JSON wire-safety, so one unrepresentable handler result answers\n * that one call with an error instead of failing the whole batch.\n *\n * Like `interrupts.ts`, this is parameterized rather than self-wiring: the\n * host component reads `useStatewireRuns().inputRequests` and\n * `useStatewireSendCommand()` itself (both mockable per surface) and passes\n * them in, together with its ownership tracker and write-consent policy.\n */\n\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useRef } from 'react';\nimport {\n type ClientToolCallRequest,\n type ClientToolRequestTracker,\n type ClientToolResultEnvelope,\n clientToolCallArgs,\n clientToolErrorEnvelope,\n clientToolResultsResumeValue,\n clientToolSuccessEnvelope,\n isClientToolInterrupt,\n} from './client-tools';\n\n/** A locally executable tool, normalized across the per-surface tool shapes. */\nexport interface ClientToolBridgeTool {\n /** Snake_case tool id the model calls (e.g. `update_sheet_range`). */\n name: string;\n /** Mutating tools gate on the surface's write-consent flow (`confirmWrites`). */\n requiresWrite?: boolean;\n run: (\n args: Record<string, unknown>,\n context: { toolCallId: string },\n ) => Promise<unknown>;\n}\n\n/**\n * Batch write-consent outcome. `proceed` runs the write calls, `declined`\n * answers them with the declined message while reads still run, `abandoned`\n * releases the claim without resuming (the request died, or the consent UI was\n * dismissed by the run moving on). Grant persistence (a thread set, a stored\n * preference) is the surface's own policy, applied inside its callback.\n */\nexport type ClientToolWriteConsentDecision = 'proceed' | 'declined' | 'abandoned';\n\nexport interface ClientToolCallSettledEvent {\n toolName: string;\n status: 'success' | 'error' | 'declined';\n errorType?: string;\n}\n\n/** Default resume error for a write call the user declined. */\nexport const CLIENT_TOOL_WRITE_DECLINED_MESSAGE =\n 'The user declined this edit. Do not retry it; ask the user how to proceed.';\n\n/** Default resume error for a call naming a tool this surface does not have. */\nexport function defaultMissingClientToolMessage(toolName: string): string {\n return `This surface has no tool named '${toolName}'.`;\n}\n\n/** Statewire request ids are unique per run, but a thread scope keeps a\n * process-wide claim unambiguous when two conversations are open at once. */\nexport function clientToolClaimKey(threadId: string, requestId: string): string {\n return `${threadId}:${requestId}`;\n}\n\nconst CLIENT_TOOL_RESUME_RETRY_DELAYS_MS = [2_000, 5_000, 10_000, 15_000, 30_000];\nconst MAX_CLIENT_TOOL_RESUME_ATTEMPTS = 25;\n\nfunction clientToolResumeRetryDelayMs(attemptNumber: number): number {\n const index = Math.min(\n Math.max(attemptNumber - 1, 0),\n CLIENT_TOOL_RESUME_RETRY_DELAYS_MS.length - 1,\n );\n return CLIENT_TOOL_RESUME_RETRY_DELAYS_MS[index] ?? 30_000;\n}\n\n/**\n * A wire-safe copy of a handler's return value: handlers can return values\n * JSON cannot represent (DOM nodes, host objects, bigints), which must never\n * reach the transport. Throws for an unrepresentable value so the caller can\n * answer that one call with an error instead of stranding the whole batch.\n */\nexport function jsonSafeClientToolResult(value: unknown): unknown {\n const serialized = JSON.stringify(value);\n return serialized === undefined ? null : JSON.parse(serialized);\n}\n\n/** A parked, unanswered client-tool input request. */\nexport function isPendingClientToolRequest(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n if (request.type !== 'interrupt' || 'response' in request) return false;\n return isClientToolInterrupt((request as { payload?: unknown }).payload);\n}\n\nexport interface ExecuteClientToolBatchOptions {\n calls: readonly ClientToolCallRequest[];\n tools: readonly ClientToolBridgeTool[];\n /** Whether the parked request still exists and this surface may answer it.\n * Checked before consent resolution, before every call, and before the\n * results are handed back for resume. */\n isLive: () => boolean;\n /** Batch write gate; absent means writes run without consent (surfaces that\n * only register read tools). Called once per batch, before any handler. */\n confirmWrites?: (batch: {\n writeToolNames: readonly string[];\n }) => Promise<ClientToolWriteConsentDecision>;\n writeDeclinedMessage?: string;\n missingToolMessage?: (toolName: string) => string;\n /** Per-call telemetry (the Office add-in's tool events). */\n onCallSettled?: (event: ClientToolCallSettledEvent) => void;\n /**\n * Called once, after the write-consent gate resolves and immediately before\n * the first handler runs — the point where side effects begin. There is no\n * await between this callback and the first handler invocation, so a caller\n * marking \"execution started\" here can never miss a handler that ran.\n */\n onExecutionStart?: () => void;\n}\n\n/**\n * Execute one batched client-tool park: consent, per-call execution with\n * wire-safe envelopes, liveness aborts. Returns the per-interrupt result map\n * to resume with, or `null` when the batch was abandoned (the request died,\n * consent was abandoned, or the surface went away) — the caller must release\n * its claim and must NOT resume.\n */\nexport async function executeClientToolBatch(\n options: ExecuteClientToolBatchOptions,\n): Promise<Record<string, ClientToolResultEnvelope> | null> {\n const {\n calls,\n tools,\n isLive,\n confirmWrites,\n writeDeclinedMessage = CLIENT_TOOL_WRITE_DECLINED_MESSAGE,\n missingToolMessage = defaultMissingClientToolMessage,\n onCallSettled,\n onExecutionStart,\n } = options;\n const registry = new Map(tools.map((tool) => [tool.name, tool]));\n\n // One consent covers the whole batch (and whatever scope the surface's\n // callback persists): writes are declined together while reads still run.\n let writesDeclined = false;\n const writeToolNames = calls\n .filter((call) => registry.get(call.tool_name)?.requiresWrite === true)\n .map((call) => call.tool_name);\n if (writeToolNames.length > 0 && confirmWrites) {\n const decision = await confirmWrites({ writeToolNames });\n if (decision === 'abandoned' || !isLive()) {\n // The run was stopped (or answered elsewhere) while consent was\n // pending: abort before any handler mutates the user's asset.\n return null;\n }\n writesDeclined = decision === 'declined';\n }\n\n onExecutionStart?.();\n\n const results: Record<string, ClientToolResultEnvelope> = {};\n for (const call of calls) {\n if (!isLive()) return null;\n const tool = registry.get(call.tool_name);\n let envelope: ClientToolResultEnvelope;\n let status: ClientToolCallSettledEvent['status'] = 'success';\n let errorType: string | undefined;\n if (!tool) {\n envelope = clientToolErrorEnvelope(missingToolMessage(call.tool_name));\n status = 'error';\n errorType = 'UnknownClientTool';\n } else if (tool.requiresWrite === true && writesDeclined) {\n envelope = clientToolErrorEnvelope(writeDeclinedMessage);\n status = 'declined';\n } else {\n try {\n envelope = clientToolSuccessEnvelope(\n jsonSafeClientToolResult(\n await tool.run(clientToolCallArgs(call), {\n toolCallId: call.interrupt_id,\n }),\n ),\n );\n } catch (error) {\n envelope = clientToolErrorEnvelope(error);\n status = 'error';\n errorType = error instanceof Error ? error.name : typeof error;\n }\n }\n results[call.interrupt_id] = envelope;\n onCallSettled?.({\n toolName: call.tool_name,\n status,\n ...(errorType !== undefined ? { errorType } : {}),\n });\n }\n\n // Abandoned mid-flight: resuming a dead request would error, and a\n // re-emitted one must be executable again.\n return isLive() ? results : null;\n}\n\nexport interface UseStatewireClientToolBridgeOptions {\n threadId: string;\n tools: readonly ClientToolBridgeTool[];\n /**\n * Ownership domain for parked-request claims. Pass a module-scoped tracker\n * to make ownership process-wide across every mount of the surface (two\n * mounted surfaces of one conversation execute each request exactly once),\n * or a per-mount tracker to scope ownership to one mounted bridge.\n */\n tracker: ClientToolRequestTracker;\n /** The surface's `useStatewireRuns().inputRequests` read. */\n inputRequests: readonly StatewireThread.InputRequestState[] | undefined;\n /** Dispatch the `run/input` resume on the surface's transport. The value is\n * already wire-safe (per-call JSON round-trip inside the batch engine). */\n /** Dispatch the answer and settle only once the Statewire host accepts or\n * rejects the command. The bridge deliberately keeps replaying while this\n * promise is pending: `run/input` removes the request optimistically before\n * the server has accepted the answer. */\n sendResume: (\n requestId: string,\n value: Record<string, unknown>,\n ) => PromiseLike<unknown>;\n /** Batch write gate, with the parked request's id so the surface can close\n * its consent UI the instant that request disappears. */\n confirmWrites?: (batch: {\n requestId: string;\n writeToolNames: readonly string[];\n }) => Promise<ClientToolWriteConsentDecision>;\n writeDeclinedMessage?: string;\n missingToolMessage?: (toolName: string) => string;\n onCallSettled?: (event: ClientToolCallSettledEvent) => void;\n /** Log prefix for resume-dispatch failures. */\n logLabel?: string;\n}\n\n/**\n * The claim lifecycle around {@link executeClientToolBatch}: claim the pending\n * request once per ownership domain, execute, resume — releasing the claim on\n * every path that ran no handler, so re-emitted requests recover.\n *\n * Unmount splits by whether side effects began:\n * - a claim whose batch has NOT started executing (claimed this tick, or\n * parked on write consent) is released, so another mount — or a remount —\n * can execute the re-emitted request instead of stranding the parked run;\n * - a claim whose batch HAS started stays held: handlers cannot be cancelled,\n * so releasing would let a sibling mount re-claim and re-execute a mutation\n * whose side effects already ran. The detached execution caches its result\n * and attempts one resume through its captured transport; a later mount can\n * replay the same result without repeating the side effect.\n */\nexport function useStatewireClientToolBridge(\n options: UseStatewireClientToolBridgeOptions,\n): void {\n const { threadId, tracker, inputRequests, logLabel = '[AthenaSDK]' } = options;\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const toolNames = new Set(options.tools.map((tool) => tool.name));\n const pendingRequest = inputRequests?.find((request) => {\n if (!isPendingClientToolRequest(request)) return false;\n const payload = (request as StatewireThread.CustomInputRequest).payload;\n if (!isClientToolInterrupt(payload)) return false;\n // Every attached tab sees the same park. An observer without these tools\n // must not claim it and race the executing surface with an unknown-tool\n // error (for example, Olympus watching an Excel conversation).\n // A cached result already belongs to this tracker and can be replayed\n // without loading its handlers again.\n return (\n tracker.getCachedResume(clientToolClaimKey(threadId, request.id)) !== undefined ||\n payload.context.requests.every((call) => toolNames.has(call.tool_name))\n );\n });\n\n // Claims this bridge holds but has not resumed, split by whether their\n // batch has begun running handlers. The ref Set instances are stable (only\n // their contents mutate), so the unmount cleanup sees every claim.\n const ownedClaimsRef = useRef<Set<string>>(new Set());\n const startedClaimsRef = useRef<Set<string>>(new Set());\n const resumeOwnerRef = useRef(Symbol('client-tool-resume-owner'));\n const resumeClaimsRef = useRef<Set<string>>(new Set());\n const resumeTimersRef = useRef<\n Map<string, { requestId: string; timer: ReturnType<typeof setTimeout> }>\n >(new Map());\n const activeRef = useRef(true);\n const releaseClaim = useCallback(\n (claim: string) => {\n // Release the shared tracker entry only while THIS mount still owns it.\n // A detached batch can settle after the unmount cleanup already released\n // (and disowned) its claim; by then the tracker entry may belong to\n // another mount that claimed, executed, and resumed — releasing it here\n // would let a stale re-emission of the same request execute again.\n const owned = ownedClaimsRef.current.delete(claim);\n startedClaimsRef.current.delete(claim);\n if (owned) tracker.release(claim);\n },\n [tracker],\n );\n const stopResumeReplay = useCallback(\n (claim: string) => {\n const timerEntry = resumeTimersRef.current.get(claim);\n if (timerEntry) clearTimeout(timerEntry.timer);\n resumeTimersRef.current.delete(claim);\n if (resumeClaimsRef.current.delete(claim)) {\n tracker.releaseResumeReplay(claim, resumeOwnerRef.current);\n }\n },\n [tracker],\n );\n useEffect(() => {\n activeRef.current = true;\n const ownedClaims = ownedClaimsRef.current;\n const startedClaims = startedClaimsRef.current;\n return () => {\n activeRef.current = false;\n for (const claim of Array.from(resumeClaimsRef.current)) {\n stopResumeReplay(claim);\n }\n for (const claim of ownedClaims) {\n // Started batches keep their claim — their detached execution settles\n // it (resume, or release on a failed dispatch). Releasing here would\n // double-execute the side effects that already ran.\n if (startedClaims.has(claim)) continue;\n ownedClaims.delete(claim);\n tracker.release(claim);\n }\n };\n }, [stopResumeReplay, tracker]);\n\n // Handlers (and consent) are async, so the run can be stopped or the request\n // answered elsewhere while we await. The live ids let us abandon an\n // unfinished local execution when its request is no longer present. Once a\n // result is cached, only command settlement may stop its replay: Statewire\n // removes submitted requests optimistically before the host accepts them.\n const liveRequestIdsRef = useRef<ReadonlySet<string>>(new Set());\n liveRequestIdsRef.current = new Set((inputRequests ?? []).map((request) => request.id));\n\n const startResumeReplay = useCallback(\n (claim: string, requestId: string) => {\n if (!activeRef.current || resumeClaimsRef.current.has(claim)) return;\n if (!tracker.claimResumeReplay(claim, resumeOwnerRef.current)) return;\n resumeClaimsRef.current.add(claim);\n\n const dispatch = (): void => {\n resumeTimersRef.current.delete(claim);\n if (!activeRef.current) {\n stopResumeReplay(claim);\n return;\n }\n const attempt = tracker.takeResumeAttempt(claim, {\n owner: resumeOwnerRef.current,\n maxAttempts: MAX_CLIENT_TOOL_RESUME_ATTEMPTS,\n });\n if (!attempt) {\n stopResumeReplay(claim);\n return;\n }\n\n try {\n const settled = optionsRef.current.sendResume(requestId, attempt.value);\n // A fulfilled command result is the authoritative acknowledgement:\n // the host accepted and applied the answer. The local input request\n // disappearing is NOT an acknowledgement because the Statewire\n // reducer does that optimistically as soon as the command is\n // submitted.\n void Promise.resolve(settled).then(\n () => {\n // Keep the execution claim, but discard the cached result once\n // the host accepts it. A stale pending-request render can no\n // longer replay the command or re-execute the local mutation.\n tracker.settleResume(claim);\n stopResumeReplay(claim);\n },\n (error) => {\n // A rejected attempt may have raced another still-pending\n // replay. Keep the bounded loop alive; any accepted sibling\n // stops it, while the global attempt cap prevents storms.\n console.error(`${logLabel} failed to resume client tool results:`, error);\n },\n );\n } catch (error) {\n // Keep the cached result and retry it. Re-executing a mutating local\n // handler would be unsafe; replaying its id-stamped result is safe.\n console.error(`${logLabel} failed to resume client tool results:`, error);\n }\n\n if (\n attempt.attemptNumber >= MAX_CLIENT_TOOL_RESUME_ATTEMPTS ||\n !activeRef.current ||\n !resumeClaimsRef.current.has(claim)\n ) {\n stopResumeReplay(claim);\n return;\n }\n const timer = setTimeout(dispatch, clientToolResumeRetryDelayMs(attempt.attemptNumber));\n resumeTimersRef.current.set(claim, { requestId, timer });\n };\n\n dispatch();\n },\n [logLabel, stopResumeReplay, tracker],\n );\n\n const executeRequest = useCallback(\n async (request: StatewireThread.CustomInputRequest) => {\n const payload = request.payload;\n if (!isClientToolInterrupt(payload)) return;\n const claim = clientToolClaimKey(threadId, request.id);\n const {\n tools,\n confirmWrites,\n writeDeclinedMessage,\n missingToolMessage,\n onCallSettled,\n sendResume,\n } = optionsRef.current;\n\n const results = await executeClientToolBatch({\n calls: payload.context.requests,\n tools,\n // Live while the request still exists AND this surface may answer it:\n // mounted, or already past the point of no return (handlers started —\n // the detached execution must finish and settle, never re-execute).\n // After unmount the request-id snapshot freezes at its last observed\n // state, which keeps a started batch's own request visible to it.\n isLive: () =>\n liveRequestIdsRef.current.has(request.id) &&\n (activeRef.current || startedClaimsRef.current.has(claim)),\n onExecutionStart: () => {\n startedClaimsRef.current.add(claim);\n },\n ...(confirmWrites\n ? {\n confirmWrites: ({ writeToolNames }: { writeToolNames: readonly string[] }) =>\n confirmWrites({ requestId: request.id, writeToolNames }),\n }\n : {}),\n ...(writeDeclinedMessage !== undefined ? { writeDeclinedMessage } : {}),\n ...(missingToolMessage ? { missingToolMessage } : {}),\n ...(onCallSettled ? { onCallSettled } : {}),\n });\n if (results === null) {\n releaseClaim(claim);\n return;\n }\n\n const resumeValue = clientToolResultsResumeValue(results);\n tracker.cacheResume(claim, resumeValue);\n // Execution is complete, but the exactly-once claim remains. A pending\n // re-emission must replay this cached value, never run the handlers again.\n ownedClaimsRef.current.delete(claim);\n startedClaimsRef.current.delete(claim);\n\n if (!activeRef.current) {\n // The handler crossed an unmount. Preserve the pre-existing best-effort\n // settle through its captured transport, but leave the cached result\n // available for a later mount if this dispatch is lost.\n if (tracker.claimResumeReplay(claim, resumeOwnerRef.current)) {\n const attempt = tracker.takeResumeAttempt(claim, {\n owner: resumeOwnerRef.current,\n maxAttempts: MAX_CLIENT_TOOL_RESUME_ATTEMPTS,\n });\n if (attempt) {\n try {\n void Promise.resolve(sendResume(request.id, attempt.value)).then(\n () => tracker.settleResume(claim),\n (error) => {\n console.error(`${logLabel} failed to resume client tool results:`, error);\n },\n );\n } catch (error) {\n console.error(`${logLabel} failed to resume client tool results:`, error);\n }\n }\n // Ownership only fences this best-effort dispatch; a future mount\n // may claim and replay the cached value if it did not settle.\n tracker.releaseResumeReplay(claim, resumeOwnerRef.current);\n }\n return;\n }\n\n startResumeReplay(claim, request.id);\n },\n [logLabel, releaseClaim, startResumeReplay, threadId, tracker],\n );\n\n useEffect(() => {\n if (!pendingRequest) return;\n const claim = clientToolClaimKey(threadId, pendingRequest.id);\n if (!tracker.claim(claim)) {\n // The local handler already ran. This is the reconnect/remount path:\n // replay its cached answer instead of double-executing the side effect.\n if (tracker.getCachedResume(claim)) {\n startResumeReplay(claim, pendingRequest.id);\n }\n return;\n }\n ownedClaimsRef.current.add(claim);\n void executeRequest(pendingRequest);\n }, [executeRequest, pendingRequest, startResumeReplay, threadId, tracker]);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-tools.cjs","sources":["../../../../agent-runtime/src/client-tools.ts"],"sourcesContent":["/**\n * Client-executed tools for statewire deep-agent threads.\n *\n * A client (the Office add-in, the Chrome extension, …) declares tools it can\n * execute locally by sending their JSON-schema definitions through\n * `runConfig.custom.client_tools`. The deep-agent runtime binds them as\n * interrupt-backed tools: when the model calls one, the run parks on a\n * LangGraph interrupt whose payload carries the tool name and arguments, the\n * statewire host publishes it as an `interrupt` input request, and the client\n * executes the tool and resumes the run with the result envelope.\n *\n * This module is transport-agnostic: it holds the wire contract (definitions,\n * interrupt payload shape, result envelopes) and a duplicate-execution guard.\n * The React hook that watches input requests lives with each client, next to\n * its host-specific tool handlers.\n */\n\nimport {\n CLIENT_TOOL_INTERRUPT_SOURCE,\n CLIENT_TOOL_RESULT_STATUS_ERROR,\n CLIENT_TOOL_RESULT_STATUS_SUCCESS,\n} from './generated/runconfig-contract';\n\n/** `source`/`type` stamped on the interrupt payload by the agora runtime. */\nexport { CLIENT_TOOL_INTERRUPT_SOURCE } from './generated/runconfig-contract';\n\n/** JSON-schema description of one client-executed tool. */\nexport interface ClientToolDefinition {\n /** Snake_case tool id the model calls (e.g. `update_sheet_range`). */\n name: string;\n description: string;\n /** JSON schema for the tool arguments (`{\"type\": \"object\", ...}`). */\n parameters: Record<string, unknown>;\n /** Mutating tools gate on the client's write-approval flow before running. */\n requiresWrite: boolean;\n}\n\n/** Per-call context the client-tool bridge hands a handler; absent when a host\n * invokes a handler outside a parked run. */\nexport interface ClientToolHandlerContext {\n /** The model's tool-call id, for handlers that correlate calls. */\n toolCallId: string;\n}\n\nexport type ClientToolHandler = (\n args: Record<string, unknown>,\n context?: ClientToolHandlerContext,\n) => Promise<unknown>;\n\nexport interface RegisteredClientTool {\n definition: ClientToolDefinition;\n handler: ClientToolHandler;\n}\n\n/** One parked tool call inside a batched client-tool input request. */\nexport interface ClientToolCallRequest {\n /** LangGraph interrupt id — the key the resume result map must use. */\n interrupt_id: string;\n tool_name: string;\n tool_args: Record<string, unknown>;\n requires_write?: boolean;\n}\n\n/**\n * The input-request payload the agora runtime publishes for parked client\n * tool calls. A parallel model turn parks several interrupts at once; the\n * runtime batches them into one payload and the client answers once with\n * `{ client_tool_results: { <interrupt_id>: <result envelope> } }`.\n */\nexport interface ClientToolInterruptPayload {\n source: typeof CLIENT_TOOL_INTERRUPT_SOURCE;\n type: typeof CLIENT_TOOL_INTERRUPT_SOURCE;\n message: string;\n context: {\n requests: ClientToolCallRequest[];\n };\n}\n\nfunction isClientToolCallRequest(value: unknown): value is ClientToolCallRequest {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const candidate = value as Partial<ClientToolCallRequest>;\n return (\n typeof candidate.interrupt_id === 'string' &&\n candidate.interrupt_id.length > 0 &&\n typeof candidate.tool_name === 'string'\n );\n}\n\n/**\n * Loose source check for surfaces that must NOT handle a client-tool park\n * (e.g. the generic approval banner). Unlike `isClientToolInterrupt`, this\n * matches any payload stamped with the client-tool source — including shapes\n * from a mismatched client/server version — so a skewed deploy can never\n * surface a generic \"Continue\" resume that corrupts a parked tool call.\n */\nexport function hasClientToolInterruptSource(payload: unknown): boolean {\n return (\n !!payload &&\n typeof payload === 'object' &&\n !Array.isArray(payload) &&\n (payload as { source?: unknown }).source === CLIENT_TOOL_INTERRUPT_SOURCE\n );\n}\n\nexport function isClientToolInterrupt(\n payload: unknown,\n): payload is ClientToolInterruptPayload {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return false;\n }\n const candidate = payload as Partial<ClientToolInterruptPayload>;\n return (\n candidate.source === CLIENT_TOOL_INTERRUPT_SOURCE &&\n candidate.type === CLIENT_TOOL_INTERRUPT_SOURCE &&\n typeof candidate.context === 'object' &&\n candidate.context !== null &&\n Array.isArray(candidate.context.requests) &&\n candidate.context.requests.length > 0 &&\n candidate.context.requests.every(isClientToolCallRequest)\n );\n}\n\n/** Tool arguments from a call request, defaulting to an empty object. */\nexport function clientToolCallArgs(\n request: ClientToolCallRequest,\n): Record<string, unknown> {\n const args = request.tool_args;\n return args && typeof args === 'object' && !Array.isArray(args) ? args : {};\n}\n\n/** The `run/input` resume value answering a batched client-tool park. */\nexport function clientToolResultsResumeValue(\n results: Record<string, ClientToolResultEnvelope>,\n): Record<string, unknown> {\n return { client_tool_results: results };\n}\n\n/** Wire entries for `runConfig.custom.client_tools`. */\nexport function clientToolRunConfigEntries(\n tools: readonly RegisteredClientTool[],\n): Record<string, unknown>[] {\n return tools.map(({ definition }) => ({\n name: definition.name,\n description: definition.description,\n parameters: definition.parameters,\n requires_write: definition.requiresWrite,\n }));\n}\n\n/** Successful tool execution, resumed back into the parked run. */\nexport interface ClientToolSuccessEnvelope {\n status: typeof CLIENT_TOOL_RESULT_STATUS_SUCCESS;\n result: unknown;\n}\n\n/** Failed or declined tool execution, resumed back into the parked run. */\nexport interface ClientToolErrorEnvelope {\n status: typeof CLIENT_TOOL_RESULT_STATUS_ERROR;\n error: string;\n}\n\nexport type ClientToolResultEnvelope =\n | ClientToolSuccessEnvelope\n | ClientToolErrorEnvelope;\n\nexport function clientToolSuccessEnvelope(\n result: unknown,\n): ClientToolSuccessEnvelope {\n return {\n status: CLIENT_TOOL_RESULT_STATUS_SUCCESS,\n result: result === undefined ? null : result,\n };\n}\n\nexport function clientToolErrorEnvelope(error: unknown): ClientToolErrorEnvelope {\n const message =\n error instanceof Error\n ? error.message\n : typeof error === 'string'\n ? error\n : 'Client tool execution failed.';\n return { status: CLIENT_TOOL_RESULT_STATUS_ERROR, error: message };\n}\n\n/**\n * Once-per-request execution guard. Statewire re-emits the pending input\n * request on every state update, so an executor effect can observe the same\n * request many times; the tracker makes exactly one observation win.\n */\nexport interface ClientToolRequestTracker {\n /** True exactly once per request id; later calls return false. */\n claim(requestId: string): boolean;\n /** Un-claim a request that was NOT resumed (e.g. abandoned mid-approval),\n * so a re-emitted request can be executed instead of stranding the run. */\n release(requestId: string): void;\n has(requestId: string): boolean;\n /** Cache the completed, wire-safe resume value without releasing the\n * exactly-once execution claim. A reconnect may replay this value, but it\n * must never execute the local handler again. */\n cacheResume(requestId: string, value: Record<string, unknown>): void;\n getCachedResume(requestId: string): ClientToolCachedResume | undefined;\n /** Give one mounted bridge ownership of retrying a cached resume. */\n claimResumeReplay(requestId: string, owner: symbol): boolean;\n releaseResumeReplay(requestId: string, owner: symbol): void;\n /** Record authoritative host acceptance. The execution claim remains held\n * so a briefly stale request cannot execute its local side effects again. */\n settleResume(requestId: string): void;\n /** Atomically consume one bounded replay attempt for the current owner. */\n takeResumeAttempt(\n requestId: string,\n options: { owner: symbol; maxAttempts: number },\n ): ClientToolResumeAttempt | undefined;\n}\n\nexport interface ClientToolCachedResume {\n value: Record<string, unknown>;\n attemptCount: number;\n}\n\nexport interface ClientToolResumeAttempt {\n value: Record<string, unknown>;\n attemptNumber: number;\n}\n\nexport interface CreateClientToolRequestTrackerOptions {\n /** Maximum completed resume values retained for reconnect recovery. Active\n * executions are never evicted. */\n maxCachedResumes?: number;\n}\n\ninterface ClientToolRequestEntry {\n resumeValue?: Record<string, unknown>;\n resumeAttemptCount: number;\n resumeOwner?: symbol;\n settled?: boolean;\n}\n\nconst DEFAULT_MAX_CACHED_CLIENT_TOOL_RESUMES = 128;\n\nexport function createClientToolRequestTracker(\n options: CreateClientToolRequestTrackerOptions = {},\n): ClientToolRequestTracker {\n const maxCachedResumes = options.maxCachedResumes ?? DEFAULT_MAX_CACHED_CLIENT_TOOL_RESUMES;\n const requests = new Map<string, ClientToolRequestEntry>();\n\n const pruneCompletedRequests = (protectedRequestId?: string): void => {\n let completedCount = 0;\n for (const entry of requests.values()) {\n if (entry.resumeValue !== undefined || entry.settled === true) completedCount += 1;\n }\n if (completedCount <= maxCachedResumes) return;\n\n // Map iteration is insertion order. Only inactive, completed entries are\n // eligible: evicting an executing request could allow a document mutation\n // to run twice, evicting an active replay would split ownership, and the\n // newly completed request must survive long enough for its first dispatch.\n for (const [requestId, entry] of requests) {\n if (completedCount <= maxCachedResumes) break;\n if (\n requestId === protectedRequestId ||\n (entry.resumeValue === undefined && entry.settled !== true) ||\n entry.resumeOwner !== undefined\n ) {\n continue;\n }\n requests.delete(requestId);\n completedCount -= 1;\n }\n };\n\n return {\n claim(requestId: string): boolean {\n if (requests.has(requestId)) return false;\n requests.set(requestId, { resumeAttemptCount: 0 });\n return true;\n },\n release(requestId: string): void {\n requests.delete(requestId);\n },\n has(requestId: string): boolean {\n return requests.has(requestId);\n },\n cacheResume(requestId: string, value: Record<string, unknown>): void {\n const entry = requests.get(requestId) ?? { resumeAttemptCount: 0 };\n entry.resumeValue = value;\n entry.settled = false;\n requests.delete(requestId);\n requests.set(requestId, entry);\n pruneCompletedRequests(requestId);\n },\n getCachedResume(requestId: string): ClientToolCachedResume | undefined {\n const entry = requests.get(requestId);\n if (!entry || entry.resumeValue === undefined) return undefined;\n return {\n value: entry.resumeValue,\n attemptCount: entry.resumeAttemptCount,\n };\n },\n claimResumeReplay(requestId: string, owner: symbol): boolean {\n const entry = requests.get(requestId);\n if (!entry || entry.resumeValue === undefined) return false;\n if (entry.resumeOwner !== undefined && entry.resumeOwner !== owner) {\n return false;\n }\n entry.resumeOwner = owner;\n return true;\n },\n releaseResumeReplay(requestId: string, owner: symbol): void {\n const entry = requests.get(requestId);\n if (entry?.resumeOwner === owner) entry.resumeOwner = undefined;\n pruneCompletedRequests();\n },\n settleResume(requestId: string): void {\n const entry = requests.get(requestId);\n if (!entry) return;\n entry.resumeValue = undefined;\n entry.resumeOwner = undefined;\n entry.settled = true;\n requests.delete(requestId);\n requests.set(requestId, entry);\n pruneCompletedRequests(requestId);\n },\n takeResumeAttempt(\n requestId: string,\n attemptOptions: { owner: symbol; maxAttempts: number },\n ): ClientToolResumeAttempt | undefined {\n const entry = requests.get(requestId);\n if (\n !entry ||\n entry.resumeValue === undefined ||\n entry.resumeOwner !== attemptOptions.owner ||\n entry.resumeAttemptCount >= attemptOptions.maxAttempts\n ) {\n return undefined;\n }\n entry.resumeAttemptCount += 1;\n return {\n value: entry.resumeValue,\n attemptNumber: entry.resumeAttemptCount,\n };\n },\n };\n}\n"],"names":["CLIENT_TOOL_INTERRUPT_SOURCE","CLIENT_TOOL_RESULT_STATUS_SUCCESS","CLIENT_TOOL_RESULT_STATUS_ERROR"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-tools.js","sources":["../../../../agent-runtime/src/client-tools.ts"],"sourcesContent":["/**\n * Client-executed tools for statewire deep-agent threads.\n *\n * A client (the Office add-in, the Chrome extension, …) declares tools it can\n * execute locally by sending their JSON-schema definitions through\n * `runConfig.custom.client_tools`. The deep-agent runtime binds them as\n * interrupt-backed tools: when the model calls one, the run parks on a\n * LangGraph interrupt whose payload carries the tool name and arguments, the\n * statewire host publishes it as an `interrupt` input request, and the client\n * executes the tool and resumes the run with the result envelope.\n *\n * This module is transport-agnostic: it holds the wire contract (definitions,\n * interrupt payload shape, result envelopes) and a duplicate-execution guard.\n * The React hook that watches input requests lives with each client, next to\n * its host-specific tool handlers.\n */\n\nimport {\n CLIENT_TOOL_INTERRUPT_SOURCE,\n CLIENT_TOOL_RESULT_STATUS_ERROR,\n CLIENT_TOOL_RESULT_STATUS_SUCCESS,\n} from './generated/runconfig-contract';\n\n/** `source`/`type` stamped on the interrupt payload by the agora runtime. */\nexport { CLIENT_TOOL_INTERRUPT_SOURCE } from './generated/runconfig-contract';\n\n/** JSON-schema description of one client-executed tool. */\nexport interface ClientToolDefinition {\n /** Snake_case tool id the model calls (e.g. `update_sheet_range`). */\n name: string;\n description: string;\n /** JSON schema for the tool arguments (`{\"type\": \"object\", ...}`). */\n parameters: Record<string, unknown>;\n /** Mutating tools gate on the client's write-approval flow before running. */\n requiresWrite: boolean;\n}\n\n/** Per-call context the client-tool bridge hands a handler; absent when a host\n * invokes a handler outside a parked run. */\nexport interface ClientToolHandlerContext {\n /** The model's tool-call id, for handlers that correlate calls. */\n toolCallId: string;\n}\n\nexport type ClientToolHandler = (\n args: Record<string, unknown>,\n context?: ClientToolHandlerContext,\n) => Promise<unknown>;\n\nexport interface RegisteredClientTool {\n definition: ClientToolDefinition;\n handler: ClientToolHandler;\n}\n\n/** One parked tool call inside a batched client-tool input request. */\nexport interface ClientToolCallRequest {\n /** LangGraph interrupt id — the key the resume result map must use. */\n interrupt_id: string;\n tool_name: string;\n tool_args: Record<string, unknown>;\n requires_write?: boolean;\n}\n\n/**\n * The input-request payload the agora runtime publishes for parked client\n * tool calls. A parallel model turn parks several interrupts at once; the\n * runtime batches them into one payload and the client answers once with\n * `{ client_tool_results: { <interrupt_id>: <result envelope> } }`.\n */\nexport interface ClientToolInterruptPayload {\n source: typeof CLIENT_TOOL_INTERRUPT_SOURCE;\n type: typeof CLIENT_TOOL_INTERRUPT_SOURCE;\n message: string;\n context: {\n requests: ClientToolCallRequest[];\n };\n}\n\nfunction isClientToolCallRequest(value: unknown): value is ClientToolCallRequest {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const candidate = value as Partial<ClientToolCallRequest>;\n return (\n typeof candidate.interrupt_id === 'string' &&\n candidate.interrupt_id.length > 0 &&\n typeof candidate.tool_name === 'string'\n );\n}\n\n/**\n * Loose source check for surfaces that must NOT handle a client-tool park\n * (e.g. the generic approval banner). Unlike `isClientToolInterrupt`, this\n * matches any payload stamped with the client-tool source — including shapes\n * from a mismatched client/server version — so a skewed deploy can never\n * surface a generic \"Continue\" resume that corrupts a parked tool call.\n */\nexport function hasClientToolInterruptSource(payload: unknown): boolean {\n return (\n !!payload &&\n typeof payload === 'object' &&\n !Array.isArray(payload) &&\n (payload as { source?: unknown }).source === CLIENT_TOOL_INTERRUPT_SOURCE\n );\n}\n\nexport function isClientToolInterrupt(\n payload: unknown,\n): payload is ClientToolInterruptPayload {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return false;\n }\n const candidate = payload as Partial<ClientToolInterruptPayload>;\n return (\n candidate.source === CLIENT_TOOL_INTERRUPT_SOURCE &&\n candidate.type === CLIENT_TOOL_INTERRUPT_SOURCE &&\n typeof candidate.context === 'object' &&\n candidate.context !== null &&\n Array.isArray(candidate.context.requests) &&\n candidate.context.requests.length > 0 &&\n candidate.context.requests.every(isClientToolCallRequest)\n );\n}\n\n/** Tool arguments from a call request, defaulting to an empty object. */\nexport function clientToolCallArgs(\n request: ClientToolCallRequest,\n): Record<string, unknown> {\n const args = request.tool_args;\n return args && typeof args === 'object' && !Array.isArray(args) ? args : {};\n}\n\n/** The `run/input` resume value answering a batched client-tool park. */\nexport function clientToolResultsResumeValue(\n results: Record<string, ClientToolResultEnvelope>,\n): Record<string, unknown> {\n return { client_tool_results: results };\n}\n\n/** Wire entries for `runConfig.custom.client_tools`. */\nexport function clientToolRunConfigEntries(\n tools: readonly RegisteredClientTool[],\n): Record<string, unknown>[] {\n return tools.map(({ definition }) => ({\n name: definition.name,\n description: definition.description,\n parameters: definition.parameters,\n requires_write: definition.requiresWrite,\n }));\n}\n\n/** Successful tool execution, resumed back into the parked run. */\nexport interface ClientToolSuccessEnvelope {\n status: typeof CLIENT_TOOL_RESULT_STATUS_SUCCESS;\n result: unknown;\n}\n\n/** Failed or declined tool execution, resumed back into the parked run. */\nexport interface ClientToolErrorEnvelope {\n status: typeof CLIENT_TOOL_RESULT_STATUS_ERROR;\n error: string;\n}\n\nexport type ClientToolResultEnvelope =\n | ClientToolSuccessEnvelope\n | ClientToolErrorEnvelope;\n\nexport function clientToolSuccessEnvelope(\n result: unknown,\n): ClientToolSuccessEnvelope {\n return {\n status: CLIENT_TOOL_RESULT_STATUS_SUCCESS,\n result: result === undefined ? null : result,\n };\n}\n\nexport function clientToolErrorEnvelope(error: unknown): ClientToolErrorEnvelope {\n const message =\n error instanceof Error\n ? error.message\n : typeof error === 'string'\n ? error\n : 'Client tool execution failed.';\n return { status: CLIENT_TOOL_RESULT_STATUS_ERROR, error: message };\n}\n\n/**\n * Once-per-request execution guard. Statewire re-emits the pending input\n * request on every state update, so an executor effect can observe the same\n * request many times; the tracker makes exactly one observation win.\n */\nexport interface ClientToolRequestTracker {\n /** True exactly once per request id; later calls return false. */\n claim(requestId: string): boolean;\n /** Un-claim a request that was NOT resumed (e.g. abandoned mid-approval),\n * so a re-emitted request can be executed instead of stranding the run. */\n release(requestId: string): void;\n has(requestId: string): boolean;\n /** Cache the completed, wire-safe resume value without releasing the\n * exactly-once execution claim. A reconnect may replay this value, but it\n * must never execute the local handler again. */\n cacheResume(requestId: string, value: Record<string, unknown>): void;\n getCachedResume(requestId: string): ClientToolCachedResume | undefined;\n /** Give one mounted bridge ownership of retrying a cached resume. */\n claimResumeReplay(requestId: string, owner: symbol): boolean;\n releaseResumeReplay(requestId: string, owner: symbol): void;\n /** Record authoritative host acceptance. The execution claim remains held\n * so a briefly stale request cannot execute its local side effects again. */\n settleResume(requestId: string): void;\n /** Atomically consume one bounded replay attempt for the current owner. */\n takeResumeAttempt(\n requestId: string,\n options: { owner: symbol; maxAttempts: number },\n ): ClientToolResumeAttempt | undefined;\n}\n\nexport interface ClientToolCachedResume {\n value: Record<string, unknown>;\n attemptCount: number;\n}\n\nexport interface ClientToolResumeAttempt {\n value: Record<string, unknown>;\n attemptNumber: number;\n}\n\nexport interface CreateClientToolRequestTrackerOptions {\n /** Maximum completed resume values retained for reconnect recovery. Active\n * executions are never evicted. */\n maxCachedResumes?: number;\n}\n\ninterface ClientToolRequestEntry {\n resumeValue?: Record<string, unknown>;\n resumeAttemptCount: number;\n resumeOwner?: symbol;\n settled?: boolean;\n}\n\nconst DEFAULT_MAX_CACHED_CLIENT_TOOL_RESUMES = 128;\n\nexport function createClientToolRequestTracker(\n options: CreateClientToolRequestTrackerOptions = {},\n): ClientToolRequestTracker {\n const maxCachedResumes = options.maxCachedResumes ?? DEFAULT_MAX_CACHED_CLIENT_TOOL_RESUMES;\n const requests = new Map<string, ClientToolRequestEntry>();\n\n const pruneCompletedRequests = (protectedRequestId?: string): void => {\n let completedCount = 0;\n for (const entry of requests.values()) {\n if (entry.resumeValue !== undefined || entry.settled === true) completedCount += 1;\n }\n if (completedCount <= maxCachedResumes) return;\n\n // Map iteration is insertion order. Only inactive, completed entries are\n // eligible: evicting an executing request could allow a document mutation\n // to run twice, evicting an active replay would split ownership, and the\n // newly completed request must survive long enough for its first dispatch.\n for (const [requestId, entry] of requests) {\n if (completedCount <= maxCachedResumes) break;\n if (\n requestId === protectedRequestId ||\n (entry.resumeValue === undefined && entry.settled !== true) ||\n entry.resumeOwner !== undefined\n ) {\n continue;\n }\n requests.delete(requestId);\n completedCount -= 1;\n }\n };\n\n return {\n claim(requestId: string): boolean {\n if (requests.has(requestId)) return false;\n requests.set(requestId, { resumeAttemptCount: 0 });\n return true;\n },\n release(requestId: string): void {\n requests.delete(requestId);\n },\n has(requestId: string): boolean {\n return requests.has(requestId);\n },\n cacheResume(requestId: string, value: Record<string, unknown>): void {\n const entry = requests.get(requestId) ?? { resumeAttemptCount: 0 };\n entry.resumeValue = value;\n entry.settled = false;\n requests.delete(requestId);\n requests.set(requestId, entry);\n pruneCompletedRequests(requestId);\n },\n getCachedResume(requestId: string): ClientToolCachedResume | undefined {\n const entry = requests.get(requestId);\n if (!entry || entry.resumeValue === undefined) return undefined;\n return {\n value: entry.resumeValue,\n attemptCount: entry.resumeAttemptCount,\n };\n },\n claimResumeReplay(requestId: string, owner: symbol): boolean {\n const entry = requests.get(requestId);\n if (!entry || entry.resumeValue === undefined) return false;\n if (entry.resumeOwner !== undefined && entry.resumeOwner !== owner) {\n return false;\n }\n entry.resumeOwner = owner;\n return true;\n },\n releaseResumeReplay(requestId: string, owner: symbol): void {\n const entry = requests.get(requestId);\n if (entry?.resumeOwner === owner) entry.resumeOwner = undefined;\n pruneCompletedRequests();\n },\n settleResume(requestId: string): void {\n const entry = requests.get(requestId);\n if (!entry) return;\n entry.resumeValue = undefined;\n entry.resumeOwner = undefined;\n entry.settled = true;\n requests.delete(requestId);\n requests.set(requestId, entry);\n pruneCompletedRequests(requestId);\n },\n takeResumeAttempt(\n requestId: string,\n attemptOptions: { owner: symbol; maxAttempts: number },\n ): ClientToolResumeAttempt | undefined {\n const entry = requests.get(requestId);\n if (\n !entry ||\n entry.resumeValue === undefined ||\n entry.resumeOwner !== attemptOptions.owner ||\n entry.resumeAttemptCount >= attemptOptions.maxAttempts\n ) {\n return undefined;\n }\n entry.resumeAttemptCount += 1;\n return {\n value: entry.resumeValue,\n attemptNumber: entry.resumeAttemptCount,\n };\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const DEEP_AGENT_COMPOSER_MARKDOWN_KEY = "composer_markdown";
4
+ const DRAFT_CHARACTER_REFERENCES = {
5
+ lt: "<",
6
+ gt: ">",
7
+ amp: "&",
8
+ "#32": " ",
9
+ "#9": " "
10
+ };
11
+ const comparableText = (text) => text.replace(/\\/g, "");
12
+ const comparableDraft = (markdown) => comparableText(markdown).replace(
13
+ /&(lt|gt|amp|#32|#9);/g,
14
+ (reference, name) => DRAFT_CHARACTER_REFERENCES[name] ?? reference
15
+ );
16
+ const MAX_EXPANSION = 4;
17
+ const EXPANSION_SLACK_CHARS = 1024;
18
+ function isComposerMarkdownOfText({
19
+ markdown,
20
+ text
21
+ }) {
22
+ return markdown.length <= text.length * MAX_EXPANSION + EXPANSION_SLACK_CHARS && comparableDraft(markdown) === comparableText(text);
23
+ }
24
+ const DRAFT_WHITESPACE_REFERENCES = ["&#32;", "&#9;"];
25
+ const isWhitespace = (character) => /\s/.test(character);
26
+ const DRAFT_HARD_BREAK = "\\\n";
27
+ function endsInEscapingBackslash({
28
+ markdown,
29
+ start,
30
+ end
31
+ }) {
32
+ let backslashes = 0;
33
+ while (end - backslashes > start && markdown.charAt(end - backslashes - 1) === "\\") {
34
+ backslashes += 1;
35
+ }
36
+ return backslashes % 2 === 1;
37
+ }
38
+ function trimComposerDraft(markdown) {
39
+ let start = 0;
40
+ let end = markdown.length;
41
+ for (; ; ) {
42
+ while (start < end && isWhitespace(markdown.charAt(start))) start += 1;
43
+ const reference = [...DRAFT_WHITESPACE_REFERENCES, DRAFT_HARD_BREAK].find(
44
+ (candidate) => start + candidate.length <= end && markdown.startsWith(candidate, start)
45
+ );
46
+ if (reference === void 0) break;
47
+ start += reference.length;
48
+ }
49
+ for (; ; ) {
50
+ while (end > start && isWhitespace(markdown.charAt(end - 1))) end -= 1;
51
+ const reference = DRAFT_WHITESPACE_REFERENCES.find(
52
+ (candidate) => end - candidate.length >= start && markdown.endsWith(candidate, end)
53
+ );
54
+ if (reference !== void 0) {
55
+ end -= reference.length;
56
+ } else if (markdown.charAt(end) === "\n" && endsInEscapingBackslash({ markdown, start, end })) {
57
+ end -= 1;
58
+ } else {
59
+ break;
60
+ }
61
+ }
62
+ return markdown.slice(start, end);
63
+ }
64
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
65
+ function readDeepAgentComposerMarkdown({
66
+ metadata,
67
+ text
68
+ }) {
69
+ if (!isRecord(metadata)) return null;
70
+ const markdown = metadata[DEEP_AGENT_COMPOSER_MARKDOWN_KEY];
71
+ return typeof markdown === "string" && isComposerMarkdownOfText({ markdown, text }) ? markdown : null;
72
+ }
73
+ const STAGED_COMPOSER_MARKDOWN_TTL_MS = 1e4;
74
+ const STAGED_COMPOSER_MARKDOWN = Symbol.for("@athenaintel/agent-runtime/staged-composer-markdown");
75
+ function stagedComposerMarkdownByThread() {
76
+ const host = globalThis;
77
+ return host[STAGED_COMPOSER_MARKDOWN] ?? (host[STAGED_COMPOSER_MARKDOWN] = /* @__PURE__ */ new Map());
78
+ }
79
+ function stageComposerMarkdown({
80
+ threadId,
81
+ text,
82
+ markdown,
83
+ now = Date.now()
84
+ }) {
85
+ const staged = stagedComposerMarkdownByThread();
86
+ if (markdown === text) {
87
+ staged.delete(threadId);
88
+ return;
89
+ }
90
+ staged.set(threadId, { markdown, stagedAt: now });
91
+ }
92
+ function takeStagedComposerMarkdownMetadata(threadId, now = Date.now()) {
93
+ const staged = stagedComposerMarkdownByThread();
94
+ const entry = staged.get(threadId);
95
+ staged.delete(threadId);
96
+ if (entry === void 0 || now - entry.stagedAt > STAGED_COMPOSER_MARKDOWN_TTL_MS) {
97
+ return void 0;
98
+ }
99
+ return { [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: entry.markdown };
100
+ }
101
+ function withStagedComposerMarkdown({
102
+ threadId,
103
+ getMessageMetadata
104
+ }) {
105
+ return () => {
106
+ const staged = takeStagedComposerMarkdownMetadata(threadId);
107
+ const captured = getMessageMetadata == null ? void 0 : getMessageMetadata();
108
+ if (staged === void 0) return captured;
109
+ const merge = (host) => ({
110
+ ...host,
111
+ ...staged
112
+ });
113
+ return captured instanceof Promise ? captured.then(merge) : merge(captured);
114
+ };
115
+ }
116
+ exports.DEEP_AGENT_COMPOSER_MARKDOWN_KEY = DEEP_AGENT_COMPOSER_MARKDOWN_KEY;
117
+ exports.STAGED_COMPOSER_MARKDOWN_TTL_MS = STAGED_COMPOSER_MARKDOWN_TTL_MS;
118
+ exports.isComposerMarkdownOfText = isComposerMarkdownOfText;
119
+ exports.readDeepAgentComposerMarkdown = readDeepAgentComposerMarkdown;
120
+ exports.stageComposerMarkdown = stageComposerMarkdown;
121
+ exports.takeStagedComposerMarkdownMetadata = takeStagedComposerMarkdownMetadata;
122
+ exports.trimComposerDraft = trimComposerDraft;
123
+ exports.withStagedComposerMarkdown = withStagedComposerMarkdown;
124
+ //# sourceMappingURL=composer-markdown.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer-markdown.cjs","sources":["../../../../agent-runtime/src/composer-markdown.ts"],"sourcesContent":["/**\n * The composer markdown a chat message was sent from.\n *\n * The chat composer sends a message's plain text exactly as typed, because the\n * model reads the text literally, and writes real formatting (bold, lists,\n * code, mention chips) as markdown. The sent text alone no longer says which\n * characters are formatting and which were typed: literal `**strong**` and\n * bold `strong` both send `**strong**`, and `List<T>`, `a\\\\b` or `2*3*4` read\n * as markup when the text is rendered as markdown. So the composer also\n * attaches the draft the text was serialized from (the same document with its\n * plain text escaped for markdown) under {@link DEEP_AGENT_COMPOSER_MARKDOWN_KEY}\n * in the message's metadata, and only when the two differ.\n *\n * It rides the statewire message, the queue and steer lanes and the persisted\n * HumanMessage (`additional_kwargs.metadata`, which the converter surfaces as\n * `metadata.custom`). It is display-only: the bubble renders it, and Edit,\n * branch-and-edit and a withdrawn turn load it back into the composer. The\n * model reads the message text, never this. A message without it (older\n * messages, other clients) renders its text as before.\n *\n * agora validates it with the same rule\n * (`agora/graphs/athena_deep_agent/composer_markdown.py`).\n */\nexport const DEEP_AGENT_COMPOSER_MARKDOWN_KEY = 'composer_markdown';\n\nconst DRAFT_CHARACTER_REFERENCES: Readonly<Record<string, string>> = {\n lt: '<',\n gt: '>',\n amp: '&',\n '#32': ' ',\n '#9': '\\t',\n};\n\n/**\n * A sent text in comparable form: every backslash dropped (the draft's\n * escapes, and the `\\` before a hard break's newline, are backslashes).\n */\nconst comparableText = (text: string): string => text.replace(/\\\\/g, '');\n\n/**\n * A draft in comparable form: every backslash dropped, then the character\n * references the draft serializer writes decoded in one pass (`&lt;` and\n * `&gt;` for typed `<` and `>`, `&amp;` for a typed `&` that starts a\n * reference, so typed `&lt;` is `&amp;lt;`, and `&#32;` / `&#9;` for the space\n * or tab a line starts with, which markdown would otherwise drop).\n */\nconst comparableDraft = (markdown: string): string =>\n comparableText(markdown).replace(\n /&(lt|gt|amp|#32|#9);/g,\n (reference, name: string) => DRAFT_CHARACTER_REFERENCES[name] ?? reference\n );\n\n/**\n * Escaping at most turns one character into four (`<` → `&lt;`). A line's\n * first space becomes five (`&#32;`), but only once a line, so a whole line\n * with its line break stays under four a character; the slack covers short\n * texts. agora caps a stored draft the same way.\n */\nconst MAX_EXPANSION = 4;\nconst EXPANSION_SLACK_CHARS = 1024;\n\n/**\n * Whether `markdown` is the draft of `text`: no longer than escaping could make\n * it, and equal to it once backslashes are dropped and the draft's character\n * references decoded. A draft that would show other\n * words than the text the model read (another message's, a client's mistake) is\n * not taken; such a message renders its text as before.\n */\nexport function isComposerMarkdownOfText({\n markdown,\n text,\n}: {\n markdown: string;\n text: string;\n}): boolean {\n return (\n markdown.length <= text.length * MAX_EXPANSION + EXPANSION_SLACK_CHARS &&\n comparableDraft(markdown) === comparableText(text)\n );\n}\n\n/** The references a draft writes a line's first space or tab as. */\nconst DRAFT_WHITESPACE_REFERENCES: readonly string[] = ['&#32;', '&#9;'];\n\nconst isWhitespace = (character: string): boolean => /\\s/.test(character);\n\n/** A hard break in a draft: `\\` before the newline (the text holds the newline alone). */\nconst DRAFT_HARD_BREAK = '\\\\\\n';\n\n/** Whether an odd run of backslashes ends at `end`: its last one escapes what follows, not a typed `\\` (`\\\\`). */\nfunction endsInEscapingBackslash({\n markdown,\n start,\n end,\n}: {\n markdown: string;\n start: number;\n end: number;\n}): boolean {\n let backslashes = 0;\n while (end - backslashes > start && markdown.charAt(end - backslashes - 1) === '\\\\') {\n backslashes += 1;\n }\n return backslashes % 2 === 1;\n}\n\n/**\n * `markdown`, a draft, trimmed as its text is trimmed (`text.trim()`): a\n * send that trims its text stays a draft of it. `markdown.trim()` would keep\n * the `&#32;` a draft writes for the space its first line starts with, and the\n * `\\` of a line break whose newline it trims.\n */\nexport function trimComposerDraft(markdown: string): string {\n let start = 0;\n let end = markdown.length;\n for (;;) {\n while (start < end && isWhitespace(markdown.charAt(start))) start += 1;\n const reference = [...DRAFT_WHITESPACE_REFERENCES, DRAFT_HARD_BREAK].find(\n (candidate) => start + candidate.length <= end && markdown.startsWith(candidate, start)\n );\n if (reference === undefined) break;\n start += reference.length;\n }\n for (;;) {\n while (end > start && isWhitespace(markdown.charAt(end - 1))) end -= 1;\n const reference = DRAFT_WHITESPACE_REFERENCES.find(\n (candidate) => end - candidate.length >= start && markdown.endsWith(candidate, end)\n );\n if (reference !== undefined) {\n end -= reference.length;\n } else if (\n markdown.charAt(end) === '\\n' &&\n endsInEscapingBackslash({ markdown, start, end })\n ) {\n // The newline just trimmed was a line break's: its `\\` goes with it.\n end -= 1;\n } else {\n break;\n }\n }\n return markdown.slice(start, end);\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/**\n * The composer markdown to render or load for a message whose metadata is\n * `metadata` (a message's `metadata.custom`, or a lane entry's `metadata`) and\n * whose text is `text`; `null` when it carries none, or one that is not a\n * draft of `text`.\n */\nexport function readDeepAgentComposerMarkdown({\n metadata,\n text,\n}: {\n metadata: unknown;\n text: string;\n}): string | null {\n if (!isRecord(metadata)) return null;\n const markdown: unknown = metadata[DEEP_AGENT_COMPOSER_MARKDOWN_KEY];\n return typeof markdown === 'string' && isComposerMarkdownOfText({ markdown, text })\n ? markdown\n : null;\n}\n\n/**\n * How long a staged composer markdown waits for its submission\n * ({@link stageComposerMarkdown}).\n */\nexport const STAGED_COMPOSER_MARKDOWN_TTL_MS = 10_000;\n\ninterface StagedComposerMarkdown {\n readonly markdown: string;\n readonly stagedAt: number;\n}\n\n/**\n * The staged drafts live on `globalThis`, not in module scope, because two\n * copies of this module run in one page: the SDK bundles agent-runtime into its\n * dist, so its composer stages through the SDK's copy, while the Chrome\n * extension and the Office add-in build their runtime on agent-runtime directly\n * and take the draft through their own (see `asset-attachment.ts`).\n */\nconst STAGED_COMPOSER_MARKDOWN = Symbol.for('@athenaintel/agent-runtime/staged-composer-markdown');\n\ntype StagedComposerMarkdownHost = typeof globalThis & {\n [STAGED_COMPOSER_MARKDOWN]?: Map<string, StagedComposerMarkdown>;\n};\n\nfunction stagedComposerMarkdownByThread(): Map<string, StagedComposerMarkdown> {\n const host = globalThis as StagedComposerMarkdownHost;\n return (host[STAGED_COMPOSER_MARKDOWN] ??= new Map());\n}\n\n/**\n * Stage the draft `markdown` of `text` for `threadId`'s next submission.\n *\n * statewire captures message metadata once per new user message\n * (`getMessageMetadata`, patched into `@assistant-ui/react-statewire@0.30.0`)\n * without saying which message it is for, so a composer stages its draft right\n * before it sends and the runtime's getter takes it for the submission that\n * follows ({@link withStagedComposerMarkdown}). A draft equal to its text adds\n * nothing and clears the slot. A slot nobody takes within\n * {@link STAGED_COMPOSER_MARKDOWN_TTL_MS} expires; one a submission the\n * composer did not make takes is harmless, since a composer markdown that is\n * not a draft of the message's own text is neither kept by agora nor rendered.\n */\nexport function stageComposerMarkdown({\n threadId,\n text,\n markdown,\n now = Date.now(),\n}: {\n threadId: string;\n text: string;\n markdown: string;\n /** Test seam: the time of the send. */\n now?: number;\n}): void {\n const staged = stagedComposerMarkdownByThread();\n if (markdown === text) {\n staged.delete(threadId);\n return;\n }\n staged.set(threadId, { markdown, stagedAt: now });\n}\n\n/**\n * The message metadata for `threadId`'s submission now being journaled: the\n * staged composer markdown, if one is waiting and fresh. Takes the slot.\n */\nexport function takeStagedComposerMarkdownMetadata(\n threadId: string,\n now: number = Date.now()\n): Record<string, unknown> | undefined {\n const staged = stagedComposerMarkdownByThread();\n const entry = staged.get(threadId);\n staged.delete(threadId);\n if (entry === undefined || now - entry.stagedAt > STAGED_COMPOSER_MARKDOWN_TTL_MS) {\n return undefined;\n }\n return { [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: entry.markdown };\n}\n\ntype MessageMetadataGetter = () =>\n | Record<string, unknown>\n | undefined\n | Promise<Record<string, unknown> | undefined>;\n\n/**\n * A runtime's `getMessageMetadata` that also carries the composer markdown a\n * composer staged for `threadId` ({@link stageComposerMarkdown}), on top of the\n * host's own metadata (`getMessageMetadata`, when it has one). The slot is\n * taken synchronously at submission, before the host's capture resolves, so a\n * draft never rides a later message. With nothing staged the host's own value\n * is returned as is.\n */\nexport function withStagedComposerMarkdown({\n threadId,\n getMessageMetadata,\n}: {\n threadId: string;\n getMessageMetadata?: MessageMetadataGetter | undefined;\n}): MessageMetadataGetter {\n return () => {\n const staged = takeStagedComposerMarkdownMetadata(threadId);\n const captured = getMessageMetadata?.();\n if (staged === undefined) return captured;\n const merge = (host: Record<string, unknown> | undefined): Record<string, unknown> => ({\n ...host,\n ...staged,\n });\n return captured instanceof Promise ? captured.then(merge) : merge(captured);\n };\n}\n\n/** Test seam: drop every staged composer markdown. */\nexport function resetStagedComposerMarkdown(): void {\n stagedComposerMarkdownByThread().clear();\n}\n\n/**\n * `metadata` with the composer markdown set to the draft `markdown` of `text`,\n * or without it when the draft equals the text (it adds nothing then).\n * Returns `undefined` when nothing is left.\n */\nexport function withDeepAgentComposerMarkdown({\n metadata,\n text,\n markdown,\n}: {\n metadata: Readonly<Record<string, unknown>> | undefined;\n text: string;\n markdown: string;\n}): Record<string, unknown> | undefined {\n const { [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: _previous, ...rest } = metadata ?? {};\n const next: Record<string, unknown> =\n markdown === text ? rest : { ...rest, [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: markdown };\n return Object.keys(next).length > 0 ? next : undefined;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ const DEEP_AGENT_COMPOSER_MARKDOWN_KEY = "composer_markdown";
2
+ const DRAFT_CHARACTER_REFERENCES = {
3
+ lt: "<",
4
+ gt: ">",
5
+ amp: "&",
6
+ "#32": " ",
7
+ "#9": " "
8
+ };
9
+ const comparableText = (text) => text.replace(/\\/g, "");
10
+ const comparableDraft = (markdown) => comparableText(markdown).replace(
11
+ /&(lt|gt|amp|#32|#9);/g,
12
+ (reference, name) => DRAFT_CHARACTER_REFERENCES[name] ?? reference
13
+ );
14
+ const MAX_EXPANSION = 4;
15
+ const EXPANSION_SLACK_CHARS = 1024;
16
+ function isComposerMarkdownOfText({
17
+ markdown,
18
+ text
19
+ }) {
20
+ return markdown.length <= text.length * MAX_EXPANSION + EXPANSION_SLACK_CHARS && comparableDraft(markdown) === comparableText(text);
21
+ }
22
+ const DRAFT_WHITESPACE_REFERENCES = ["&#32;", "&#9;"];
23
+ const isWhitespace = (character) => /\s/.test(character);
24
+ const DRAFT_HARD_BREAK = "\\\n";
25
+ function endsInEscapingBackslash({
26
+ markdown,
27
+ start,
28
+ end
29
+ }) {
30
+ let backslashes = 0;
31
+ while (end - backslashes > start && markdown.charAt(end - backslashes - 1) === "\\") {
32
+ backslashes += 1;
33
+ }
34
+ return backslashes % 2 === 1;
35
+ }
36
+ function trimComposerDraft(markdown) {
37
+ let start = 0;
38
+ let end = markdown.length;
39
+ for (; ; ) {
40
+ while (start < end && isWhitespace(markdown.charAt(start))) start += 1;
41
+ const reference = [...DRAFT_WHITESPACE_REFERENCES, DRAFT_HARD_BREAK].find(
42
+ (candidate) => start + candidate.length <= end && markdown.startsWith(candidate, start)
43
+ );
44
+ if (reference === void 0) break;
45
+ start += reference.length;
46
+ }
47
+ for (; ; ) {
48
+ while (end > start && isWhitespace(markdown.charAt(end - 1))) end -= 1;
49
+ const reference = DRAFT_WHITESPACE_REFERENCES.find(
50
+ (candidate) => end - candidate.length >= start && markdown.endsWith(candidate, end)
51
+ );
52
+ if (reference !== void 0) {
53
+ end -= reference.length;
54
+ } else if (markdown.charAt(end) === "\n" && endsInEscapingBackslash({ markdown, start, end })) {
55
+ end -= 1;
56
+ } else {
57
+ break;
58
+ }
59
+ }
60
+ return markdown.slice(start, end);
61
+ }
62
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
63
+ function readDeepAgentComposerMarkdown({
64
+ metadata,
65
+ text
66
+ }) {
67
+ if (!isRecord(metadata)) return null;
68
+ const markdown = metadata[DEEP_AGENT_COMPOSER_MARKDOWN_KEY];
69
+ return typeof markdown === "string" && isComposerMarkdownOfText({ markdown, text }) ? markdown : null;
70
+ }
71
+ const STAGED_COMPOSER_MARKDOWN_TTL_MS = 1e4;
72
+ const STAGED_COMPOSER_MARKDOWN = Symbol.for("@athenaintel/agent-runtime/staged-composer-markdown");
73
+ function stagedComposerMarkdownByThread() {
74
+ const host = globalThis;
75
+ return host[STAGED_COMPOSER_MARKDOWN] ?? (host[STAGED_COMPOSER_MARKDOWN] = /* @__PURE__ */ new Map());
76
+ }
77
+ function stageComposerMarkdown({
78
+ threadId,
79
+ text,
80
+ markdown,
81
+ now = Date.now()
82
+ }) {
83
+ const staged = stagedComposerMarkdownByThread();
84
+ if (markdown === text) {
85
+ staged.delete(threadId);
86
+ return;
87
+ }
88
+ staged.set(threadId, { markdown, stagedAt: now });
89
+ }
90
+ function takeStagedComposerMarkdownMetadata(threadId, now = Date.now()) {
91
+ const staged = stagedComposerMarkdownByThread();
92
+ const entry = staged.get(threadId);
93
+ staged.delete(threadId);
94
+ if (entry === void 0 || now - entry.stagedAt > STAGED_COMPOSER_MARKDOWN_TTL_MS) {
95
+ return void 0;
96
+ }
97
+ return { [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: entry.markdown };
98
+ }
99
+ function withStagedComposerMarkdown({
100
+ threadId,
101
+ getMessageMetadata
102
+ }) {
103
+ return () => {
104
+ const staged = takeStagedComposerMarkdownMetadata(threadId);
105
+ const captured = getMessageMetadata == null ? void 0 : getMessageMetadata();
106
+ if (staged === void 0) return captured;
107
+ const merge = (host) => ({
108
+ ...host,
109
+ ...staged
110
+ });
111
+ return captured instanceof Promise ? captured.then(merge) : merge(captured);
112
+ };
113
+ }
114
+ export {
115
+ DEEP_AGENT_COMPOSER_MARKDOWN_KEY,
116
+ STAGED_COMPOSER_MARKDOWN_TTL_MS,
117
+ isComposerMarkdownOfText,
118
+ readDeepAgentComposerMarkdown,
119
+ stageComposerMarkdown,
120
+ takeStagedComposerMarkdownMetadata,
121
+ trimComposerDraft,
122
+ withStagedComposerMarkdown
123
+ };
124
+ //# sourceMappingURL=composer-markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer-markdown.js","sources":["../../../../agent-runtime/src/composer-markdown.ts"],"sourcesContent":["/**\n * The composer markdown a chat message was sent from.\n *\n * The chat composer sends a message's plain text exactly as typed, because the\n * model reads the text literally, and writes real formatting (bold, lists,\n * code, mention chips) as markdown. The sent text alone no longer says which\n * characters are formatting and which were typed: literal `**strong**` and\n * bold `strong` both send `**strong**`, and `List<T>`, `a\\\\b` or `2*3*4` read\n * as markup when the text is rendered as markdown. So the composer also\n * attaches the draft the text was serialized from (the same document with its\n * plain text escaped for markdown) under {@link DEEP_AGENT_COMPOSER_MARKDOWN_KEY}\n * in the message's metadata, and only when the two differ.\n *\n * It rides the statewire message, the queue and steer lanes and the persisted\n * HumanMessage (`additional_kwargs.metadata`, which the converter surfaces as\n * `metadata.custom`). It is display-only: the bubble renders it, and Edit,\n * branch-and-edit and a withdrawn turn load it back into the composer. The\n * model reads the message text, never this. A message without it (older\n * messages, other clients) renders its text as before.\n *\n * agora validates it with the same rule\n * (`agora/graphs/athena_deep_agent/composer_markdown.py`).\n */\nexport const DEEP_AGENT_COMPOSER_MARKDOWN_KEY = 'composer_markdown';\n\nconst DRAFT_CHARACTER_REFERENCES: Readonly<Record<string, string>> = {\n lt: '<',\n gt: '>',\n amp: '&',\n '#32': ' ',\n '#9': '\\t',\n};\n\n/**\n * A sent text in comparable form: every backslash dropped (the draft's\n * escapes, and the `\\` before a hard break's newline, are backslashes).\n */\nconst comparableText = (text: string): string => text.replace(/\\\\/g, '');\n\n/**\n * A draft in comparable form: every backslash dropped, then the character\n * references the draft serializer writes decoded in one pass (`&lt;` and\n * `&gt;` for typed `<` and `>`, `&amp;` for a typed `&` that starts a\n * reference, so typed `&lt;` is `&amp;lt;`, and `&#32;` / `&#9;` for the space\n * or tab a line starts with, which markdown would otherwise drop).\n */\nconst comparableDraft = (markdown: string): string =>\n comparableText(markdown).replace(\n /&(lt|gt|amp|#32|#9);/g,\n (reference, name: string) => DRAFT_CHARACTER_REFERENCES[name] ?? reference\n );\n\n/**\n * Escaping at most turns one character into four (`<` → `&lt;`). A line's\n * first space becomes five (`&#32;`), but only once a line, so a whole line\n * with its line break stays under four a character; the slack covers short\n * texts. agora caps a stored draft the same way.\n */\nconst MAX_EXPANSION = 4;\nconst EXPANSION_SLACK_CHARS = 1024;\n\n/**\n * Whether `markdown` is the draft of `text`: no longer than escaping could make\n * it, and equal to it once backslashes are dropped and the draft's character\n * references decoded. A draft that would show other\n * words than the text the model read (another message's, a client's mistake) is\n * not taken; such a message renders its text as before.\n */\nexport function isComposerMarkdownOfText({\n markdown,\n text,\n}: {\n markdown: string;\n text: string;\n}): boolean {\n return (\n markdown.length <= text.length * MAX_EXPANSION + EXPANSION_SLACK_CHARS &&\n comparableDraft(markdown) === comparableText(text)\n );\n}\n\n/** The references a draft writes a line's first space or tab as. */\nconst DRAFT_WHITESPACE_REFERENCES: readonly string[] = ['&#32;', '&#9;'];\n\nconst isWhitespace = (character: string): boolean => /\\s/.test(character);\n\n/** A hard break in a draft: `\\` before the newline (the text holds the newline alone). */\nconst DRAFT_HARD_BREAK = '\\\\\\n';\n\n/** Whether an odd run of backslashes ends at `end`: its last one escapes what follows, not a typed `\\` (`\\\\`). */\nfunction endsInEscapingBackslash({\n markdown,\n start,\n end,\n}: {\n markdown: string;\n start: number;\n end: number;\n}): boolean {\n let backslashes = 0;\n while (end - backslashes > start && markdown.charAt(end - backslashes - 1) === '\\\\') {\n backslashes += 1;\n }\n return backslashes % 2 === 1;\n}\n\n/**\n * `markdown`, a draft, trimmed as its text is trimmed (`text.trim()`): a\n * send that trims its text stays a draft of it. `markdown.trim()` would keep\n * the `&#32;` a draft writes for the space its first line starts with, and the\n * `\\` of a line break whose newline it trims.\n */\nexport function trimComposerDraft(markdown: string): string {\n let start = 0;\n let end = markdown.length;\n for (;;) {\n while (start < end && isWhitespace(markdown.charAt(start))) start += 1;\n const reference = [...DRAFT_WHITESPACE_REFERENCES, DRAFT_HARD_BREAK].find(\n (candidate) => start + candidate.length <= end && markdown.startsWith(candidate, start)\n );\n if (reference === undefined) break;\n start += reference.length;\n }\n for (;;) {\n while (end > start && isWhitespace(markdown.charAt(end - 1))) end -= 1;\n const reference = DRAFT_WHITESPACE_REFERENCES.find(\n (candidate) => end - candidate.length >= start && markdown.endsWith(candidate, end)\n );\n if (reference !== undefined) {\n end -= reference.length;\n } else if (\n markdown.charAt(end) === '\\n' &&\n endsInEscapingBackslash({ markdown, start, end })\n ) {\n // The newline just trimmed was a line break's: its `\\` goes with it.\n end -= 1;\n } else {\n break;\n }\n }\n return markdown.slice(start, end);\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/**\n * The composer markdown to render or load for a message whose metadata is\n * `metadata` (a message's `metadata.custom`, or a lane entry's `metadata`) and\n * whose text is `text`; `null` when it carries none, or one that is not a\n * draft of `text`.\n */\nexport function readDeepAgentComposerMarkdown({\n metadata,\n text,\n}: {\n metadata: unknown;\n text: string;\n}): string | null {\n if (!isRecord(metadata)) return null;\n const markdown: unknown = metadata[DEEP_AGENT_COMPOSER_MARKDOWN_KEY];\n return typeof markdown === 'string' && isComposerMarkdownOfText({ markdown, text })\n ? markdown\n : null;\n}\n\n/**\n * How long a staged composer markdown waits for its submission\n * ({@link stageComposerMarkdown}).\n */\nexport const STAGED_COMPOSER_MARKDOWN_TTL_MS = 10_000;\n\ninterface StagedComposerMarkdown {\n readonly markdown: string;\n readonly stagedAt: number;\n}\n\n/**\n * The staged drafts live on `globalThis`, not in module scope, because two\n * copies of this module run in one page: the SDK bundles agent-runtime into its\n * dist, so its composer stages through the SDK's copy, while the Chrome\n * extension and the Office add-in build their runtime on agent-runtime directly\n * and take the draft through their own (see `asset-attachment.ts`).\n */\nconst STAGED_COMPOSER_MARKDOWN = Symbol.for('@athenaintel/agent-runtime/staged-composer-markdown');\n\ntype StagedComposerMarkdownHost = typeof globalThis & {\n [STAGED_COMPOSER_MARKDOWN]?: Map<string, StagedComposerMarkdown>;\n};\n\nfunction stagedComposerMarkdownByThread(): Map<string, StagedComposerMarkdown> {\n const host = globalThis as StagedComposerMarkdownHost;\n return (host[STAGED_COMPOSER_MARKDOWN] ??= new Map());\n}\n\n/**\n * Stage the draft `markdown` of `text` for `threadId`'s next submission.\n *\n * statewire captures message metadata once per new user message\n * (`getMessageMetadata`, patched into `@assistant-ui/react-statewire@0.30.0`)\n * without saying which message it is for, so a composer stages its draft right\n * before it sends and the runtime's getter takes it for the submission that\n * follows ({@link withStagedComposerMarkdown}). A draft equal to its text adds\n * nothing and clears the slot. A slot nobody takes within\n * {@link STAGED_COMPOSER_MARKDOWN_TTL_MS} expires; one a submission the\n * composer did not make takes is harmless, since a composer markdown that is\n * not a draft of the message's own text is neither kept by agora nor rendered.\n */\nexport function stageComposerMarkdown({\n threadId,\n text,\n markdown,\n now = Date.now(),\n}: {\n threadId: string;\n text: string;\n markdown: string;\n /** Test seam: the time of the send. */\n now?: number;\n}): void {\n const staged = stagedComposerMarkdownByThread();\n if (markdown === text) {\n staged.delete(threadId);\n return;\n }\n staged.set(threadId, { markdown, stagedAt: now });\n}\n\n/**\n * The message metadata for `threadId`'s submission now being journaled: the\n * staged composer markdown, if one is waiting and fresh. Takes the slot.\n */\nexport function takeStagedComposerMarkdownMetadata(\n threadId: string,\n now: number = Date.now()\n): Record<string, unknown> | undefined {\n const staged = stagedComposerMarkdownByThread();\n const entry = staged.get(threadId);\n staged.delete(threadId);\n if (entry === undefined || now - entry.stagedAt > STAGED_COMPOSER_MARKDOWN_TTL_MS) {\n return undefined;\n }\n return { [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: entry.markdown };\n}\n\ntype MessageMetadataGetter = () =>\n | Record<string, unknown>\n | undefined\n | Promise<Record<string, unknown> | undefined>;\n\n/**\n * A runtime's `getMessageMetadata` that also carries the composer markdown a\n * composer staged for `threadId` ({@link stageComposerMarkdown}), on top of the\n * host's own metadata (`getMessageMetadata`, when it has one). The slot is\n * taken synchronously at submission, before the host's capture resolves, so a\n * draft never rides a later message. With nothing staged the host's own value\n * is returned as is.\n */\nexport function withStagedComposerMarkdown({\n threadId,\n getMessageMetadata,\n}: {\n threadId: string;\n getMessageMetadata?: MessageMetadataGetter | undefined;\n}): MessageMetadataGetter {\n return () => {\n const staged = takeStagedComposerMarkdownMetadata(threadId);\n const captured = getMessageMetadata?.();\n if (staged === undefined) return captured;\n const merge = (host: Record<string, unknown> | undefined): Record<string, unknown> => ({\n ...host,\n ...staged,\n });\n return captured instanceof Promise ? captured.then(merge) : merge(captured);\n };\n}\n\n/** Test seam: drop every staged composer markdown. */\nexport function resetStagedComposerMarkdown(): void {\n stagedComposerMarkdownByThread().clear();\n}\n\n/**\n * `metadata` with the composer markdown set to the draft `markdown` of `text`,\n * or without it when the draft equals the text (it adds nothing then).\n * Returns `undefined` when nothing is left.\n */\nexport function withDeepAgentComposerMarkdown({\n metadata,\n text,\n markdown,\n}: {\n metadata: Readonly<Record<string, unknown>> | undefined;\n text: string;\n markdown: string;\n}): Record<string, unknown> | undefined {\n const { [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: _previous, ...rest } = metadata ?? {};\n const next: Record<string, unknown> =\n markdown === text ? rest : { ...rest, [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: markdown };\n return Object.keys(next).length > 0 ? next : undefined;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-index.cjs","sources":["../../../../agent-runtime/src/conversation-index.ts"],"sourcesContent":["import { withKey } from '@assistant-ui/tap';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useStatewire, type StatewireClient } from 'statewire';\nimport { initialAuthDenialTrackerState, observeAuthDenial } from './auth-denial';\nimport type { ConversationAttention, ConversationAttentionSource } from './conversation-list';\nimport { projectDeepAgentConnection } from './errors';\nimport { StatewireSuspended } from './statewire-suspended';\nimport { statewireTransport } from './statewire-transport';\nimport type { DeepAgentHeadersResolver } from './use-deep-agent-thread';\n\n/**\n * Client side of the conversation-index statewire (issue #29088): agora\n * replicates the newest page of one viewer's conversation list as one state\n * object, so thread creation / lifecycle / title changes arrive as ops\n * instead of newest-page polling. The replicated rows are renderable and the\n * typed commands extend or refresh the inclusive window; reconnects are free\n * because the protocol's snapshot-first attach IS the newest-page reconcile.\n *\n * A viewer is a `(workspaceId, userId)` pair and every pair is its own\n * instance (`/conversation-index/<workspaceId>-<userId>`): workspaces are\n * tenants, so a tab in one workspace never shares a window with a tab in\n * another, and switching workspaces attaches to a different instance instead\n * of retagging the one other tabs are still reading.\n */\nexport interface ConversationIndexEntry {\n thread_id: string;\n title: string;\n updated_at: string;\n state: string | null;\n runtime: string | null;\n start_channel: string | null;\n asset_type: string;\n asset_id: string | null;\n app_id: string | null;\n trigger_type: string | null;\n last_message_preview: string | null;\n message_count: number | null;\n is_unread: boolean | null;\n /**\n * Canonical status_v2 planes, present once the publishing agora reports them\n * (absent from snapshots replicated by an older pod during a rollout).\n */\n session_status?: string | null;\n run_status?: string | null;\n /**\n * The viewer's attention state and its one-line next action, present once\n * the publishing agora derives them (absent from snapshots replicated by\n * an older pod). Pull-request state is never pushed — it rides the list\n * fetch only (`include_pull_requests`).\n */\n attention?: ConversationAttention | null;\n next_action?: string | null;\n attention_source?: ConversationAttentionSource | null;\n is_favorited?: boolean | null;\n}\n\n/**\n * When one of this viewer's own asset subscriptions on a thread last queued or\n * settled an event: thread id → ISO instant. A contentless \"re-read now\" for\n * Olympus's Watching card, which reads what changed through its own gated\n * query; it never says what changed, who changed it, or where. Absent from an\n * older agora's snapshot.\n */\nexport type ConversationWatchActivity = Readonly<Record<string, string>>;\n\ninterface ConversationIndexState {\n conversations?: ConversationIndexEntry[];\n has_more?: boolean;\n total_count?: number;\n watch_activity?: ConversationWatchActivity;\n}\n\ninterface ConversationIndexCommands {\n [key: string]: (...args: never[]) => unknown;\n refresh(): void;\n load_more(): void;\n}\n\nexport interface UseConversationIndexOptions {\n /** Base URL of the conversation-index host (the `…/v2/conversation-index` mount). */\n baseUrl: string;\n /** The workspace being viewed — half of the instance id. Callers gate mounting on it. */\n workspaceId: string;\n /** The signed-in user's id — the other half. Callers gate mounting on it. */\n userId: string;\n /** Transport selection: SSE when true, WebSocket otherwise. */\n sse?: boolean;\n headers: DeepAgentHeadersResolver;\n /** Park the transport until the caller has a resolved principal. */\n enabled?: boolean;\n}\n\nexport interface UseConversationIndexResult {\n /** Newest-page entries, or `null` until the attach snapshot applies. */\n entries: ConversationIndexEntry[] | null;\n hasNextPage: boolean;\n totalCount: number | null;\n /**\n * The viewer's watch-activity stamps (see {@link ConversationWatchActivity});\n * `null` until the snapshot applies or when the host does not send them.\n */\n watchActivity: ConversationWatchActivity | null;\n connection: StatewireClient.Connection;\n refresh: () => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\n/** The conversation-index host is mounted beside the threads host. */\nexport function conversationIndexUrlFromStatewireUrl(statewireUrl: string): string {\n return statewireUrl.replace(/\\/threads\\/?$/, '/conversation-index');\n}\n\n/** Instance id of one viewer's index; mirrors agora's `conversation_index_id`. */\nexport function conversationIndexId({\n workspaceId,\n userId,\n}: {\n workspaceId: string;\n userId: string;\n}): string {\n return `${workspaceId}-${userId}`;\n}\n\n/**\n * Attach to the live conversation index of one `(workspace, user)` viewer.\n */\nexport function useConversationIndex({\n baseUrl,\n workspaceId,\n userId,\n sse = false,\n headers,\n enabled = true,\n}: UseConversationIndexOptions): UseConversationIndexResult {\n const headersRef = useRef(headers);\n headersRef.current = headers;\n const indexId = conversationIndexId({ workspaceId, userId });\n // Auth-denial park (same policy as the thread transport): the index is a\n // pure push signal, so after a streak of `unauthorized` denials the retry\n // loop stops instead of hammering the attach endpoint every 30s for as long\n // as the tab lives. A remount or the parked connection's `reconnect()`\n // re-arms it.\n const [parked, setParked] = useState<{ indexId: string; message: string | undefined } | null>(\n null\n );\n const denialRef = useRef({ indexId, state: initialAuthDenialTrackerState() });\n if (denialRef.current.indexId !== indexId) {\n denialRef.current = { indexId, state: initialAuthDenialTrackerState() };\n }\n const isParked = parked !== null && parked.indexId === indexId;\n const client = useStatewire<ConversationIndexState | undefined, ConversationIndexCommands>({\n transport: withKey(\n indexId,\n !enabled || isParked\n ? StatewireSuspended<ConversationIndexCommands>({\n message:\n parked?.message ??\n (enabled\n ? 'Conversation index authorization was refused.'\n : 'Conversation index is waiting for a signed-in user and workspace.'),\n ...(enabled\n ? {\n onReconnectRequested: () => {\n denialRef.current = { indexId, state: initialAuthDenialTrackerState() };\n setParked(null);\n },\n }\n : {}),\n })\n : statewireTransport<ConversationIndexState, ConversationIndexCommands>(\n {\n url: `${baseUrl.replace(/\\/+$/, '')}/${encodeURIComponent(indexId)}`,\n headers: (ctx: { staleAuth: boolean }) => headersRef.current(ctx),\n },\n { sse }\n )\n ),\n initialState: undefined,\n });\n // Observing during render is idempotent (the tracker dedupes by attempt\n // number), so a re-render of the same connection state cannot double-count.\n // The tracker consumes the projected vocabulary, never the raw connection.\n const denial = observeAuthDenial(\n denialRef.current.state,\n projectDeepAgentConnection(client.connection)\n );\n denialRef.current.state = denial.state;\n const shouldPark = denial.park;\n const parkMessage = denial.state.message;\n useEffect(() => {\n if (!enabled || !shouldPark) return;\n setParked((prev) =>\n prev?.indexId === indexId ? prev : { indexId, message: parkMessage }\n );\n }, [enabled, shouldPark, parkMessage, indexId]);\n const commandsRef = useRef(client.commands);\n commandsRef.current = client.commands;\n const refresh = useCallback(() => commandsRef.current.refresh(), []);\n const loadMore = useCallback(() => commandsRef.current.load_more(), []);\n return {\n entries: client.state?.conversations ?? null,\n hasNextPage: client.state?.has_more ?? false,\n totalCount: client.state?.total_count ?? null,\n watchActivity: client.state?.watch_activity ?? null,\n connection: client.connection,\n refresh,\n loadMore,\n };\n}\n\n/**\n * Stable change fingerprint over a snapshot's watch-activity stamps: the\n * well-formed `[threadId, instant]` pairs, sorted by thread. An empty string\n * means \"nothing sent yet\" (no snapshot, or a host that predates the stamps),\n * so an effect keyed on it skips; an applied-but-empty map is `'[]'`.\n */\nexport function watchActivityFingerprint(activity: unknown): string {\n if (activity === null || typeof activity !== 'object' || Array.isArray(activity)) return '';\n return JSON.stringify(\n Object.entries(activity)\n .filter((entry): entry is [string, string] => typeof entry[1] === 'string')\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n );\n}\n\n/**\n * Stable change fingerprint over the entries a list surface reacts to. An\n * empty string means \"no snapshot yet\" — effects keyed on the fingerprint\n * should treat it as a no-op rather than an empty list. An applied-but-empty\n * snapshot serializes to `'[]'`, so deleting the last conversation still\n * changes the fingerprint. JSON-encoding keeps arbitrary titles from\n * colliding across field or entry boundaries.\n */\nexport function conversationIndexFingerprint(\n entries: ConversationIndexEntry[] | null\n): string {\n if (entries === null) return '';\n return JSON.stringify(\n entries.map((entry) => [\n entry.thread_id,\n entry.updated_at,\n entry.state,\n entry.title,\n entry.run_status ?? null,\n entry.is_unread ?? null,\n entry.attention ?? null,\n entry.next_action ?? null,\n entry.attention_source ?? null,\n entry.is_favorited ?? null,\n ])\n );\n}\n"],"names":["useRef","useState","initialAuthDenialTrackerState","useStatewire","withKey","StatewireSuspended","statewireTransport","observeAuthDenial","projectDeepAgentConnection","useEffect","useCallback"],"mappings":";;;;;;;;;AA4GO,SAAS,qCAAqC,cAA8B;AACjF,SAAO,aAAa,QAAQ,iBAAiB,qBAAqB;AACpE;AAGO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAGW;AACT,SAAO,GAAG,WAAW,IAAI,MAAM;AACjC;AAKO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA,UAAU;AACZ,GAA4D;;AAC1D,QAAM,aAAaA,MAAAA,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,UAAU,oBAAoB,EAAE,aAAa,QAAQ;AAM3D,QAAM,CAAC,QAAQ,SAAS,IAAIC,MAAAA;AAAAA,IAC1B;AAAA,EAAA;AAEF,QAAM,YAAYD,MAAAA,OAAO,EAAE,SAAS,OAAOE,WAAAA,8BAAA,GAAiC;AAC5E,MAAI,UAAU,QAAQ,YAAY,SAAS;AACzC,cAAU,UAAU,EAAE,SAAS,OAAOA,WAAAA,gCAA8B;AAAA,EACtE;AACA,QAAM,WAAW,WAAW,QAAQ,OAAO,YAAY;AACvD,QAAM,SAASC,UAAAA,aAA4E;AAAA,IACzF,WAAWC,IAAAA;AAAAA,MACT;AAAA,MACA,CAAC,WAAW,WACRC,sCAA8C;AAAA,QAC5C,UACE,iCAAQ,aACP,UACG,kDACA;AAAA,QACN,GAAI,UACA;AAAA,UACE,sBAAsB,MAAM;AAC1B,sBAAU,UAAU,EAAE,SAAS,OAAOH,WAAAA,gCAA8B;AACpE,sBAAU,IAAI;AAAA,UAChB;AAAA,QAAA,IAEF,CAAA;AAAA,MAAC,CACN,IACDI,mBAAAA;AAAAA,QACE;AAAA,UACE,KAAK,GAAG,QAAQ,QAAQ,QAAQ,EAAE,CAAC,IAAI,mBAAmB,OAAO,CAAC;AAAA,UAClE,SAAS,CAAC,QAAgC,WAAW,QAAQ,GAAG;AAAA,QAAA;AAAA,QAElE,EAAE,IAAA;AAAA,MAAI;AAAA,IACR;AAAA,IAEN,cAAc;AAAA,EAAA,CACf;AAID,QAAM,SAASC,WAAAA;AAAAA,IACb,UAAU,QAAQ;AAAA,IAClBC,OAAAA,2BAA2B,OAAO,UAAU;AAAA,EAAA;AAE9C,YAAU,QAAQ,QAAQ,OAAO;AACjC,QAAM,aAAa,OAAO;AAC1B,QAAM,cAAc,OAAO,MAAM;AACjCC,QAAAA,UAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,WAAY;AAC7B;AAAA,MAAU,CAAC,UACT,6BAAM,aAAY,UAAU,OAAO,EAAE,SAAS,SAAS,YAAA;AAAA,IAAY;AAAA,EAEvE,GAAG,CAAC,SAAS,YAAY,aAAa,OAAO,CAAC;AAC9C,QAAM,cAAcT,MAAAA,OAAO,OAAO,QAAQ;AAC1C,cAAY,UAAU,OAAO;AAC7B,QAAM,UAAUU,MAAAA,YAAY,MAAM,YAAY,QAAQ,QAAA,GAAW,EAAE;AACnE,QAAM,WAAWA,MAAAA,YAAY,MAAM,YAAY,QAAQ,UAAA,GAAa,EAAE;AACtE,SAAO;AAAA,IACL,WAAS,YAAO,UAAP,mBAAc,kBAAiB;AAAA,IACxC,eAAa,YAAO,UAAP,mBAAc,aAAY;AAAA,IACvC,cAAY,YAAO,UAAP,mBAAc,gBAAe;AAAA,IACzC,iBAAe,YAAO,UAAP,mBAAc,mBAAkB;AAAA,IAC/C,YAAY,OAAO;AAAA,IACnB;AAAA,IACA;AAAA,EAAA;AAEJ;;;;"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-index.js","sources":["../../../../agent-runtime/src/conversation-index.ts"],"sourcesContent":["import { withKey } from '@assistant-ui/tap';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useStatewire, type StatewireClient } from 'statewire';\nimport { initialAuthDenialTrackerState, observeAuthDenial } from './auth-denial';\nimport type { ConversationAttention, ConversationAttentionSource } from './conversation-list';\nimport { projectDeepAgentConnection } from './errors';\nimport { StatewireSuspended } from './statewire-suspended';\nimport { statewireTransport } from './statewire-transport';\nimport type { DeepAgentHeadersResolver } from './use-deep-agent-thread';\n\n/**\n * Client side of the conversation-index statewire (issue #29088): agora\n * replicates the newest page of one viewer's conversation list as one state\n * object, so thread creation / lifecycle / title changes arrive as ops\n * instead of newest-page polling. The replicated rows are renderable and the\n * typed commands extend or refresh the inclusive window; reconnects are free\n * because the protocol's snapshot-first attach IS the newest-page reconcile.\n *\n * A viewer is a `(workspaceId, userId)` pair and every pair is its own\n * instance (`/conversation-index/<workspaceId>-<userId>`): workspaces are\n * tenants, so a tab in one workspace never shares a window with a tab in\n * another, and switching workspaces attaches to a different instance instead\n * of retagging the one other tabs are still reading.\n */\nexport interface ConversationIndexEntry {\n thread_id: string;\n title: string;\n updated_at: string;\n state: string | null;\n runtime: string | null;\n start_channel: string | null;\n asset_type: string;\n asset_id: string | null;\n app_id: string | null;\n trigger_type: string | null;\n last_message_preview: string | null;\n message_count: number | null;\n is_unread: boolean | null;\n /**\n * Canonical status_v2 planes, present once the publishing agora reports them\n * (absent from snapshots replicated by an older pod during a rollout).\n */\n session_status?: string | null;\n run_status?: string | null;\n /**\n * The viewer's attention state and its one-line next action, present once\n * the publishing agora derives them (absent from snapshots replicated by\n * an older pod). Pull-request state is never pushed — it rides the list\n * fetch only (`include_pull_requests`).\n */\n attention?: ConversationAttention | null;\n next_action?: string | null;\n attention_source?: ConversationAttentionSource | null;\n is_favorited?: boolean | null;\n}\n\n/**\n * When one of this viewer's own asset subscriptions on a thread last queued or\n * settled an event: thread id → ISO instant. A contentless \"re-read now\" for\n * Olympus's Watching card, which reads what changed through its own gated\n * query; it never says what changed, who changed it, or where. Absent from an\n * older agora's snapshot.\n */\nexport type ConversationWatchActivity = Readonly<Record<string, string>>;\n\ninterface ConversationIndexState {\n conversations?: ConversationIndexEntry[];\n has_more?: boolean;\n total_count?: number;\n watch_activity?: ConversationWatchActivity;\n}\n\ninterface ConversationIndexCommands {\n [key: string]: (...args: never[]) => unknown;\n refresh(): void;\n load_more(): void;\n}\n\nexport interface UseConversationIndexOptions {\n /** Base URL of the conversation-index host (the `…/v2/conversation-index` mount). */\n baseUrl: string;\n /** The workspace being viewed — half of the instance id. Callers gate mounting on it. */\n workspaceId: string;\n /** The signed-in user's id — the other half. Callers gate mounting on it. */\n userId: string;\n /** Transport selection: SSE when true, WebSocket otherwise. */\n sse?: boolean;\n headers: DeepAgentHeadersResolver;\n /** Park the transport until the caller has a resolved principal. */\n enabled?: boolean;\n}\n\nexport interface UseConversationIndexResult {\n /** Newest-page entries, or `null` until the attach snapshot applies. */\n entries: ConversationIndexEntry[] | null;\n hasNextPage: boolean;\n totalCount: number | null;\n /**\n * The viewer's watch-activity stamps (see {@link ConversationWatchActivity});\n * `null` until the snapshot applies or when the host does not send them.\n */\n watchActivity: ConversationWatchActivity | null;\n connection: StatewireClient.Connection;\n refresh: () => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\n/** The conversation-index host is mounted beside the threads host. */\nexport function conversationIndexUrlFromStatewireUrl(statewireUrl: string): string {\n return statewireUrl.replace(/\\/threads\\/?$/, '/conversation-index');\n}\n\n/** Instance id of one viewer's index; mirrors agora's `conversation_index_id`. */\nexport function conversationIndexId({\n workspaceId,\n userId,\n}: {\n workspaceId: string;\n userId: string;\n}): string {\n return `${workspaceId}-${userId}`;\n}\n\n/**\n * Attach to the live conversation index of one `(workspace, user)` viewer.\n */\nexport function useConversationIndex({\n baseUrl,\n workspaceId,\n userId,\n sse = false,\n headers,\n enabled = true,\n}: UseConversationIndexOptions): UseConversationIndexResult {\n const headersRef = useRef(headers);\n headersRef.current = headers;\n const indexId = conversationIndexId({ workspaceId, userId });\n // Auth-denial park (same policy as the thread transport): the index is a\n // pure push signal, so after a streak of `unauthorized` denials the retry\n // loop stops instead of hammering the attach endpoint every 30s for as long\n // as the tab lives. A remount or the parked connection's `reconnect()`\n // re-arms it.\n const [parked, setParked] = useState<{ indexId: string; message: string | undefined } | null>(\n null\n );\n const denialRef = useRef({ indexId, state: initialAuthDenialTrackerState() });\n if (denialRef.current.indexId !== indexId) {\n denialRef.current = { indexId, state: initialAuthDenialTrackerState() };\n }\n const isParked = parked !== null && parked.indexId === indexId;\n const client = useStatewire<ConversationIndexState | undefined, ConversationIndexCommands>({\n transport: withKey(\n indexId,\n !enabled || isParked\n ? StatewireSuspended<ConversationIndexCommands>({\n message:\n parked?.message ??\n (enabled\n ? 'Conversation index authorization was refused.'\n : 'Conversation index is waiting for a signed-in user and workspace.'),\n ...(enabled\n ? {\n onReconnectRequested: () => {\n denialRef.current = { indexId, state: initialAuthDenialTrackerState() };\n setParked(null);\n },\n }\n : {}),\n })\n : statewireTransport<ConversationIndexState, ConversationIndexCommands>(\n {\n url: `${baseUrl.replace(/\\/+$/, '')}/${encodeURIComponent(indexId)}`,\n headers: (ctx: { staleAuth: boolean }) => headersRef.current(ctx),\n },\n { sse }\n )\n ),\n initialState: undefined,\n });\n // Observing during render is idempotent (the tracker dedupes by attempt\n // number), so a re-render of the same connection state cannot double-count.\n // The tracker consumes the projected vocabulary, never the raw connection.\n const denial = observeAuthDenial(\n denialRef.current.state,\n projectDeepAgentConnection(client.connection)\n );\n denialRef.current.state = denial.state;\n const shouldPark = denial.park;\n const parkMessage = denial.state.message;\n useEffect(() => {\n if (!enabled || !shouldPark) return;\n setParked((prev) =>\n prev?.indexId === indexId ? prev : { indexId, message: parkMessage }\n );\n }, [enabled, shouldPark, parkMessage, indexId]);\n const commandsRef = useRef(client.commands);\n commandsRef.current = client.commands;\n const refresh = useCallback(() => commandsRef.current.refresh(), []);\n const loadMore = useCallback(() => commandsRef.current.load_more(), []);\n return {\n entries: client.state?.conversations ?? null,\n hasNextPage: client.state?.has_more ?? false,\n totalCount: client.state?.total_count ?? null,\n watchActivity: client.state?.watch_activity ?? null,\n connection: client.connection,\n refresh,\n loadMore,\n };\n}\n\n/**\n * Stable change fingerprint over a snapshot's watch-activity stamps: the\n * well-formed `[threadId, instant]` pairs, sorted by thread. An empty string\n * means \"nothing sent yet\" (no snapshot, or a host that predates the stamps),\n * so an effect keyed on it skips; an applied-but-empty map is `'[]'`.\n */\nexport function watchActivityFingerprint(activity: unknown): string {\n if (activity === null || typeof activity !== 'object' || Array.isArray(activity)) return '';\n return JSON.stringify(\n Object.entries(activity)\n .filter((entry): entry is [string, string] => typeof entry[1] === 'string')\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n );\n}\n\n/**\n * Stable change fingerprint over the entries a list surface reacts to. An\n * empty string means \"no snapshot yet\" — effects keyed on the fingerprint\n * should treat it as a no-op rather than an empty list. An applied-but-empty\n * snapshot serializes to `'[]'`, so deleting the last conversation still\n * changes the fingerprint. JSON-encoding keeps arbitrary titles from\n * colliding across field or entry boundaries.\n */\nexport function conversationIndexFingerprint(\n entries: ConversationIndexEntry[] | null\n): string {\n if (entries === null) return '';\n return JSON.stringify(\n entries.map((entry) => [\n entry.thread_id,\n entry.updated_at,\n entry.state,\n entry.title,\n entry.run_status ?? null,\n entry.is_unread ?? null,\n entry.attention ?? null,\n entry.next_action ?? null,\n entry.attention_source ?? null,\n entry.is_favorited ?? null,\n ])\n );\n}\n"],"names":[],"mappings":";;;;;;;AA4GO,SAAS,qCAAqC,cAA8B;AACjF,SAAO,aAAa,QAAQ,iBAAiB,qBAAqB;AACpE;AAGO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAGW;AACT,SAAO,GAAG,WAAW,IAAI,MAAM;AACjC;AAKO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA,UAAU;AACZ,GAA4D;;AAC1D,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,UAAU,oBAAoB,EAAE,aAAa,QAAQ;AAM3D,QAAM,CAAC,QAAQ,SAAS,IAAI;AAAA,IAC1B;AAAA,EAAA;AAEF,QAAM,YAAY,OAAO,EAAE,SAAS,OAAO,8BAAA,GAAiC;AAC5E,MAAI,UAAU,QAAQ,YAAY,SAAS;AACzC,cAAU,UAAU,EAAE,SAAS,OAAO,gCAA8B;AAAA,EACtE;AACA,QAAM,WAAW,WAAW,QAAQ,OAAO,YAAY;AACvD,QAAM,SAAS,aAA4E;AAAA,IACzF,WAAW;AAAA,MACT;AAAA,MACA,CAAC,WAAW,WACR,mBAA8C;AAAA,QAC5C,UACE,iCAAQ,aACP,UACG,kDACA;AAAA,QACN,GAAI,UACA;AAAA,UACE,sBAAsB,MAAM;AAC1B,sBAAU,UAAU,EAAE,SAAS,OAAO,gCAA8B;AACpE,sBAAU,IAAI;AAAA,UAChB;AAAA,QAAA,IAEF,CAAA;AAAA,MAAC,CACN,IACD;AAAA,QACE;AAAA,UACE,KAAK,GAAG,QAAQ,QAAQ,QAAQ,EAAE,CAAC,IAAI,mBAAmB,OAAO,CAAC;AAAA,UAClE,SAAS,CAAC,QAAgC,WAAW,QAAQ,GAAG;AAAA,QAAA;AAAA,QAElE,EAAE,IAAA;AAAA,MAAI;AAAA,IACR;AAAA,IAEN,cAAc;AAAA,EAAA,CACf;AAID,QAAM,SAAS;AAAA,IACb,UAAU,QAAQ;AAAA,IAClB,2BAA2B,OAAO,UAAU;AAAA,EAAA;AAE9C,YAAU,QAAQ,QAAQ,OAAO;AACjC,QAAM,aAAa,OAAO;AAC1B,QAAM,cAAc,OAAO,MAAM;AACjC,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,WAAY;AAC7B;AAAA,MAAU,CAAC,UACT,6BAAM,aAAY,UAAU,OAAO,EAAE,SAAS,SAAS,YAAA;AAAA,IAAY;AAAA,EAEvE,GAAG,CAAC,SAAS,YAAY,aAAa,OAAO,CAAC;AAC9C,QAAM,cAAc,OAAO,OAAO,QAAQ;AAC1C,cAAY,UAAU,OAAO;AAC7B,QAAM,UAAU,YAAY,MAAM,YAAY,QAAQ,QAAA,GAAW,EAAE;AACnE,QAAM,WAAW,YAAY,MAAM,YAAY,QAAQ,UAAA,GAAa,EAAE;AACtE,SAAO;AAAA,IACL,WAAS,YAAO,UAAP,mBAAc,kBAAiB;AAAA,IACxC,eAAa,YAAO,UAAP,mBAAc,aAAY;AAAA,IACvC,cAAY,YAAO,UAAP,mBAAc,gBAAe;AAAA,IACzC,iBAAe,YAAO,UAAP,mBAAc,mBAAkB;AAAA,IAC/C,YAAY,OAAO;AAAA,IACnB;AAAA,IACA;AAAA,EAAA;AAEJ;"}