@athenaintel/react 0.20.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/README.md +86 -0
  2. package/dist/assets/use-asset-embed.cjs +8 -0
  3. package/dist/assets/use-asset-embed.cjs.map +1 -1
  4. package/dist/assets/use-asset-embed.js +8 -0
  5. package/dist/assets/use-asset-embed.js.map +1 -1
  6. package/dist/chat/StatewireApprovalCard.cjs +22 -4
  7. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  8. package/dist/chat/StatewireApprovalCard.d.ts +14 -0
  9. package/dist/chat/StatewireApprovalCard.js +23 -5
  10. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  11. package/dist/chat/StatewireClientToolBridge.cjs +2 -1
  12. package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
  13. package/dist/chat/StatewireClientToolBridge.js +2 -1
  14. package/dist/chat/StatewireClientToolBridge.js.map +1 -1
  15. package/dist/chat/StatewireQueuedMessages.cjs +2 -1
  16. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  17. package/dist/chat/StatewireQueuedMessages.js +2 -1
  18. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  19. package/dist/chat/athena-citation-links.cjs +17 -1
  20. package/dist/chat/athena-citation-links.cjs.map +1 -1
  21. package/dist/chat/athena-citation-links.js +17 -1
  22. package/dist/chat/athena-citation-links.js.map +1 -1
  23. package/dist/chat/statewire-client-tools.cjs +2 -1
  24. package/dist/chat/statewire-client-tools.cjs.map +1 -1
  25. package/dist/chat/statewire-client-tools.js +2 -1
  26. package/dist/chat/statewire-client-tools.js.map +1 -1
  27. package/dist/chat/super-grouping/SuperGroupingCard.cjs +69 -187
  28. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  29. package/dist/chat/super-grouping/SuperGroupingCard.d.ts +10 -6
  30. package/dist/chat/super-grouping/SuperGroupingCard.js +73 -191
  31. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  32. package/dist/chat/super-grouping/ToolTimeline.cjs +131 -0
  33. package/dist/chat/super-grouping/ToolTimeline.cjs.map +1 -0
  34. package/dist/chat/super-grouping/ToolTimeline.d.ts +16 -0
  35. package/dist/chat/super-grouping/ToolTimeline.js +131 -0
  36. package/dist/chat/super-grouping/ToolTimeline.js.map +1 -0
  37. package/dist/chat/super-grouping/activity-timeline.cjs +14 -8
  38. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  39. package/dist/chat/super-grouping/activity-timeline.d.ts +3 -0
  40. package/dist/chat/super-grouping/activity-timeline.js +14 -8
  41. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  42. package/dist/chat/super-grouping/timeline-step.cjs +65 -0
  43. package/dist/chat/super-grouping/timeline-step.cjs.map +1 -0
  44. package/dist/chat/super-grouping/timeline-step.d.ts +17 -0
  45. package/dist/chat/super-grouping/timeline-step.js +65 -0
  46. package/dist/chat/super-grouping/timeline-step.js.map +1 -0
  47. package/dist/chat/tiptap-text.cjs +8 -8
  48. package/dist/chat/tiptap-text.cjs.map +1 -1
  49. package/dist/chat/use-athena-run-live.cjs +2 -1
  50. package/dist/chat/use-athena-run-live.cjs.map +1 -1
  51. package/dist/chat/use-athena-run-live.js +2 -1
  52. package/dist/chat/use-athena-run-live.js.map +1 -1
  53. package/dist/chat/use-statewire-approval-request.cjs +7 -2
  54. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  55. package/dist/chat/use-statewire-approval-request.d.ts +12 -2
  56. package/dist/chat/use-statewire-approval-request.js +8 -3
  57. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  58. package/dist/chat-ui/composer/tiptap-composer.cjs +24 -65
  59. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  60. package/dist/chat-ui/composer/tiptap-composer.js +24 -65
  61. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  62. package/dist/chat-ui/index.cjs +2 -1
  63. package/dist/chat-ui/index.cjs.map +1 -1
  64. package/dist/chat-ui/index.js +2 -1
  65. package/dist/chat-ui/index.js.map +1 -1
  66. package/dist/chat-ui/mentions/mention-chip.cjs +49 -4
  67. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  68. package/dist/chat-ui/mentions/mention-chip.d.ts +11 -0
  69. package/dist/chat-ui/mentions/mention-chip.js +50 -5
  70. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  71. package/dist/chat-ui/mentions/mention-extension.cjs +18 -6
  72. package/dist/chat-ui/mentions/mention-extension.cjs.map +1 -1
  73. package/dist/chat-ui/mentions/mention-extension.d.ts +2 -0
  74. package/dist/chat-ui/mentions/mention-extension.js +18 -6
  75. package/dist/chat-ui/mentions/mention-extension.js.map +1 -1
  76. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.cjs +1 -1
  77. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.cjs.map +1 -1
  78. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.js +1 -1
  79. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.js.map +1 -1
  80. package/dist/chat-ui/mentions/suggestions/components/menu-item.cjs +9 -6
  81. package/dist/chat-ui/mentions/suggestions/components/menu-item.cjs.map +1 -1
  82. package/dist/chat-ui/mentions/suggestions/components/menu-item.d.ts +2 -2
  83. package/dist/chat-ui/mentions/suggestions/components/menu-item.js +10 -7
  84. package/dist/chat-ui/mentions/suggestions/components/menu-item.js.map +1 -1
  85. package/dist/chat-ui/mentions/suggestions/components/menu-list.cjs +2 -2
  86. package/dist/chat-ui/mentions/suggestions/components/menu-list.cjs.map +1 -1
  87. package/dist/chat-ui/mentions/suggestions/components/menu-list.d.ts +2 -2
  88. package/dist/chat-ui/mentions/suggestions/components/menu-list.js +2 -2
  89. package/dist/chat-ui/mentions/suggestions/components/menu-list.js.map +1 -1
  90. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +2 -1
  91. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
  92. package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +3 -2
  93. package/dist/chat-ui/mentions/suggestions/data/api-client.js +2 -1
  94. package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
  95. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +6 -6
  96. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs.map +1 -1
  97. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js +6 -6
  98. package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
  99. package/dist/chat-ui/messages/message-components.cjs +6 -29
  100. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  101. package/dist/chat-ui/messages/message-components.d.ts +1 -13
  102. package/dist/chat-ui/messages/message-components.js +8 -31
  103. package/dist/chat-ui/messages/message-components.js.map +1 -1
  104. package/dist/chat-ui/messages/runtime-message.cjs +3 -2
  105. package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
  106. package/dist/chat-ui/messages/runtime-message.js +3 -2
  107. package/dist/chat-ui/messages/runtime-message.js.map +1 -1
  108. package/dist/chat-ui/messages/statewire-continue-run.cjs +23 -8
  109. package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
  110. package/dist/chat-ui/messages/statewire-continue-run.d.ts +14 -4
  111. package/dist/chat-ui/messages/statewire-continue-run.js +23 -8
  112. package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
  113. package/dist/chat-ui/messages/user-edit-composer.cjs +2 -1
  114. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  115. package/dist/chat-ui/messages/user-edit-composer.js +2 -1
  116. package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
  117. package/dist/chat-ui/rich-text/chat-message-body.cjs +76 -0
  118. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -0
  119. package/dist/chat-ui/rich-text/chat-message-body.d.ts +5 -0
  120. package/dist/chat-ui/rich-text/chat-message-body.js +76 -0
  121. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -0
  122. package/dist/chat-ui/rich-text/chat-node-views.cjs +51 -0
  123. package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -0
  124. package/dist/chat-ui/rich-text/chat-node-views.d.ts +13 -0
  125. package/dist/chat-ui/rich-text/chat-node-views.js +51 -0
  126. package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -0
  127. package/dist/chat-ui/rich-text/chat-text-schema.cjs +70 -0
  128. package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -0
  129. package/dist/chat-ui/rich-text/chat-text-schema.d.ts +7 -0
  130. package/dist/chat-ui/rich-text/chat-text-schema.js +70 -0
  131. package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -0
  132. package/dist/chat-ui/rich-text/dom-output-spec.cjs +83 -0
  133. package/dist/chat-ui/rich-text/dom-output-spec.cjs.map +1 -0
  134. package/dist/chat-ui/rich-text/dom-output-spec.d.ts +11 -0
  135. package/dist/chat-ui/rich-text/dom-output-spec.js +83 -0
  136. package/dist/chat-ui/rich-text/dom-output-spec.js.map +1 -0
  137. package/dist/chat-ui/statewire.cjs +2 -1
  138. package/dist/chat-ui/statewire.cjs.map +1 -1
  139. package/dist/chat-ui/statewire.js +2 -1
  140. package/dist/chat-ui/statewire.js.map +1 -1
  141. package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -1
  142. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  143. package/dist/chat-ui/thread/statewire-history-edge.js +2 -1
  144. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  145. package/dist/index.cjs +2 -1
  146. package/dist/index.cjs.map +1 -1
  147. package/dist/index.js +2 -1
  148. package/dist/index.js.map +1 -1
  149. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +32 -0
  150. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +1 -0
  151. package/dist/node_modules/qrcode-generator/dist/qrcode.js +31 -0
  152. package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +1 -0
  153. package/dist/node_modules/zod/v4/classic/errors.cjs +44 -0
  154. package/dist/node_modules/zod/v4/classic/errors.cjs.map +1 -0
  155. package/dist/node_modules/zod/v4/classic/errors.js +44 -0
  156. package/dist/node_modules/zod/v4/classic/errors.js.map +1 -0
  157. package/dist/node_modules/zod/v4/classic/iso.cjs +43 -0
  158. package/dist/node_modules/zod/v4/classic/iso.cjs.map +1 -0
  159. package/dist/node_modules/zod/v4/classic/iso.js +43 -0
  160. package/dist/node_modules/zod/v4/classic/iso.js.map +1 -0
  161. package/dist/node_modules/zod/v4/classic/parse.cjs +29 -0
  162. package/dist/node_modules/zod/v4/classic/parse.cjs.map +1 -0
  163. package/dist/node_modules/zod/v4/classic/parse.js +29 -0
  164. package/dist/node_modules/zod/v4/classic/parse.js.map +1 -0
  165. package/dist/node_modules/zod/v4/classic/schemas.cjs +871 -0
  166. package/dist/node_modules/zod/v4/classic/schemas.cjs.map +1 -0
  167. package/dist/node_modules/zod/v4/classic/schemas.js +871 -0
  168. package/dist/node_modules/zod/v4/classic/schemas.js.map +1 -0
  169. package/dist/node_modules/zod/v4/core/api.cjs +570 -0
  170. package/dist/node_modules/zod/v4/core/api.cjs.map +1 -0
  171. package/dist/node_modules/zod/v4/core/api.js +570 -0
  172. package/dist/node_modules/zod/v4/core/api.js.map +1 -0
  173. package/dist/node_modules/zod/v4/core/checks.cjs +411 -0
  174. package/dist/node_modules/zod/v4/core/checks.cjs.map +1 -0
  175. package/dist/node_modules/zod/v4/core/checks.js +411 -0
  176. package/dist/node_modules/zod/v4/core/checks.js.map +1 -0
  177. package/dist/node_modules/zod/v4/core/core.cjs +77 -0
  178. package/dist/node_modules/zod/v4/core/core.cjs.map +1 -0
  179. package/dist/node_modules/zod/v4/core/core.js +77 -0
  180. package/dist/node_modules/zod/v4/core/core.js.map +1 -0
  181. package/dist/node_modules/zod/v4/core/doc.cjs +38 -0
  182. package/dist/node_modules/zod/v4/core/doc.cjs.map +1 -0
  183. package/dist/node_modules/zod/v4/core/doc.js +38 -0
  184. package/dist/node_modules/zod/v4/core/doc.js.map +1 -0
  185. package/dist/node_modules/zod/v4/core/errors.cjs +76 -0
  186. package/dist/node_modules/zod/v4/core/errors.cjs.map +1 -0
  187. package/dist/node_modules/zod/v4/core/errors.js +76 -0
  188. package/dist/node_modules/zod/v4/core/errors.js.map +1 -0
  189. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +312 -0
  190. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +1 -0
  191. package/dist/node_modules/zod/v4/core/json-schema-processors.js +312 -0
  192. package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +1 -0
  193. package/dist/node_modules/zod/v4/core/parse.cjs +96 -0
  194. package/dist/node_modules/zod/v4/core/parse.cjs.map +1 -0
  195. package/dist/node_modules/zod/v4/core/parse.js +96 -0
  196. package/dist/node_modules/zod/v4/core/parse.js.map +1 -0
  197. package/dist/node_modules/zod/v4/core/regexes.cjs +86 -0
  198. package/dist/node_modules/zod/v4/core/regexes.cjs.map +1 -0
  199. package/dist/node_modules/zod/v4/core/regexes.js +86 -0
  200. package/dist/node_modules/zod/v4/core/regexes.js.map +1 -0
  201. package/dist/node_modules/zod/v4/core/registries.cjs +52 -0
  202. package/dist/node_modules/zod/v4/core/registries.cjs.map +1 -0
  203. package/dist/node_modules/zod/v4/core/registries.js +52 -0
  204. package/dist/node_modules/zod/v4/core/registries.js.map +1 -0
  205. package/dist/node_modules/zod/v4/core/schemas.cjs +1412 -0
  206. package/dist/node_modules/zod/v4/core/schemas.cjs.map +1 -0
  207. package/dist/node_modules/zod/v4/core/schemas.js +1413 -0
  208. package/dist/node_modules/zod/v4/core/schemas.js.map +1 -0
  209. package/dist/node_modules/zod/v4/core/to-json-schema.cjs +370 -0
  210. package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +1 -0
  211. package/dist/node_modules/zod/v4/core/to-json-schema.js +370 -0
  212. package/dist/node_modules/zod/v4/core/to-json-schema.js.map +1 -0
  213. package/dist/node_modules/zod/v4/core/util.cjs +444 -0
  214. package/dist/node_modules/zod/v4/core/util.cjs.map +1 -0
  215. package/dist/node_modules/zod/v4/core/util.js +444 -0
  216. package/dist/node_modules/zod/v4/core/util.js.map +1 -0
  217. package/dist/node_modules/zod/v4/core/versions.cjs +9 -0
  218. package/dist/node_modules/zod/v4/core/versions.cjs.map +1 -0
  219. package/dist/node_modules/zod/v4/core/versions.js +9 -0
  220. package/dist/node_modules/zod/v4/core/versions.js.map +1 -0
  221. package/dist/packages/agent-runtime/src/client-tool-bridge.cjs +7 -1
  222. package/dist/packages/agent-runtime/src/client-tool-bridge.cjs.map +1 -1
  223. package/dist/packages/agent-runtime/src/client-tool-bridge.js +7 -1
  224. package/dist/packages/agent-runtime/src/client-tool-bridge.js.map +1 -1
  225. package/dist/packages/agent-runtime/src/conversation-index.cjs +19 -10
  226. package/dist/packages/agent-runtime/src/conversation-index.cjs.map +1 -1
  227. package/dist/packages/agent-runtime/src/conversation-index.js +19 -10
  228. package/dist/packages/agent-runtime/src/conversation-index.js.map +1 -1
  229. package/dist/packages/agent-runtime/src/conversation-list.cjs +4 -0
  230. package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
  231. package/dist/packages/agent-runtime/src/conversation-list.js +4 -0
  232. package/dist/packages/agent-runtime/src/conversation-list.js.map +1 -1
  233. package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
  234. package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
  235. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +2 -0
  236. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  237. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +2 -0
  238. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  239. package/dist/packages/agent-runtime/src/interrupts.cjs +31 -7
  240. package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
  241. package/dist/packages/agent-runtime/src/interrupts.js +31 -7
  242. package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
  243. package/dist/packages/agent-runtime/src/messages.cjs +35 -5
  244. package/dist/packages/agent-runtime/src/messages.cjs.map +1 -1
  245. package/dist/packages/agent-runtime/src/messages.js +35 -5
  246. package/dist/packages/agent-runtime/src/messages.js.map +1 -1
  247. package/dist/packages/agent-runtime/src/protocol.cjs +4 -6
  248. package/dist/packages/agent-runtime/src/protocol.cjs.map +1 -1
  249. package/dist/packages/agent-runtime/src/protocol.js +4 -6
  250. package/dist/packages/agent-runtime/src/protocol.js.map +1 -1
  251. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +296 -14
  252. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  253. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +293 -11
  254. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  255. package/dist/packages/agent-runtime/src/screen-point.cjs +68 -0
  256. package/dist/packages/agent-runtime/src/screen-point.cjs.map +1 -0
  257. package/dist/packages/agent-runtime/src/screen-point.js +68 -0
  258. package/dist/packages/agent-runtime/src/screen-point.js.map +1 -0
  259. package/dist/packages/agent-runtime/src/statewire-transport.cjs +6 -1
  260. package/dist/packages/agent-runtime/src/statewire-transport.cjs.map +1 -1
  261. package/dist/packages/agent-runtime/src/statewire-transport.js +6 -1
  262. package/dist/packages/agent-runtime/src/statewire-transport.js.map +1 -1
  263. package/dist/packages/agent-runtime/src/thread-handoff.cjs +85 -0
  264. package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +1 -0
  265. package/dist/packages/agent-runtime/src/thread-handoff.js +85 -0
  266. package/dist/packages/agent-runtime/src/thread-handoff.js.map +1 -0
  267. package/dist/packages/agent-runtime/src/thread-snapshot-cache.cjs +1 -1
  268. package/dist/packages/agent-runtime/src/thread-snapshot-cache.cjs.map +1 -1
  269. package/dist/packages/agent-runtime/src/thread-snapshot-cache.js +1 -1
  270. package/dist/packages/agent-runtime/src/thread-snapshot-cache.js.map +1 -1
  271. package/dist/packages/agent-runtime/src/tool-metadata.cjs +0 -8
  272. package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
  273. package/dist/packages/agent-runtime/src/tool-metadata.js +0 -8
  274. package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
  275. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +14 -0
  276. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -0
  277. package/dist/packages/agent-runtime/src/use-deep-agent-config.js +14 -0
  278. package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -0
  279. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs +21 -5
  280. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -1
  281. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js +21 -5
  282. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -1
  283. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.cjs +13 -3
  284. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
  285. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.js +14 -4
  286. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
  287. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs +35 -15
  288. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  289. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js +35 -15
  290. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  291. package/dist/packages/agent-runtime/src/voice-presentation.cjs.map +1 -1
  292. package/dist/packages/agent-runtime/src/voice-presentation.js.map +1 -1
  293. package/dist/packages/react/package.json.cjs +1 -1
  294. package/dist/packages/react/package.json.js +1 -1
  295. package/dist/provider/AthenaProvider.cjs +2 -1
  296. package/dist/provider/AthenaProvider.cjs.map +1 -1
  297. package/dist/provider/AthenaProvider.js +2 -1
  298. package/dist/provider/AthenaProvider.js.map +1 -1
  299. package/dist/runtime/statewire-lifecycle.cjs +2 -1
  300. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  301. package/dist/runtime/statewire-lifecycle.js +2 -1
  302. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  303. package/dist/runtime/useAthenaStatewireRuntime.cjs +2 -1
  304. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  305. package/dist/runtime/useAthenaStatewireRuntime.js +2 -1
  306. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  307. package/dist/styles.css +1 -1
  308. package/dist/threads/api.cjs +29 -0
  309. package/dist/threads/api.cjs.map +1 -1
  310. package/dist/threads/api.d.ts +15 -0
  311. package/dist/threads/api.js +29 -0
  312. package/dist/threads/api.js.map +1 -1
  313. package/dist/threads/conversation-list-fetcher.cjs +4 -0
  314. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  315. package/dist/threads/conversation-list-fetcher.js +4 -0
  316. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  317. package/dist/threads/credential-shape.cjs +15 -0
  318. package/dist/threads/credential-shape.cjs.map +1 -0
  319. package/dist/threads/credential-shape.d.ts +17 -0
  320. package/dist/threads/credential-shape.js +15 -0
  321. package/dist/threads/credential-shape.js.map +1 -0
  322. package/dist/threads/statewire-thread-list.cjs +37 -4
  323. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  324. package/dist/threads/statewire-thread-list.js +38 -5
  325. package/dist/threads/statewire-thread-list.js.map +1 -1
  326. package/dist/threads/use-rest-conversation-history.cjs +2 -1
  327. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  328. package/dist/threads/use-rest-conversation-history.js +2 -1
  329. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  330. package/dist/threads/use-statewire-conversation-history.cjs +5 -2
  331. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  332. package/dist/threads/use-statewire-conversation-history.d.ts +4 -1
  333. package/dist/threads/use-statewire-conversation-history.js +5 -2
  334. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  335. package/dist/toolkits/index.cjs +0 -7
  336. package/dist/toolkits/index.cjs.map +1 -1
  337. package/dist/toolkits/index.d.ts +0 -2
  338. package/dist/toolkits/index.js +0 -7
  339. package/dist/toolkits/index.js.map +1 -1
  340. package/dist/tools/index.cjs +2 -1
  341. package/dist/tools/index.cjs.map +1 -1
  342. package/dist/tools/index.js +2 -1
  343. package/dist/tools/index.js.map +1 -1
  344. package/dist/tools/misc-tool-uis.cjs +2 -1
  345. package/dist/tools/misc-tool-uis.cjs.map +1 -1
  346. package/dist/tools/misc-tool-uis.js +2 -1
  347. package/dist/tools/misc-tool-uis.js.map +1 -1
  348. package/dist/tools/primitives.cjs +2 -1
  349. package/dist/tools/primitives.cjs.map +1 -1
  350. package/dist/tools/primitives.js +2 -1
  351. package/dist/tools/primitives.js.map +1 -1
  352. package/dist/tools/tool-fallback.cjs +2 -1
  353. package/dist/tools/tool-fallback.cjs.map +1 -1
  354. package/dist/tools/tool-fallback.js +2 -1
  355. package/dist/tools/tool-fallback.js.map +1 -1
  356. package/dist/tools/voice-update-tool-ui.cjs +2 -1
  357. package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
  358. package/dist/tools/voice-update-tool-ui.js +2 -1
  359. package/dist/tools/voice-update-tool-ui.js.map +1 -1
  360. package/dist/voice/index.cjs +6 -0
  361. package/dist/voice/index.cjs.map +1 -0
  362. package/dist/voice/index.d.ts +2 -0
  363. package/dist/voice/index.js +6 -0
  364. package/dist/voice/index.js.map +1 -0
  365. package/dist/voice/use-athena-voice-session.cjs +315 -0
  366. package/dist/voice/use-athena-voice-session.cjs.map +1 -0
  367. package/dist/voice/use-athena-voice-session.d.ts +180 -0
  368. package/dist/voice/use-athena-voice-session.js +315 -0
  369. package/dist/voice/use-athena-voice-session.js.map +1 -0
  370. package/package.json +18 -4
