@athenaintel/react 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/README.md +74 -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/messages/message-components.cjs +6 -29
  96. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  97. package/dist/chat-ui/messages/message-components.d.ts +1 -13
  98. package/dist/chat-ui/messages/message-components.js +8 -31
  99. package/dist/chat-ui/messages/message-components.js.map +1 -1
  100. package/dist/chat-ui/messages/runtime-message.cjs +3 -2
  101. package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
  102. package/dist/chat-ui/messages/runtime-message.js +3 -2
  103. package/dist/chat-ui/messages/runtime-message.js.map +1 -1
  104. package/dist/chat-ui/messages/statewire-continue-run.cjs +23 -8
  105. package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
  106. package/dist/chat-ui/messages/statewire-continue-run.d.ts +14 -4
  107. package/dist/chat-ui/messages/statewire-continue-run.js +23 -8
  108. package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
  109. package/dist/chat-ui/messages/user-edit-composer.cjs +2 -1
  110. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  111. package/dist/chat-ui/messages/user-edit-composer.js +2 -1
  112. package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
  113. package/dist/chat-ui/rich-text/chat-message-body.cjs +76 -0
  114. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -0
  115. package/dist/chat-ui/rich-text/chat-message-body.d.ts +5 -0
  116. package/dist/chat-ui/rich-text/chat-message-body.js +76 -0
  117. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -0
  118. package/dist/chat-ui/rich-text/chat-node-views.cjs +51 -0
  119. package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -0
  120. package/dist/chat-ui/rich-text/chat-node-views.d.ts +13 -0
  121. package/dist/chat-ui/rich-text/chat-node-views.js +51 -0
  122. package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -0
  123. package/dist/chat-ui/rich-text/chat-text-schema.cjs +70 -0
  124. package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -0
  125. package/dist/chat-ui/rich-text/chat-text-schema.d.ts +7 -0
  126. package/dist/chat-ui/rich-text/chat-text-schema.js +70 -0
  127. package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -0
  128. package/dist/chat-ui/rich-text/dom-output-spec.cjs +83 -0
  129. package/dist/chat-ui/rich-text/dom-output-spec.cjs.map +1 -0
  130. package/dist/chat-ui/rich-text/dom-output-spec.d.ts +11 -0
  131. package/dist/chat-ui/rich-text/dom-output-spec.js +83 -0
  132. package/dist/chat-ui/rich-text/dom-output-spec.js.map +1 -0
  133. package/dist/chat-ui/statewire.cjs +2 -1
  134. package/dist/chat-ui/statewire.cjs.map +1 -1
  135. package/dist/chat-ui/statewire.js +2 -1
  136. package/dist/chat-ui/statewire.js.map +1 -1
  137. package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -1
  138. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  139. package/dist/chat-ui/thread/statewire-history-edge.js +2 -1
  140. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  141. package/dist/index.cjs +2 -1
  142. package/dist/index.cjs.map +1 -1
  143. package/dist/index.js +2 -1
  144. package/dist/index.js.map +1 -1
  145. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +32 -0
  146. package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +1 -0
  147. package/dist/node_modules/qrcode-generator/dist/qrcode.js +31 -0
  148. package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +1 -0
  149. package/dist/node_modules/zod/v4/classic/errors.cjs +44 -0
  150. package/dist/node_modules/zod/v4/classic/errors.cjs.map +1 -0
  151. package/dist/node_modules/zod/v4/classic/errors.js +44 -0
  152. package/dist/node_modules/zod/v4/classic/errors.js.map +1 -0
  153. package/dist/node_modules/zod/v4/classic/iso.cjs +43 -0
  154. package/dist/node_modules/zod/v4/classic/iso.cjs.map +1 -0
  155. package/dist/node_modules/zod/v4/classic/iso.js +43 -0
  156. package/dist/node_modules/zod/v4/classic/iso.js.map +1 -0
  157. package/dist/node_modules/zod/v4/classic/parse.cjs +29 -0
  158. package/dist/node_modules/zod/v4/classic/parse.cjs.map +1 -0
  159. package/dist/node_modules/zod/v4/classic/parse.js +29 -0
  160. package/dist/node_modules/zod/v4/classic/parse.js.map +1 -0
  161. package/dist/node_modules/zod/v4/classic/schemas.cjs +871 -0
  162. package/dist/node_modules/zod/v4/classic/schemas.cjs.map +1 -0
  163. package/dist/node_modules/zod/v4/classic/schemas.js +871 -0
  164. package/dist/node_modules/zod/v4/classic/schemas.js.map +1 -0
  165. package/dist/node_modules/zod/v4/core/api.cjs +570 -0
  166. package/dist/node_modules/zod/v4/core/api.cjs.map +1 -0
  167. package/dist/node_modules/zod/v4/core/api.js +570 -0
  168. package/dist/node_modules/zod/v4/core/api.js.map +1 -0
  169. package/dist/node_modules/zod/v4/core/checks.cjs +411 -0
  170. package/dist/node_modules/zod/v4/core/checks.cjs.map +1 -0
  171. package/dist/node_modules/zod/v4/core/checks.js +411 -0
  172. package/dist/node_modules/zod/v4/core/checks.js.map +1 -0
  173. package/dist/node_modules/zod/v4/core/core.cjs +77 -0
  174. package/dist/node_modules/zod/v4/core/core.cjs.map +1 -0
  175. package/dist/node_modules/zod/v4/core/core.js +77 -0
  176. package/dist/node_modules/zod/v4/core/core.js.map +1 -0
  177. package/dist/node_modules/zod/v4/core/doc.cjs +38 -0
  178. package/dist/node_modules/zod/v4/core/doc.cjs.map +1 -0
  179. package/dist/node_modules/zod/v4/core/doc.js +38 -0
  180. package/dist/node_modules/zod/v4/core/doc.js.map +1 -0
  181. package/dist/node_modules/zod/v4/core/errors.cjs +76 -0
  182. package/dist/node_modules/zod/v4/core/errors.cjs.map +1 -0
  183. package/dist/node_modules/zod/v4/core/errors.js +76 -0
  184. package/dist/node_modules/zod/v4/core/errors.js.map +1 -0
  185. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +312 -0
  186. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +1 -0
  187. package/dist/node_modules/zod/v4/core/json-schema-processors.js +312 -0
  188. package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +1 -0
  189. package/dist/node_modules/zod/v4/core/parse.cjs +96 -0
  190. package/dist/node_modules/zod/v4/core/parse.cjs.map +1 -0
  191. package/dist/node_modules/zod/v4/core/parse.js +96 -0
  192. package/dist/node_modules/zod/v4/core/parse.js.map +1 -0
  193. package/dist/node_modules/zod/v4/core/regexes.cjs +86 -0
  194. package/dist/node_modules/zod/v4/core/regexes.cjs.map +1 -0
  195. package/dist/node_modules/zod/v4/core/regexes.js +86 -0
  196. package/dist/node_modules/zod/v4/core/regexes.js.map +1 -0
  197. package/dist/node_modules/zod/v4/core/registries.cjs +52 -0
  198. package/dist/node_modules/zod/v4/core/registries.cjs.map +1 -0
  199. package/dist/node_modules/zod/v4/core/registries.js +52 -0
  200. package/dist/node_modules/zod/v4/core/registries.js.map +1 -0
  201. package/dist/node_modules/zod/v4/core/schemas.cjs +1412 -0
  202. package/dist/node_modules/zod/v4/core/schemas.cjs.map +1 -0
  203. package/dist/node_modules/zod/v4/core/schemas.js +1413 -0
  204. package/dist/node_modules/zod/v4/core/schemas.js.map +1 -0
  205. package/dist/node_modules/zod/v4/core/to-json-schema.cjs +370 -0
  206. package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +1 -0
  207. package/dist/node_modules/zod/v4/core/to-json-schema.js +370 -0
  208. package/dist/node_modules/zod/v4/core/to-json-schema.js.map +1 -0
  209. package/dist/node_modules/zod/v4/core/util.cjs +444 -0
  210. package/dist/node_modules/zod/v4/core/util.cjs.map +1 -0
  211. package/dist/node_modules/zod/v4/core/util.js +444 -0
  212. package/dist/node_modules/zod/v4/core/util.js.map +1 -0
  213. package/dist/node_modules/zod/v4/core/versions.cjs +9 -0
  214. package/dist/node_modules/zod/v4/core/versions.cjs.map +1 -0
  215. package/dist/node_modules/zod/v4/core/versions.js +9 -0
  216. package/dist/node_modules/zod/v4/core/versions.js.map +1 -0
  217. package/dist/packages/agent-runtime/src/client-tool-bridge.cjs +7 -1
  218. package/dist/packages/agent-runtime/src/client-tool-bridge.cjs.map +1 -1
  219. package/dist/packages/agent-runtime/src/client-tool-bridge.js +7 -1
  220. package/dist/packages/agent-runtime/src/client-tool-bridge.js.map +1 -1
  221. package/dist/packages/agent-runtime/src/conversation-list.cjs +4 -0
  222. package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
  223. package/dist/packages/agent-runtime/src/conversation-list.js +4 -0
  224. package/dist/packages/agent-runtime/src/conversation-list.js.map +1 -1
  225. package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
  226. package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
  227. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +2 -0
  228. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  229. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +2 -0
  230. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  231. package/dist/packages/agent-runtime/src/interrupts.cjs +31 -7
  232. package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
  233. package/dist/packages/agent-runtime/src/interrupts.js +31 -7
  234. package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
  235. package/dist/packages/agent-runtime/src/messages.cjs +35 -5
  236. package/dist/packages/agent-runtime/src/messages.cjs.map +1 -1
  237. package/dist/packages/agent-runtime/src/messages.js +35 -5
  238. package/dist/packages/agent-runtime/src/messages.js.map +1 -1
  239. package/dist/packages/agent-runtime/src/protocol.cjs +4 -6
  240. package/dist/packages/agent-runtime/src/protocol.cjs.map +1 -1
  241. package/dist/packages/agent-runtime/src/protocol.js +4 -6
  242. package/dist/packages/agent-runtime/src/protocol.js.map +1 -1
  243. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +296 -14
  244. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  245. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +293 -11
  246. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  247. package/dist/packages/agent-runtime/src/screen-point.cjs +68 -0
  248. package/dist/packages/agent-runtime/src/screen-point.cjs.map +1 -0
  249. package/dist/packages/agent-runtime/src/screen-point.js +68 -0
  250. package/dist/packages/agent-runtime/src/screen-point.js.map +1 -0
  251. package/dist/packages/agent-runtime/src/statewire-transport.cjs +6 -1
  252. package/dist/packages/agent-runtime/src/statewire-transport.cjs.map +1 -1
  253. package/dist/packages/agent-runtime/src/statewire-transport.js +6 -1
  254. package/dist/packages/agent-runtime/src/statewire-transport.js.map +1 -1
  255. package/dist/packages/agent-runtime/src/thread-handoff.cjs +85 -0
  256. package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +1 -0
  257. package/dist/packages/agent-runtime/src/thread-handoff.js +85 -0
  258. package/dist/packages/agent-runtime/src/thread-handoff.js.map +1 -0
  259. package/dist/packages/agent-runtime/src/thread-snapshot-cache.cjs +1 -1
  260. package/dist/packages/agent-runtime/src/thread-snapshot-cache.cjs.map +1 -1
  261. package/dist/packages/agent-runtime/src/thread-snapshot-cache.js +1 -1
  262. package/dist/packages/agent-runtime/src/thread-snapshot-cache.js.map +1 -1
  263. package/dist/packages/agent-runtime/src/tool-metadata.cjs +0 -8
  264. package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
  265. package/dist/packages/agent-runtime/src/tool-metadata.js +0 -8
  266. package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
  267. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +14 -0
  268. package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -0
  269. package/dist/packages/agent-runtime/src/use-deep-agent-config.js +14 -0
  270. package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -0
  271. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs +21 -5
  272. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -1
  273. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js +21 -5
  274. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -1
  275. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.cjs +13 -3
  276. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
  277. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.js +14 -4
  278. package/dist/packages/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
  279. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs +35 -15
  280. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  281. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js +35 -15
  282. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  283. package/dist/packages/agent-runtime/src/voice-presentation.cjs.map +1 -1
  284. package/dist/packages/agent-runtime/src/voice-presentation.js.map +1 -1
  285. package/dist/packages/react/package.json.cjs +1 -1
  286. package/dist/packages/react/package.json.js +1 -1
  287. package/dist/provider/AthenaProvider.cjs +2 -1
  288. package/dist/provider/AthenaProvider.cjs.map +1 -1
  289. package/dist/provider/AthenaProvider.js +2 -1
  290. package/dist/provider/AthenaProvider.js.map +1 -1
  291. package/dist/runtime/statewire-lifecycle.cjs +2 -1
  292. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  293. package/dist/runtime/statewire-lifecycle.js +2 -1
  294. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  295. package/dist/runtime/useAthenaStatewireRuntime.cjs +2 -1
  296. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  297. package/dist/runtime/useAthenaStatewireRuntime.js +2 -1
  298. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  299. package/dist/styles.css +1 -1
  300. package/dist/threads/conversation-list-fetcher.cjs +4 -0
  301. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  302. package/dist/threads/conversation-list-fetcher.js +4 -0
  303. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  304. package/dist/threads/statewire-thread-list.cjs +2 -1
  305. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  306. package/dist/threads/statewire-thread-list.js +2 -1
  307. package/dist/threads/statewire-thread-list.js.map +1 -1
  308. package/dist/threads/use-rest-conversation-history.cjs +2 -1
  309. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  310. package/dist/threads/use-rest-conversation-history.js +2 -1
  311. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  312. package/dist/threads/use-statewire-conversation-history.cjs +2 -1
  313. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  314. package/dist/threads/use-statewire-conversation-history.d.ts +1 -0
  315. package/dist/threads/use-statewire-conversation-history.js +2 -1
  316. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  317. package/dist/tools/index.cjs +2 -1
  318. package/dist/tools/index.cjs.map +1 -1
  319. package/dist/tools/index.js +2 -1
  320. package/dist/tools/index.js.map +1 -1
  321. package/dist/tools/misc-tool-uis.cjs +2 -1
  322. package/dist/tools/misc-tool-uis.cjs.map +1 -1
  323. package/dist/tools/misc-tool-uis.js +2 -1
  324. package/dist/tools/misc-tool-uis.js.map +1 -1
  325. package/dist/tools/primitives.cjs +2 -1
  326. package/dist/tools/primitives.cjs.map +1 -1
  327. package/dist/tools/primitives.js +2 -1
  328. package/dist/tools/primitives.js.map +1 -1
  329. package/dist/tools/tool-fallback.cjs +2 -1
  330. package/dist/tools/tool-fallback.cjs.map +1 -1
  331. package/dist/tools/tool-fallback.js +2 -1
  332. package/dist/tools/tool-fallback.js.map +1 -1
  333. package/dist/tools/voice-update-tool-ui.cjs +2 -1
  334. package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
  335. package/dist/tools/voice-update-tool-ui.js +2 -1
  336. package/dist/tools/voice-update-tool-ui.js.map +1 -1
  337. package/dist/voice/index.cjs +6 -0
  338. package/dist/voice/index.cjs.map +1 -0
  339. package/dist/voice/index.d.ts +2 -0
  340. package/dist/voice/index.js +6 -0
  341. package/dist/voice/index.js.map +1 -0
  342. package/dist/voice/use-athena-voice-session.cjs +315 -0
  343. package/dist/voice/use-athena-voice-session.cjs.map +1 -0
  344. package/dist/voice/use-athena-voice-session.d.ts +145 -0
  345. package/dist/voice/use-athena-voice-session.js +315 -0
  346. package/dist/voice/use-athena-voice-session.js.map +1 -0
  347. package/package.json +17 -3