@@ -9,9 +9,16 @@ function isRecord(value) {
9
9
  function isPendingInterrupt(request) {
10
10
  return request.type === "interrupt" && !("response" in request);
11
11
  }
12
+ const LEGACY_SYSTEM_PAUSE_INTERRUPT_SOURCES = /* @__PURE__ */ new Set(["deep_research_pending"]);
13
+ function isSystemPauseInterrupt(payload) {
14
+ if (!isRecord(payload)) return false;
15
+ if (payload.requires_user_action === false) return true;
16
+ return typeof payload.source === "string" && LEGACY_SYSTEM_PAUSE_INTERRUPT_SOURCES.has(payload.source);
17
+ }
12
18
  function isApprovalCardInterrupt(request) {
13
19
  if (!isPendingInterrupt(request)) return false;
14
- return !clientTools.hasClientToolInterruptSource(request.payload);
20
+ const payload = request.payload;
21
+ return !clientTools.hasClientToolInterruptSource(payload) && !isSystemPauseInterrupt(payload);
15
22
  }
16
23
  function asHitlApproval(value) {
17
24
  if (!isRecord(value)) return null;
@@ -52,11 +59,12 @@ function hitlResumeApprove() {
52
59
  function hitlResumeReject(reason = HITL_REJECT_REASON) {
53
60
  return { action: "reject", reason };
54
61
  }
55
- function hitlResumeContinue() {
56
- return { action: "continue" };
62
+ const INTERRUPT_DECLINE_REASON = "User declined the request.";
63
+ function interruptResumeDecline(reason = INTERRUPT_DECLINE_REASON) {
64
+ return { action: "reject", reason };
57
65
  }
58
66
  function useApprovalResumeLock(options) {
59
- const { requestId, isRunning, sendResume, pendingResumeGraceMs } = options;
67
+ const { requestId, isRunning, sendResume, pendingResumeGraceMs, onResumeRejected } = options;
60
68
  const [pending, setPending] = react.useState(false);
61
69
  const prevRequestIdRef = react.useRef(requestId);
62
70
  if (prevRequestIdRef.current !== requestId) {
@@ -87,14 +95,28 @@ function useApprovalResumeLock(options) {
87
95
  }, [pending, requestId, pendingResumeGraceMs]);
88
96
  const sendResumeRef = react.useRef(sendResume);
89
97
  sendResumeRef.current = sendResume;
98
+ const onResumeRejectedRef = react.useRef(onResumeRejected);
99
+ onResumeRejectedRef.current = onResumeRejected;
100
+ const currentRequestIdRef = react.useRef(requestId);
101
+ currentRequestIdRef.current = requestId;
90
102
  const pendingRef = react.useRef(pending);
91
103
  pendingRef.current = pending;
92
104
  const resume = react.useCallback(
93
105
  (value) => {
94
- if (requestId === void 0 || pendingRef.current) return;
106
+ if (requestId === void 0 || pendingRef.current) return void 0;
95
107
  setPending(true);
96
108
  pendingRef.current = true;
97
- sendResumeRef.current(requestId, value);
109
+ const dispatched = sendResumeRef.current(requestId, value);
110
+ if (!(dispatched instanceof Promise)) return void 0;
111
+ dispatched.then(void 0, (error) => {
112
+ var _a;
113
+ const current = currentRequestIdRef.current;
114
+ if (current !== void 0 && current !== requestId) return;
115
+ setPending(false);
116
+ pendingRef.current = false;
117
+ (_a = onResumeRejectedRef.current) == null ? void 0 : _a.call(onResumeRejectedRef, error, requestId);
118
+ });
119
+ return dispatched;
98
120
  },
99
121
  [requestId]
100
122
  );
@@ -103,13 +125,15 @@ function useApprovalResumeLock(options) {
103
125
  exports.DEFAULT_INTERRUPT_PAUSED_MESSAGE = DEFAULT_INTERRUPT_PAUSED_MESSAGE;
104
126
  exports.HITL_PENDING_RESUME_GRACE_MS = HITL_PENDING_RESUME_GRACE_MS;
105
127
  exports.HITL_REJECT_REASON = HITL_REJECT_REASON;
128
+ exports.INTERRUPT_DECLINE_REASON = INTERRUPT_DECLINE_REASON;
106
129
  exports.asHitlApproval = asHitlApproval;
107
130
  exports.findAbandonedInterrupt = findAbandonedInterrupt;
108
131
  exports.hitlResumeApprove = hitlResumeApprove;
109
- exports.hitlResumeContinue = hitlResumeContinue;
110
132
  exports.hitlResumeReject = hitlResumeReject;
133
+ exports.interruptResumeDecline = interruptResumeDecline;
111
134
  exports.isApprovalCardInterrupt = isApprovalCardInterrupt;
112
135
  exports.isPendingInterrupt = isPendingInterrupt;
136
+ exports.isSystemPauseInterrupt = isSystemPauseInterrupt;
113
137
  exports.pendingActionRequests = pendingActionRequests;
114
138
  exports.readAbandonedInputRequests = readAbandonedInputRequests;
115
139
  exports.readInterruptMessage = readInterruptMessage;
@@ -1 +1 @@
1
- {"version":3,"file":"interrupts.cjs","sources":["../../../../../agent-runtime/src/interrupts.ts"],"sourcesContent":["/**\n * Human-in-the-loop interrupt primitives shared by every statewire chat\n * surface (Olympus, the React SDK, the Office add-in, the Chrome extension,\n * mobile).\n *\n * The agora deep-agent runtime parks a gated run on an `interrupt` input\n * request whose payload carries the Athena `hitl_approval` shape\n * (`athena_deep_agent/hitl_bridge.py`). A surface answers it with\n * `{ type: 'run/input', requestId, response: { type: 'resume', value } }` —\n * the exact command the backend's resume path consumes. This module holds the\n * payload contract (narrowing, resume values) and the shared resume-lock state\n * machine; rendering stays with each surface.\n *\n * Deliberately parameterized rather than self-wiring: these helpers never call\n * `useStatewireRuns`/`useAuiState` themselves. Each surface reads its own\n * statewire hooks (which its tests already mock) and passes the values in, so\n * the shared semantics cannot drift while per-surface wiring stays testable.\n */\n\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { hasClientToolInterruptSource } from './client-tools';\nimport { readDeepAgentStreamValues } from './statewire-state';\n\n/** Human-in-the-loop approval payload the agora deep-agent runtime parks a run\n * on (shaped by `athena_deep_agent/hitl_bridge.py`). */\nexport interface HitlApprovalPayload {\n source: string;\n type: string;\n message: string;\n /** Risk headline/facts/warnings, when the host includes them. */\n approval_context?: unknown;\n context?: {\n tool_name?: string;\n tool_id?: string;\n tool_call_id?: string;\n tool_args?: Record<string, unknown>;\n pending_action_count?: number;\n approval_context?: unknown;\n /**\n * Every pending gated call (name + args), in decision order. Absent on\n * older backends — fall back to the single `tool_name`/`tool_args` pair.\n * Per-dispatch payloads also carry each lane's `tool_call_id` (the lane\n * card key) and `interrupt_id` (the lane resume key).\n */\n action_requests?: {\n name?: string;\n args?: Record<string, unknown>;\n tool_call_id?: string;\n interrupt_id?: string;\n }[];\n /**\n * True when each dispatch parked on its own interrupt: decisions submit\n * per lane via `lane_decisions` and each approved lane starts immediately,\n * instead of the legacy batch where one resume must answer every lane.\n */\n per_dispatch?: boolean;\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** A pending `interrupt` input request; a present `response` key marks it answered. */\nexport function isPendingInterrupt(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n return request.type === 'interrupt' && !('response' in request);\n}\n\n/**\n * A pending interrupt an approval card may answer. Client-tool parks are\n * excluded — the client-tool bridge owns those, and a generic Continue resume\n * would corrupt the parked tool call. The source check is deliberately loose\n * (any payload stamped with the client-tool source) so a skewed deploy can\n * never route one here.\n */\nexport function isApprovalCardInterrupt(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n if (!isPendingInterrupt(request)) return false;\n return !hasClientToolInterruptSource((request as { payload?: unknown }).payload);\n}\n\n/** Narrow an interrupt payload to the Athena HITL approval shape. */\nexport function asHitlApproval(value: unknown): HitlApprovalPayload | null {\n if (!isRecord(value)) return null;\n return value.type === 'hitl' &&\n value.source === 'hitl_approval' &&\n typeof value.message === 'string'\n ? (value as unknown as HitlApprovalPayload)\n : null;\n}\n\n/** Default paused-run copy when an interrupt payload carries no message. */\nexport const DEFAULT_INTERRUPT_PAUSED_MESSAGE =\n 'The agent is paused and waiting for input.';\n\n/** Best-effort human-readable message from any interrupt payload. */\nexport function readInterruptMessage(\n value: unknown,\n fallback: string = DEFAULT_INTERRUPT_PAUSED_MESSAGE,\n): string {\n if (isRecord(value) && typeof value.message === 'string' && value.message.length > 0) {\n return value.message;\n }\n return fallback;\n}\n\nexport interface PendingActionRequest {\n name: string;\n args: Record<string, unknown>;\n /** The lane's `task` tool-call id (present on per-dispatch payloads). */\n toolCallId?: string;\n /** The lane's langgraph interrupt id — the `lane_decisions` resume key. */\n interruptId?: string;\n}\n\n/**\n * Every gated call this approval covers, in decision order. One resume answers\n * ALL of them, so every entry must stay reviewable somewhere. Falls back to\n * the single first-call fields on older backends without `action_requests`.\n */\nexport function pendingActionRequests(hitl: HitlApprovalPayload | null): PendingActionRequest[] {\n const context = hitl?.context;\n if (!context) return [];\n // The payload crosses the wire with only its top-level shape narrowed\n // (`asHitlApproval`), so the nested collection must be validated before any\n // array/property access: a malformed host value must degrade to \"no\n // reviewable entries\", never crash the approval surface.\n const requests: Record<string, unknown>[] = Array.isArray(context.action_requests)\n ? context.action_requests.filter(isRecord)\n : context.tool_name && context.tool_args\n ? [{ name: context.tool_name, args: context.tool_args }]\n : [];\n return requests.map((request) => ({\n name: typeof request.name === 'string' ? request.name : 'tool',\n args: isRecord(request.args) ? request.args : {},\n ...(typeof request.tool_call_id === 'string' && { toolCallId: request.tool_call_id }),\n ...(typeof request.interrupt_id === 'string' && { interruptId: request.interrupt_id }),\n }));\n}\n\n/** An interrupt a non-resume dispatch dropped, published by the host. */\nexport interface AbandonedInputRequest {\n id: string;\n type: string;\n}\n\n/**\n * `useStatewireState` selector body for the host-published abandoned input\n * requests. Reads through {@link readDeepAgentStreamValues} so the selector is\n * safe on the pre-attach (`undefined`) wire state, and returns the raw array\n * reference so the subscription stays shallow-stable — derive (`.find`) on the\n * result, never inside the selector.\n */\nexport function readAbandonedInputRequests(\n state: unknown,\n): readonly AbandonedInputRequest[] | undefined {\n const abandoned = readDeepAgentStreamValues(state).abandonedInputRequests;\n // Wire value: anything non-array (including a malformed host publication)\n // reads as \"none\" rather than crashing the consuming surface. Identity-\n // preserving for the valid case, so it stays selector-safe.\n return Array.isArray(abandoned)\n ? (abandoned as readonly AbandonedInputRequest[])\n : undefined;\n}\n\n/** The abandoned `interrupt` entry, if any — the \"approval was cancelled\" notice. */\nexport function findAbandonedInterrupt(\n abandoned: readonly AbandonedInputRequest[] | undefined,\n): AbandonedInputRequest | undefined {\n return abandoned?.find(\n (request) => isRecord(request) && request.type === 'interrupt' && typeof request.id === 'string',\n );\n}\n\n/** Canonical reason string carried by a user rejection. */\nexport const HITL_REJECT_REASON = 'User rejected the action.';\n\n/**\n * Default {@link UseApprovalResumeLockOptions.pendingResumeGraceMs} for every\n * approval surface: how long a clicked decision waits for its resume run to\n * start before the actions unlock again. Covers a `run/input` the host\n * rejected or a disconnected transport swallowed before any run began — the\n * one failure neither identity-change nor observed-run recovery can detect.\n */\nexport const HITL_PENDING_RESUME_GRACE_MS = 15_000;\n\n/** `run/input` resume value approving the gated call(s). */\nexport function hitlResumeApprove(): Record<string, string> {\n return { action: 'approve' };\n}\n\n/** `run/input` resume value rejecting the gated call(s). */\nexport function hitlResumeReject(reason: string = HITL_REJECT_REASON): Record<string, string> {\n return { action: 'reject', reason };\n}\n\n/** `run/input` resume value for a non-HITL park (generic Continue). */\nexport function hitlResumeContinue(): Record<string, string> {\n return { action: 'continue' };\n}\n\nexport interface UseApprovalResumeLockOptions<TValue> {\n /** The pending interrupt's request id (`undefined` when nothing is parked). */\n requestId: string | undefined;\n /** The surface's thread `isRunning` read (mockable per surface). */\n isRunning: boolean;\n /** Dispatch the `run/input` resume for `requestId` on the surface's transport. */\n sendResume: (requestId: string, value: TValue) => void;\n /**\n * Re-enable the actions when no resume run started within this window —\n * covers a `run/input` that was rejected or lost before any run began, so\n * neither reset path below can fire. Omit to keep the pre-existing surface\n * behavior of staying locked until a resume run is observed.\n */\n pendingResumeGraceMs?: number;\n}\n\nexport interface ApprovalResumeLock<TValue> {\n /** True after a resume until the request identity changes (or recovery fires). */\n pending: boolean;\n /** Send the resume value, locking the actions. No-op while pending or unparked. */\n resume: (value: TValue) => void;\n}\n\n/**\n * Optimistic action lock shared by every approval surface: disable the\n * decision buttons the moment one is clicked, until the request identity\n * changes (it clears after the resume, or a fresh interrupt arrives). This\n * closes the double-click → duplicate-resume window.\n *\n * Recovery paths, in order of specificity:\n * 1. Identity change — the resume landed (or a new interrupt replaced the\n * request). Reset during render via React's \"previous value\" pattern.\n * 2. A resume run was observed starting and then settling with the request\n * still pending (failed/cancelled resume) — clear the lock so the user can\n * answer again.\n * 3. Optional grace window ({@link UseApprovalResumeLockOptions.pendingResumeGraceMs})\n * for a `run/input` that never started a run at all.\n */\nexport function useApprovalResumeLock<TValue = Record<string, string>>(\n options: UseApprovalResumeLockOptions<TValue>,\n): ApprovalResumeLock<TValue> {\n const { requestId, isRunning, sendResume, pendingResumeGraceMs } = options;\n\n const [pending, setPending] = useState(false);\n const prevRequestIdRef = useRef(requestId);\n if (prevRequestIdRef.current !== requestId) {\n prevRequestIdRef.current = requestId;\n if (pending) setPending(false);\n }\n\n // Track the resume run we kicked off (isRunning observed true→false while\n // pending) and clear `pending` when it ends with the request unresolved.\n const sawResumeRunRef = useRef(false);\n useEffect(() => {\n if (!pending) {\n sawResumeRunRef.current = false;\n return;\n }\n if (isRunning) {\n sawResumeRunRef.current = true;\n return;\n }\n if (sawResumeRunRef.current) {\n sawResumeRunRef.current = false;\n setPending(false);\n }\n }, [pending, isRunning]);\n\n // Pre-start failure recovery: re-enable after the grace window when no\n // resume run began; answering the still-parked request again is exactly\n // what the user needs to be able to do.\n useEffect(() => {\n if (!pending || pendingResumeGraceMs === undefined) return;\n const timer = setTimeout(() => {\n if (!sawResumeRunRef.current) setPending(false);\n }, pendingResumeGraceMs);\n return () => clearTimeout(timer);\n }, [pending, requestId, pendingResumeGraceMs]);\n\n const sendResumeRef = useRef(sendResume);\n sendResumeRef.current = sendResume;\n const pendingRef = useRef(pending);\n pendingRef.current = pending;\n\n const resume = useCallback(\n (value: TValue) => {\n if (requestId === undefined || pendingRef.current) return;\n setPending(true);\n pendingRef.current = true;\n sendResumeRef.current(requestId, value);\n },\n [requestId],\n );\n\n return { pending, resume };\n}\n"],"names":["hasClientToolInterruptSource","readDeepAgentStreamValues","useState","useRef","useEffect","useCallback"],"mappings":"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"}
1
+ {"version":3,"file":"interrupts.cjs","sources":["../../../../../agent-runtime/src/interrupts.ts"],"sourcesContent":["/**\n * Human-in-the-loop interrupt primitives shared by every statewire chat\n * surface (Olympus, the React SDK, the Office add-in, the Chrome extension,\n * mobile).\n *\n * The agora deep-agent runtime parks a gated run on an `interrupt` input\n * request whose payload carries the Athena `hitl_approval` shape\n * (`athena_deep_agent/hitl_bridge.py`). A surface answers it with\n * `{ type: 'run/input', requestId, response: { type: 'resume', value } }` —\n * the exact command the backend's resume path consumes. This module holds the\n * payload contract (narrowing, resume values) and the shared resume-lock state\n * machine; rendering stays with each surface.\n *\n * Deliberately parameterized rather than self-wiring: these helpers never call\n * `useStatewireRuns`/`useAuiState` themselves. Each surface reads its own\n * statewire hooks (which its tests already mock) and passes the values in, so\n * the shared semantics cannot drift while per-surface wiring stays testable.\n */\n\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { hasClientToolInterruptSource } from './client-tools';\nimport { readDeepAgentStreamValues } from './statewire-state';\n\n/** Human-in-the-loop approval payload the agora deep-agent runtime parks a run\n * on (shaped by `athena_deep_agent/hitl_bridge.py`). */\nexport interface HitlApprovalPayload {\n source: string;\n type: string;\n message: string;\n /** Risk headline/facts/warnings, when the host includes them. */\n approval_context?: unknown;\n context?: {\n tool_name?: string;\n tool_id?: string;\n tool_call_id?: string;\n tool_args?: Record<string, unknown>;\n pending_action_count?: number;\n approval_context?: unknown;\n /**\n * Every pending gated call (name + args), in decision order. Absent on\n * older backends — fall back to the single `tool_name`/`tool_args` pair.\n * Per-dispatch payloads also carry each lane's `tool_call_id` (the lane\n * card key) and `interrupt_id` (the lane resume key).\n */\n action_requests?: {\n name?: string;\n args?: Record<string, unknown>;\n tool_call_id?: string;\n interrupt_id?: string;\n }[];\n /**\n * True when each dispatch parked on its own interrupt: decisions submit\n * per lane via `lane_decisions` and each approved lane starts immediately,\n * instead of the legacy batch where one resume must answer every lane.\n */\n per_dispatch?: boolean;\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** A pending `interrupt` input request; a present `response` key marks it answered. */\nexport function isPendingInterrupt(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n return request.type === 'interrupt' && !('response' in request);\n}\n\n/**\n * Interrupt sources that were system pauses before the host stamped\n * `requires_user_action: false` on them (a background deep-research wait). Kept\n * only for the deploy skew between an older agora and a newer client; delete\n * once every deployment's raiser stamps the flag.\n */\nconst LEGACY_SYSTEM_PAUSE_INTERRUPT_SOURCES: ReadonlySet<string> = new Set(['deep_research_pending']);\n\n/**\n * A park nobody answers: the agent is still working (a background research\n * wait) and the backend resumes it itself. The raiser stamps\n * `requires_user_action: false`; older raisers are recognised by source.\n */\nexport function isSystemPauseInterrupt(payload: unknown): boolean {\n if (!isRecord(payload)) return false;\n if (payload.requires_user_action === false) return true;\n return (\n typeof payload.source === 'string' && LEGACY_SYSTEM_PAUSE_INTERRUPT_SOURCES.has(payload.source)\n );\n}\n\n/**\n * A pending interrupt an approval card may answer. Two kinds are excluded —\n * client-tool parks (the client-tool bridge owns those, and a generic resume\n * would corrupt the parked tool call) and system pauses (nothing to answer; the\n * backend resumes them). The source checks are deliberately loose so a skewed\n * deploy can never route one here.\n */\nexport function isApprovalCardInterrupt(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n if (!isPendingInterrupt(request)) return false;\n const payload = (request as { payload?: unknown }).payload;\n return !hasClientToolInterruptSource(payload) && !isSystemPauseInterrupt(payload);\n}\n\n/** Narrow an interrupt payload to the Athena HITL approval shape. */\nexport function asHitlApproval(value: unknown): HitlApprovalPayload | null {\n if (!isRecord(value)) return null;\n return value.type === 'hitl' &&\n value.source === 'hitl_approval' &&\n typeof value.message === 'string'\n ? (value as unknown as HitlApprovalPayload)\n : null;\n}\n\n/** Default paused-run copy when an interrupt payload carries no message. */\nexport const DEFAULT_INTERRUPT_PAUSED_MESSAGE =\n 'The agent is paused and waiting for input.';\n\n/** Best-effort human-readable message from any interrupt payload. */\nexport function readInterruptMessage(\n value: unknown,\n fallback: string = DEFAULT_INTERRUPT_PAUSED_MESSAGE,\n): string {\n if (isRecord(value) && typeof value.message === 'string' && value.message.length > 0) {\n return value.message;\n }\n return fallback;\n}\n\nexport interface PendingActionRequest {\n name: string;\n args: Record<string, unknown>;\n /** The lane's `task` tool-call id (present on per-dispatch payloads). */\n toolCallId?: string;\n /** The lane's langgraph interrupt id — the `lane_decisions` resume key. */\n interruptId?: string;\n}\n\n/**\n * Every gated call this approval covers, in decision order. One resume answers\n * ALL of them, so every entry must stay reviewable somewhere. Falls back to\n * the single first-call fields on older backends without `action_requests`.\n */\nexport function pendingActionRequests(hitl: HitlApprovalPayload | null): PendingActionRequest[] {\n const context = hitl?.context;\n if (!context) return [];\n // The payload crosses the wire with only its top-level shape narrowed\n // (`asHitlApproval`), so the nested collection must be validated before any\n // array/property access: a malformed host value must degrade to \"no\n // reviewable entries\", never crash the approval surface.\n const requests: Record<string, unknown>[] = Array.isArray(context.action_requests)\n ? context.action_requests.filter(isRecord)\n : context.tool_name && context.tool_args\n ? [{ name: context.tool_name, args: context.tool_args }]\n : [];\n return requests.map((request) => ({\n name: typeof request.name === 'string' ? request.name : 'tool',\n args: isRecord(request.args) ? request.args : {},\n ...(typeof request.tool_call_id === 'string' && { toolCallId: request.tool_call_id }),\n ...(typeof request.interrupt_id === 'string' && { interruptId: request.interrupt_id }),\n }));\n}\n\n/** An interrupt a non-resume dispatch dropped, published by the host. */\nexport interface AbandonedInputRequest {\n id: string;\n type: string;\n}\n\n/**\n * `useStatewireState` selector body for the host-published abandoned input\n * requests. Reads through {@link readDeepAgentStreamValues} so the selector is\n * safe on the pre-attach (`undefined`) wire state, and returns the raw array\n * reference so the subscription stays shallow-stable — derive (`.find`) on the\n * result, never inside the selector.\n */\nexport function readAbandonedInputRequests(\n state: unknown,\n): readonly AbandonedInputRequest[] | undefined {\n const abandoned = readDeepAgentStreamValues(state).abandonedInputRequests;\n // Wire value: anything non-array (including a malformed host publication)\n // reads as \"none\" rather than crashing the consuming surface. Identity-\n // preserving for the valid case, so it stays selector-safe.\n return Array.isArray(abandoned)\n ? (abandoned as readonly AbandonedInputRequest[])\n : undefined;\n}\n\n/** The abandoned `interrupt` entry, if any — the \"approval was cancelled\" notice. */\nexport function findAbandonedInterrupt(\n abandoned: readonly AbandonedInputRequest[] | undefined,\n): AbandonedInputRequest | undefined {\n return abandoned?.find(\n (request) => isRecord(request) && request.type === 'interrupt' && typeof request.id === 'string',\n );\n}\n\n/** Canonical reason string carried by a user rejection. */\nexport const HITL_REJECT_REASON = 'User rejected the action.';\n\n/**\n * Default {@link UseApprovalResumeLockOptions.pendingResumeGraceMs} for every\n * approval surface: how long a clicked decision waits for its resume run to\n * start before the actions unlock again. Covers a `run/input` the host\n * rejected or a disconnected transport swallowed before any run began — the\n * one failure neither identity-change nor observed-run recovery can detect.\n */\nexport const HITL_PENDING_RESUME_GRACE_MS = 15_000;\n\n/** `run/input` resume value approving the gated call(s). */\nexport function hitlResumeApprove(): Record<string, string> {\n return { action: 'approve' };\n}\n\n/** `run/input` resume value rejecting the gated call(s). */\nexport function hitlResumeReject(reason: string = HITL_REJECT_REASON): Record<string, string> {\n return { action: 'reject', reason };\n}\n\n/** Canonical reason string carried by a user declining a non-HITL park. */\nexport const INTERRUPT_DECLINE_REASON = 'User declined the request.';\n\n/**\n * `run/input` resume value declining a non-HITL park. `{ action: 'reject' }`\n * is the one value every typed raiser treats as a clean user decline\n * (`FormCancelledError` / `ToolRejectedError`); a generic `continue` would be\n * rejected by their resume schemas and surface as a tool error the agent then\n * improvises around.\n */\nexport function interruptResumeDecline(\n reason: string = INTERRUPT_DECLINE_REASON,\n): Record<string, string> {\n return { action: 'reject', reason };\n}\n\nexport interface UseApprovalResumeLockOptions<TValue> {\n /** The pending interrupt's request id (`undefined` when nothing is parked). */\n requestId: string | undefined;\n /** The surface's thread `isRunning` read (mockable per surface). */\n isRunning: boolean;\n /**\n * Dispatch the `run/input` resume for `requestId` on the surface's transport.\n * Return the dispatch promise so a refused or lost command releases the lock\n * at once and reaches `onResumeRejected`; a `void` return leaves the\n * recovery paths below as the only release.\n */\n sendResume: (requestId: string, value: TValue) => void | Promise<unknown>;\n /**\n * The dispatch `sendResume` returned rejected — the host refused the\n * `run/input` or the transport lost it. The lock is already released when\n * this fires; the surface tells the user, since the request is back on\n * screen with nothing else saying why.\n */\n onResumeRejected?: (error: unknown, requestId: string) => void;\n /**\n * Re-enable the actions when no resume run started within this window —\n * covers a `run/input` that was rejected or lost before any run began, so\n * neither reset path below can fire. Omit to keep the pre-existing surface\n * behavior of staying locked until a resume run is observed.\n */\n pendingResumeGraceMs?: number;\n}\n\nexport interface ApprovalResumeLock<TValue> {\n /** True after a resume until the request identity changes (or recovery fires). */\n pending: boolean;\n /**\n * Send the resume value, locking the actions. No-op (returns `undefined`)\n * while pending or unparked. Returns the dispatch promise when `sendResume`\n * produced one, so a surface can hold its own state until the host settles.\n */\n resume: (value: TValue) => Promise<unknown> | undefined;\n}\n\n/**\n * Optimistic action lock shared by every approval surface: disable the\n * decision buttons the moment one is clicked, until the request identity\n * changes (it clears after the resume, or a fresh interrupt arrives). This\n * closes the double-click → duplicate-resume window.\n *\n * Recovery paths, in order of specificity:\n * 0. The dispatch itself rejected (a `sendResume` that returned a promise) —\n * released at once, and reported through `onResumeRejected`.\n * 1. Identity change — the resume landed (or a new interrupt replaced the\n * request). Reset during render via React's \"previous value\" pattern.\n * 2. A resume run was observed starting and then settling with the request\n * still pending (failed/cancelled resume) — clear the lock so the user can\n * answer again.\n * 3. Optional grace window ({@link UseApprovalResumeLockOptions.pendingResumeGraceMs})\n * for a `run/input` that never started a run at all.\n */\nexport function useApprovalResumeLock<TValue = Record<string, string>>(\n options: UseApprovalResumeLockOptions<TValue>,\n): ApprovalResumeLock<TValue> {\n const { requestId, isRunning, sendResume, pendingResumeGraceMs, onResumeRejected } = options;\n\n const [pending, setPending] = useState(false);\n const prevRequestIdRef = useRef(requestId);\n if (prevRequestIdRef.current !== requestId) {\n prevRequestIdRef.current = requestId;\n if (pending) setPending(false);\n }\n\n // Track the resume run we kicked off (isRunning observed true→false while\n // pending) and clear `pending` when it ends with the request unresolved.\n const sawResumeRunRef = useRef(false);\n useEffect(() => {\n if (!pending) {\n sawResumeRunRef.current = false;\n return;\n }\n if (isRunning) {\n sawResumeRunRef.current = true;\n return;\n }\n if (sawResumeRunRef.current) {\n sawResumeRunRef.current = false;\n setPending(false);\n }\n }, [pending, isRunning]);\n\n // Pre-start failure recovery: re-enable after the grace window when no\n // resume run began; answering the still-parked request again is exactly\n // what the user needs to be able to do.\n useEffect(() => {\n if (!pending || pendingResumeGraceMs === undefined) return;\n const timer = setTimeout(() => {\n if (!sawResumeRunRef.current) setPending(false);\n }, pendingResumeGraceMs);\n return () => clearTimeout(timer);\n }, [pending, requestId, pendingResumeGraceMs]);\n\n const sendResumeRef = useRef(sendResume);\n sendResumeRef.current = sendResume;\n const onResumeRejectedRef = useRef(onResumeRejected);\n onResumeRejectedRef.current = onResumeRejected;\n const currentRequestIdRef = useRef(requestId);\n currentRequestIdRef.current = requestId;\n const pendingRef = useRef(pending);\n pendingRef.current = pending;\n\n const resume = useCallback(\n (value: TValue): Promise<unknown> | undefined => {\n if (requestId === undefined || pendingRef.current) return undefined;\n setPending(true);\n pendingRef.current = true;\n const dispatched: unknown = sendResumeRef.current(requestId, value);\n if (!(dispatched instanceof Promise)) return undefined;\n dispatched.then(undefined, (error: unknown) => {\n // A late refusal for a request the lock has since moved past (the\n // park was re-keyed, or a newer answer is in flight) must neither\n // release that answer's lock nor be reported as a failure of it.\n const current = currentRequestIdRef.current;\n if (current !== undefined && current !== requestId) return;\n setPending(false);\n pendingRef.current = false;\n onResumeRejectedRef.current?.(error, requestId);\n });\n return dispatched;\n },\n [requestId],\n );\n\n return { pending, resume };\n}\n"],"names":["hasClientToolInterruptSource","readDeepAgentStreamValues","useState","useRef","useEffect","useCallback"],"mappings":"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"}
@@ -7,9 +7,16 @@ function isRecord(value) {
7
7
  function isPendingInterrupt(request) {
8
8
  return request.type === "interrupt" && !("response" in request);
9
9
  }
10
+ const LEGACY_SYSTEM_PAUSE_INTERRUPT_SOURCES = /* @__PURE__ */ new Set(["deep_research_pending"]);
11
+ function isSystemPauseInterrupt(payload) {
12
+ if (!isRecord(payload)) return false;
13
+ if (payload.requires_user_action === false) return true;
14
+ return typeof payload.source === "string" && LEGACY_SYSTEM_PAUSE_INTERRUPT_SOURCES.has(payload.source);
15
+ }
10
16
  function isApprovalCardInterrupt(request) {
11
17
  if (!isPendingInterrupt(request)) return false;
12
- return !hasClientToolInterruptSource(request.payload);
18
+ const payload = request.payload;
19
+ return !hasClientToolInterruptSource(payload) && !isSystemPauseInterrupt(payload);
13
20
  }
14
21
  function asHitlApproval(value) {
15
22
  if (!isRecord(value)) return null;
@@ -50,11 +57,12 @@ function hitlResumeApprove() {
50
57
  function hitlResumeReject(reason = HITL_REJECT_REASON) {
51
58
  return { action: "reject", reason };
52
59
  }
53
- function hitlResumeContinue() {
54
- return { action: "continue" };
60
+ const INTERRUPT_DECLINE_REASON = "User declined the request.";
61
+ function interruptResumeDecline(reason = INTERRUPT_DECLINE_REASON) {
62
+ return { action: "reject", reason };
55
63
  }
56
64
  function useApprovalResumeLock(options) {
57
- const { requestId, isRunning, sendResume, pendingResumeGraceMs } = options;
65
+ const { requestId, isRunning, sendResume, pendingResumeGraceMs, onResumeRejected } = options;
58
66
  const [pending, setPending] = useState(false);
59
67
  const prevRequestIdRef = useRef(requestId);
60
68
  if (prevRequestIdRef.current !== requestId) {
@@ -85,14 +93,28 @@ function useApprovalResumeLock(options) {
85
93
  }, [pending, requestId, pendingResumeGraceMs]);
86
94
  const sendResumeRef = useRef(sendResume);
87
95
  sendResumeRef.current = sendResume;
96
+ const onResumeRejectedRef = useRef(onResumeRejected);
97
+ onResumeRejectedRef.current = onResumeRejected;
98
+ const currentRequestIdRef = useRef(requestId);
99
+ currentRequestIdRef.current = requestId;
88
100
  const pendingRef = useRef(pending);
89
101
  pendingRef.current = pending;
90
102
  const resume = useCallback(
91
103
  (value) => {
92
- if (requestId === void 0 || pendingRef.current) return;
104
+ if (requestId === void 0 || pendingRef.current) return void 0;
93
105
  setPending(true);
94
106
  pendingRef.current = true;
95
- sendResumeRef.current(requestId, value);
107
+ const dispatched = sendResumeRef.current(requestId, value);
108
+ if (!(dispatched instanceof Promise)) return void 0;
109
+ dispatched.then(void 0, (error) => {
110
+ var _a;
111
+ const current = currentRequestIdRef.current;
112
+ if (current !== void 0 && current !== requestId) return;
113
+ setPending(false);
114
+ pendingRef.current = false;
115
+ (_a = onResumeRejectedRef.current) == null ? void 0 : _a.call(onResumeRejectedRef, error, requestId);
116
+ });
117
+ return dispatched;
96
118
  },
97
119
  [requestId]
98
120
  );
@@ -102,13 +124,15 @@ export {
102
124
  DEFAULT_INTERRUPT_PAUSED_MESSAGE,
103
125
  HITL_PENDING_RESUME_GRACE_MS,
104
126
  HITL_REJECT_REASON,
127
+ INTERRUPT_DECLINE_REASON,
105
128
  asHitlApproval,
106
129
  findAbandonedInterrupt,
107
130
  hitlResumeApprove,
108
- hitlResumeContinue,
109
131
  hitlResumeReject,
132
+ interruptResumeDecline,
110
133
  isApprovalCardInterrupt,
111
134
  isPendingInterrupt,
135
+ isSystemPauseInterrupt,
112
136
  pendingActionRequests,
113
137
  readAbandonedInputRequests,
114
138
  readInterruptMessage,
@@ -1 +1 @@
1
- {"version":3,"file":"interrupts.js","sources":["../../../../../agent-runtime/src/interrupts.ts"],"sourcesContent":["/**\n * Human-in-the-loop interrupt primitives shared by every statewire chat\n * surface (Olympus, the React SDK, the Office add-in, the Chrome extension,\n * mobile).\n *\n * The agora deep-agent runtime parks a gated run on an `interrupt` input\n * request whose payload carries the Athena `hitl_approval` shape\n * (`athena_deep_agent/hitl_bridge.py`). A surface answers it with\n * `{ type: 'run/input', requestId, response: { type: 'resume', value } }` —\n * the exact command the backend's resume path consumes. This module holds the\n * payload contract (narrowing, resume values) and the shared resume-lock state\n * machine; rendering stays with each surface.\n *\n * Deliberately parameterized rather than self-wiring: these helpers never call\n * `useStatewireRuns`/`useAuiState` themselves. Each surface reads its own\n * statewire hooks (which its tests already mock) and passes the values in, so\n * the shared semantics cannot drift while per-surface wiring stays testable.\n */\n\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { hasClientToolInterruptSource } from './client-tools';\nimport { readDeepAgentStreamValues } from './statewire-state';\n\n/** Human-in-the-loop approval payload the agora deep-agent runtime parks a run\n * on (shaped by `athena_deep_agent/hitl_bridge.py`). */\nexport interface HitlApprovalPayload {\n source: string;\n type: string;\n message: string;\n /** Risk headline/facts/warnings, when the host includes them. */\n approval_context?: unknown;\n context?: {\n tool_name?: string;\n tool_id?: string;\n tool_call_id?: string;\n tool_args?: Record<string, unknown>;\n pending_action_count?: number;\n approval_context?: unknown;\n /**\n * Every pending gated call (name + args), in decision order. Absent on\n * older backends — fall back to the single `tool_name`/`tool_args` pair.\n * Per-dispatch payloads also carry each lane's `tool_call_id` (the lane\n * card key) and `interrupt_id` (the lane resume key).\n */\n action_requests?: {\n name?: string;\n args?: Record<string, unknown>;\n tool_call_id?: string;\n interrupt_id?: string;\n }[];\n /**\n * True when each dispatch parked on its own interrupt: decisions submit\n * per lane via `lane_decisions` and each approved lane starts immediately,\n * instead of the legacy batch where one resume must answer every lane.\n */\n per_dispatch?: boolean;\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** A pending `interrupt` input request; a present `response` key marks it answered. */\nexport function isPendingInterrupt(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n return request.type === 'interrupt' && !('response' in request);\n}\n\n/**\n * A pending interrupt an approval card may answer. Client-tool parks are\n * excluded — the client-tool bridge owns those, and a generic Continue resume\n * would corrupt the parked tool call. The source check is deliberately loose\n * (any payload stamped with the client-tool source) so a skewed deploy can\n * never route one here.\n */\nexport function isApprovalCardInterrupt(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n if (!isPendingInterrupt(request)) return false;\n return !hasClientToolInterruptSource((request as { payload?: unknown }).payload);\n}\n\n/** Narrow an interrupt payload to the Athena HITL approval shape. */\nexport function asHitlApproval(value: unknown): HitlApprovalPayload | null {\n if (!isRecord(value)) return null;\n return value.type === 'hitl' &&\n value.source === 'hitl_approval' &&\n typeof value.message === 'string'\n ? (value as unknown as HitlApprovalPayload)\n : null;\n}\n\n/** Default paused-run copy when an interrupt payload carries no message. */\nexport const DEFAULT_INTERRUPT_PAUSED_MESSAGE =\n 'The agent is paused and waiting for input.';\n\n/** Best-effort human-readable message from any interrupt payload. */\nexport function readInterruptMessage(\n value: unknown,\n fallback: string = DEFAULT_INTERRUPT_PAUSED_MESSAGE,\n): string {\n if (isRecord(value) && typeof value.message === 'string' && value.message.length > 0) {\n return value.message;\n }\n return fallback;\n}\n\nexport interface PendingActionRequest {\n name: string;\n args: Record<string, unknown>;\n /** The lane's `task` tool-call id (present on per-dispatch payloads). */\n toolCallId?: string;\n /** The lane's langgraph interrupt id — the `lane_decisions` resume key. */\n interruptId?: string;\n}\n\n/**\n * Every gated call this approval covers, in decision order. One resume answers\n * ALL of them, so every entry must stay reviewable somewhere. Falls back to\n * the single first-call fields on older backends without `action_requests`.\n */\nexport function pendingActionRequests(hitl: HitlApprovalPayload | null): PendingActionRequest[] {\n const context = hitl?.context;\n if (!context) return [];\n // The payload crosses the wire with only its top-level shape narrowed\n // (`asHitlApproval`), so the nested collection must be validated before any\n // array/property access: a malformed host value must degrade to \"no\n // reviewable entries\", never crash the approval surface.\n const requests: Record<string, unknown>[] = Array.isArray(context.action_requests)\n ? context.action_requests.filter(isRecord)\n : context.tool_name && context.tool_args\n ? [{ name: context.tool_name, args: context.tool_args }]\n : [];\n return requests.map((request) => ({\n name: typeof request.name === 'string' ? request.name : 'tool',\n args: isRecord(request.args) ? request.args : {},\n ...(typeof request.tool_call_id === 'string' && { toolCallId: request.tool_call_id }),\n ...(typeof request.interrupt_id === 'string' && { interruptId: request.interrupt_id }),\n }));\n}\n\n/** An interrupt a non-resume dispatch dropped, published by the host. */\nexport interface AbandonedInputRequest {\n id: string;\n type: string;\n}\n\n/**\n * `useStatewireState` selector body for the host-published abandoned input\n * requests. Reads through {@link readDeepAgentStreamValues} so the selector is\n * safe on the pre-attach (`undefined`) wire state, and returns the raw array\n * reference so the subscription stays shallow-stable — derive (`.find`) on the\n * result, never inside the selector.\n */\nexport function readAbandonedInputRequests(\n state: unknown,\n): readonly AbandonedInputRequest[] | undefined {\n const abandoned = readDeepAgentStreamValues(state).abandonedInputRequests;\n // Wire value: anything non-array (including a malformed host publication)\n // reads as \"none\" rather than crashing the consuming surface. Identity-\n // preserving for the valid case, so it stays selector-safe.\n return Array.isArray(abandoned)\n ? (abandoned as readonly AbandonedInputRequest[])\n : undefined;\n}\n\n/** The abandoned `interrupt` entry, if any — the \"approval was cancelled\" notice. */\nexport function findAbandonedInterrupt(\n abandoned: readonly AbandonedInputRequest[] | undefined,\n): AbandonedInputRequest | undefined {\n return abandoned?.find(\n (request) => isRecord(request) && request.type === 'interrupt' && typeof request.id === 'string',\n );\n}\n\n/** Canonical reason string carried by a user rejection. */\nexport const HITL_REJECT_REASON = 'User rejected the action.';\n\n/**\n * Default {@link UseApprovalResumeLockOptions.pendingResumeGraceMs} for every\n * approval surface: how long a clicked decision waits for its resume run to\n * start before the actions unlock again. Covers a `run/input` the host\n * rejected or a disconnected transport swallowed before any run began — the\n * one failure neither identity-change nor observed-run recovery can detect.\n */\nexport const HITL_PENDING_RESUME_GRACE_MS = 15_000;\n\n/** `run/input` resume value approving the gated call(s). */\nexport function hitlResumeApprove(): Record<string, string> {\n return { action: 'approve' };\n}\n\n/** `run/input` resume value rejecting the gated call(s). */\nexport function hitlResumeReject(reason: string = HITL_REJECT_REASON): Record<string, string> {\n return { action: 'reject', reason };\n}\n\n/** `run/input` resume value for a non-HITL park (generic Continue). */\nexport function hitlResumeContinue(): Record<string, string> {\n return { action: 'continue' };\n}\n\nexport interface UseApprovalResumeLockOptions<TValue> {\n /** The pending interrupt's request id (`undefined` when nothing is parked). */\n requestId: string | undefined;\n /** The surface's thread `isRunning` read (mockable per surface). */\n isRunning: boolean;\n /** Dispatch the `run/input` resume for `requestId` on the surface's transport. */\n sendResume: (requestId: string, value: TValue) => void;\n /**\n * Re-enable the actions when no resume run started within this window —\n * covers a `run/input` that was rejected or lost before any run began, so\n * neither reset path below can fire. Omit to keep the pre-existing surface\n * behavior of staying locked until a resume run is observed.\n */\n pendingResumeGraceMs?: number;\n}\n\nexport interface ApprovalResumeLock<TValue> {\n /** True after a resume until the request identity changes (or recovery fires). */\n pending: boolean;\n /** Send the resume value, locking the actions. No-op while pending or unparked. */\n resume: (value: TValue) => void;\n}\n\n/**\n * Optimistic action lock shared by every approval surface: disable the\n * decision buttons the moment one is clicked, until the request identity\n * changes (it clears after the resume, or a fresh interrupt arrives). This\n * closes the double-click → duplicate-resume window.\n *\n * Recovery paths, in order of specificity:\n * 1. Identity change — the resume landed (or a new interrupt replaced the\n * request). Reset during render via React's \"previous value\" pattern.\n * 2. A resume run was observed starting and then settling with the request\n * still pending (failed/cancelled resume) — clear the lock so the user can\n * answer again.\n * 3. Optional grace window ({@link UseApprovalResumeLockOptions.pendingResumeGraceMs})\n * for a `run/input` that never started a run at all.\n */\nexport function useApprovalResumeLock<TValue = Record<string, string>>(\n options: UseApprovalResumeLockOptions<TValue>,\n): ApprovalResumeLock<TValue> {\n const { requestId, isRunning, sendResume, pendingResumeGraceMs } = options;\n\n const [pending, setPending] = useState(false);\n const prevRequestIdRef = useRef(requestId);\n if (prevRequestIdRef.current !== requestId) {\n prevRequestIdRef.current = requestId;\n if (pending) setPending(false);\n }\n\n // Track the resume run we kicked off (isRunning observed true→false while\n // pending) and clear `pending` when it ends with the request unresolved.\n const sawResumeRunRef = useRef(false);\n useEffect(() => {\n if (!pending) {\n sawResumeRunRef.current = false;\n return;\n }\n if (isRunning) {\n sawResumeRunRef.current = true;\n return;\n }\n if (sawResumeRunRef.current) {\n sawResumeRunRef.current = false;\n setPending(false);\n }\n }, [pending, isRunning]);\n\n // Pre-start failure recovery: re-enable after the grace window when no\n // resume run began; answering the still-parked request again is exactly\n // what the user needs to be able to do.\n useEffect(() => {\n if (!pending || pendingResumeGraceMs === undefined) return;\n const timer = setTimeout(() => {\n if (!sawResumeRunRef.current) setPending(false);\n }, pendingResumeGraceMs);\n return () => clearTimeout(timer);\n }, [pending, requestId, pendingResumeGraceMs]);\n\n const sendResumeRef = useRef(sendResume);\n sendResumeRef.current = sendResume;\n const pendingRef = useRef(pending);\n pendingRef.current = pending;\n\n const resume = useCallback(\n (value: TValue) => {\n if (requestId === undefined || pendingRef.current) return;\n setPending(true);\n pendingRef.current = true;\n sendResumeRef.current(requestId, value);\n },\n [requestId],\n );\n\n return { pending, resume };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"interrupts.js","sources":["../../../../../agent-runtime/src/interrupts.ts"],"sourcesContent":["/**\n * Human-in-the-loop interrupt primitives shared by every statewire chat\n * surface (Olympus, the React SDK, the Office add-in, the Chrome extension,\n * mobile).\n *\n * The agora deep-agent runtime parks a gated run on an `interrupt` input\n * request whose payload carries the Athena `hitl_approval` shape\n * (`athena_deep_agent/hitl_bridge.py`). A surface answers it with\n * `{ type: 'run/input', requestId, response: { type: 'resume', value } }` —\n * the exact command the backend's resume path consumes. This module holds the\n * payload contract (narrowing, resume values) and the shared resume-lock state\n * machine; rendering stays with each surface.\n *\n * Deliberately parameterized rather than self-wiring: these helpers never call\n * `useStatewireRuns`/`useAuiState` themselves. Each surface reads its own\n * statewire hooks (which its tests already mock) and passes the values in, so\n * the shared semantics cannot drift while per-surface wiring stays testable.\n */\n\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { hasClientToolInterruptSource } from './client-tools';\nimport { readDeepAgentStreamValues } from './statewire-state';\n\n/** Human-in-the-loop approval payload the agora deep-agent runtime parks a run\n * on (shaped by `athena_deep_agent/hitl_bridge.py`). */\nexport interface HitlApprovalPayload {\n source: string;\n type: string;\n message: string;\n /** Risk headline/facts/warnings, when the host includes them. */\n approval_context?: unknown;\n context?: {\n tool_name?: string;\n tool_id?: string;\n tool_call_id?: string;\n tool_args?: Record<string, unknown>;\n pending_action_count?: number;\n approval_context?: unknown;\n /**\n * Every pending gated call (name + args), in decision order. Absent on\n * older backends — fall back to the single `tool_name`/`tool_args` pair.\n * Per-dispatch payloads also carry each lane's `tool_call_id` (the lane\n * card key) and `interrupt_id` (the lane resume key).\n */\n action_requests?: {\n name?: string;\n args?: Record<string, unknown>;\n tool_call_id?: string;\n interrupt_id?: string;\n }[];\n /**\n * True when each dispatch parked on its own interrupt: decisions submit\n * per lane via `lane_decisions` and each approved lane starts immediately,\n * instead of the legacy batch where one resume must answer every lane.\n */\n per_dispatch?: boolean;\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** A pending `interrupt` input request; a present `response` key marks it answered. */\nexport function isPendingInterrupt(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n return request.type === 'interrupt' && !('response' in request);\n}\n\n/**\n * Interrupt sources that were system pauses before the host stamped\n * `requires_user_action: false` on them (a background deep-research wait). Kept\n * only for the deploy skew between an older agora and a newer client; delete\n * once every deployment's raiser stamps the flag.\n */\nconst LEGACY_SYSTEM_PAUSE_INTERRUPT_SOURCES: ReadonlySet<string> = new Set(['deep_research_pending']);\n\n/**\n * A park nobody answers: the agent is still working (a background research\n * wait) and the backend resumes it itself. The raiser stamps\n * `requires_user_action: false`; older raisers are recognised by source.\n */\nexport function isSystemPauseInterrupt(payload: unknown): boolean {\n if (!isRecord(payload)) return false;\n if (payload.requires_user_action === false) return true;\n return (\n typeof payload.source === 'string' && LEGACY_SYSTEM_PAUSE_INTERRUPT_SOURCES.has(payload.source)\n );\n}\n\n/**\n * A pending interrupt an approval card may answer. Two kinds are excluded —\n * client-tool parks (the client-tool bridge owns those, and a generic resume\n * would corrupt the parked tool call) and system pauses (nothing to answer; the\n * backend resumes them). The source checks are deliberately loose so a skewed\n * deploy can never route one here.\n */\nexport function isApprovalCardInterrupt(\n request: StatewireThread.InputRequestState,\n): request is StatewireThread.CustomInputRequest {\n if (!isPendingInterrupt(request)) return false;\n const payload = (request as { payload?: unknown }).payload;\n return !hasClientToolInterruptSource(payload) && !isSystemPauseInterrupt(payload);\n}\n\n/** Narrow an interrupt payload to the Athena HITL approval shape. */\nexport function asHitlApproval(value: unknown): HitlApprovalPayload | null {\n if (!isRecord(value)) return null;\n return value.type === 'hitl' &&\n value.source === 'hitl_approval' &&\n typeof value.message === 'string'\n ? (value as unknown as HitlApprovalPayload)\n : null;\n}\n\n/** Default paused-run copy when an interrupt payload carries no message. */\nexport const DEFAULT_INTERRUPT_PAUSED_MESSAGE =\n 'The agent is paused and waiting for input.';\n\n/** Best-effort human-readable message from any interrupt payload. */\nexport function readInterruptMessage(\n value: unknown,\n fallback: string = DEFAULT_INTERRUPT_PAUSED_MESSAGE,\n): string {\n if (isRecord(value) && typeof value.message === 'string' && value.message.length > 0) {\n return value.message;\n }\n return fallback;\n}\n\nexport interface PendingActionRequest {\n name: string;\n args: Record<string, unknown>;\n /** The lane's `task` tool-call id (present on per-dispatch payloads). */\n toolCallId?: string;\n /** The lane's langgraph interrupt id — the `lane_decisions` resume key. */\n interruptId?: string;\n}\n\n/**\n * Every gated call this approval covers, in decision order. One resume answers\n * ALL of them, so every entry must stay reviewable somewhere. Falls back to\n * the single first-call fields on older backends without `action_requests`.\n */\nexport function pendingActionRequests(hitl: HitlApprovalPayload | null): PendingActionRequest[] {\n const context = hitl?.context;\n if (!context) return [];\n // The payload crosses the wire with only its top-level shape narrowed\n // (`asHitlApproval`), so the nested collection must be validated before any\n // array/property access: a malformed host value must degrade to \"no\n // reviewable entries\", never crash the approval surface.\n const requests: Record<string, unknown>[] = Array.isArray(context.action_requests)\n ? context.action_requests.filter(isRecord)\n : context.tool_name && context.tool_args\n ? [{ name: context.tool_name, args: context.tool_args }]\n : [];\n return requests.map((request) => ({\n name: typeof request.name === 'string' ? request.name : 'tool',\n args: isRecord(request.args) ? request.args : {},\n ...(typeof request.tool_call_id === 'string' && { toolCallId: request.tool_call_id }),\n ...(typeof request.interrupt_id === 'string' && { interruptId: request.interrupt_id }),\n }));\n}\n\n/** An interrupt a non-resume dispatch dropped, published by the host. */\nexport interface AbandonedInputRequest {\n id: string;\n type: string;\n}\n\n/**\n * `useStatewireState` selector body for the host-published abandoned input\n * requests. Reads through {@link readDeepAgentStreamValues} so the selector is\n * safe on the pre-attach (`undefined`) wire state, and returns the raw array\n * reference so the subscription stays shallow-stable — derive (`.find`) on the\n * result, never inside the selector.\n */\nexport function readAbandonedInputRequests(\n state: unknown,\n): readonly AbandonedInputRequest[] | undefined {\n const abandoned = readDeepAgentStreamValues(state).abandonedInputRequests;\n // Wire value: anything non-array (including a malformed host publication)\n // reads as \"none\" rather than crashing the consuming surface. Identity-\n // preserving for the valid case, so it stays selector-safe.\n return Array.isArray(abandoned)\n ? (abandoned as readonly AbandonedInputRequest[])\n : undefined;\n}\n\n/** The abandoned `interrupt` entry, if any — the \"approval was cancelled\" notice. */\nexport function findAbandonedInterrupt(\n abandoned: readonly AbandonedInputRequest[] | undefined,\n): AbandonedInputRequest | undefined {\n return abandoned?.find(\n (request) => isRecord(request) && request.type === 'interrupt' && typeof request.id === 'string',\n );\n}\n\n/** Canonical reason string carried by a user rejection. */\nexport const HITL_REJECT_REASON = 'User rejected the action.';\n\n/**\n * Default {@link UseApprovalResumeLockOptions.pendingResumeGraceMs} for every\n * approval surface: how long a clicked decision waits for its resume run to\n * start before the actions unlock again. Covers a `run/input` the host\n * rejected or a disconnected transport swallowed before any run began — the\n * one failure neither identity-change nor observed-run recovery can detect.\n */\nexport const HITL_PENDING_RESUME_GRACE_MS = 15_000;\n\n/** `run/input` resume value approving the gated call(s). */\nexport function hitlResumeApprove(): Record<string, string> {\n return { action: 'approve' };\n}\n\n/** `run/input` resume value rejecting the gated call(s). */\nexport function hitlResumeReject(reason: string = HITL_REJECT_REASON): Record<string, string> {\n return { action: 'reject', reason };\n}\n\n/** Canonical reason string carried by a user declining a non-HITL park. */\nexport const INTERRUPT_DECLINE_REASON = 'User declined the request.';\n\n/**\n * `run/input` resume value declining a non-HITL park. `{ action: 'reject' }`\n * is the one value every typed raiser treats as a clean user decline\n * (`FormCancelledError` / `ToolRejectedError`); a generic `continue` would be\n * rejected by their resume schemas and surface as a tool error the agent then\n * improvises around.\n */\nexport function interruptResumeDecline(\n reason: string = INTERRUPT_DECLINE_REASON,\n): Record<string, string> {\n return { action: 'reject', reason };\n}\n\nexport interface UseApprovalResumeLockOptions<TValue> {\n /** The pending interrupt's request id (`undefined` when nothing is parked). */\n requestId: string | undefined;\n /** The surface's thread `isRunning` read (mockable per surface). */\n isRunning: boolean;\n /**\n * Dispatch the `run/input` resume for `requestId` on the surface's transport.\n * Return the dispatch promise so a refused or lost command releases the lock\n * at once and reaches `onResumeRejected`; a `void` return leaves the\n * recovery paths below as the only release.\n */\n sendResume: (requestId: string, value: TValue) => void | Promise<unknown>;\n /**\n * The dispatch `sendResume` returned rejected — the host refused the\n * `run/input` or the transport lost it. The lock is already released when\n * this fires; the surface tells the user, since the request is back on\n * screen with nothing else saying why.\n */\n onResumeRejected?: (error: unknown, requestId: string) => void;\n /**\n * Re-enable the actions when no resume run started within this window —\n * covers a `run/input` that was rejected or lost before any run began, so\n * neither reset path below can fire. Omit to keep the pre-existing surface\n * behavior of staying locked until a resume run is observed.\n */\n pendingResumeGraceMs?: number;\n}\n\nexport interface ApprovalResumeLock<TValue> {\n /** True after a resume until the request identity changes (or recovery fires). */\n pending: boolean;\n /**\n * Send the resume value, locking the actions. No-op (returns `undefined`)\n * while pending or unparked. Returns the dispatch promise when `sendResume`\n * produced one, so a surface can hold its own state until the host settles.\n */\n resume: (value: TValue) => Promise<unknown> | undefined;\n}\n\n/**\n * Optimistic action lock shared by every approval surface: disable the\n * decision buttons the moment one is clicked, until the request identity\n * changes (it clears after the resume, or a fresh interrupt arrives). This\n * closes the double-click → duplicate-resume window.\n *\n * Recovery paths, in order of specificity:\n * 0. The dispatch itself rejected (a `sendResume` that returned a promise) —\n * released at once, and reported through `onResumeRejected`.\n * 1. Identity change — the resume landed (or a new interrupt replaced the\n * request). Reset during render via React's \"previous value\" pattern.\n * 2. A resume run was observed starting and then settling with the request\n * still pending (failed/cancelled resume) — clear the lock so the user can\n * answer again.\n * 3. Optional grace window ({@link UseApprovalResumeLockOptions.pendingResumeGraceMs})\n * for a `run/input` that never started a run at all.\n */\nexport function useApprovalResumeLock<TValue = Record<string, string>>(\n options: UseApprovalResumeLockOptions<TValue>,\n): ApprovalResumeLock<TValue> {\n const { requestId, isRunning, sendResume, pendingResumeGraceMs, onResumeRejected } = options;\n\n const [pending, setPending] = useState(false);\n const prevRequestIdRef = useRef(requestId);\n if (prevRequestIdRef.current !== requestId) {\n prevRequestIdRef.current = requestId;\n if (pending) setPending(false);\n }\n\n // Track the resume run we kicked off (isRunning observed true→false while\n // pending) and clear `pending` when it ends with the request unresolved.\n const sawResumeRunRef = useRef(false);\n useEffect(() => {\n if (!pending) {\n sawResumeRunRef.current = false;\n return;\n }\n if (isRunning) {\n sawResumeRunRef.current = true;\n return;\n }\n if (sawResumeRunRef.current) {\n sawResumeRunRef.current = false;\n setPending(false);\n }\n }, [pending, isRunning]);\n\n // Pre-start failure recovery: re-enable after the grace window when no\n // resume run began; answering the still-parked request again is exactly\n // what the user needs to be able to do.\n useEffect(() => {\n if (!pending || pendingResumeGraceMs === undefined) return;\n const timer = setTimeout(() => {\n if (!sawResumeRunRef.current) setPending(false);\n }, pendingResumeGraceMs);\n return () => clearTimeout(timer);\n }, [pending, requestId, pendingResumeGraceMs]);\n\n const sendResumeRef = useRef(sendResume);\n sendResumeRef.current = sendResume;\n const onResumeRejectedRef = useRef(onResumeRejected);\n onResumeRejectedRef.current = onResumeRejected;\n const currentRequestIdRef = useRef(requestId);\n currentRequestIdRef.current = requestId;\n const pendingRef = useRef(pending);\n pendingRef.current = pending;\n\n const resume = useCallback(\n (value: TValue): Promise<unknown> | undefined => {\n if (requestId === undefined || pendingRef.current) return undefined;\n setPending(true);\n pendingRef.current = true;\n const dispatched: unknown = sendResumeRef.current(requestId, value);\n if (!(dispatched instanceof Promise)) return undefined;\n dispatched.then(undefined, (error: unknown) => {\n // A late refusal for a request the lock has since moved past (the\n // park was re-keyed, or a newer answer is in flight) must neither\n // release that answer's lock nor be reported as a failure of it.\n const current = currentRequestIdRef.current;\n if (current !== undefined && current !== requestId) return;\n setPending(false);\n pendingRef.current = false;\n onResumeRejectedRef.current?.(error, requestId);\n });\n return dispatched;\n },\n [requestId],\n );\n\n return { pending, resume };\n}\n"],"names":[],"mappings":"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"}
@@ -155,6 +155,23 @@ function liftTerminationIntoMetadata(message) {
155
155
  }
156
156
  };
157
157
  }
158
+ const ATHENA_ERRORS_KEY = "errors";
159
+ function liftErrorsIntoMetadata(message) {
160
+ if (message.type !== "ai") return message;
161
+ const kwargs = message.additional_kwargs;
162
+ const errors = kwargs == null ? void 0 : kwargs[ATHENA_ERRORS_KEY];
163
+ if (!Array.isArray(errors) || errors.length === 0) return message;
164
+ const existingMetadata = kwargs == null ? void 0 : kwargs.metadata;
165
+ const metadata = typeof existingMetadata === "object" && existingMetadata !== null ? existingMetadata : {};
166
+ if (Array.isArray(metadata[ATHENA_ERRORS_KEY])) return message;
167
+ return {
168
+ ...message,
169
+ additional_kwargs: {
170
+ ...kwargs,
171
+ metadata: { ...metadata, [ATHENA_ERRORS_KEY]: errors }
172
+ }
173
+ };
174
+ }
158
175
  function liftModelIntoMetadata(message) {
159
176
  if (message.type !== "ai") return message;
160
177
  const responseMetadata = message.response_metadata;
@@ -360,7 +377,9 @@ function liftMessageIntoMetadataChannels(message) {
360
377
  liftCompactionBoundaryIntoMetadata(
361
378
  liftConversationStatsIntoMetadata(
362
379
  liftModelIntoMetadata(
363
- liftTerminationIntoMetadata(liftRunMarkerIntoMetadata(message))
380
+ liftErrorsIntoMetadata(
381
+ liftTerminationIntoMetadata(liftRunMarkerIntoMetadata(message))
382
+ )
364
383
  )
365
384
  )
366
385
  )
@@ -382,21 +401,31 @@ function sanitizeLangChainMessages(messages, { toolOutputs } = {}) {
382
401
  function isLangGraphState(value) {
383
402
  return typeof value === "object" && value !== null && "messages" in value && Array.isArray(value.messages);
384
403
  }
385
- function convertDeepAgentState(state, { laneProjection = "both", selfAuthor = null, toolOutputs } = {}) {
404
+ function convertDeepAgentState(state, {
405
+ laneProjection = "both",
406
+ selfAuthor = null,
407
+ toolOutputs,
408
+ withdrawnTurns
409
+ } = {}) {
386
410
  var _a, _b;
387
411
  const values = statewireState.readDeepAgentStreamValues(state);
388
412
  const status = (_b = (_a = values.runs) == null ? void 0 : _a[0]) == null ? void 0 : _b.status;
389
413
  const isRunning = status === "running";
390
- const laneTail = status === "stopped" || status === "error" ? [] : laneTailMessages(values, laneProjection, selfAuthor);
391
- const stateMessages = isLangGraphState(values) ? sanitizeLangChainMessages(values.messages, { toolOutputs }) : [];
414
+ const isWithdrawn = (message) => withdrawnTurns !== void 0 && message.id !== void 0 && withdrawnTurns.isWithdrawn(message.id);
415
+ const laneTail = (status === "stopped" || status === "error" ? [] : laneTailMessages(values, laneProjection, selfAuthor)).filter((message) => !isWithdrawn(message));
416
+ const sanitized = isLangGraphState(values) ? sanitizeLangChainMessages(values.messages, { toolOutputs }) : [];
417
+ const tail = sanitized[sanitized.length - 1];
418
+ const tailWithdrawn = tail !== void 0 && isWithdrawn(tail);
419
+ const stateMessages = sanitized.filter((message) => !isWithdrawn(message));
392
420
  return {
393
421
  messages: messageConverter.toThreadMessages(
394
- [...withPendingAssistantMessage(stateMessages, isRunning), ...laneTail],
422
+ [...withPendingAssistantMessage(stateMessages, isRunning && !tailWithdrawn), ...laneTail],
395
423
  isRunning
396
424
  ),
397
425
  isRunning
398
426
  };
399
427
  }
428
+ exports.ATHENA_ERRORS_KEY = ATHENA_ERRORS_KEY;
400
429
  exports.ATHENA_HANDOFF_KEY = ATHENA_HANDOFF_KEY;
401
430
  exports.ATHENA_QUEUED_LANE_KEY = ATHENA_QUEUED_LANE_KEY;
402
431
  exports.ATHENA_RUN_MARKER_CANCELLED = ATHENA_RUN_MARKER_CANCELLED;
@@ -419,6 +448,7 @@ exports.liftAopSourceIntoMetadata = liftAopSourceIntoMetadata;
419
448
  exports.liftBranchIntoMetadata = liftBranchIntoMetadata;
420
449
  exports.liftCompactionBoundaryIntoMetadata = liftCompactionBoundaryIntoMetadata;
421
450
  exports.liftConversationStatsIntoMetadata = liftConversationStatsIntoMetadata;
451
+ exports.liftErrorsIntoMetadata = liftErrorsIntoMetadata;
422
452
  exports.liftHandoffSummaryIntoMetadata = liftHandoffSummaryIntoMetadata;
423
453
  exports.liftModelIntoMetadata = liftModelIntoMetadata;
424
454
  exports.liftRunMarkerIntoMetadata = liftRunMarkerIntoMetadata;