@@ -1,6 +1,12 @@
1
- import { LocalStorageSession } from "@assistant-ui/react-statewire/local-storage";
1
+ import { resource, useResource } from "@assistant-ui/tap";
2
+ import { KeyValueSessionStorage } from "statewire/storage";
3
+ import { useRef, useMemo, useEffect } from "react";
2
4
  const SESSION_PREFIX = "aui:statewire-session:";
5
+ const SESSION_LOCK_PREFIX = "aui:statewire-session-lock:";
6
+ const SESSION_ACTIVE_PREFIX = "aui:statewire-session-active:";
3
7
  const LEGACY_PROTOCOL = { name: "athena", version: "2026-09-07" };
8
+ const DAY_MS = 24 * 60 * 60 * 1e3;
9
+ const READABLE_STORAGE_VERSIONS = /* @__PURE__ */ new Set([1, 2]);
4
10
  const browserGlobals = globalThis;
5
11
  function readPersistedFailedSendStamps(sessionKey) {
6
12
  const storage = browserGlobals.localStorage;
@@ -28,19 +34,295 @@ function readFailedSendStampsFromRecord(record) {
28
34
  }
29
35
  return stamps;
30
36
  }
31
- const QuotaSafeLocalStorageSession = ({
32
- threadId,
33
- key,
34
- ttl = 7 * 24 * 60 * 60 * 1e3,
35
- max = 50
36
- }) => LocalStorageSession({
37
+ function isStorageQuotaError(error) {
38
+ if (typeof error !== "object" || error === null) return false;
39
+ const { name, code, message } = error;
40
+ return name === "QuotaExceededError" || name === "NS_ERROR_DOM_QUOTA_REACHED" || code === 22 || code === 1014 || typeof message === "string" && /exceeded the quota|quotaexceedederror|ns_error_dom_quota_reached/i.test(
41
+ message
42
+ );
43
+ }
44
+ function asStatewireQuotaError(error) {
45
+ if (error instanceof Error && error.name === "QuotaExceededError")
46
+ return error;
47
+ const normalized = new Error(
48
+ error instanceof Error ? error.message : "storage quota exceeded"
49
+ );
50
+ normalized.name = "QuotaExceededError";
51
+ return normalized;
52
+ }
53
+ function unreadableRecord(raw) {
54
+ let parsed;
55
+ try {
56
+ parsed = JSON.parse(raw);
57
+ } catch (error) {
58
+ return error instanceof Error ? error : new Error("stored session record is not JSON");
59
+ }
60
+ if (typeof parsed !== "object" || parsed === null) {
61
+ return new Error("stored session record is not an object");
62
+ }
63
+ const version = parsed.statewireStorageVersion;
64
+ if (version !== void 0 && !READABLE_STORAGE_VERSIONS.has(version)) {
65
+ return new Error(
66
+ `stored session record uses storage version ${String(version)}`
67
+ );
68
+ }
69
+ return void 0;
70
+ }
71
+ function browserBackend({
72
+ storage,
73
+ locks
74
+ }) {
75
+ return {
76
+ getItem: (key) => storage.getItem(key),
77
+ setItem: (key, value) => storage.setItem(key, value),
78
+ removeItem: (key) => storage.removeItem(key),
79
+ getAllKeys: () => Array.from(
80
+ { length: storage.length },
81
+ (_, index) => storage.key(index)
82
+ ).filter((key) => key !== null),
83
+ withUnused: async (key, action) => {
84
+ await locks.request(
85
+ SESSION_ACTIVE_PREFIX + key,
86
+ { ifAvailable: true },
87
+ async (active) => {
88
+ if (active === null) return;
89
+ await locks.request(
90
+ SESSION_LOCK_PREFIX + key.slice(SESSION_PREFIX.length),
91
+ { ifAvailable: true },
92
+ async (lock) => {
93
+ if (lock === null) return;
94
+ await action();
95
+ }
96
+ );
97
+ }
98
+ );
99
+ }
100
+ };
101
+ }
102
+ function quotaTolerantBackend({
103
+ inner,
104
+ onDegraded
105
+ }) {
106
+ const quotaSurfaced = /* @__PURE__ */ new Set();
107
+ const overlay = /* @__PURE__ */ new Map();
108
+ const shielded = /* @__PURE__ */ new Set();
109
+ const checked = /* @__PURE__ */ new Map();
110
+ const readable = (key, raw) => {
111
+ const previous = checked.get(key);
112
+ if (previous !== void 0 && previous.raw === raw) return previous.verdict;
113
+ const verdict = unreadableRecord(raw);
114
+ checked.set(key, { raw, verdict });
115
+ return verdict;
116
+ };
117
+ return {
118
+ getItem: (key) => {
119
+ const shadowed = overlay.get(key);
120
+ if (shadowed !== void 0) return shadowed;
121
+ if (shielded.has(key)) return null;
122
+ let raw;
123
+ try {
124
+ raw = inner.getItem(key);
125
+ } catch (error) {
126
+ shielded.add(key);
127
+ onDegraded({ operation: "getItem", key, error });
128
+ return null;
129
+ }
130
+ if (raw === null) return null;
131
+ const unreadable = readable(key, raw);
132
+ if (unreadable === void 0) return raw;
133
+ shielded.add(key);
134
+ onDegraded({ operation: "record", key, error: unreadable });
135
+ return null;
136
+ },
137
+ setItem: (key, value) => {
138
+ if (shielded.has(key)) {
139
+ overlay.set(key, value);
140
+ return;
141
+ }
142
+ try {
143
+ inner.setItem(key, value);
144
+ overlay.delete(key);
145
+ quotaSurfaced.delete(key);
146
+ } catch (error) {
147
+ if (isStorageQuotaError(error) && !quotaSurfaced.has(key)) {
148
+ quotaSurfaced.add(key);
149
+ throw asStatewireQuotaError(error);
150
+ }
151
+ overlay.set(key, value);
152
+ onDegraded({ operation: "setItem", key, error });
153
+ }
154
+ },
155
+ removeItem: (key) => {
156
+ overlay.delete(key);
157
+ if (shielded.has(key)) return;
158
+ try {
159
+ inner.removeItem(key);
160
+ } catch (error) {
161
+ onDegraded({ operation: "removeItem", key, error });
162
+ }
163
+ },
164
+ getAllKeys: () => {
165
+ const keys = new Set(overlay.keys());
166
+ try {
167
+ for (const key of inner.getAllKeys()) keys.add(key);
168
+ } catch (error) {
169
+ onDegraded({ operation: "getAllKeys", key: "", error });
170
+ }
171
+ return [...keys];
172
+ },
173
+ withUnused: async (key, action) => {
174
+ try {
175
+ await inner.withUnused(key, action);
176
+ } catch (error) {
177
+ onDegraded({ operation: "withUnused", key, error });
178
+ }
179
+ }
180
+ };
181
+ }
182
+ const sharedBackends = /* @__PURE__ */ new WeakMap();
183
+ function sharedBackend({
184
+ storage,
185
+ locks
186
+ }) {
187
+ let shared = sharedBackends.get(storage);
188
+ if (shared === void 0) {
189
+ const listeners = /* @__PURE__ */ new Set();
190
+ shared = {
191
+ listeners,
192
+ backend: quotaTolerantBackend({
193
+ inner: browserBackend({ storage, locks }),
194
+ onDegraded: (degradation) => {
195
+ for (const listener of listeners) listener(degradation);
196
+ }
197
+ })
198
+ };
199
+ sharedBackends.set(storage, shared);
200
+ }
201
+ return shared;
202
+ }
203
+ const useQuotaSafeLocalStorageSession = ({
37
204
  threadId,
38
- ...key === void 0 ? {} : { key },
39
- legacyProtocol: LEGACY_PROTOCOL,
40
- retention: { maxAgeMs: ttl, maxEntries: max }
41
- });
205
+ key = (id) => id,
206
+ ttl = 7 * DAY_MS,
207
+ max = 50,
208
+ onStorageDegraded
209
+ }) => {
210
+ var _a;
211
+ const storage = browserGlobals.localStorage;
212
+ const locks = (_a = browserGlobals.navigator) == null ? void 0 : _a.locks;
213
+ if (!storage)
214
+ throw new Error(
215
+ "statewire: QuotaSafeLocalStorageSession requires localStorage"
216
+ );
217
+ if (!locks)
218
+ throw new Error(
219
+ "statewire: QuotaSafeLocalStorageSession requires the Web Locks API (navigator.locks)"
220
+ );
221
+ const sessionKey = key(threadId);
222
+ const storageKey = SESSION_PREFIX + sessionKey;
223
+ const lockName = SESSION_LOCK_PREFIX + sessionKey;
224
+ const latestOnDegraded = useRef(onStorageDegraded);
225
+ latestOnDegraded.current = onStorageDegraded;
226
+ const reportDegradation = useMemo(() => {
227
+ const reported = /* @__PURE__ */ new Set();
228
+ return (degradation) => {
229
+ const handler = latestOnDegraded.current;
230
+ if (handler) {
231
+ try {
232
+ handler(degradation);
233
+ } catch {
234
+ }
235
+ return;
236
+ }
237
+ const dedupeKey = `${degradation.operation}:${degradation.key}`;
238
+ if (reported.has(dedupeKey)) return;
239
+ reported.add(dedupeKey);
240
+ console.warn(
241
+ `[statewire] session storage degraded to memory only (${degradation.operation} ${degradation.key})`,
242
+ degradation.error
243
+ );
244
+ };
245
+ }, []);
246
+ const shared = sharedBackend({ storage, locks });
247
+ shared.listeners.add(reportDegradation);
248
+ useEffect(() => {
249
+ shared.listeners.add(reportDegradation);
250
+ return () => {
251
+ shared.listeners.delete(reportDegradation);
252
+ };
253
+ }, [shared, reportDegradation]);
254
+ useEffect(() => {
255
+ let release;
256
+ let disposed = false;
257
+ Promise.resolve(
258
+ locks.request(
259
+ SESSION_ACTIVE_PREFIX + storageKey,
260
+ { mode: "shared" },
261
+ () => {
262
+ if (disposed) return;
263
+ return new Promise((resolve) => {
264
+ release = resolve;
265
+ });
266
+ }
267
+ )
268
+ ).catch(
269
+ (error) => reportDegradation({ operation: "lock", key: storageKey, error })
270
+ );
271
+ return () => {
272
+ disposed = true;
273
+ release == null ? void 0 : release();
274
+ };
275
+ }, [locks, storageKey, reportDegradation]);
276
+ const sessionStorage = useResource(
277
+ KeyValueSessionStorage({
278
+ backend: shared.backend,
279
+ key: sessionKey,
280
+ namespace: SESSION_PREFIX,
281
+ legacyProtocol: LEGACY_PROTOCOL,
282
+ retention: { maxAgeMs: ttl, maxEntries: max },
283
+ // A failure statewire raises itself (a structurally invalid stored
284
+ // record) still stops the session; it is at least reported the same way.
285
+ onError: (error) => reportDegradation({ operation: "session", key: storageKey, error })
286
+ })
287
+ );
288
+ return useMemo(
289
+ () => ({
290
+ ...sessionStorage,
291
+ lock: async (fn) => {
292
+ let granted = false;
293
+ try {
294
+ return await locks.request(lockName, async () => {
295
+ granted = true;
296
+ return fn();
297
+ });
298
+ } catch (error) {
299
+ if (granted) throw error;
300
+ reportDegradation({ operation: "lock", key: storageKey, error });
301
+ return fn();
302
+ }
303
+ },
304
+ subscribe: (listener) => {
305
+ var _a2;
306
+ const onStorage = (event) => {
307
+ if (event.key === storageKey) listener();
308
+ };
309
+ (_a2 = browserGlobals.addEventListener) == null ? void 0 : _a2.call(browserGlobals, "storage", onStorage);
310
+ return () => {
311
+ var _a3;
312
+ return (_a3 = browserGlobals.removeEventListener) == null ? void 0 : _a3.call(browserGlobals, "storage", onStorage);
313
+ };
314
+ }
315
+ }),
316
+ [sessionStorage, locks, storageKey, lockName, reportDegradation]
317
+ );
318
+ };
319
+ const QuotaSafeLocalStorageSession = resource(
320
+ useQuotaSafeLocalStorageSession
321
+ );
42
322
  export {
43
323
  QuotaSafeLocalStorageSession,
324
+ isStorageQuotaError,
325
+ quotaTolerantBackend,
44
326
  readFailedSendStampsFromRecord,
45
327
  readPersistedFailedSendStamps
46
328
  };
@@ -1 +1 @@
1
- {"version":3,"file":"quota-safe-local-storage-session.js","sources":["../../../../../agent-runtime/src/quota-safe-local-storage-session.ts"],"sourcesContent":["import { LocalStorageSession } from \"@assistant-ui/react-statewire/local-storage\";\n\nconst SESSION_PREFIX = \"aui:statewire-session:\";\nconst LEGACY_PROTOCOL = { name: \"athena\", version: \"2026-09-07\" };\nconst browserGlobals = globalThis as {\n localStorage?: { getItem(key: string): string | null };\n};\n\nexport function readPersistedFailedSendStamps(\n sessionKey: string,\n): ReadonlyMap<string, number> | null {\n const storage = browserGlobals.localStorage;\n if (!storage) return null;\n let record: { failed?: unknown } | null;\n try {\n const raw = storage.getItem(SESSION_PREFIX + sessionKey);\n record =\n raw === null ? null : (JSON.parse(raw) as { failed?: unknown } | null);\n } catch {\n return null;\n }\n return readFailedSendStampsFromRecord(record);\n}\n\n/**\n * Send stamps (message id → epoch ms of the original send) off an already\n * loaded session record's failed lane — the storage-agnostic half of\n * {@link readPersistedFailedSendStamps}, for hosts whose session store is not\n * localStorage (the React Native AsyncStorage-backed store reads its record\n * synchronously through `SessionStorage.load()`).\n *\n * Returns `null` for no record (every failed entry then counts as fresh) and\n * an empty map for a record without failures.\n */\nexport function readFailedSendStampsFromRecord(\n record: { failed?: unknown } | null | undefined,\n): ReadonlyMap<string, number> | null {\n if (!record || typeof record !== \"object\") return null;\n const stamps = new Map<string, number>();\n if (!Array.isArray(record.failed)) return stamps;\n for (const entry of record.failed) {\n const failed = entry as {\n ts?: unknown;\n params?: readonly { message?: { id?: unknown } }[];\n } | null;\n const messageId = failed?.params?.[0]?.message?.id;\n if (typeof messageId === \"string\" && typeof failed?.ts === \"number\") {\n stamps.set(messageId, failed.ts);\n }\n }\n return stamps;\n}\n\n/** Athena's browser session keys and retention policy. */\nexport const QuotaSafeLocalStorageSession = ({\n threadId,\n key,\n ttl = 7 * 24 * 60 * 60 * 1000,\n max = 50,\n}: QuotaSafeLocalStorageSession.Options) =>\n LocalStorageSession({\n threadId,\n ...(key === undefined ? {} : { key }),\n legacyProtocol: LEGACY_PROTOCOL,\n retention: { maxAgeMs: ttl, maxEntries: max },\n });\n\nexport namespace QuotaSafeLocalStorageSession {\n export type Options = {\n threadId: string;\n key?: (threadId: string) => string;\n ttl?: number;\n max?: number;\n };\n}\n"],"names":[],"mappings":";AAEA,MAAM,iBAAiB;AACvB,MAAM,kBAAkB,EAAE,MAAM,UAAU,SAAS,aAAA;AACnD,MAAM,iBAAiB;AAIhB,SAAS,8BACd,YACoC;AACpC,QAAM,UAAU,eAAe;AAC/B,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,QAAQ,QAAQ,iBAAiB,UAAU;AACvD,aACE,QAAQ,OAAO,OAAQ,KAAK,MAAM,GAAG;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,+BAA+B,MAAM;AAC9C;AAYO,SAAS,+BACd,QACoC;;AACpC,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,6BAAa,IAAA;AACnB,MAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,EAAG,QAAO;AAC1C,aAAW,SAAS,OAAO,QAAQ;AACjC,UAAM,SAAS;AAIf,UAAM,aAAY,kDAAQ,WAAR,mBAAiB,OAAjB,mBAAqB,YAArB,mBAA8B;AAChD,QAAI,OAAO,cAAc,YAAY,QAAO,iCAAQ,QAAO,UAAU;AACnE,aAAO,IAAI,WAAW,OAAO,EAAE;AAAA,IACjC;AAAA,EACF;AACA,SAAO;AACT;AAGO,MAAM,+BAA+B,CAAC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA,MAAM,IAAI,KAAK,KAAK,KAAK;AAAA,EACzB,MAAM;AACR,MACE,oBAAoB;AAAA,EAClB;AAAA,EACA,GAAI,QAAQ,SAAY,CAAA,IAAK,EAAE,IAAA;AAAA,EAC/B,gBAAgB;AAAA,EAChB,WAAW,EAAE,UAAU,KAAK,YAAY,IAAA;AAC1C,CAAC;"}
1
+ {"version":3,"file":"quota-safe-local-storage-session.js","sources":["../../../../../agent-runtime/src/quota-safe-local-storage-session.ts"],"sourcesContent":["import { resource, useResource } from \"@assistant-ui/tap\";\nimport type { StatewireClient } from \"statewire\";\nimport { KeyValueSessionStorage } from \"statewire/storage\";\nimport { useEffect, useMemo, useRef } from \"react\";\n\nconst SESSION_PREFIX = \"aui:statewire-session:\";\nconst SESSION_LOCK_PREFIX = \"aui:statewire-session-lock:\";\nconst SESSION_ACTIVE_PREFIX = \"aui:statewire-session-active:\";\nconst LEGACY_PROTOCOL = { name: \"athena\", version: \"2026-09-07\" };\nconst DAY_MS = 24 * 60 * 60 * 1000;\n/** The stored-record formats the statewire this build ships reads (it writes 2). */\nconst READABLE_STORAGE_VERSIONS: ReadonlySet<unknown> = new Set([1, 2]);\n\n/** Structural view of the browser storage global — this package compiles\n * without the DOM lib (React Native honesty; see `globals.d.ts`). */\ntype StorageLike = {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n key(index: number): string | null;\n readonly length: number;\n};\n\ntype LockGrant = object | null;\n\n/** The Web Locks API surface the session uses (`navigator.locks`). */\ntype LockManagerLike = {\n request<T>(\n name: string,\n callback: (lock: LockGrant) => T | Promise<T>,\n ): Promise<T>;\n request<T>(\n name: string,\n options: { mode?: \"shared\" | \"exclusive\"; ifAvailable?: boolean },\n callback: (lock: LockGrant) => T | Promise<T>,\n ): Promise<T>;\n};\n\ntype StorageEventLike = { key: string | null };\n\ntype BrowserGlobals = {\n localStorage?: StorageLike;\n navigator?: { locks?: LockManagerLike };\n addEventListener?: (\n type: \"storage\",\n listener: (event: StorageEventLike) => void,\n ) => void;\n removeEventListener?: (\n type: \"storage\",\n listener: (event: StorageEventLike) => void,\n ) => void;\n};\n\nconst browserGlobals = globalThis as BrowserGlobals;\n\nexport function readPersistedFailedSendStamps(\n sessionKey: string,\n): ReadonlyMap<string, number> | null {\n const storage = browserGlobals.localStorage;\n if (!storage) return null;\n let record: { failed?: unknown } | null;\n try {\n const raw = storage.getItem(SESSION_PREFIX + sessionKey);\n record =\n raw === null ? null : (JSON.parse(raw) as { failed?: unknown } | null);\n } catch {\n return null;\n }\n return readFailedSendStampsFromRecord(record);\n}\n\n/**\n * Send stamps (message id → epoch ms of the original send) off an already\n * loaded session record's failed lane — the storage-agnostic half of\n * {@link readPersistedFailedSendStamps}, for hosts whose session store is not\n * localStorage (the React Native AsyncStorage-backed store reads its record\n * synchronously through `SessionStorage.load()`).\n *\n * Returns `null` for no record (every failed entry then counts as fresh) and\n * an empty map for a record without failures.\n */\nexport function readFailedSendStampsFromRecord(\n record: { failed?: unknown } | null | undefined,\n): ReadonlyMap<string, number> | null {\n if (!record || typeof record !== \"object\") return null;\n const stamps = new Map<string, number>();\n if (!Array.isArray(record.failed)) return stamps;\n for (const entry of record.failed) {\n const failed = entry as {\n ts?: unknown;\n params?: readonly { message?: { id?: unknown } }[];\n } | null;\n const messageId = failed?.params?.[0]?.message?.id;\n if (typeof messageId === \"string\" && typeof failed?.ts === \"number\") {\n stamps.set(messageId, failed.ts);\n }\n }\n return stamps;\n}\n\n/** A storage operation the session survived by degrading instead of failing. */\nexport type SessionStorageDegradation = {\n operation:\n | \"getItem\"\n | \"setItem\"\n | \"removeItem\"\n | \"getAllKeys\"\n | \"withUnused\"\n /** The lock manager refused the transaction fence; it ran unfenced. */\n | \"lock\"\n /**\n * The stored record is one this build cannot read (not JSON, or a newer\n * storage version): served as absent, and the key is shielded from being\n * overwritten so the record survives this tab.\n */\n | \"record\"\n /** statewire's own storage failure (a structurally invalid stored record). */\n | \"session\";\n key: string;\n error: unknown;\n};\n\n/**\n * Whether `error` is the browser refusing a write for lack of room, under\n * every name the engines use — `QuotaExceededError` (Chromium, WebKit, current\n * Firefox), `NS_ERROR_DOM_QUOTA_REACHED` (older Firefox), the legacy\n * `DOMException` codes 22 (WebKit) and 1014 (Firefox) — or a plain error whose\n * message carries one of those (storage proxies and WebViews rewrap the\n * exception). Olympus's `isStorageQuotaError` recognises the same set; the two\n * stay in step.\n */\nexport function isStorageQuotaError(error: unknown): boolean {\n if (typeof error !== \"object\" || error === null) return false;\n const { name, code, message } = error as {\n name?: unknown;\n code?: unknown;\n message?: unknown;\n };\n return (\n name === \"QuotaExceededError\" ||\n name === \"NS_ERROR_DOM_QUOTA_REACHED\" ||\n code === 22 ||\n code === 1014 ||\n (typeof message === \"string\" &&\n /exceeded the quota|quotaexceedederror|ns_error_dom_quota_reached/i.test(\n message,\n ))\n );\n}\n\n/** The one quota shape statewire's retention sweep recognises. */\nfunction asStatewireQuotaError(error: unknown): Error {\n if (error instanceof Error && error.name === \"QuotaExceededError\")\n return error;\n const normalized = new Error(\n error instanceof Error ? error.message : \"storage quota exceeded\",\n );\n normalized.name = \"QuotaExceededError\";\n return normalized;\n}\n\n/**\n * Why `raw` is a stored record this build cannot read, or `undefined` when it\n * may be one. Mirrors the two decode failures statewire raises for a record it\n * cannot read — invalid JSON, and a `statewireStorageVersion` newer than the\n * ones this build's statewire decodes (a tab left open across a\n * statewire upgrade, reading the record a newer tab keeps) — because statewire\n * throws those out of the resource at mount instead of treating the record as\n * absent. A structurally invalid record of the right version still fails\n * inside statewire.\n */\nfunction unreadableRecord(raw: string): Error | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return error instanceof Error\n ? error\n : new Error(\"stored session record is not JSON\");\n }\n if (typeof parsed !== \"object\" || parsed === null) {\n return new Error(\"stored session record is not an object\");\n }\n const version = (parsed as { statewireStorageVersion?: unknown })\n .statewireStorageVersion;\n if (version !== undefined && !READABLE_STORAGE_VERSIONS.has(version)) {\n return new Error(\n `stored session record uses storage version ${String(version)}`,\n );\n }\n return undefined;\n}\n\ntype SyncBackend = {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n getAllKeys: () => readonly string[];\n withUnused: (key: string, action: () => Promise<void>) => Promise<void>;\n};\n\n/**\n * The browser backend `@assistant-ui/react-statewire`'s `LocalStorageSession`\n * uses, reproduced here because that resource does not take a backend: raw\n * `localStorage`, with retention cleanup fenced by the same Web Locks so a\n * sweep never removes a record another tab is actively sharing.\n */\nfunction browserBackend({\n storage,\n locks,\n}: {\n storage: StorageLike;\n locks: LockManagerLike;\n}): SyncBackend {\n return {\n getItem: (key) => storage.getItem(key),\n setItem: (key, value) => storage.setItem(key, value),\n removeItem: (key) => storage.removeItem(key),\n getAllKeys: () =>\n Array.from({ length: storage.length }, (_, index) =>\n storage.key(index),\n ).filter((key): key is string => key !== null),\n withUnused: async (key, action) => {\n await locks.request(\n SESSION_ACTIVE_PREFIX + key,\n { ifAvailable: true },\n async (active) => {\n if (active === null) return;\n await locks.request(\n SESSION_LOCK_PREFIX + key.slice(SESSION_PREFIX.length),\n { ifAvailable: true },\n async (lock) => {\n if (lock === null) return;\n await action();\n },\n );\n },\n );\n },\n };\n}\n\n/**\n * Wraps a synchronous key-value backend so a failing origin degrades the\n * session to memory-only persistence instead of stopping the connection.\n *\n * statewire's session storage latches the first unrecognised write failure\n * and from then on rejects every send (`stopped/storage-error`, \"session\n * storage failed\" on the connection chip). It only recovers a\n * `QuotaExceededError` by that exact name — sweeping old session records and\n * retrying the write once — and latches again when the retry fails too. Here\n * the first quota failure per key is surfaced under that name so the sweep\n * runs; the retry that follows it and every other storage failure are absorbed\n * and reported through `onDegraded`, so chat keeps working.\n *\n * An absorbed write is kept in an in-memory overlay that `getItem` serves\n * first: statewire re-reads the record through `load()` on every shared-mode\n * transaction, so a stale persisted copy would otherwise win back its old\n * `nextSeq`, `clientId` and command journal. The overlay is per tab — another\n * tab sharing the thread still reads the last persisted copy, which is the\n * memory-only degradation this backend exists to allow. A later successful\n * write clears the overlay entry.\n *\n * A key whose read threw, or whose stored value this build cannot read (see\n * {@link unreadableRecord}), is served as absent and shielded: writes to it\n * stay in the overlay for good, so a fresh lane minted over an unreadable\n * record never overwrites what another tab or a newer build persisted.\n */\nexport function quotaTolerantBackend({\n inner,\n onDegraded,\n}: {\n inner: SyncBackend;\n onDegraded: (degradation: SessionStorageDegradation) => void;\n}): KeyValueSessionStorage.Backend {\n // Keys whose write already surfaced a quota error to statewire: until a\n // write succeeds again, later failures on the key are absorbed instead of\n // surfaced, or every save would re-run statewire's sweep (which evicts\n // other threads' idle records) and the failure would latch on the retry.\n const quotaSurfaced = new Set<string>();\n // Absorbed writes, served ahead of the backend (see the function docs).\n const overlay = new Map<string, string>();\n // Keys served as absent and never written through (see the function docs).\n const shielded = new Set<string>();\n // statewire re-reads and re-decodes the record on every transaction; the\n // readability check is memoised on the raw string so it costs one string\n // comparison per read, not a second parse.\n const checked = new Map<\n string,\n { raw: string; verdict: Error | undefined }\n >();\n const readable = (key: string, raw: string): Error | undefined => {\n const previous = checked.get(key);\n if (previous !== undefined && previous.raw === raw) return previous.verdict;\n const verdict = unreadableRecord(raw);\n checked.set(key, { raw, verdict });\n return verdict;\n };\n return {\n getItem: (key) => {\n const shadowed = overlay.get(key);\n if (shadowed !== undefined) return shadowed;\n if (shielded.has(key)) return null;\n let raw: string | null;\n try {\n raw = inner.getItem(key);\n } catch (error) {\n shielded.add(key);\n onDegraded({ operation: \"getItem\", key, error });\n return null;\n }\n if (raw === null) return null;\n const unreadable = readable(key, raw);\n if (unreadable === undefined) return raw;\n shielded.add(key);\n onDegraded({ operation: \"record\", key, error: unreadable });\n return null;\n },\n setItem: (key, value) => {\n if (shielded.has(key)) {\n overlay.set(key, value);\n return;\n }\n try {\n inner.setItem(key, value);\n overlay.delete(key);\n quotaSurfaced.delete(key);\n } catch (error) {\n if (isStorageQuotaError(error) && !quotaSurfaced.has(key)) {\n quotaSurfaced.add(key);\n throw asStatewireQuotaError(error);\n }\n overlay.set(key, value);\n onDegraded({ operation: \"setItem\", key, error });\n }\n },\n removeItem: (key) => {\n overlay.delete(key);\n if (shielded.has(key)) return;\n try {\n inner.removeItem(key);\n } catch (error) {\n onDegraded({ operation: \"removeItem\", key, error });\n }\n },\n getAllKeys: () => {\n const keys = new Set<string>(overlay.keys());\n try {\n for (const key of inner.getAllKeys()) keys.add(key);\n } catch (error) {\n onDegraded({ operation: \"getAllKeys\", key: \"\", error });\n }\n return [...keys];\n },\n withUnused: async (key, action) => {\n try {\n await inner.withUnused(key, action);\n } catch (error) {\n onDegraded({ operation: \"withUnused\", key, error });\n }\n },\n };\n}\n\ntype DegradationListener = (degradation: SessionStorageDegradation) => void;\n\ntype SharedBackend = {\n backend: KeyValueSessionStorage.Backend;\n listeners: Set<DegradationListener>;\n};\n\n/**\n * One backend per storage object — per origin, in a browser — shared by every\n * session mounted in the tab, exactly as the upstream resource's module-level\n * backend is. statewire keys its in-tab \"active\" bookkeeping by backend\n * identity, so a mount's retention sweep only skips the records of other\n * mounts on the *same* backend; and the absorbed-write overlay has to outlive\n * a remount of the same thread (a thread switch and back) or the remount would\n * read the stale persisted copy. Degradations fan out to every mounted\n * session's reporter.\n */\nconst sharedBackends = new WeakMap<StorageLike, SharedBackend>();\n\nfunction sharedBackend({\n storage,\n locks,\n}: {\n storage: StorageLike;\n locks: LockManagerLike;\n}): SharedBackend {\n let shared = sharedBackends.get(storage);\n if (shared === undefined) {\n const listeners = new Set<DegradationListener>();\n shared = {\n listeners,\n backend: quotaTolerantBackend({\n inner: browserBackend({ storage, locks }),\n onDegraded: (degradation) => {\n for (const listener of listeners) listener(degradation);\n },\n }),\n };\n sharedBackends.set(storage, shared);\n }\n return shared;\n}\n\n/**\n * Athena's browser session store: `@assistant-ui/react-statewire`'s\n * `LocalStorageSession` contract (per-thread key under the shared prefix,\n * cross-tab Web Lock, storage-event subscription, retention) on top of\n * {@link quotaTolerantBackend}, with Athena's legacy protocol and retention\n * policy.\n */\nconst useQuotaSafeLocalStorageSession = ({\n threadId,\n key = (id) => id,\n ttl = 7 * DAY_MS,\n max = 50,\n onStorageDegraded,\n}: QuotaSafeLocalStorageSession.Options): StatewireClient.SessionStorage => {\n const storage = browserGlobals.localStorage;\n const locks = browserGlobals.navigator?.locks;\n if (!storage)\n throw new Error(\n \"statewire: QuotaSafeLocalStorageSession requires localStorage\",\n );\n if (!locks)\n throw new Error(\n \"statewire: QuotaSafeLocalStorageSession requires the Web Locks API (navigator.locks)\",\n );\n const sessionKey = key(threadId);\n const storageKey = SESSION_PREFIX + sessionKey;\n const lockName = SESSION_LOCK_PREFIX + sessionKey;\n\n const latestOnDegraded = useRef(onStorageDegraded);\n latestOnDegraded.current = onStorageDegraded;\n const reportDegradation = useMemo(() => {\n const reported = new Set<string>();\n return (degradation: SessionStorageDegradation) => {\n const handler = latestOnDegraded.current;\n if (handler) {\n try {\n handler(degradation);\n } catch {\n // Reporting runs inside the session's recovery path and must never\n // become the failure it is reporting.\n }\n return;\n }\n const dedupeKey = `${degradation.operation}:${degradation.key}`;\n if (reported.has(dedupeKey)) return;\n reported.add(dedupeKey);\n console.warn(\n `[statewire] session storage degraded to memory only (${degradation.operation} ${degradation.key})`,\n degradation.error,\n );\n };\n }, []);\n\n const shared = sharedBackend({ storage, locks });\n // Registered during render as well as in the effect: the read statewire\n // makes while mounting below happens before any effect runs, and tap may\n // clean an effect up and re-run it within one commit.\n shared.listeners.add(reportDegradation);\n useEffect(() => {\n shared.listeners.add(reportDegradation);\n return () => {\n shared.listeners.delete(reportDegradation);\n };\n }, [shared, reportDegradation]);\n\n // Hold the record's shared \"active\" lock while mounted so another tab's\n // retention sweep skips it (the same fence the upstream resource holds).\n useEffect(() => {\n let release: (() => void) | undefined;\n let disposed = false;\n Promise.resolve(\n locks.request(\n SESSION_ACTIVE_PREFIX + storageKey,\n { mode: \"shared\" },\n () => {\n if (disposed) return;\n return new Promise<void>((resolve) => {\n release = resolve;\n });\n },\n ),\n ).catch((error: unknown) =>\n reportDegradation({ operation: \"lock\", key: storageKey, error }),\n );\n return () => {\n disposed = true;\n release?.();\n };\n }, [locks, storageKey, reportDegradation]);\n\n const sessionStorage = useResource(\n KeyValueSessionStorage({\n backend: shared.backend,\n key: sessionKey,\n namespace: SESSION_PREFIX,\n legacyProtocol: LEGACY_PROTOCOL,\n retention: { maxAgeMs: ttl, maxEntries: max },\n // A failure statewire raises itself (a structurally invalid stored\n // record) still stops the session; it is at least reported the same way.\n onError: (error) =>\n reportDegradation({ operation: \"session\", key: storageKey, error }),\n }),\n );\n\n return useMemo(\n () => ({\n ...sessionStorage,\n lock: async <T>(fn: () => T | Promise<T>): Promise<T> => {\n let granted = false;\n try {\n return await locks.request(lockName, async () => {\n granted = true;\n return fn();\n });\n } catch (error) {\n if (granted) throw error;\n // The lock manager refused the request itself (a detached or\n // back/forward-cached document, a lockdown-mode origin): the\n // transaction runs unfenced rather than stopping the session.\n reportDegradation({ operation: \"lock\", key: storageKey, error });\n return fn();\n }\n },\n subscribe: (listener: () => void) => {\n const onStorage = (event: StorageEventLike) => {\n if (event.key === storageKey) listener();\n };\n browserGlobals.addEventListener?.(\"storage\", onStorage);\n return () => browserGlobals.removeEventListener?.(\"storage\", onStorage);\n },\n }),\n [sessionStorage, locks, storageKey, lockName, reportDegradation],\n );\n};\n\n/** Athena's browser session keys and retention policy. */\nexport const QuotaSafeLocalStorageSession = resource(\n useQuotaSafeLocalStorageSession,\n);\n\nexport namespace QuotaSafeLocalStorageSession {\n export type Options = {\n threadId: string;\n key?: (threadId: string) => string;\n ttl?: number;\n max?: number;\n /**\n * Called when a storage operation failed and the session carried on in\n * memory. Defaults to one `console.warn` per operation and key.\n */\n onStorageDegraded?: (degradation: SessionStorageDegradation) => void;\n };\n}\n"],"names":["_a"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const SCREEN_POINT_PROTOCOL = "athena-point:";
4
+ const SCREEN_POINT_HREF_PREFIX = `${SCREEN_POINT_PROTOCOL}//`;
5
+ const SCREEN_POINT_MAX_EXTENT = 32767;
6
+ const NUMBER_PATTERN = /^~?\s*(-?\d+(?:\.\d+)?)$/;
7
+ function parseCoordinateNumber(raw) {
8
+ if (raw === null || raw.trim() === "") return { ok: false, error: "missing" };
9
+ const match = NUMBER_PATTERN.exec(raw.trim());
10
+ if (!match) return { ok: false, error: "non-finite-number" };
11
+ const value = Number(match[1]);
12
+ if (!Number.isFinite(value)) return { ok: false, error: "non-finite-number" };
13
+ if (value < 0) return { ok: false, error: "negative-number" };
14
+ if (value > SCREEN_POINT_MAX_EXTENT) return { ok: false, error: "extent-too-large" };
15
+ return { ok: true, value: Math.round(value) };
16
+ }
17
+ function isScreenPointHref(href) {
18
+ return typeof href === "string" && href.startsWith(SCREEN_POINT_HREF_PREFIX);
19
+ }
20
+ function parseScreenPointHref(href) {
21
+ if (!isScreenPointHref(href)) return { ok: false, error: "not-screen-point" };
22
+ const queryStart = href.indexOf("?");
23
+ if (queryStart === -1) return { ok: false, error: "missing-coordinate" };
24
+ const params = new URLSearchParams(href.slice(queryStart + 1));
25
+ const x = parseCoordinateNumber(params.get("x"));
26
+ const y = parseCoordinateNumber(params.get("y"));
27
+ if (!x.ok) return { ok: false, error: x.error === "missing" ? "missing-coordinate" : x.error };
28
+ if (!y.ok) return { ok: false, error: y.error === "missing" ? "missing-coordinate" : y.error };
29
+ const widthRaw = params.get("w") ?? params.get("width") ?? params.get("basisWidth");
30
+ const heightRaw = params.get("h") ?? params.get("height") ?? params.get("basisHeight");
31
+ let basis = null;
32
+ if (widthRaw !== null || heightRaw !== null) {
33
+ const width = parseCoordinateNumber(widthRaw);
34
+ const height = parseCoordinateNumber(heightRaw);
35
+ if (!width.ok || !height.ok) {
36
+ const failed = !width.ok ? width : height;
37
+ if (failed.ok) return { ok: false, error: "degenerate-basis" };
38
+ return {
39
+ ok: false,
40
+ error: failed.error === "missing" ? "degenerate-basis" : failed.error
41
+ };
42
+ }
43
+ if (width.value <= 0 || height.value <= 0) return { ok: false, error: "degenerate-basis" };
44
+ basis = { width: width.value, height: height.value };
45
+ if (x.value > basis.width || y.value > basis.height) {
46
+ return { ok: false, error: "out-of-bounds" };
47
+ }
48
+ }
49
+ const tabRaw = params.get("tab") ?? params.get("tab_id") ?? params.get("tabId");
50
+ let tabId = null;
51
+ if (tabRaw !== null && tabRaw.trim() !== "") {
52
+ const tab = Number(tabRaw);
53
+ tabId = Number.isInteger(tab) && tab >= 0 ? tab : null;
54
+ }
55
+ return { ok: true, point: { x: x.value, y: y.value, basis, tabId } };
56
+ }
57
+ function formatScreenPoint(point) {
58
+ const pair = `(${point.x}, ${point.y})`;
59
+ if (!point.basis) return `${pair} — screenshot size not recorded`;
60
+ return `${pair} in a ${point.basis.width}×${point.basis.height} screenshot`;
61
+ }
62
+ exports.SCREEN_POINT_HREF_PREFIX = SCREEN_POINT_HREF_PREFIX;
63
+ exports.SCREEN_POINT_MAX_EXTENT = SCREEN_POINT_MAX_EXTENT;
64
+ exports.SCREEN_POINT_PROTOCOL = SCREEN_POINT_PROTOCOL;
65
+ exports.formatScreenPoint = formatScreenPoint;
66
+ exports.isScreenPointHref = isScreenPointHref;
67
+ exports.parseScreenPointHref = parseScreenPointHref;
68
+ //# sourceMappingURL=screen-point.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen-point.cjs","sources":["../../../../../agent-runtime/src/screen-point.ts"],"sourcesContent":["/**\n * Screen points: the agent's on-screen coordinates as structured, renderable\n * data.\n *\n * Browser agents already speak in `[x, y]` pairs — the `coordinate` argument\n * of `chrome_computer` / `device_computer` / `computer` is a pixel in the\n * screenshot the model was shown. In prose those pairs arrive as free text\n * (\"Delete at ~(1038, 81)\"), which no surface can render as anything but\n * characters. A screen point is that same pair written as a markdown link\n * whose destination carries the pair *and the dimensions of the screenshot it\n * was read from*:\n *\n * [Delete](athena-point://screenshot?x=1038&y=81&w=1280&h=720&tab=8811)\n *\n * The agent keeps its native vocabulary (the coordinate it already has, the\n * screenshot size it was told) and does no arithmetic; every chat surface\n * that understands the scheme renders the link as a chip and, because the\n * basis dimensions travel with the point, can place a marker at the right\n * relative spot on any later rendition of that screenshot however the\n * transcript is laid out or the user's window is resized. Surfaces that do\n * not understand the scheme show the label as plain text (a link with an\n * unknown protocol), which is exactly the prose we have today — the format\n * degrades to the status quo, never below it.\n *\n * Pure string and number work, UI-library-free, shared by every chat surface\n * (Olympus, the React SDK hosts, the Chrome extension side panel) so a point\n * that is valid on one surface is valid on all of them.\n */\n\n/** URL scheme of a screen-point link. */\nexport const SCREEN_POINT_PROTOCOL = \"athena-point:\";\n/** Host of a screen-point link: the coordinate space the pair lives in. */\nexport const SCREEN_POINT_HOST = \"screenshot\";\n/** `href.startsWith(SCREEN_POINT_HREF_PREFIX)` is the cheap pre-check every renderer runs. */\nexport const SCREEN_POINT_HREF_PREFIX = `${SCREEN_POINT_PROTOCOL}//`;\n/**\n * The largest coordinate or dimension a point may carry. No screenshot the\n * runtime delivers approaches this; anything larger is a typo or a unit mix-up.\n */\nexport const SCREEN_POINT_MAX_EXTENT = 32_767;\n/**\n * The model-facing CUA screenshot size the Chrome extension emits by default\n * (`athena-in-chrome/extension/src/background.ts`, `CHROME_DEFAULT_WIDTH` /\n * `CHROME_DEFAULT_HEIGHT` in `chrome_browser_tools.py`). Surfaces may offer\n * it as the fallback basis for a point that omits `w`/`h`, clearly labelled\n * as assumed.\n */\nexport const DEFAULT_CUA_SCREENSHOT_SIZE = { width: 1280, height: 720 } as const;\n\n/** Width and height of a coordinate space, in that space's pixels. */\nexport interface ScreenSize {\n width: number;\n height: number;\n}\n\n/**\n * A point in screenshot space. `basis` is the size of the screenshot the pair\n * was read from; `null` when the link omitted it, in which case the point can\n * be shown but not placed. `tabId` is the Chrome tab the screenshot came from\n * when the agent had one.\n */\nexport interface ScreenPoint {\n x: number;\n y: number;\n basis: ScreenSize | null;\n tabId: number | null;\n}\n\n/**\n * Why a link that starts with the scheme could not become a point. Rendered\n * surfaces show the label as text with the reason in a tooltip, so a broken\n * link never becomes an interactive marker that points nowhere.\n */\nexport type ScreenPointParseError =\n | \"not-screen-point\"\n | \"missing-coordinate\"\n | \"non-finite-number\"\n | \"negative-number\"\n | \"degenerate-basis\"\n | \"out-of-bounds\"\n | \"extent-too-large\";\n\nexport type ScreenPointParseResult =\n | { ok: true; point: ScreenPoint }\n | { ok: false; error: ScreenPointParseError };\n\n/** A number the agent may have written with a decimal point or a `~`: keep the integer pixel. */\nconst NUMBER_PATTERN = /^~?\\s*(-?\\d+(?:\\.\\d+)?)$/;\n\ntype NumberParse =\n | { ok: true; value: number }\n | { ok: false; error: \"non-finite-number\" | \"negative-number\" | \"extent-too-large\" }\n | { ok: false; error: \"missing\" };\n\nfunction parseCoordinateNumber(raw: string | null): NumberParse {\n if (raw === null || raw.trim() === \"\") return { ok: false, error: \"missing\" };\n const match = NUMBER_PATTERN.exec(raw.trim());\n if (!match) return { ok: false, error: \"non-finite-number\" };\n const value = Number(match[1]);\n if (!Number.isFinite(value)) return { ok: false, error: \"non-finite-number\" };\n if (value < 0) return { ok: false, error: \"negative-number\" };\n if (value > SCREEN_POINT_MAX_EXTENT) return { ok: false, error: \"extent-too-large\" };\n return { ok: true, value: Math.round(value) };\n}\n\n/** Whether `href` is a screen-point link at all (the cheap pre-check). */\nexport function isScreenPointHref(href: string | null | undefined): href is string {\n return typeof href === \"string\" && href.startsWith(SCREEN_POINT_HREF_PREFIX);\n}\n\n/**\n * Parse a screen-point link. Accepts the canonical form\n * `athena-point://screenshot?x=…&y=…&w=…&h=…&tab=…` and tolerates what a\n * model plausibly writes instead: `width`/`height` for `w`/`h`, `tab_id` for\n * `tab`, decimals, a leading `~`, and an empty or different host. Rejects,\n * with a reason, anything that would place a marker in the wrong spot:\n * non-finite or negative numbers, a zero or negative basis, a point outside\n * its basis, and absurd extents.\n */\nexport function parseScreenPointHref(href: string | null | undefined): ScreenPointParseResult {\n if (!isScreenPointHref(href)) return { ok: false, error: \"not-screen-point\" };\n\n // `new URL` treats a non-special scheme with an empty host inconsistently\n // across engines; the query is the only part we read, so slice it off.\n const queryStart = href.indexOf(\"?\");\n if (queryStart === -1) return { ok: false, error: \"missing-coordinate\" };\n const params = new URLSearchParams(href.slice(queryStart + 1));\n\n const x = parseCoordinateNumber(params.get(\"x\"));\n const y = parseCoordinateNumber(params.get(\"y\"));\n if (!x.ok) return { ok: false, error: x.error === \"missing\" ? \"missing-coordinate\" : x.error };\n if (!y.ok) return { ok: false, error: y.error === \"missing\" ? \"missing-coordinate\" : y.error };\n\n const widthRaw = params.get(\"w\") ?? params.get(\"width\") ?? params.get(\"basisWidth\");\n const heightRaw = params.get(\"h\") ?? params.get(\"height\") ?? params.get(\"basisHeight\");\n let basis: ScreenSize | null = null;\n if (widthRaw !== null || heightRaw !== null) {\n const width = parseCoordinateNumber(widthRaw);\n const height = parseCoordinateNumber(heightRaw);\n // One dimension without the other is not a basis.\n if (!width.ok || !height.ok) {\n const failed = !width.ok ? width : height;\n if (failed.ok) return { ok: false, error: \"degenerate-basis\" };\n return {\n ok: false,\n error: failed.error === \"missing\" ? \"degenerate-basis\" : failed.error,\n };\n }\n if (width.value <= 0 || height.value <= 0) return { ok: false, error: \"degenerate-basis\" };\n basis = { width: width.value, height: height.value };\n if (x.value > basis.width || y.value > basis.height) {\n return { ok: false, error: \"out-of-bounds\" };\n }\n }\n\n const tabRaw = params.get(\"tab\") ?? params.get(\"tab_id\") ?? params.get(\"tabId\");\n let tabId: number | null = null;\n if (tabRaw !== null && tabRaw.trim() !== \"\") {\n const tab = Number(tabRaw);\n tabId = Number.isInteger(tab) && tab >= 0 ? tab : null;\n }\n\n return { ok: true, point: { x: x.value, y: y.value, basis, tabId } };\n}\n\n/** The canonical link for a point — what the prompt teaches the agent to write. */\nexport function serializeScreenPointHref(point: ScreenPoint): string {\n const params = new URLSearchParams();\n params.set(\"x\", String(point.x));\n params.set(\"y\", String(point.y));\n if (point.basis) {\n params.set(\"w\", String(point.basis.width));\n params.set(\"h\", String(point.basis.height));\n }\n if (point.tabId !== null) params.set(\"tab\", String(point.tabId));\n return `${SCREEN_POINT_HREF_PREFIX}${SCREEN_POINT_HOST}?${params.toString()}`;\n}\n\n/**\n * Where the point sits as a fraction of its basis, `0..1` on each axis, or\n * `null` when the point has no basis to be relative to. This is the only\n * number a renderer needs: `left: ${fx * 100}%` places the marker on any\n * rendition of the screenshot, whatever size it is drawn at.\n */\nexport function screenPointFraction(point: ScreenPoint): { fx: number; fy: number } | null {\n if (!point.basis) return null;\n return { fx: point.x / point.basis.width, fy: point.y / point.basis.height };\n}\n\n/** Whether two sizes are the same to within the few-pixel jitter a resize or scrollbar introduces. */\nexport function sameScreenSize({\n a,\n b,\n tolerancePx = 4,\n}: {\n a: ScreenSize;\n b: ScreenSize;\n tolerancePx?: number;\n}): boolean {\n return Math.abs(a.width - b.width) <= tolerancePx && Math.abs(a.height - b.height) <= tolerancePx;\n}\n\n/**\n * How a point's basis relates to the screenshot it is about to be drawn on.\n * Mirrors the runtime's dispatch contract (`browser_coordinate_contract.json`):\n * the same size or a few pixels of jitter is `identity`; a uniform ratio on\n * both axes (browser zoom, a resized window at the same aspect) is\n * `uniform-scale` and the fraction still lands on the same element; anything\n * else is a `mismatch` — a different aspect ratio means the screenshot shows a\n * different framing (chrome headroom, a rotated device, a different tab) and\n * a fraction placed on it would point at the wrong thing.\n */\nexport type ScreenBasisFit =\n | { kind: \"identity\" }\n | { kind: \"uniform-scale\"; scale: number }\n | { kind: \"mismatch\"; aspectDelta: number };\n\nexport function fitScreenBasis({\n basis,\n target,\n aspectTolerance = 0.02,\n}: {\n basis: ScreenSize;\n target: ScreenSize;\n aspectTolerance?: number;\n}): ScreenBasisFit {\n if (\n basis.width <= 0 ||\n basis.height <= 0 ||\n target.width <= 0 ||\n target.height <= 0 ||\n ![basis.width, basis.height, target.width, target.height].every(Number.isFinite)\n ) {\n return { kind: \"mismatch\", aspectDelta: Number.POSITIVE_INFINITY };\n }\n if (sameScreenSize({ a: basis, b: target })) return { kind: \"identity\" };\n const scaleX = target.width / basis.width;\n const scaleY = target.height / basis.height;\n const aspectDelta = Math.abs(scaleX - scaleY) / Math.max(scaleX, scaleY);\n if (aspectDelta <= aspectTolerance) return { kind: \"uniform-scale\", scale: (scaleX + scaleY) / 2 };\n return { kind: \"mismatch\", aspectDelta };\n}\n\n/** Human-readable point for tooltips and accessible names: `(1038, 81) in a 1280×720 screenshot`. */\nexport function formatScreenPoint(point: ScreenPoint): string {\n const pair = `(${point.x}, ${point.y})`;\n if (!point.basis) return `${pair} — screenshot size not recorded`;\n return `${pair} in a ${point.basis.width}×${point.basis.height} screenshot`;\n}\n\n/** The sentence a surface shows for a link that failed to parse. */\nexport function describeScreenPointError(error: ScreenPointParseError): string {\n switch (error) {\n case \"not-screen-point\":\n return \"Not a screen point\";\n case \"missing-coordinate\":\n return \"Screen point is missing x or y\";\n case \"non-finite-number\":\n return \"Screen point has a non-numeric value\";\n case \"negative-number\":\n return \"Screen point has a negative value\";\n case \"degenerate-basis\":\n return \"Screen point has an invalid screenshot size\";\n case \"out-of-bounds\":\n return \"Screen point lies outside its screenshot\";\n case \"extent-too-large\":\n return \"Screen point value is implausibly large\";\n }\n}\n\n/**\n * Screen points written into a text block, in order. Used by surfaces that\n * work on the raw markdown (the static message body's node parser, the\n * extension's plain-text composer) and by tests. Only the link form is\n * recognised — a bare `(1038, 81)` in prose is text, never a point, so a\n * model that was not taught the scheme changes nothing.\n */\nexport function findScreenPointLinks(\n text: string,\n): Array<{ start: number; end: number; label: string; href: string }> {\n const results: Array<{ start: number; end: number; label: string; href: string }> = [];\n const pattern = /\\[([^\\]\\n]*)\\]\\((athena-point:\\/\\/[^\\s)]*)\\)/g;\n for (const match of text.matchAll(pattern)) {\n if (match.index === undefined) continue;\n results.push({\n start: match.index,\n end: match.index + match[0].length,\n label: match[1],\n href: match[2],\n });\n }\n return results;\n}\n"],"names":[],"mappings":";;AA8BO,MAAM,wBAAwB;AAI9B,MAAM,2BAA2B,GAAG,qBAAqB;AAKzD,MAAM,0BAA0B;AAgDvC,MAAM,iBAAiB;AAOvB,SAAS,sBAAsB,KAAiC;AAC9D,MAAI,QAAQ,QAAQ,IAAI,KAAA,MAAW,GAAI,QAAO,EAAE,IAAI,OAAO,OAAO,UAAA;AAClE,QAAM,QAAQ,eAAe,KAAK,IAAI,MAAM;AAC5C,MAAI,CAAC,MAAO,QAAO,EAAE,IAAI,OAAO,OAAO,oBAAA;AACvC,QAAM,QAAQ,OAAO,MAAM,CAAC,CAAC;AAC7B,MAAI,CAAC,OAAO,SAAS,KAAK,UAAU,EAAE,IAAI,OAAO,OAAO,oBAAA;AACxD,MAAI,QAAQ,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,kBAAA;AAC1C,MAAI,QAAQ,wBAAyB,QAAO,EAAE,IAAI,OAAO,OAAO,mBAAA;AAChE,SAAO,EAAE,IAAI,MAAM,OAAO,KAAK,MAAM,KAAK,EAAA;AAC5C;AAGO,SAAS,kBAAkB,MAAiD;AACjF,SAAO,OAAO,SAAS,YAAY,KAAK,WAAW,wBAAwB;AAC7E;AAWO,SAAS,qBAAqB,MAAyD;AAC5F,MAAI,CAAC,kBAAkB,IAAI,UAAU,EAAE,IAAI,OAAO,OAAO,mBAAA;AAIzD,QAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,MAAI,eAAe,GAAI,QAAO,EAAE,IAAI,OAAO,OAAO,qBAAA;AAClD,QAAM,SAAS,IAAI,gBAAgB,KAAK,MAAM,aAAa,CAAC,CAAC;AAE7D,QAAM,IAAI,sBAAsB,OAAO,IAAI,GAAG,CAAC;AAC/C,QAAM,IAAI,sBAAsB,OAAO,IAAI,GAAG,CAAC;AAC/C,MAAI,CAAC,EAAE,GAAI,QAAO,EAAE,IAAI,OAAO,OAAO,EAAE,UAAU,YAAY,uBAAuB,EAAE,MAAA;AACvF,MAAI,CAAC,EAAE,GAAI,QAAO,EAAE,IAAI,OAAO,OAAO,EAAE,UAAU,YAAY,uBAAuB,EAAE,MAAA;AAEvF,QAAM,WAAW,OAAO,IAAI,GAAG,KAAK,OAAO,IAAI,OAAO,KAAK,OAAO,IAAI,YAAY;AAClF,QAAM,YAAY,OAAO,IAAI,GAAG,KAAK,OAAO,IAAI,QAAQ,KAAK,OAAO,IAAI,aAAa;AACrF,MAAI,QAA2B;AAC/B,MAAI,aAAa,QAAQ,cAAc,MAAM;AAC3C,UAAM,QAAQ,sBAAsB,QAAQ;AAC5C,UAAM,SAAS,sBAAsB,SAAS;AAE9C,QAAI,CAAC,MAAM,MAAM,CAAC,OAAO,IAAI;AAC3B,YAAM,SAAS,CAAC,MAAM,KAAK,QAAQ;AACnC,UAAI,OAAO,GAAI,QAAO,EAAE,IAAI,OAAO,OAAO,mBAAA;AAC1C,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO,OAAO,UAAU,YAAY,qBAAqB,OAAO;AAAA,MAAA;AAAA,IAEpE;AACA,QAAI,MAAM,SAAS,KAAK,OAAO,SAAS,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,mBAAA;AACtE,YAAQ,EAAE,OAAO,MAAM,OAAO,QAAQ,OAAO,MAAA;AAC7C,QAAI,EAAE,QAAQ,MAAM,SAAS,EAAE,QAAQ,MAAM,QAAQ;AACnD,aAAO,EAAE,IAAI,OAAO,OAAO,gBAAA;AAAA,IAC7B;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,IAAI,KAAK,KAAK,OAAO,IAAI,QAAQ,KAAK,OAAO,IAAI,OAAO;AAC9E,MAAI,QAAuB;AAC3B,MAAI,WAAW,QAAQ,OAAO,KAAA,MAAW,IAAI;AAC3C,UAAM,MAAM,OAAO,MAAM;AACzB,YAAQ,OAAO,UAAU,GAAG,KAAK,OAAO,IAAI,MAAM;AAAA,EACpD;AAEA,SAAO,EAAE,IAAI,MAAM,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,EAAE,OAAO,OAAO,QAAM;AACnE;AAiFO,SAAS,kBAAkB,OAA4B;AAC5D,QAAM,OAAO,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC;AACpC,MAAI,CAAC,MAAM,MAAO,QAAO,GAAG,IAAI;AAChC,SAAO,GAAG,IAAI,SAAS,MAAM,MAAM,KAAK,IAAI,MAAM,MAAM,MAAM;AAChE;;;;;;;"}
@@ -0,0 +1,68 @@
1
+ const SCREEN_POINT_PROTOCOL = "athena-point:";
2
+ const SCREEN_POINT_HREF_PREFIX = `${SCREEN_POINT_PROTOCOL}//`;
3
+ const SCREEN_POINT_MAX_EXTENT = 32767;
4
+ const NUMBER_PATTERN = /^~?\s*(-?\d+(?:\.\d+)?)$/;
5
+ function parseCoordinateNumber(raw) {
6
+ if (raw === null || raw.trim() === "") return { ok: false, error: "missing" };
7
+ const match = NUMBER_PATTERN.exec(raw.trim());
8
+ if (!match) return { ok: false, error: "non-finite-number" };
9
+ const value = Number(match[1]);
10
+ if (!Number.isFinite(value)) return { ok: false, error: "non-finite-number" };
11
+ if (value < 0) return { ok: false, error: "negative-number" };
12
+ if (value > SCREEN_POINT_MAX_EXTENT) return { ok: false, error: "extent-too-large" };
13
+ return { ok: true, value: Math.round(value) };
14
+ }
15
+ function isScreenPointHref(href) {
16
+ return typeof href === "string" && href.startsWith(SCREEN_POINT_HREF_PREFIX);
17
+ }
18
+ function parseScreenPointHref(href) {
19
+ if (!isScreenPointHref(href)) return { ok: false, error: "not-screen-point" };
20
+ const queryStart = href.indexOf("?");
21
+ if (queryStart === -1) return { ok: false, error: "missing-coordinate" };
22
+ const params = new URLSearchParams(href.slice(queryStart + 1));
23
+ const x = parseCoordinateNumber(params.get("x"));
24
+ const y = parseCoordinateNumber(params.get("y"));
25
+ if (!x.ok) return { ok: false, error: x.error === "missing" ? "missing-coordinate" : x.error };
26
+ if (!y.ok) return { ok: false, error: y.error === "missing" ? "missing-coordinate" : y.error };
27
+ const widthRaw = params.get("w") ?? params.get("width") ?? params.get("basisWidth");
28
+ const heightRaw = params.get("h") ?? params.get("height") ?? params.get("basisHeight");
29
+ let basis = null;
30
+ if (widthRaw !== null || heightRaw !== null) {
31
+ const width = parseCoordinateNumber(widthRaw);
32
+ const height = parseCoordinateNumber(heightRaw);
33
+ if (!width.ok || !height.ok) {
34
+ const failed = !width.ok ? width : height;
35
+ if (failed.ok) return { ok: false, error: "degenerate-basis" };
36
+ return {
37
+ ok: false,
38
+ error: failed.error === "missing" ? "degenerate-basis" : failed.error
39
+ };
40
+ }
41
+ if (width.value <= 0 || height.value <= 0) return { ok: false, error: "degenerate-basis" };
42
+ basis = { width: width.value, height: height.value };
43
+ if (x.value > basis.width || y.value > basis.height) {
44
+ return { ok: false, error: "out-of-bounds" };
45
+ }
46
+ }
47
+ const tabRaw = params.get("tab") ?? params.get("tab_id") ?? params.get("tabId");
48
+ let tabId = null;
49
+ if (tabRaw !== null && tabRaw.trim() !== "") {
50
+ const tab = Number(tabRaw);
51
+ tabId = Number.isInteger(tab) && tab >= 0 ? tab : null;
52
+ }
53
+ return { ok: true, point: { x: x.value, y: y.value, basis, tabId } };
54
+ }
55
+ function formatScreenPoint(point) {
56
+ const pair = `(${point.x}, ${point.y})`;
57
+ if (!point.basis) return `${pair} — screenshot size not recorded`;
58
+ return `${pair} in a ${point.basis.width}×${point.basis.height} screenshot`;
59
+ }
60
+ export {
61
+ SCREEN_POINT_HREF_PREFIX,
62
+ SCREEN_POINT_MAX_EXTENT,
63
+ SCREEN_POINT_PROTOCOL,
64
+ formatScreenPoint,
65
+ isScreenPointHref,
66
+ parseScreenPointHref
67
+ };
68
+ //# sourceMappingURL=screen-point.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen-point.js","sources":["../../../../../agent-runtime/src/screen-point.ts"],"sourcesContent":["/**\n * Screen points: the agent's on-screen coordinates as structured, renderable\n * data.\n *\n * Browser agents already speak in `[x, y]` pairs — the `coordinate` argument\n * of `chrome_computer` / `device_computer` / `computer` is a pixel in the\n * screenshot the model was shown. In prose those pairs arrive as free text\n * (\"Delete at ~(1038, 81)\"), which no surface can render as anything but\n * characters. A screen point is that same pair written as a markdown link\n * whose destination carries the pair *and the dimensions of the screenshot it\n * was read from*:\n *\n * [Delete](athena-point://screenshot?x=1038&y=81&w=1280&h=720&tab=8811)\n *\n * The agent keeps its native vocabulary (the coordinate it already has, the\n * screenshot size it was told) and does no arithmetic; every chat surface\n * that understands the scheme renders the link as a chip and, because the\n * basis dimensions travel with the point, can place a marker at the right\n * relative spot on any later rendition of that screenshot however the\n * transcript is laid out or the user's window is resized. Surfaces that do\n * not understand the scheme show the label as plain text (a link with an\n * unknown protocol), which is exactly the prose we have today — the format\n * degrades to the status quo, never below it.\n *\n * Pure string and number work, UI-library-free, shared by every chat surface\n * (Olympus, the React SDK hosts, the Chrome extension side panel) so a point\n * that is valid on one surface is valid on all of them.\n */\n\n/** URL scheme of a screen-point link. */\nexport const SCREEN_POINT_PROTOCOL = \"athena-point:\";\n/** Host of a screen-point link: the coordinate space the pair lives in. */\nexport const SCREEN_POINT_HOST = \"screenshot\";\n/** `href.startsWith(SCREEN_POINT_HREF_PREFIX)` is the cheap pre-check every renderer runs. */\nexport const SCREEN_POINT_HREF_PREFIX = `${SCREEN_POINT_PROTOCOL}//`;\n/**\n * The largest coordinate or dimension a point may carry. No screenshot the\n * runtime delivers approaches this; anything larger is a typo or a unit mix-up.\n */\nexport const SCREEN_POINT_MAX_EXTENT = 32_767;\n/**\n * The model-facing CUA screenshot size the Chrome extension emits by default\n * (`athena-in-chrome/extension/src/background.ts`, `CHROME_DEFAULT_WIDTH` /\n * `CHROME_DEFAULT_HEIGHT` in `chrome_browser_tools.py`). Surfaces may offer\n * it as the fallback basis for a point that omits `w`/`h`, clearly labelled\n * as assumed.\n */\nexport const DEFAULT_CUA_SCREENSHOT_SIZE = { width: 1280, height: 720 } as const;\n\n/** Width and height of a coordinate space, in that space's pixels. */\nexport interface ScreenSize {\n width: number;\n height: number;\n}\n\n/**\n * A point in screenshot space. `basis` is the size of the screenshot the pair\n * was read from; `null` when the link omitted it, in which case the point can\n * be shown but not placed. `tabId` is the Chrome tab the screenshot came from\n * when the agent had one.\n */\nexport interface ScreenPoint {\n x: number;\n y: number;\n basis: ScreenSize | null;\n tabId: number | null;\n}\n\n/**\n * Why a link that starts with the scheme could not become a point. Rendered\n * surfaces show the label as text with the reason in a tooltip, so a broken\n * link never becomes an interactive marker that points nowhere.\n */\nexport type ScreenPointParseError =\n | \"not-screen-point\"\n | \"missing-coordinate\"\n | \"non-finite-number\"\n | \"negative-number\"\n | \"degenerate-basis\"\n | \"out-of-bounds\"\n | \"extent-too-large\";\n\nexport type ScreenPointParseResult =\n | { ok: true; point: ScreenPoint }\n | { ok: false; error: ScreenPointParseError };\n\n/** A number the agent may have written with a decimal point or a `~`: keep the integer pixel. */\nconst NUMBER_PATTERN = /^~?\\s*(-?\\d+(?:\\.\\d+)?)$/;\n\ntype NumberParse =\n | { ok: true; value: number }\n | { ok: false; error: \"non-finite-number\" | \"negative-number\" | \"extent-too-large\" }\n | { ok: false; error: \"missing\" };\n\nfunction parseCoordinateNumber(raw: string | null): NumberParse {\n if (raw === null || raw.trim() === \"\") return { ok: false, error: \"missing\" };\n const match = NUMBER_PATTERN.exec(raw.trim());\n if (!match) return { ok: false, error: \"non-finite-number\" };\n const value = Number(match[1]);\n if (!Number.isFinite(value)) return { ok: false, error: \"non-finite-number\" };\n if (value < 0) return { ok: false, error: \"negative-number\" };\n if (value > SCREEN_POINT_MAX_EXTENT) return { ok: false, error: \"extent-too-large\" };\n return { ok: true, value: Math.round(value) };\n}\n\n/** Whether `href` is a screen-point link at all (the cheap pre-check). */\nexport function isScreenPointHref(href: string | null | undefined): href is string {\n return typeof href === \"string\" && href.startsWith(SCREEN_POINT_HREF_PREFIX);\n}\n\n/**\n * Parse a screen-point link. Accepts the canonical form\n * `athena-point://screenshot?x=…&y=…&w=…&h=…&tab=…` and tolerates what a\n * model plausibly writes instead: `width`/`height` for `w`/`h`, `tab_id` for\n * `tab`, decimals, a leading `~`, and an empty or different host. Rejects,\n * with a reason, anything that would place a marker in the wrong spot:\n * non-finite or negative numbers, a zero or negative basis, a point outside\n * its basis, and absurd extents.\n */\nexport function parseScreenPointHref(href: string | null | undefined): ScreenPointParseResult {\n if (!isScreenPointHref(href)) return { ok: false, error: \"not-screen-point\" };\n\n // `new URL` treats a non-special scheme with an empty host inconsistently\n // across engines; the query is the only part we read, so slice it off.\n const queryStart = href.indexOf(\"?\");\n if (queryStart === -1) return { ok: false, error: \"missing-coordinate\" };\n const params = new URLSearchParams(href.slice(queryStart + 1));\n\n const x = parseCoordinateNumber(params.get(\"x\"));\n const y = parseCoordinateNumber(params.get(\"y\"));\n if (!x.ok) return { ok: false, error: x.error === \"missing\" ? \"missing-coordinate\" : x.error };\n if (!y.ok) return { ok: false, error: y.error === \"missing\" ? \"missing-coordinate\" : y.error };\n\n const widthRaw = params.get(\"w\") ?? params.get(\"width\") ?? params.get(\"basisWidth\");\n const heightRaw = params.get(\"h\") ?? params.get(\"height\") ?? params.get(\"basisHeight\");\n let basis: ScreenSize | null = null;\n if (widthRaw !== null || heightRaw !== null) {\n const width = parseCoordinateNumber(widthRaw);\n const height = parseCoordinateNumber(heightRaw);\n // One dimension without the other is not a basis.\n if (!width.ok || !height.ok) {\n const failed = !width.ok ? width : height;\n if (failed.ok) return { ok: false, error: \"degenerate-basis\" };\n return {\n ok: false,\n error: failed.error === \"missing\" ? \"degenerate-basis\" : failed.error,\n };\n }\n if (width.value <= 0 || height.value <= 0) return { ok: false, error: \"degenerate-basis\" };\n basis = { width: width.value, height: height.value };\n if (x.value > basis.width || y.value > basis.height) {\n return { ok: false, error: \"out-of-bounds\" };\n }\n }\n\n const tabRaw = params.get(\"tab\") ?? params.get(\"tab_id\") ?? params.get(\"tabId\");\n let tabId: number | null = null;\n if (tabRaw !== null && tabRaw.trim() !== \"\") {\n const tab = Number(tabRaw);\n tabId = Number.isInteger(tab) && tab >= 0 ? tab : null;\n }\n\n return { ok: true, point: { x: x.value, y: y.value, basis, tabId } };\n}\n\n/** The canonical link for a point — what the prompt teaches the agent to write. */\nexport function serializeScreenPointHref(point: ScreenPoint): string {\n const params = new URLSearchParams();\n params.set(\"x\", String(point.x));\n params.set(\"y\", String(point.y));\n if (point.basis) {\n params.set(\"w\", String(point.basis.width));\n params.set(\"h\", String(point.basis.height));\n }\n if (point.tabId !== null) params.set(\"tab\", String(point.tabId));\n return `${SCREEN_POINT_HREF_PREFIX}${SCREEN_POINT_HOST}?${params.toString()}`;\n}\n\n/**\n * Where the point sits as a fraction of its basis, `0..1` on each axis, or\n * `null` when the point has no basis to be relative to. This is the only\n * number a renderer needs: `left: ${fx * 100}%` places the marker on any\n * rendition of the screenshot, whatever size it is drawn at.\n */\nexport function screenPointFraction(point: ScreenPoint): { fx: number; fy: number } | null {\n if (!point.basis) return null;\n return { fx: point.x / point.basis.width, fy: point.y / point.basis.height };\n}\n\n/** Whether two sizes are the same to within the few-pixel jitter a resize or scrollbar introduces. */\nexport function sameScreenSize({\n a,\n b,\n tolerancePx = 4,\n}: {\n a: ScreenSize;\n b: ScreenSize;\n tolerancePx?: number;\n}): boolean {\n return Math.abs(a.width - b.width) <= tolerancePx && Math.abs(a.height - b.height) <= tolerancePx;\n}\n\n/**\n * How a point's basis relates to the screenshot it is about to be drawn on.\n * Mirrors the runtime's dispatch contract (`browser_coordinate_contract.json`):\n * the same size or a few pixels of jitter is `identity`; a uniform ratio on\n * both axes (browser zoom, a resized window at the same aspect) is\n * `uniform-scale` and the fraction still lands on the same element; anything\n * else is a `mismatch` — a different aspect ratio means the screenshot shows a\n * different framing (chrome headroom, a rotated device, a different tab) and\n * a fraction placed on it would point at the wrong thing.\n */\nexport type ScreenBasisFit =\n | { kind: \"identity\" }\n | { kind: \"uniform-scale\"; scale: number }\n | { kind: \"mismatch\"; aspectDelta: number };\n\nexport function fitScreenBasis({\n basis,\n target,\n aspectTolerance = 0.02,\n}: {\n basis: ScreenSize;\n target: ScreenSize;\n aspectTolerance?: number;\n}): ScreenBasisFit {\n if (\n basis.width <= 0 ||\n basis.height <= 0 ||\n target.width <= 0 ||\n target.height <= 0 ||\n ![basis.width, basis.height, target.width, target.height].every(Number.isFinite)\n ) {\n return { kind: \"mismatch\", aspectDelta: Number.POSITIVE_INFINITY };\n }\n if (sameScreenSize({ a: basis, b: target })) return { kind: \"identity\" };\n const scaleX = target.width / basis.width;\n const scaleY = target.height / basis.height;\n const aspectDelta = Math.abs(scaleX - scaleY) / Math.max(scaleX, scaleY);\n if (aspectDelta <= aspectTolerance) return { kind: \"uniform-scale\", scale: (scaleX + scaleY) / 2 };\n return { kind: \"mismatch\", aspectDelta };\n}\n\n/** Human-readable point for tooltips and accessible names: `(1038, 81) in a 1280×720 screenshot`. */\nexport function formatScreenPoint(point: ScreenPoint): string {\n const pair = `(${point.x}, ${point.y})`;\n if (!point.basis) return `${pair} — screenshot size not recorded`;\n return `${pair} in a ${point.basis.width}×${point.basis.height} screenshot`;\n}\n\n/** The sentence a surface shows for a link that failed to parse. */\nexport function describeScreenPointError(error: ScreenPointParseError): string {\n switch (error) {\n case \"not-screen-point\":\n return \"Not a screen point\";\n case \"missing-coordinate\":\n return \"Screen point is missing x or y\";\n case \"non-finite-number\":\n return \"Screen point has a non-numeric value\";\n case \"negative-number\":\n return \"Screen point has a negative value\";\n case \"degenerate-basis\":\n return \"Screen point has an invalid screenshot size\";\n case \"out-of-bounds\":\n return \"Screen point lies outside its screenshot\";\n case \"extent-too-large\":\n return \"Screen point value is implausibly large\";\n }\n}\n\n/**\n * Screen points written into a text block, in order. Used by surfaces that\n * work on the raw markdown (the static message body's node parser, the\n * extension's plain-text composer) and by tests. Only the link form is\n * recognised — a bare `(1038, 81)` in prose is text, never a point, so a\n * model that was not taught the scheme changes nothing.\n */\nexport function findScreenPointLinks(\n text: string,\n): Array<{ start: number; end: number; label: string; href: string }> {\n const results: Array<{ start: number; end: number; label: string; href: string }> = [];\n const pattern = /\\[([^\\]\\n]*)\\]\\((athena-point:\\/\\/[^\\s)]*)\\)/g;\n for (const match of text.matchAll(pattern)) {\n if (match.index === undefined) continue;\n results.push({\n start: match.index,\n end: match.index + match[0].length,\n label: match[1],\n href: match[2],\n });\n }\n return results;\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const statewire = require("statewire");
4
- const statewireTransport = (options, { sse = false } = {}) => sse ? statewire.StatewireHttp({ protocol: "athena", ...options }) : statewire.StatewireWebsocket({ protocol: "athena", ...options });
4
+ const DEEP_AGENT_STATEWIRE_PROTOCOL = "athena";
5
+ const statewireTransport = (options, { sse = false } = {}) => sse ? statewire.StatewireHttp({ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL, ...options }) : statewire.StatewireWebsocket({
6
+ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,
7
+ ...options
8
+ });
9
+ exports.DEEP_AGENT_STATEWIRE_PROTOCOL = DEEP_AGENT_STATEWIRE_PROTOCOL;
5
10
  exports.statewireTransport = statewireTransport;
6
11
  //# sourceMappingURL=statewire-transport.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"statewire-transport.cjs","sources":["../../../../../agent-runtime/src/statewire-transport.ts"],"sourcesContent":["import { type Statewire, type StatewireClient, StatewireHttp, StatewireWebsocket } from 'statewire';\nimport type { DeepAgentStreamValues } from './types';\n\nexport type StatewireTransportOptions = StatewireClient.HttpOptions &\n StatewireClient.WebsocketOptions;\n\n/**\n * Build the deep-agent statewire transport: WebSocket by default, SSE when the\n * host app's toggle asks for it. The toggle source is host-specific (env var,\n * extension flag, config store), so the caller resolves it to a boolean.\n *\n * `TState` defaults to the deep-agent wire shape: the thread mounts that make\n * up nearly every caller get a typed state without naming it, and the other\n * mounts (the conversation index) name theirs.\n */\nexport const statewireTransport = <\n TState = DeepAgentStreamValues,\n TCommands extends Statewire.Commands = Statewire.Commands,\n>(\n options: StatewireTransportOptions,\n { sse = false }: { sse?: boolean } = {}\n) =>\n sse\n ? StatewireHttp<TState, TCommands>({ protocol: 'athena', ...options })\n : StatewireWebsocket<TState, TCommands>({ protocol: 'athena', ...options });\n"],"names":["StatewireHttp","StatewireWebsocket"],"mappings":";;;AAeO,MAAM,qBAAqB,CAIhC,SACA,EAAE,MAAM,UAA6B,CAAA,MAErC,MACIA,wBAAiC,EAAE,UAAU,UAAU,GAAG,QAAA,CAAS,IACnEC,UAAAA,mBAAsC,EAAE,UAAU,UAAU,GAAG,QAAA,CAAS;;"}
1
+ {"version":3,"file":"statewire-transport.cjs","sources":["../../../../../agent-runtime/src/statewire-transport.ts"],"sourcesContent":["import { type Statewire, type StatewireClient, StatewireHttp, StatewireWebsocket } from 'statewire';\nimport type { DeepAgentStreamValues } from './types';\n\nexport type StatewireTransportOptions = StatewireClient.HttpOptions &\n StatewireClient.WebsocketOptions;\n\n/**\n * The application protocol the deep-agent transport stamps on every command.\n * The thread client must declare the same protocol: statewire's core applies\n * its optimistic folds only to commands of its own protocol, so a mismatch\n * leaves a send invisible until the server's frame lands.\n */\nexport const DEEP_AGENT_STATEWIRE_PROTOCOL = 'athena';\n\n/**\n * Build the deep-agent statewire transport: WebSocket by default, SSE when the\n * host app's toggle asks for it. The toggle source is host-specific (env var,\n * extension flag, config store), so the caller resolves it to a boolean.\n *\n * `TState` defaults to the deep-agent wire shape: the thread mounts that make\n * up nearly every caller get a typed state without naming it, and the other\n * mounts (the conversation index) name theirs.\n */\nexport const statewireTransport = <\n TState = DeepAgentStreamValues,\n TCommands extends Statewire.Commands = Statewire.Commands,\n>(\n options: StatewireTransportOptions,\n { sse = false }: { sse?: boolean } = {}\n) =>\n sse\n ? StatewireHttp<TState, TCommands>({ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL, ...options })\n : StatewireWebsocket<TState, TCommands>({\n protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,\n ...options,\n });\n"],"names":["StatewireHttp","StatewireWebsocket"],"mappings":";;;AAYO,MAAM,gCAAgC;AAWtC,MAAM,qBAAqB,CAIhC,SACA,EAAE,MAAM,MAAA,IAA6B,CAAA,MAErC,MACIA,UAAAA,cAAiC,EAAE,UAAU,+BAA+B,GAAG,QAAA,CAAS,IACxFC,UAAAA,mBAAsC;AAAA,EACpC,UAAU;AAAA,EACV,GAAG;AACL,CAAC;;;"}
@@ -1,6 +1,11 @@
1
1
  import { StatewireHttp, StatewireWebsocket } from "statewire";
2
- const statewireTransport = (options, { sse = false } = {}) => sse ? StatewireHttp({ protocol: "athena", ...options }) : StatewireWebsocket({ protocol: "athena", ...options });
2
+ const DEEP_AGENT_STATEWIRE_PROTOCOL = "athena";
3
+ const statewireTransport = (options, { sse = false } = {}) => sse ? StatewireHttp({ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL, ...options }) : StatewireWebsocket({
4
+ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,
5
+ ...options
6
+ });
3
7
  export {
8
+ DEEP_AGENT_STATEWIRE_PROTOCOL,
4
9
  statewireTransport
5
10
  };
6
11
  //# sourceMappingURL=statewire-transport.js.map