@athenaintel/react 0.42.2 → 0.48.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 (685) hide show
  1. package/README.md +225 -3
  2. package/dist/agent-runtime/src/answer-label.cjs +137 -0
  3. package/dist/agent-runtime/src/answer-label.cjs.map +1 -0
  4. package/dist/agent-runtime/src/answer-label.d.ts +104 -0
  5. package/dist/agent-runtime/src/answer-label.js +137 -0
  6. package/dist/agent-runtime/src/answer-label.js.map +1 -0
  7. package/dist/agent-runtime/src/answer-rows.cjs +180 -0
  8. package/dist/agent-runtime/src/answer-rows.cjs.map +1 -0
  9. package/dist/agent-runtime/src/answer-rows.d.ts +86 -0
  10. package/dist/agent-runtime/src/answer-rows.js +180 -0
  11. package/dist/agent-runtime/src/answer-rows.js.map +1 -0
  12. package/dist/agent-runtime/src/client-tool-bridge.cjs +15 -15
  13. package/dist/agent-runtime/src/client-tool-bridge.cjs.map +1 -1
  14. package/dist/agent-runtime/src/conversation-index.cjs +8 -8
  15. package/dist/agent-runtime/src/conversation-index.cjs.map +1 -1
  16. package/dist/agent-runtime/src/conversation-list-groups.d.ts +74 -0
  17. package/dist/agent-runtime/src/decision-checks.cjs +207 -0
  18. package/dist/agent-runtime/src/decision-checks.cjs.map +1 -0
  19. package/dist/agent-runtime/src/decision-checks.d.ts +142 -0
  20. package/dist/agent-runtime/src/decision-checks.js +207 -0
  21. package/dist/agent-runtime/src/decision-checks.js.map +1 -0
  22. package/dist/agent-runtime/src/desktop-handoff.d.ts +12 -0
  23. package/dist/agent-runtime/src/errors.cjs +18 -0
  24. package/dist/agent-runtime/src/errors.cjs.map +1 -1
  25. package/dist/agent-runtime/src/errors.d.ts +7 -0
  26. package/dist/agent-runtime/src/errors.js +18 -0
  27. package/dist/agent-runtime/src/errors.js.map +1 -1
  28. package/dist/agent-runtime/src/generated/runconfig-contract.cjs +12 -0
  29. package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  30. package/dist/agent-runtime/src/generated/runconfig-contract.d.ts +6 -6
  31. package/dist/agent-runtime/src/generated/runconfig-contract.js +12 -0
  32. package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  33. package/dist/agent-runtime/src/handoff-arrival.cjs +60 -0
  34. package/dist/agent-runtime/src/handoff-arrival.cjs.map +1 -0
  35. package/dist/agent-runtime/src/handoff-arrival.d.ts +46 -0
  36. package/dist/agent-runtime/src/handoff-arrival.js +60 -0
  37. package/dist/agent-runtime/src/handoff-arrival.js.map +1 -0
  38. package/dist/agent-runtime/src/index.d.ts +15 -8
  39. package/dist/agent-runtime/src/interrupts.cjs +43 -18
  40. package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
  41. package/dist/agent-runtime/src/interrupts.d.ts +19 -2
  42. package/dist/agent-runtime/src/interrupts.js +28 -3
  43. package/dist/agent-runtime/src/interrupts.js.map +1 -1
  44. package/dist/agent-runtime/src/lane-history.cjs +8 -8
  45. package/dist/agent-runtime/src/lane-history.cjs.map +1 -1
  46. package/dist/agent-runtime/src/messages.cjs +43 -8
  47. package/dist/agent-runtime/src/messages.cjs.map +1 -1
  48. package/dist/agent-runtime/src/messages.d.ts +25 -3
  49. package/dist/agent-runtime/src/messages.js +43 -8
  50. package/dist/agent-runtime/src/messages.js.map +1 -1
  51. package/dist/agent-runtime/src/offline-status.cjs +36 -0
  52. package/dist/agent-runtime/src/offline-status.cjs.map +1 -0
  53. package/dist/agent-runtime/src/offline-status.d.ts +118 -3
  54. package/dist/agent-runtime/src/offline-status.js +36 -0
  55. package/dist/agent-runtime/src/offline-status.js.map +1 -0
  56. package/dist/agent-runtime/src/queue.cjs +0 -9
  57. package/dist/agent-runtime/src/queue.cjs.map +1 -1
  58. package/dist/agent-runtime/src/queue.js +0 -9
  59. package/dist/agent-runtime/src/queue.js.map +1 -1
  60. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs +9 -9
  61. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  62. package/dist/agent-runtime/src/runtime-catalog.cjs +42 -0
  63. package/dist/agent-runtime/src/runtime-catalog.cjs.map +1 -0
  64. package/dist/agent-runtime/src/runtime-catalog.d.ts +35 -0
  65. package/dist/agent-runtime/src/runtime-catalog.js +42 -0
  66. package/dist/agent-runtime/src/runtime-catalog.js.map +1 -0
  67. package/dist/agent-runtime/src/runtime-messages.cjs.map +1 -1
  68. package/dist/agent-runtime/src/runtime-messages.d.ts +1 -1
  69. package/dist/agent-runtime/src/runtime-messages.js.map +1 -1
  70. package/dist/agent-runtime/src/runtime.d.ts +40 -0
  71. package/dist/agent-runtime/src/statewire-suspended.cjs +3 -3
  72. package/dist/agent-runtime/src/statewire-suspended.cjs.map +1 -1
  73. package/dist/agent-runtime/src/subagents.cjs +32 -2
  74. package/dist/agent-runtime/src/subagents.cjs.map +1 -1
  75. package/dist/agent-runtime/src/subagents.d.ts +9 -0
  76. package/dist/agent-runtime/src/subagents.js +30 -0
  77. package/dist/agent-runtime/src/subagents.js.map +1 -1
  78. package/dist/agent-runtime/src/thread-driver.cjs +67 -0
  79. package/dist/agent-runtime/src/thread-driver.cjs.map +1 -0
  80. package/dist/agent-runtime/src/thread-driver.d.ts +49 -0
  81. package/dist/agent-runtime/src/thread-driver.js +67 -0
  82. package/dist/agent-runtime/src/thread-driver.js.map +1 -0
  83. package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +172 -0
  84. package/dist/agent-runtime/src/thread-handoff-protocol.d.ts +756 -0
  85. package/dist/agent-runtime/src/thread-handoff-qr.d.ts +11 -0
  86. package/dist/agent-runtime/src/thread-handoff.d.ts +3 -364
  87. package/dist/agent-runtime/src/thread-status.cjs +3 -3
  88. package/dist/agent-runtime/src/thread-status.cjs.map +1 -1
  89. package/dist/agent-runtime/src/tool-metadata.cjs +210 -33
  90. package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
  91. package/dist/agent-runtime/src/tool-metadata.d.ts +12 -0
  92. package/dist/agent-runtime/src/tool-metadata.js +210 -33
  93. package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
  94. package/dist/agent-runtime/src/tool-names.cjs +2 -0
  95. package/dist/agent-runtime/src/tool-names.cjs.map +1 -1
  96. package/dist/agent-runtime/src/tool-names.d.ts +2 -0
  97. package/dist/agent-runtime/src/tool-names.js +2 -0
  98. package/dist/agent-runtime/src/tool-names.js.map +1 -1
  99. package/dist/agent-runtime/src/types.d.ts +10 -2
  100. package/dist/agent-runtime/src/use-conversation-list.cjs +11 -11
  101. package/dist/agent-runtime/src/use-conversation-list.cjs.map +1 -1
  102. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs +24 -19
  103. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs.map +1 -1
  104. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.d.ts +2 -1
  105. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js +6 -1
  106. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js.map +1 -1
  107. package/dist/agent-runtime/src/use-deep-agent-config.cjs +14 -14
  108. package/dist/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
  109. package/dist/agent-runtime/src/use-deep-agent-edit-submit.cjs +6 -6
  110. package/dist/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -1
  111. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +16 -16
  112. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
  113. package/dist/agent-runtime/src/use-deep-agent-thread.cjs +45 -45
  114. package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  115. package/dist/agent-runtime/src/use-deep-agent-voice.cjs +572 -134
  116. package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
  117. package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +137 -10
  118. package/dist/agent-runtime/src/use-deep-agent-voice.js +514 -76
  119. package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -1
  120. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +26 -26
  121. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
  122. package/dist/agent-runtime/src/use-historical-edit-attachments.cjs +4 -4
  123. package/dist/agent-runtime/src/use-historical-edit-attachments.cjs.map +1 -1
  124. package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +28 -26
  125. package/dist/agent-runtime/src/voice-media.cjs +117 -0
  126. package/dist/agent-runtime/src/voice-media.cjs.map +1 -0
  127. package/dist/agent-runtime/src/voice-media.d.ts +49 -0
  128. package/dist/agent-runtime/src/voice-media.js +117 -0
  129. package/dist/agent-runtime/src/voice-media.js.map +1 -0
  130. package/dist/agent-runtime/src/voice-wire-types.d.ts +32 -0
  131. package/dist/assets/AssetPanel.cjs +11 -11
  132. package/dist/assets/AssetPanel.cjs.map +1 -1
  133. package/dist/assets/AthenaAssetEmbed.cjs +11 -9
  134. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  135. package/dist/assets/AthenaAssetEmbed.d.ts +7 -0
  136. package/dist/assets/AthenaAssetEmbed.js +4 -2
  137. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  138. package/dist/assets/AthenaLayout.cjs +16 -16
  139. package/dist/assets/AthenaLayout.cjs.map +1 -1
  140. package/dist/assets/use-asset-content.cjs +4 -4
  141. package/dist/assets/use-asset-content.cjs.map +1 -1
  142. package/dist/assets/use-asset-embed.cjs +14 -7
  143. package/dist/assets/use-asset-embed.cjs.map +1 -1
  144. package/dist/assets/use-asset-embed.d.ts +11 -1
  145. package/dist/assets/use-asset-embed.js +10 -3
  146. package/dist/assets/use-asset-embed.js.map +1 -1
  147. package/dist/auth/AthenaAuthContext.cjs +3 -3
  148. package/dist/auth/AthenaAuthContext.cjs.map +1 -1
  149. package/dist/auth/AthenaAuthProvider.cjs +8 -8
  150. package/dist/auth/AthenaAuthProvider.cjs.map +1 -1
  151. package/dist/auth/AthenaCredentialSourceContext.cjs +5 -5
  152. package/dist/auth/AthenaCredentialSourceContext.cjs.map +1 -1
  153. package/dist/auth/AthenaSSOProvider.cjs +14 -14
  154. package/dist/auth/AthenaSSOProvider.cjs.map +1 -1
  155. package/dist/auth/credential-source.cjs +26 -13
  156. package/dist/auth/credential-source.cjs.map +1 -1
  157. package/dist/auth/credential-source.d.ts +8 -0
  158. package/dist/auth/credential-source.js +26 -13
  159. package/dist/auth/credential-source.js.map +1 -1
  160. package/dist/catalog/agent-model-options.cjs +25 -17
  161. package/dist/catalog/agent-model-options.cjs.map +1 -1
  162. package/dist/catalog/agent-model-options.d.ts +38 -5
  163. package/dist/catalog/agent-model-options.js +25 -17
  164. package/dist/catalog/agent-model-options.js.map +1 -1
  165. package/dist/catalog/channel-refusal.cjs +36 -0
  166. package/dist/catalog/channel-refusal.cjs.map +1 -0
  167. package/dist/catalog/channel-refusal.d.ts +29 -0
  168. package/dist/catalog/channel-refusal.js +36 -0
  169. package/dist/catalog/channel-refusal.js.map +1 -0
  170. package/dist/catalog/chat-pickers.cjs +60 -24
  171. package/dist/catalog/chat-pickers.cjs.map +1 -1
  172. package/dist/catalog/chat-pickers.d.ts +12 -2
  173. package/dist/catalog/chat-pickers.js +56 -20
  174. package/dist/catalog/chat-pickers.js.map +1 -1
  175. package/dist/catalog/runtime-catalog.cjs +89 -23
  176. package/dist/catalog/runtime-catalog.cjs.map +1 -1
  177. package/dist/catalog/runtime-catalog.d.ts +8 -2
  178. package/dist/catalog/runtime-catalog.js +83 -17
  179. package/dist/catalog/runtime-catalog.js.map +1 -1
  180. package/dist/catalog/use-athena-chat-mode.cjs +12 -12
  181. package/dist/catalog/use-athena-chat-mode.cjs.map +1 -1
  182. package/dist/chat/AthenaChat.cjs +155 -67
  183. package/dist/chat/AthenaChat.cjs.map +1 -1
  184. package/dist/chat/AthenaChat.d.ts +42 -2
  185. package/dist/chat/AthenaChat.js +106 -18
  186. package/dist/chat/AthenaChat.js.map +1 -1
  187. package/dist/chat/AthenaChatErrorBoundary.cjs +3 -3
  188. package/dist/chat/AthenaChatErrorBoundary.cjs.map +1 -1
  189. package/dist/chat/DrivingOnChip.cjs +23 -0
  190. package/dist/chat/DrivingOnChip.cjs.map +1 -0
  191. package/dist/chat/DrivingOnChip.d.ts +17 -0
  192. package/dist/chat/DrivingOnChip.js +23 -0
  193. package/dist/chat/DrivingOnChip.js.map +1 -0
  194. package/dist/chat/StatewireApprovalCard.cjs +125 -31
  195. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  196. package/dist/chat/StatewireApprovalCard.d.ts +73 -1
  197. package/dist/chat/StatewireApprovalCard.js +115 -21
  198. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  199. package/dist/chat/StatewireClientToolBridge.cjs +2 -2
  200. package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
  201. package/dist/chat/StatewireQueuedMessages.cjs +25 -63
  202. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  203. package/dist/chat/StatewireQueuedMessages.d.ts +11 -13
  204. package/dist/chat/StatewireQueuedMessages.js +16 -54
  205. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  206. package/dist/chat/WaitingOnYouBanner.cjs +3 -3
  207. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
  208. package/dist/chat/athena-chat-welcome.cjs +2 -2
  209. package/dist/chat/athena-chat-welcome.cjs.map +1 -1
  210. package/dist/chat/athena-citation-links.cjs +5 -5
  211. package/dist/chat/athena-citation-links.cjs.map +1 -1
  212. package/dist/chat/athena-composer-controls.cjs +26 -16
  213. package/dist/chat/athena-composer-controls.cjs.map +1 -1
  214. package/dist/chat/athena-composer-controls.d.ts +6 -0
  215. package/dist/chat/athena-composer-controls.js +26 -16
  216. package/dist/chat/athena-composer-controls.js.map +1 -1
  217. package/dist/chat/athena-turn-end-status.cjs +42 -0
  218. package/dist/chat/athena-turn-end-status.cjs.map +1 -0
  219. package/dist/chat/athena-turn-end-status.d.ts +24 -0
  220. package/dist/chat/athena-turn-end-status.js +42 -0
  221. package/dist/chat/athena-turn-end-status.js.map +1 -0
  222. package/dist/chat/browser-decision-check.cjs +131 -0
  223. package/dist/chat/browser-decision-check.cjs.map +1 -0
  224. package/dist/chat/browser-decision-check.d.ts +61 -0
  225. package/dist/chat/browser-decision-check.js +131 -0
  226. package/dist/chat/browser-decision-check.js.map +1 -0
  227. package/dist/chat/chat-outbox-rows.cjs +3 -3
  228. package/dist/chat/chat-outbox-rows.cjs.map +1 -1
  229. package/dist/chat/chat-voice-controls.cjs +93 -28
  230. package/dist/chat/chat-voice-controls.cjs.map +1 -1
  231. package/dist/chat/chat-voice-controls.d.ts +36 -2
  232. package/dist/chat/chat-voice-controls.js +89 -24
  233. package/dist/chat/chat-voice-controls.js.map +1 -1
  234. package/dist/chat/composer-keyboard-hint.cjs +5 -5
  235. package/dist/chat/composer-keyboard-hint.cjs.map +1 -1
  236. package/dist/chat/device-operation.cjs +29 -0
  237. package/dist/chat/device-operation.cjs.map +1 -0
  238. package/dist/chat/device-operation.d.ts +34 -0
  239. package/dist/chat/device-operation.js +29 -0
  240. package/dist/chat/device-operation.js.map +1 -0
  241. package/dist/chat/super-grouping/CollapsibleGroup.cjs +14 -14
  242. package/dist/chat/super-grouping/CollapsibleGroup.cjs.map +1 -1
  243. package/dist/chat/super-grouping/GroupedMessageParts.cjs +4 -4
  244. package/dist/chat/super-grouping/GroupedMessageParts.cjs.map +1 -1
  245. package/dist/chat/super-grouping/SuperGroupingCard.cjs +60 -38
  246. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  247. package/dist/chat/super-grouping/SuperGroupingCard.d.ts +24 -4
  248. package/dist/chat/super-grouping/SuperGroupingCard.js +37 -15
  249. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  250. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +32 -32
  251. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  252. package/dist/chat/super-grouping/SuperGroupingTranscript.js +3 -3
  253. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  254. package/dist/chat/super-grouping/ToolTimeline.cjs +97 -37
  255. package/dist/chat/super-grouping/ToolTimeline.cjs.map +1 -1
  256. package/dist/chat/super-grouping/ToolTimeline.d.ts +14 -2
  257. package/dist/chat/super-grouping/ToolTimeline.js +94 -34
  258. package/dist/chat/super-grouping/ToolTimeline.js.map +1 -1
  259. package/dist/chat/super-grouping/activity-timeline.cjs +47 -12
  260. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  261. package/dist/chat/super-grouping/activity-timeline.d.ts +21 -0
  262. package/dist/chat/super-grouping/activity-timeline.js +47 -12
  263. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  264. package/dist/chat/super-grouping/grouping-function.cjs +4 -0
  265. package/dist/chat/super-grouping/grouping-function.cjs.map +1 -1
  266. package/dist/chat/super-grouping/grouping-function.d.ts +7 -0
  267. package/dist/chat/super-grouping/grouping-function.js +5 -1
  268. package/dist/chat/super-grouping/grouping-function.js.map +1 -1
  269. package/dist/chat/super-grouping/index.cjs +6 -0
  270. package/dist/chat/super-grouping/index.cjs.map +1 -1
  271. package/dist/chat/super-grouping/index.d.ts +6 -2
  272. package/dist/chat/super-grouping/index.js +7 -1
  273. package/dist/chat/super-grouping/index.js.map +1 -1
  274. package/dist/chat/super-grouping/timeline-step.cjs +61 -2
  275. package/dist/chat/super-grouping/timeline-step.cjs.map +1 -1
  276. package/dist/chat/super-grouping/timeline-step.d.ts +13 -1
  277. package/dist/chat/super-grouping/timeline-step.js +61 -2
  278. package/dist/chat/super-grouping/timeline-step.js.map +1 -1
  279. package/dist/chat/super-grouping/tool-part-placement.cjs +17 -0
  280. package/dist/chat/super-grouping/tool-part-placement.cjs.map +1 -0
  281. package/dist/chat/super-grouping/tool-part-placement.d.ts +22 -0
  282. package/dist/chat/super-grouping/tool-part-placement.js +17 -0
  283. package/dist/chat/super-grouping/tool-part-placement.js.map +1 -0
  284. package/dist/chat/thread-scroll-to-bottom.cjs +2 -2
  285. package/dist/chat/thread-scroll-to-bottom.cjs.map +1 -1
  286. package/dist/chat/thread-scroll-to-bottom.d.ts +10 -7
  287. package/dist/chat/thread-scroll-to-bottom.js +2 -2
  288. package/dist/chat/thread-scroll-to-bottom.js.map +1 -1
  289. package/dist/chat/tooltip-icon-button.cjs +2 -2
  290. package/dist/chat/tooltip-icon-button.cjs.map +1 -1
  291. package/dist/chat/use-athena-voice-live.cjs +11 -0
  292. package/dist/chat/use-athena-voice-live.cjs.map +1 -0
  293. package/dist/chat/use-athena-voice-live.d.ts +11 -0
  294. package/dist/chat/use-athena-voice-live.js +11 -0
  295. package/dist/chat/use-athena-voice-live.js.map +1 -0
  296. package/dist/chat/use-statewire-approval-request.cjs +56 -24
  297. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  298. package/dist/chat/use-statewire-approval-request.d.ts +33 -4
  299. package/dist/chat/use-statewire-approval-request.js +41 -9
  300. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  301. package/dist/chat-ui/auth/sign-in-card.cjs +2 -2
  302. package/dist/chat-ui/auth/sign-in-card.cjs.map +1 -1
  303. package/dist/chat-ui/composer/tiptap-composer.cjs +138 -107
  304. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  305. package/dist/chat-ui/composer/tiptap-composer.d.ts +20 -4
  306. package/dist/chat-ui/composer/tiptap-composer.js +85 -54
  307. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  308. package/dist/chat-ui/header/conversation-history-panel.cjs +49 -23
  309. package/dist/chat-ui/header/conversation-history-panel.cjs.map +1 -1
  310. package/dist/chat-ui/header/conversation-history-panel.d.ts +12 -1
  311. package/dist/chat-ui/header/conversation-history-panel.js +38 -12
  312. package/dist/chat-ui/header/conversation-history-panel.js.map +1 -1
  313. package/dist/chat-ui/index.cjs +18 -0
  314. package/dist/chat-ui/index.cjs.map +1 -1
  315. package/dist/chat-ui/index.d.ts +7 -1
  316. package/dist/chat-ui/index.js +18 -0
  317. package/dist/chat-ui/index.js.map +1 -1
  318. package/dist/chat-ui/mentions/mention-chip.cjs +147 -38
  319. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  320. package/dist/chat-ui/mentions/mention-chip.d.ts +17 -1
  321. package/dist/chat-ui/mentions/mention-chip.js +146 -37
  322. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  323. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.cjs +10 -9
  324. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.cjs.map +1 -1
  325. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.js +3 -2
  326. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.js.map +1 -1
  327. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.cjs +2 -2
  328. package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.cjs.map +1 -1
  329. package/dist/chat-ui/mentions/suggestions/components/menu-list.cjs +29 -6
  330. package/dist/chat-ui/mentions/suggestions/components/menu-list.cjs.map +1 -1
  331. package/dist/chat-ui/mentions/suggestions/components/menu-list.d.ts +7 -1
  332. package/dist/chat-ui/mentions/suggestions/components/menu-list.js +25 -2
  333. package/dist/chat-ui/mentions/suggestions/components/menu-list.js.map +1 -1
  334. package/dist/chat-ui/mentions/suggestions/components/use-keyboard-navigation.cjs +2 -2
  335. package/dist/chat-ui/mentions/suggestions/components/use-keyboard-navigation.cjs.map +1 -1
  336. package/dist/chat-ui/mentions/suggestions/components/use-menu-navigation.cjs +2 -2
  337. package/dist/chat-ui/mentions/suggestions/components/use-menu-navigation.cjs.map +1 -1
  338. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +3 -3
  339. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
  340. package/dist/chat-ui/mentions/suggestions/data/orchestrator.cjs +56 -2
  341. package/dist/chat-ui/mentions/suggestions/data/orchestrator.cjs.map +1 -1
  342. package/dist/chat-ui/mentions/suggestions/data/orchestrator.d.ts +12 -0
  343. package/dist/chat-ui/mentions/suggestions/data/orchestrator.js +56 -2
  344. package/dist/chat-ui/mentions/suggestions/data/orchestrator.js.map +1 -1
  345. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +24 -3
  346. package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
  347. package/dist/chat-ui/mentions/suggestions/data/sources/index.d.ts +7 -0
  348. package/dist/chat-ui/mentions/suggestions/data/sources/index.js +24 -3
  349. package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
  350. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.cjs +35 -13
  351. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.cjs.map +1 -1
  352. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.d.ts +15 -1
  353. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.js +28 -6
  354. package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.js.map +1 -1
  355. package/dist/chat-ui/mentions/suggestions/data/use-menu-items.cjs +9 -6
  356. package/dist/chat-ui/mentions/suggestions/data/use-menu-items.cjs.map +1 -1
  357. package/dist/chat-ui/mentions/suggestions/data/use-menu-items.d.ts +3 -0
  358. package/dist/chat-ui/mentions/suggestions/data/use-menu-items.js +5 -2
  359. package/dist/chat-ui/mentions/suggestions/data/use-menu-items.js.map +1 -1
  360. package/dist/chat-ui/mentions/use-account-workspace-id.cjs +45 -10
  361. package/dist/chat-ui/mentions/use-account-workspace-id.cjs.map +1 -1
  362. package/dist/chat-ui/mentions/use-account-workspace-id.d.ts +24 -1
  363. package/dist/chat-ui/mentions/use-account-workspace-id.js +46 -11
  364. package/dist/chat-ui/mentions/use-account-workspace-id.js.map +1 -1
  365. package/dist/chat-ui/mentions/use-member-avatar.cjs +4 -4
  366. package/dist/chat-ui/mentions/use-member-avatar.cjs.map +1 -1
  367. package/dist/chat-ui/mentions/use-mention-asset.cjs +140 -0
  368. package/dist/chat-ui/mentions/use-mention-asset.cjs.map +1 -0
  369. package/dist/chat-ui/mentions/use-mention-asset.d.ts +16 -0
  370. package/dist/chat-ui/mentions/use-mention-asset.js +140 -0
  371. package/dist/chat-ui/mentions/use-mention-asset.js.map +1 -0
  372. package/dist/chat-ui/messages/answer-label.cjs +42 -0
  373. package/dist/chat-ui/messages/answer-label.cjs.map +1 -0
  374. package/dist/chat-ui/messages/answer-label.d.ts +33 -0
  375. package/dist/chat-ui/messages/answer-label.js +42 -0
  376. package/dist/chat-ui/messages/answer-label.js.map +1 -0
  377. package/dist/chat-ui/messages/arrival-animation.cjs +13 -0
  378. package/dist/chat-ui/messages/arrival-animation.cjs.map +1 -0
  379. package/dist/chat-ui/messages/arrival-animation.d.ts +14 -0
  380. package/dist/chat-ui/messages/arrival-animation.js +13 -0
  381. package/dist/chat-ui/messages/arrival-animation.js.map +1 -0
  382. package/dist/chat-ui/messages/continue-pill.cjs +5 -5
  383. package/dist/chat-ui/messages/continue-pill.cjs.map +1 -1
  384. package/dist/chat-ui/messages/handoff-arrival-divider.cjs +44 -0
  385. package/dist/chat-ui/messages/handoff-arrival-divider.cjs.map +1 -0
  386. package/dist/chat-ui/messages/handoff-arrival-divider.d.ts +17 -0
  387. package/dist/chat-ui/messages/handoff-arrival-divider.js +44 -0
  388. package/dist/chat-ui/messages/handoff-arrival-divider.js.map +1 -0
  389. package/dist/chat-ui/messages/message-actions-config.cjs +3 -3
  390. package/dist/chat-ui/messages/message-actions-config.cjs.map +1 -1
  391. package/dist/chat-ui/messages/message-components.cjs +8 -4
  392. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  393. package/dist/chat-ui/messages/message-components.js +5 -1
  394. package/dist/chat-ui/messages/message-components.js.map +1 -1
  395. package/dist/chat-ui/messages/message-images.cjs +66 -0
  396. package/dist/chat-ui/messages/message-images.cjs.map +1 -0
  397. package/dist/chat-ui/messages/message-images.d.ts +16 -0
  398. package/dist/chat-ui/messages/message-images.js +66 -0
  399. package/dist/chat-ui/messages/message-images.js.map +1 -0
  400. package/dist/chat-ui/messages/user-edit-composer.cjs +2 -2
  401. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  402. package/dist/chat-ui/messages/wake-marker.cjs +3 -3
  403. package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
  404. package/dist/chat-ui/rich-text/chat-link.cjs +7 -6
  405. package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -1
  406. package/dist/chat-ui/rich-text/chat-link.d.ts +2 -3
  407. package/dist/chat-ui/rich-text/chat-link.js +6 -5
  408. package/dist/chat-ui/rich-text/chat-link.js.map +1 -1
  409. package/dist/chat-ui/rich-text/chat-message-body.cjs +8 -8
  410. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
  411. package/dist/chat-ui/rich-text/chat-node-views.cjs +7 -6
  412. package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -1
  413. package/dist/chat-ui/rich-text/chat-node-views.js +1 -0
  414. package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -1
  415. package/dist/chat-ui/rich-text/dom-output-spec.cjs +2 -2
  416. package/dist/chat-ui/rich-text/dom-output-spec.cjs.map +1 -1
  417. package/dist/chat-ui/statewire.cjs +3 -0
  418. package/dist/chat-ui/statewire.cjs.map +1 -1
  419. package/dist/chat-ui/statewire.d.ts +2 -1
  420. package/dist/chat-ui/statewire.js +3 -0
  421. package/dist/chat-ui/statewire.js.map +1 -1
  422. package/dist/chat-ui/thread/chat-thread-shell.cjs +43 -14
  423. package/dist/chat-ui/thread/chat-thread-shell.cjs.map +1 -1
  424. package/dist/chat-ui/thread/chat-thread-shell.d.ts +16 -2
  425. package/dist/chat-ui/thread/chat-thread-shell.js +35 -6
  426. package/dist/chat-ui/thread/chat-thread-shell.js.map +1 -1
  427. package/dist/chat-ui/thread/composer-send-or-stop.cjs +30 -20
  428. package/dist/chat-ui/thread/composer-send-or-stop.cjs.map +1 -1
  429. package/dist/chat-ui/thread/composer-send-or-stop.d.ts +8 -0
  430. package/dist/chat-ui/thread/composer-send-or-stop.js +29 -19
  431. package/dist/chat-ui/thread/composer-send-or-stop.js.map +1 -1
  432. package/dist/chat-ui/thread/statewire-history-edge.cjs +20 -21
  433. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  434. package/dist/chat-ui/thread/statewire-history-edge.js +3 -4
  435. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  436. package/dist/chat-ui/thread/thread-loading-overlay.cjs +5 -5
  437. package/dist/chat-ui/thread/thread-loading-overlay.cjs.map +1 -1
  438. package/dist/collab/client.cjs +40 -3
  439. package/dist/collab/client.cjs.map +1 -1
  440. package/dist/collab/client.d.ts +25 -0
  441. package/dist/collab/client.js +40 -3
  442. package/dist/collab/client.js.map +1 -1
  443. package/dist/collab/document-status.cjs +150 -0
  444. package/dist/collab/document-status.cjs.map +1 -0
  445. package/dist/collab/document-status.d.ts +68 -0
  446. package/dist/collab/document-status.js +150 -0
  447. package/dist/collab/document-status.js.map +1 -0
  448. package/dist/collab/index.cjs +7 -0
  449. package/dist/collab/index.cjs.map +1 -1
  450. package/dist/collab/index.d.ts +6 -1
  451. package/dist/collab/index.js +7 -0
  452. package/dist/collab/index.js.map +1 -1
  453. package/dist/collab/presence-react.cjs +11 -11
  454. package/dist/collab/presence-react.cjs.map +1 -1
  455. package/dist/collab/react.cjs +6 -6
  456. package/dist/collab/react.cjs.map +1 -1
  457. package/dist/composer/AttachmentContext.cjs +24 -11
  458. package/dist/composer/AttachmentContext.cjs.map +1 -1
  459. package/dist/composer/AttachmentContext.js +18 -5
  460. package/dist/composer/AttachmentContext.js.map +1 -1
  461. package/dist/composer/ComposerAttachmentPreview.cjs +78 -23
  462. package/dist/composer/ComposerAttachmentPreview.cjs.map +1 -1
  463. package/dist/composer/ComposerAttachmentPreview.d.ts +5 -3
  464. package/dist/composer/ComposerAttachmentPreview.js +79 -24
  465. package/dist/composer/ComposerAttachmentPreview.js.map +1 -1
  466. package/dist/composer/ComposerDropZone.cjs +7 -7
  467. package/dist/composer/ComposerDropZone.cjs.map +1 -1
  468. package/dist/composer/FileUploadButton.cjs +37 -22
  469. package/dist/composer/FileUploadButton.cjs.map +1 -1
  470. package/dist/composer/FileUploadButton.d.ts +6 -0
  471. package/dist/composer/FileUploadButton.js +34 -19
  472. package/dist/composer/FileUploadButton.js.map +1 -1
  473. package/dist/composer/QuoteContext.cjs +8 -8
  474. package/dist/composer/QuoteContext.cjs.map +1 -1
  475. package/dist/composer/composer-handoff.cjs +4 -4
  476. package/dist/composer/composer-handoff.cjs.map +1 -1
  477. package/dist/composer/useAppendToComposer.cjs +4 -4
  478. package/dist/composer/useAppendToComposer.cjs.map +1 -1
  479. package/dist/composer/useAttachFiles.cjs +23 -6
  480. package/dist/composer/useAttachFiles.cjs.map +1 -1
  481. package/dist/composer/useAttachFiles.d.ts +15 -7
  482. package/dist/composer/useAttachFiles.js +22 -5
  483. package/dist/composer/useAttachFiles.js.map +1 -1
  484. package/dist/composer/useComposerAttachment.cjs +4 -4
  485. package/dist/composer/useComposerAttachment.cjs.map +1 -1
  486. package/dist/composer/useFileUpload.cjs +21 -16
  487. package/dist/composer/useFileUpload.cjs.map +1 -1
  488. package/dist/composer/useFileUpload.d.ts +12 -0
  489. package/dist/composer/useFileUpload.js +12 -7
  490. package/dist/composer/useFileUpload.js.map +1 -1
  491. package/dist/composer/useReadComposerDraft.cjs +2 -2
  492. package/dist/composer/useReadComposerDraft.cjs.map +1 -1
  493. package/dist/composer/useSendMessage.cjs +4 -4
  494. package/dist/composer/useSendMessage.cjs.map +1 -1
  495. package/dist/diagnostics/useAthenaDiagnostics.cjs +4 -4
  496. package/dist/diagnostics/useAthenaDiagnostics.cjs.map +1 -1
  497. package/dist/dictation/audio-level-history.cjs +3 -3
  498. package/dist/dictation/audio-level-history.cjs.map +1 -1
  499. package/dist/dictation/dictation-controls.cjs +24 -20
  500. package/dist/dictation/dictation-controls.cjs.map +1 -1
  501. package/dist/dictation/dictation-controls.d.ts +6 -1
  502. package/dist/dictation/dictation-controls.js +24 -20
  503. package/dist/dictation/dictation-controls.js.map +1 -1
  504. package/dist/dictation/use-athena-dictation-engine.cjs +3 -3
  505. package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -1
  506. package/dist/dictation/use-dictation.cjs +22 -22
  507. package/dist/dictation/use-dictation.cjs.map +1 -1
  508. package/dist/index.cjs +26 -2
  509. package/dist/index.cjs.map +1 -1
  510. package/dist/index.d.ts +15 -3
  511. package/dist/index.js +26 -2
  512. package/dist/index.js.map +1 -1
  513. package/dist/keryx-http/src/receive-only-socket.cjs +40 -0
  514. package/dist/keryx-http/src/receive-only-socket.cjs.map +1 -0
  515. package/dist/keryx-http/src/receive-only-socket.d.ts +27 -0
  516. package/dist/keryx-http/src/receive-only-socket.js +40 -0
  517. package/dist/keryx-http/src/receive-only-socket.js.map +1 -0
  518. package/dist/keryx-http/src/update-attributions.cjs +111 -0
  519. package/dist/keryx-http/src/update-attributions.cjs.map +1 -0
  520. package/dist/keryx-http/src/update-attributions.d.ts +75 -0
  521. package/dist/keryx-http/src/update-attributions.js +111 -0
  522. package/dist/keryx-http/src/update-attributions.js.map +1 -0
  523. package/dist/lib/owner-document.cjs +6 -6
  524. package/dist/lib/owner-document.cjs.map +1 -1
  525. package/dist/lib/use-athena-rest-query.cjs +4 -4
  526. package/dist/lib/use-athena-rest-query.cjs.map +1 -1
  527. package/dist/lib/use-document-active.cjs +4 -4
  528. package/dist/lib/use-document-active.cjs.map +1 -1
  529. package/dist/lib/use-prefers-reduced-motion.cjs +2 -2
  530. package/dist/lib/use-prefers-reduced-motion.cjs.map +1 -1
  531. package/dist/offline/offline-history.cjs +7 -7
  532. package/dist/offline/offline-history.cjs.map +1 -1
  533. package/dist/provider/AthenaContext.cjs +3 -3
  534. package/dist/provider/AthenaContext.cjs.map +1 -1
  535. package/dist/provider/AthenaProvider.cjs +116 -79
  536. package/dist/provider/AthenaProvider.cjs.map +1 -1
  537. package/dist/provider/AthenaProvider.js +40 -3
  538. package/dist/provider/AthenaProvider.js.map +1 -1
  539. package/dist/provider/PostHogProvider.cjs +2 -2
  540. package/dist/provider/PostHogProvider.cjs.map +1 -1
  541. package/dist/provider/agent-selection-context.cjs +2 -2
  542. package/dist/provider/agent-selection-context.cjs.map +1 -1
  543. package/dist/provider/chat-mode-context.cjs +2 -2
  544. package/dist/provider/chat-mode-context.cjs.map +1 -1
  545. package/dist/provider/session-persistence-context.cjs +10 -0
  546. package/dist/provider/session-persistence-context.cjs.map +1 -0
  547. package/dist/provider/session-persistence-context.d.ts +15 -0
  548. package/dist/provider/session-persistence-context.js +10 -0
  549. package/dist/provider/session-persistence-context.js.map +1 -0
  550. package/dist/provider/statewire-history-context.cjs +4 -4
  551. package/dist/provider/statewire-history-context.cjs.map +1 -1
  552. package/dist/provider/statewire-lifecycle-context.cjs +3 -3
  553. package/dist/provider/statewire-lifecycle-context.cjs.map +1 -1
  554. package/dist/provider/statewire-window-context.cjs +3 -3
  555. package/dist/provider/statewire-window-context.cjs.map +1 -1
  556. package/dist/provider/theme-style-context.cjs +3 -3
  557. package/dist/provider/theme-style-context.cjs.map +1 -1
  558. package/dist/react/package.json.cjs +1 -1
  559. package/dist/react/package.json.js +1 -1
  560. package/dist/references/src/tool-option-schemas.d.ts +0 -34
  561. package/dist/runtime/StatewireThreadConfigLaneSync.cjs +10 -10
  562. package/dist/runtime/StatewireThreadConfigLaneSync.cjs.map +1 -1
  563. package/dist/runtime/auth-refusal-restart.cjs +7 -7
  564. package/dist/runtime/auth-refusal-restart.cjs.map +1 -1
  565. package/dist/runtime/auth.cjs +13 -13
  566. package/dist/runtime/auth.cjs.map +1 -1
  567. package/dist/runtime/statewire-lifecycle.cjs +1 -0
  568. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  569. package/dist/runtime/statewire-lifecycle.d.ts +3 -1
  570. package/dist/runtime/statewire-lifecycle.js +2 -1
  571. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  572. package/dist/runtime/statewire-server-lane-context.cjs +2 -2
  573. package/dist/runtime/statewire-server-lane-context.cjs.map +1 -1
  574. package/dist/runtime/statewire-session-lane.cjs +4 -4
  575. package/dist/runtime/statewire-session-lane.cjs.map +1 -1
  576. package/dist/runtime/useAthenaStatewireRuntime.cjs +10 -8
  577. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  578. package/dist/runtime/useAthenaStatewireRuntime.d.ts +2 -0
  579. package/dist/runtime/useAthenaStatewireRuntime.js +2 -0
  580. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  581. package/dist/styles.css +1 -1
  582. package/dist/threads/Inbox.cjs +8 -8
  583. package/dist/threads/Inbox.cjs.map +1 -1
  584. package/dist/threads/SessionList.cjs +9 -9
  585. package/dist/threads/SessionList.cjs.map +1 -1
  586. package/dist/threads/SessionsPage.cjs +11 -11
  587. package/dist/threads/SessionsPage.cjs.map +1 -1
  588. package/dist/threads/index-triggered-refresh.cjs +10 -10
  589. package/dist/threads/index-triggered-refresh.cjs.map +1 -1
  590. package/dist/threads/mark-viewed-thread-read.cjs +12 -12
  591. package/dist/threads/mark-viewed-thread-read.cjs.map +1 -1
  592. package/dist/threads/offline-session-list.cjs +3 -3
  593. package/dist/threads/offline-session-list.cjs.map +1 -1
  594. package/dist/threads/session-list-tab-share.cjs +5 -5
  595. package/dist/threads/session-list-tab-share.cjs.map +1 -1
  596. package/dist/threads/statewire-thread-list.cjs +55 -51
  597. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  598. package/dist/threads/statewire-thread-list.js +6 -2
  599. package/dist/threads/statewire-thread-list.js.map +1 -1
  600. package/dist/threads/thread-id-context.cjs +5 -5
  601. package/dist/threads/thread-id-context.cjs.map +1 -1
  602. package/dist/threads/use-athena-session-attention.cjs +2 -2
  603. package/dist/threads/use-athena-session-attention.cjs.map +1 -1
  604. package/dist/threads/use-athena-sessions.cjs +10 -10
  605. package/dist/threads/use-athena-sessions.cjs.map +1 -1
  606. package/dist/threads/use-athena-thread-model.cjs +3 -3
  607. package/dist/threads/use-athena-thread-model.cjs.map +1 -1
  608. package/dist/threads/use-rest-conversation-history.cjs +3 -3
  609. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  610. package/dist/threads/use-statewire-conversation-history.cjs +8 -8
  611. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  612. package/dist/threads/use-thread-archive.cjs +9 -9
  613. package/dist/threads/use-thread-archive.cjs.map +1 -1
  614. package/dist/threads/use-thread-favorite.cjs +2 -2
  615. package/dist/threads/use-thread-favorite.cjs.map +1 -1
  616. package/dist/threads/use-thread-field-override.cjs +20 -20
  617. package/dist/threads/use-thread-field-override.cjs.map +1 -1
  618. package/dist/threads/use-thread-read-state.cjs +4 -4
  619. package/dist/threads/use-thread-read-state.cjs.map +1 -1
  620. package/dist/threads/use-thread-rename.cjs +2 -2
  621. package/dist/threads/use-thread-rename.cjs.map +1 -1
  622. package/dist/threads/useAthenaThreadManager.cjs +2 -2
  623. package/dist/threads/useAthenaThreadManager.cjs.map +1 -1
  624. package/dist/tools/NestedPtcMessages.cjs +6 -6
  625. package/dist/tools/NestedPtcMessages.cjs.map +1 -1
  626. package/dist/tools/append-document-tool-ui.cjs +7 -7
  627. package/dist/tools/append-document-tool-ui.cjs.map +1 -1
  628. package/dist/tools/file-tool-uis.cjs +2 -2
  629. package/dist/tools/file-tool-uis.cjs.map +1 -1
  630. package/dist/tools/misc-tool-uis.cjs +2 -2
  631. package/dist/tools/misc-tool-uis.cjs.map +1 -1
  632. package/dist/tools/primitives.cjs +6 -6
  633. package/dist/tools/primitives.cjs.map +1 -1
  634. package/dist/tools/read-asset-tool-ui.cjs +6 -6
  635. package/dist/tools/read-asset-tool-ui.cjs.map +1 -1
  636. package/dist/tools/subagent-task-tool-ui.cjs +21 -21
  637. package/dist/tools/subagent-task-tool-ui.cjs.map +1 -1
  638. package/dist/tools/test-recording-tool-uis.cjs +16 -16
  639. package/dist/tools/test-recording-tool-uis.cjs.map +1 -1
  640. package/dist/tools/todo-plan-store.cjs +4 -4
  641. package/dist/tools/todo-plan-store.cjs.map +1 -1
  642. package/dist/tools/tool-appearance.cjs +3 -3
  643. package/dist/tools/tool-appearance.cjs.map +1 -1
  644. package/dist/tools/tool-fallback.cjs +14 -14
  645. package/dist/tools/tool-fallback.cjs.map +1 -1
  646. package/dist/tools/tool-summary.cjs +0 -2
  647. package/dist/tools/tool-summary.cjs.map +1 -1
  648. package/dist/tools/tool-summary.js +0 -2
  649. package/dist/tools/tool-summary.js.map +1 -1
  650. package/dist/tools/tool-uis.cjs +74 -74
  651. package/dist/tools/tool-uis.cjs.map +1 -1
  652. package/dist/tools/use-elapsed-seconds.cjs +3 -3
  653. package/dist/tools/use-elapsed-seconds.cjs.map +1 -1
  654. package/dist/tools/write-todos-tool-ui.cjs +9 -9
  655. package/dist/tools/write-todos-tool-ui.cjs.map +1 -1
  656. package/dist/ui/image-viewer.cjs +382 -0
  657. package/dist/ui/image-viewer.cjs.map +1 -0
  658. package/dist/ui/image-viewer.d.ts +50 -0
  659. package/dist/ui/image-viewer.js +382 -0
  660. package/dist/ui/image-viewer.js.map +1 -0
  661. package/dist/voice/chat.cjs +6 -0
  662. package/dist/voice/chat.cjs.map +1 -1
  663. package/dist/voice/chat.d.ts +1 -1
  664. package/dist/voice/chat.js +8 -2
  665. package/dist/voice/livekit-join.cjs +52 -101
  666. package/dist/voice/livekit-join.cjs.map +1 -1
  667. package/dist/voice/livekit-join.d.ts +1 -6
  668. package/dist/voice/livekit-join.js +52 -101
  669. package/dist/voice/livekit-join.js.map +1 -1
  670. package/dist/voice/livekit.cjs.map +1 -1
  671. package/dist/voice/livekit.d.ts +11 -1
  672. package/dist/voice/livekit.js.map +1 -1
  673. package/dist/voice/use-athena-voice-session.cjs +34 -34
  674. package/dist/voice/use-athena-voice-session.cjs.map +1 -1
  675. package/dist/workbench/ComputerLiveDesktop.cjs +13 -13
  676. package/dist/workbench/ComputerLiveDesktop.cjs.map +1 -1
  677. package/dist/workbench/SessionWorkbench.cjs +22 -22
  678. package/dist/workbench/SessionWorkbench.cjs.map +1 -1
  679. package/dist/workbench/session-workbench-context.cjs +3 -3
  680. package/dist/workbench/session-workbench-context.cjs.map +1 -1
  681. package/dist/workbench/session-workbench-panes.cjs +8 -8
  682. package/dist/workbench/session-workbench-panes.cjs.map +1 -1
  683. package/dist/workbench/workbench-layout-context.cjs +3 -3
  684. package/dist/workbench/workbench-layout-context.cjs.map +1 -1
  685. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"credential-source.cjs","sources":["../../src/auth/credential-source.ts"],"sourcesContent":["import type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\n\n/**\n * Renews a token-backed sign-in. `force: true` means the token just presented\n * was refused, or has expired: the renewer must obtain a new one rather than\n * return the one it holds. Resolves `null` when it cannot produce a token.\n */\nexport type AthenaTokenRenewer = (options: { force: boolean }) => Promise<string | null>;\n\n/** What the credential source reads, fresh, on every resolve. */\nexport interface AthenaCredentialInputs {\n /**\n * The host's own getter (`config.getToken`). It owns renewal for its\n * sign-in, so it always wins; the held `token` is its fallback when it\n * resolves nothing.\n */\n hostGetToken?: AthenaStatewireTokenGetter;\n /** The token currently held: the host's, the sign-in provider's or the host bridge's. */\n token: string | null | undefined;\n /** How that token renews (the sign-in provider or the host bridge); absent for a static token. */\n renew?: AthenaTokenRenewer;\n}\n\n/**\n * The single place every SDK network path gets the token it presents — the\n * live thread connection, the session list's index and REST calls, uploads,\n * search, voice and the collaborative-document mint — read right before it is\n * presented.\n */\nexport interface AthenaCredentialSource {\n /**\n * The token to present now. `force` renews it first (the previous one was\n * refused). A token that has expired, or expires within\n * {@link TOKEN_EXPIRY_SKEW_MS}, is renewed before it is presented, so a tab\n * waking from sleep never presents a dead token. Concurrent renewals share\n * one in-flight request, and calls made while one is in flight wait for it\n * instead of presenting the token it is replacing. A renewal never hands one\n * account's token to a call made after the signed-in account changed.\n */\n getToken: (options?: { force?: boolean }) => Promise<string | null>;\n /** Called, with no arguments, whenever a different token becomes current. */\n subscribe: (listener: () => void) => () => void;\n /**\n * How many times the identity behind the token has changed: its JWT\n * subject, or the token itself when it is opaque. Renewals for the same\n * identity leave it alone. Long-lived connections key on it, so they keep\n * running across renewals and are rebuilt when someone else signs in,\n * including under a host `getToken`, whose identity is only known once a\n * token has been resolved. Subscribers hear when it moves.\n */\n identityEpoch: () => number;\n /** Tell subscribers the held token changed (the provider calls this when its inputs do). */\n notifyTokenChanged: () => void;\n}\n\n/** A token this close to its expiry is renewed before it is presented. */\nexport const TOKEN_EXPIRY_SKEW_MS = 30_000;\n\n/**\n * The `exp` of a JWT, in milliseconds, or `null` for anything that is not a\n * JWT with a numeric `exp`. Read without verification: it only decides whether\n * to renew before presenting; the server still validates every token.\n */\nexport function jwtExpiresAtMs(token: string): number | null {\n const payload = token.split('.')[1];\n if (!payload) return null;\n try {\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const exp: unknown = (JSON.parse(atob(padded)) as { exp?: unknown }).exp;\n return typeof exp === 'number' && Number.isFinite(exp) ? exp * 1000 : null;\n } catch {\n return null;\n }\n}\n\n/** The unverified `sub` of a JWT, or `null` for anything that is not a JWT with one. */\nexport function jwtSubject(token: string): string | null {\n const payload = token.split('.')[1];\n if (!payload) return null;\n try {\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const sub: unknown = (JSON.parse(atob(padded)) as { sub?: unknown }).sub;\n return typeof sub === 'string' && sub.length > 0 ? sub : null;\n } catch {\n return null;\n }\n}\n\n/** True for a JWT that has expired or expires within {@link TOKEN_EXPIRY_SKEW_MS}. */\nexport function isTokenExpiring(token: string, now: number = Date.now()): boolean {\n const expiresAt = jwtExpiresAtMs(token);\n return expiresAt !== null && expiresAt - now <= TOKEN_EXPIRY_SKEW_MS;\n}\n\nexport function createAthenaCredentialSource(\n read: () => AthenaCredentialInputs,\n): AthenaCredentialSource {\n const listeners = new Set<() => void>();\n // The renewal in flight, and whether the host's getter runs it (the host\n // owns identity for its getter) or the sign-in provider's / bridge's renewer.\n let renewal: { token: Promise<string | null>; byHost: boolean } | null = null;\n let lastResolved: string | null = null;\n let identity: string | null = null;\n let identityEpoch = 0;\n\n const notifyListeners = () => {\n for (const listener of [...listeners]) listener();\n };\n\n /** Note who a token acts as; true when that is someone other than before. */\n const observeIdentity = (token: string | null | undefined): boolean => {\n if (!token) return false;\n const next = jwtSubject(token) ?? token;\n if (next === identity) return false;\n const changed = identity !== null;\n identity = next;\n if (changed) identityEpoch += 1;\n return changed;\n };\n\n const notifyTokenChanged = () => {\n observeIdentity(read().token);\n notifyListeners();\n };\n\n /** Record what a resolve produced; a different token than the last one is news. */\n const settle = (token: string | null): string | null => {\n const identityChanged = observeIdentity(token);\n if (token !== null && token !== lastResolved) {\n const replaced = lastResolved !== null;\n lastResolved = token;\n if (replaced || identityChanged) notifyListeners();\n } else if (identityChanged) {\n notifyListeners();\n }\n return token;\n };\n\n const shareRenewal = (\n start: () => Promise<string | null>,\n byHost: boolean,\n ): { token: Promise<string | null>; byHost: boolean } => {\n if (!renewal) {\n const entry = {\n token: start().finally(() => {\n if (renewal === entry) renewal = null;\n }),\n byHost,\n };\n renewal = entry;\n }\n return renewal;\n };\n\n /**\n * What a finished renewal hands one caller. A host getter's token is\n * presented as is: the host owns identity for its getter. A sign-in\n * provider's or bridge's renewed token is presented only while it acts as\n * the account the provider holds now. When the account changed while the\n * renewal was in flight (its JWT subject differs from the held token's, or\n * the provider holds no token any more), the held token, the current\n * account's, is presented instead. A renewal that produced nothing keeps\n * the held token: the caller presents it and the host's refusal ends the\n * attempt.\n */\n const presentable = (\n { byHost }: { byHost: boolean },\n renewed: string | null,\n ): string | null => {\n const held = read().token ?? null;\n if (byHost) return renewed ?? held;\n if (!renewed || !held) return held;\n const heldSubject = jwtSubject(held);\n const renewedSubject = jwtSubject(renewed);\n return heldSubject && renewedSubject && heldSubject !== renewedSubject ? held : renewed;\n };\n\n const awaitRenewal = async (shared: {\n token: Promise<string | null>;\n byHost: boolean;\n }): Promise<string | null> => settle(presentable(shared, await shared.token));\n\n const getToken = async ({ force = false }: { force?: boolean } = {}): Promise<string | null> => {\n if (renewal) return awaitRenewal(renewal);\n const inputs = read();\n const hostGetToken = inputs.hostGetToken;\n if (hostGetToken) {\n if (!force) return settle((await hostGetToken({ force: false })) ?? read().token ?? null);\n return awaitRenewal(\n shareRenewal(async () => (await hostGetToken({ force: true })) ?? null, true),\n );\n }\n const current = inputs.token ?? null;\n const renew = inputs.renew;\n if (!renew) return settle(current);\n const stale = force || (current !== null && isTokenExpiring(current));\n if (!stale) return settle(current);\n return awaitRenewal(shareRenewal(() => renew({ force: true }), false));\n };\n\n return {\n getToken,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n notifyTokenChanged,\n identityEpoch: () => identityEpoch,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"credential-source.cjs","sources":["../../src/auth/credential-source.ts"],"sourcesContent":["import type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\n\n/**\n * Renews a token-backed sign-in. `force: true` means the token just presented\n * was refused, or has expired: the renewer must obtain a new one rather than\n * return the one it holds. Resolves `null` when it cannot produce a token.\n */\nexport type AthenaTokenRenewer = (options: { force: boolean }) => Promise<string | null>;\n\n/** What the credential source reads, fresh, on every resolve. */\nexport interface AthenaCredentialInputs {\n /**\n * The host's own getter (`config.getToken`). It owns renewal for its\n * sign-in, so it always wins; the held `token` is its fallback when it\n * resolves nothing.\n */\n hostGetToken?: AthenaStatewireTokenGetter;\n /** The token currently held: the host's, the sign-in provider's or the host bridge's. */\n token: string | null | undefined;\n /** How that token renews (the sign-in provider or the host bridge); absent for a static token. */\n renew?: AthenaTokenRenewer;\n}\n\n/**\n * The single place every SDK network path gets the token it presents — the\n * live thread connection, the session list's index and REST calls, uploads,\n * search, voice and the collaborative-document mint — read right before it is\n * presented.\n */\nexport interface AthenaCredentialSource {\n /**\n * The token to present now. `force` renews it first (the previous one was\n * refused). A token that has expired, or expires within\n * {@link TOKEN_EXPIRY_SKEW_MS}, is renewed before it is presented, so a tab\n * waking from sleep never presents a dead token. Concurrent renewals share\n * one in-flight request, and calls made while one is in flight wait for it\n * instead of presenting the token it is replacing. A renewal never hands one\n * account's token to a call made after the signed-in account changed.\n */\n getToken: (options?: { force?: boolean }) => Promise<string | null>;\n /** Called, with no arguments, whenever a different token becomes current. */\n subscribe: (listener: () => void) => () => void;\n /**\n * How many times the identity behind the token has changed: its JWT\n * subject, or the token itself when it is opaque. Renewals for the same\n * identity leave it alone. Long-lived connections key on it, so they keep\n * running across renewals and are rebuilt when someone else signs in,\n * including under a host `getToken`, whose identity is only known once a\n * token has been resolved. Subscribers hear when it moves.\n */\n identityEpoch: () => number;\n /** Tell subscribers the held token changed (the provider calls this when its inputs do). */\n notifyTokenChanged: () => void;\n}\n\n/** A token this close to its expiry is renewed before it is presented. */\nexport const TOKEN_EXPIRY_SKEW_MS = 30_000;\n\n/** The unverified claims of a JWT, or `null` for anything that is not one. */\nfunction jwtClaims(token: string): Record<string, unknown> | null {\n const payload = token.split('.')[1];\n if (!payload) return null;\n try {\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const claims: unknown = JSON.parse(atob(padded));\n return typeof claims === 'object' && claims !== null && !Array.isArray(claims)\n ? (claims as Record<string, unknown>)\n : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `exp` of a JWT, in milliseconds, or `null` for anything that is not a\n * JWT with a numeric `exp`. Read without verification: it only decides whether\n * to renew before presenting; the server still validates every token.\n */\nexport function jwtExpiresAtMs(token: string): number | null {\n const exp = jwtClaims(token)?.exp;\n return typeof exp === 'number' && Number.isFinite(exp) ? exp * 1000 : null;\n}\n\n/** The unverified `sub` of a JWT, or `null` for anything that is not a JWT with one. */\nexport function jwtSubject(token: string): string | null {\n const sub = jwtClaims(token)?.sub;\n return typeof sub === 'string' && sub.length > 0 ? sub : null;\n}\n\n/**\n * The unverified claims that scope a JWT to a workspace or org, as one stable\n * string, or `null` when it names none: Athena's `workspace_id` (an Office\n * session's token), `org_id` (an external sign-in's), and PropelAuth's\n * active-org `org_member_info.org_id`. Two of one person's tokens scoped to\n * different workspaces read differently; a renewal keeps it.\n */\nexport function jwtScope(token: string): string | null {\n const claims = jwtClaims(token);\n if (claims === null) return null;\n const orgMember = claims.org_member_info;\n const scope = [\n ['workspace_id', claims.workspace_id],\n ['org_id', claims.org_id],\n [\n 'org_member_info.org_id',\n typeof orgMember === 'object' && orgMember !== null\n ? (orgMember as Record<string, unknown>).org_id\n : undefined,\n ],\n ]\n .filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].length > 0)\n .map(([claim, value]) => `${claim}=${value}`);\n return scope.length > 0 ? scope.join('\\u0000') : null;\n}\n\n/** True for a JWT that has expired or expires within {@link TOKEN_EXPIRY_SKEW_MS}. */\nexport function isTokenExpiring(token: string, now: number = Date.now()): boolean {\n const expiresAt = jwtExpiresAtMs(token);\n return expiresAt !== null && expiresAt - now <= TOKEN_EXPIRY_SKEW_MS;\n}\n\nexport function createAthenaCredentialSource(\n read: () => AthenaCredentialInputs,\n): AthenaCredentialSource {\n const listeners = new Set<() => void>();\n // The renewal in flight, and whether the host's getter runs it (the host\n // owns identity for its getter) or the sign-in provider's / bridge's renewer.\n let renewal: { token: Promise<string | null>; byHost: boolean } | null = null;\n let lastResolved: string | null = null;\n let identity: string | null = null;\n let identityEpoch = 0;\n\n const notifyListeners = () => {\n for (const listener of [...listeners]) listener();\n };\n\n /** Note who a token acts as; true when that is someone other than before. */\n const observeIdentity = (token: string | null | undefined): boolean => {\n if (!token) return false;\n const next = jwtSubject(token) ?? token;\n if (next === identity) return false;\n const changed = identity !== null;\n identity = next;\n if (changed) identityEpoch += 1;\n return changed;\n };\n\n const notifyTokenChanged = () => {\n observeIdentity(read().token);\n notifyListeners();\n };\n\n /** Record what a resolve produced; a different token than the last one is news. */\n const settle = (token: string | null): string | null => {\n const identityChanged = observeIdentity(token);\n if (token !== null && token !== lastResolved) {\n const replaced = lastResolved !== null;\n lastResolved = token;\n if (replaced || identityChanged) notifyListeners();\n } else if (identityChanged) {\n notifyListeners();\n }\n return token;\n };\n\n const shareRenewal = (\n start: () => Promise<string | null>,\n byHost: boolean,\n ): { token: Promise<string | null>; byHost: boolean } => {\n if (!renewal) {\n const entry = {\n token: start().finally(() => {\n if (renewal === entry) renewal = null;\n }),\n byHost,\n };\n renewal = entry;\n }\n return renewal;\n };\n\n /**\n * What a finished renewal hands one caller. A host getter's token is\n * presented as is: the host owns identity for its getter. A sign-in\n * provider's or bridge's renewed token is presented only while it acts as\n * the account the provider holds now. When the account changed while the\n * renewal was in flight (its JWT subject differs from the held token's, or\n * the provider holds no token any more), the held token, the current\n * account's, is presented instead. A renewal that produced nothing keeps\n * the held token: the caller presents it and the host's refusal ends the\n * attempt.\n */\n const presentable = (\n { byHost }: { byHost: boolean },\n renewed: string | null,\n ): string | null => {\n const held = read().token ?? null;\n if (byHost) return renewed ?? held;\n if (!renewed || !held) return held;\n const heldSubject = jwtSubject(held);\n const renewedSubject = jwtSubject(renewed);\n return heldSubject && renewedSubject && heldSubject !== renewedSubject ? held : renewed;\n };\n\n const awaitRenewal = async (shared: {\n token: Promise<string | null>;\n byHost: boolean;\n }): Promise<string | null> => settle(presentable(shared, await shared.token));\n\n const getToken = async ({ force = false }: { force?: boolean } = {}): Promise<string | null> => {\n if (renewal) return awaitRenewal(renewal);\n const inputs = read();\n const hostGetToken = inputs.hostGetToken;\n if (hostGetToken) {\n if (!force) return settle((await hostGetToken({ force: false })) ?? read().token ?? null);\n return awaitRenewal(\n shareRenewal(async () => (await hostGetToken({ force: true })) ?? null, true),\n );\n }\n const current = inputs.token ?? null;\n const renew = inputs.renew;\n if (!renew) return settle(current);\n const stale = force || (current !== null && isTokenExpiring(current));\n if (!stale) return settle(current);\n return awaitRenewal(shareRenewal(() => renew({ force: true }), false));\n };\n\n return {\n getToken,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n notifyTokenChanged,\n identityEpoch: () => identityEpoch,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -63,6 +63,14 @@ export declare const TOKEN_EXPIRY_SKEW_MS = 30000;
63
63
  export declare function jwtExpiresAtMs(token: string): number | null;
64
64
  /** The unverified `sub` of a JWT, or `null` for anything that is not a JWT with one. */
65
65
  export declare function jwtSubject(token: string): string | null;
66
+ /**
67
+ * The unverified claims that scope a JWT to a workspace or org, as one stable
68
+ * string, or `null` when it names none: Athena's `workspace_id` (an Office
69
+ * session's token), `org_id` (an external sign-in's), and PropelAuth's
70
+ * active-org `org_member_info.org_id`. Two of one person's tokens scoped to
71
+ * different workspaces read differently; a renewal keeps it.
72
+ */
73
+ export declare function jwtScope(token: string): string | null;
66
74
  /** True for a JWT that has expired or expires within {@link TOKEN_EXPIRY_SKEW_MS}. */
67
75
  export declare function isTokenExpiring(token: string, now?: number): boolean;
68
76
  export declare function createAthenaCredentialSource(read: () => AthenaCredentialInputs): AthenaCredentialSource;
@@ -1,27 +1,39 @@
1
1
  const TOKEN_EXPIRY_SKEW_MS = 3e4;
2
- function jwtExpiresAtMs(token) {
2
+ function jwtClaims(token) {
3
3
  const payload = token.split(".")[1];
4
4
  if (!payload) return null;
5
5
  try {
6
6
  const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
7
7
  const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
8
- const exp = JSON.parse(atob(padded)).exp;
9
- return typeof exp === "number" && Number.isFinite(exp) ? exp * 1e3 : null;
8
+ const claims = JSON.parse(atob(padded));
9
+ return typeof claims === "object" && claims !== null && !Array.isArray(claims) ? claims : null;
10
10
  } catch {
11
11
  return null;
12
12
  }
13
13
  }
14
+ function jwtExpiresAtMs(token) {
15
+ var _a;
16
+ const exp = (_a = jwtClaims(token)) == null ? void 0 : _a.exp;
17
+ return typeof exp === "number" && Number.isFinite(exp) ? exp * 1e3 : null;
18
+ }
14
19
  function jwtSubject(token) {
15
- const payload = token.split(".")[1];
16
- if (!payload) return null;
17
- try {
18
- const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
19
- const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
20
- const sub = JSON.parse(atob(padded)).sub;
21
- return typeof sub === "string" && sub.length > 0 ? sub : null;
22
- } catch {
23
- return null;
24
- }
20
+ var _a;
21
+ const sub = (_a = jwtClaims(token)) == null ? void 0 : _a.sub;
22
+ return typeof sub === "string" && sub.length > 0 ? sub : null;
23
+ }
24
+ function jwtScope(token) {
25
+ const claims = jwtClaims(token);
26
+ if (claims === null) return null;
27
+ const orgMember = claims.org_member_info;
28
+ const scope = [
29
+ ["workspace_id", claims.workspace_id],
30
+ ["org_id", claims.org_id],
31
+ [
32
+ "org_member_info.org_id",
33
+ typeof orgMember === "object" && orgMember !== null ? orgMember.org_id : void 0
34
+ ]
35
+ ].filter((entry) => typeof entry[1] === "string" && entry[1].length > 0).map(([claim, value]) => `${claim}=${value}`);
36
+ return scope.length > 0 ? scope.join("\0") : null;
25
37
  }
26
38
  function isTokenExpiring(token, now = Date.now()) {
27
39
  const expiresAt = jwtExpiresAtMs(token);
@@ -115,6 +127,7 @@ export {
115
127
  createAthenaCredentialSource,
116
128
  isTokenExpiring,
117
129
  jwtExpiresAtMs,
130
+ jwtScope,
118
131
  jwtSubject
119
132
  };
120
133
  //# sourceMappingURL=credential-source.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"credential-source.js","sources":["../../src/auth/credential-source.ts"],"sourcesContent":["import type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\n\n/**\n * Renews a token-backed sign-in. `force: true` means the token just presented\n * was refused, or has expired: the renewer must obtain a new one rather than\n * return the one it holds. Resolves `null` when it cannot produce a token.\n */\nexport type AthenaTokenRenewer = (options: { force: boolean }) => Promise<string | null>;\n\n/** What the credential source reads, fresh, on every resolve. */\nexport interface AthenaCredentialInputs {\n /**\n * The host's own getter (`config.getToken`). It owns renewal for its\n * sign-in, so it always wins; the held `token` is its fallback when it\n * resolves nothing.\n */\n hostGetToken?: AthenaStatewireTokenGetter;\n /** The token currently held: the host's, the sign-in provider's or the host bridge's. */\n token: string | null | undefined;\n /** How that token renews (the sign-in provider or the host bridge); absent for a static token. */\n renew?: AthenaTokenRenewer;\n}\n\n/**\n * The single place every SDK network path gets the token it presents — the\n * live thread connection, the session list's index and REST calls, uploads,\n * search, voice and the collaborative-document mint — read right before it is\n * presented.\n */\nexport interface AthenaCredentialSource {\n /**\n * The token to present now. `force` renews it first (the previous one was\n * refused). A token that has expired, or expires within\n * {@link TOKEN_EXPIRY_SKEW_MS}, is renewed before it is presented, so a tab\n * waking from sleep never presents a dead token. Concurrent renewals share\n * one in-flight request, and calls made while one is in flight wait for it\n * instead of presenting the token it is replacing. A renewal never hands one\n * account's token to a call made after the signed-in account changed.\n */\n getToken: (options?: { force?: boolean }) => Promise<string | null>;\n /** Called, with no arguments, whenever a different token becomes current. */\n subscribe: (listener: () => void) => () => void;\n /**\n * How many times the identity behind the token has changed: its JWT\n * subject, or the token itself when it is opaque. Renewals for the same\n * identity leave it alone. Long-lived connections key on it, so they keep\n * running across renewals and are rebuilt when someone else signs in,\n * including under a host `getToken`, whose identity is only known once a\n * token has been resolved. Subscribers hear when it moves.\n */\n identityEpoch: () => number;\n /** Tell subscribers the held token changed (the provider calls this when its inputs do). */\n notifyTokenChanged: () => void;\n}\n\n/** A token this close to its expiry is renewed before it is presented. */\nexport const TOKEN_EXPIRY_SKEW_MS = 30_000;\n\n/**\n * The `exp` of a JWT, in milliseconds, or `null` for anything that is not a\n * JWT with a numeric `exp`. Read without verification: it only decides whether\n * to renew before presenting; the server still validates every token.\n */\nexport function jwtExpiresAtMs(token: string): number | null {\n const payload = token.split('.')[1];\n if (!payload) return null;\n try {\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const exp: unknown = (JSON.parse(atob(padded)) as { exp?: unknown }).exp;\n return typeof exp === 'number' && Number.isFinite(exp) ? exp * 1000 : null;\n } catch {\n return null;\n }\n}\n\n/** The unverified `sub` of a JWT, or `null` for anything that is not a JWT with one. */\nexport function jwtSubject(token: string): string | null {\n const payload = token.split('.')[1];\n if (!payload) return null;\n try {\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const sub: unknown = (JSON.parse(atob(padded)) as { sub?: unknown }).sub;\n return typeof sub === 'string' && sub.length > 0 ? sub : null;\n } catch {\n return null;\n }\n}\n\n/** True for a JWT that has expired or expires within {@link TOKEN_EXPIRY_SKEW_MS}. */\nexport function isTokenExpiring(token: string, now: number = Date.now()): boolean {\n const expiresAt = jwtExpiresAtMs(token);\n return expiresAt !== null && expiresAt - now <= TOKEN_EXPIRY_SKEW_MS;\n}\n\nexport function createAthenaCredentialSource(\n read: () => AthenaCredentialInputs,\n): AthenaCredentialSource {\n const listeners = new Set<() => void>();\n // The renewal in flight, and whether the host's getter runs it (the host\n // owns identity for its getter) or the sign-in provider's / bridge's renewer.\n let renewal: { token: Promise<string | null>; byHost: boolean } | null = null;\n let lastResolved: string | null = null;\n let identity: string | null = null;\n let identityEpoch = 0;\n\n const notifyListeners = () => {\n for (const listener of [...listeners]) listener();\n };\n\n /** Note who a token acts as; true when that is someone other than before. */\n const observeIdentity = (token: string | null | undefined): boolean => {\n if (!token) return false;\n const next = jwtSubject(token) ?? token;\n if (next === identity) return false;\n const changed = identity !== null;\n identity = next;\n if (changed) identityEpoch += 1;\n return changed;\n };\n\n const notifyTokenChanged = () => {\n observeIdentity(read().token);\n notifyListeners();\n };\n\n /** Record what a resolve produced; a different token than the last one is news. */\n const settle = (token: string | null): string | null => {\n const identityChanged = observeIdentity(token);\n if (token !== null && token !== lastResolved) {\n const replaced = lastResolved !== null;\n lastResolved = token;\n if (replaced || identityChanged) notifyListeners();\n } else if (identityChanged) {\n notifyListeners();\n }\n return token;\n };\n\n const shareRenewal = (\n start: () => Promise<string | null>,\n byHost: boolean,\n ): { token: Promise<string | null>; byHost: boolean } => {\n if (!renewal) {\n const entry = {\n token: start().finally(() => {\n if (renewal === entry) renewal = null;\n }),\n byHost,\n };\n renewal = entry;\n }\n return renewal;\n };\n\n /**\n * What a finished renewal hands one caller. A host getter's token is\n * presented as is: the host owns identity for its getter. A sign-in\n * provider's or bridge's renewed token is presented only while it acts as\n * the account the provider holds now. When the account changed while the\n * renewal was in flight (its JWT subject differs from the held token's, or\n * the provider holds no token any more), the held token, the current\n * account's, is presented instead. A renewal that produced nothing keeps\n * the held token: the caller presents it and the host's refusal ends the\n * attempt.\n */\n const presentable = (\n { byHost }: { byHost: boolean },\n renewed: string | null,\n ): string | null => {\n const held = read().token ?? null;\n if (byHost) return renewed ?? held;\n if (!renewed || !held) return held;\n const heldSubject = jwtSubject(held);\n const renewedSubject = jwtSubject(renewed);\n return heldSubject && renewedSubject && heldSubject !== renewedSubject ? held : renewed;\n };\n\n const awaitRenewal = async (shared: {\n token: Promise<string | null>;\n byHost: boolean;\n }): Promise<string | null> => settle(presentable(shared, await shared.token));\n\n const getToken = async ({ force = false }: { force?: boolean } = {}): Promise<string | null> => {\n if (renewal) return awaitRenewal(renewal);\n const inputs = read();\n const hostGetToken = inputs.hostGetToken;\n if (hostGetToken) {\n if (!force) return settle((await hostGetToken({ force: false })) ?? read().token ?? null);\n return awaitRenewal(\n shareRenewal(async () => (await hostGetToken({ force: true })) ?? null, true),\n );\n }\n const current = inputs.token ?? null;\n const renew = inputs.renew;\n if (!renew) return settle(current);\n const stale = force || (current !== null && isTokenExpiring(current));\n if (!stale) return settle(current);\n return awaitRenewal(shareRenewal(() => renew({ force: true }), false));\n };\n\n return {\n getToken,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n notifyTokenChanged,\n identityEpoch: () => identityEpoch,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"credential-source.js","sources":["../../src/auth/credential-source.ts"],"sourcesContent":["import type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\n\n/**\n * Renews a token-backed sign-in. `force: true` means the token just presented\n * was refused, or has expired: the renewer must obtain a new one rather than\n * return the one it holds. Resolves `null` when it cannot produce a token.\n */\nexport type AthenaTokenRenewer = (options: { force: boolean }) => Promise<string | null>;\n\n/** What the credential source reads, fresh, on every resolve. */\nexport interface AthenaCredentialInputs {\n /**\n * The host's own getter (`config.getToken`). It owns renewal for its\n * sign-in, so it always wins; the held `token` is its fallback when it\n * resolves nothing.\n */\n hostGetToken?: AthenaStatewireTokenGetter;\n /** The token currently held: the host's, the sign-in provider's or the host bridge's. */\n token: string | null | undefined;\n /** How that token renews (the sign-in provider or the host bridge); absent for a static token. */\n renew?: AthenaTokenRenewer;\n}\n\n/**\n * The single place every SDK network path gets the token it presents — the\n * live thread connection, the session list's index and REST calls, uploads,\n * search, voice and the collaborative-document mint — read right before it is\n * presented.\n */\nexport interface AthenaCredentialSource {\n /**\n * The token to present now. `force` renews it first (the previous one was\n * refused). A token that has expired, or expires within\n * {@link TOKEN_EXPIRY_SKEW_MS}, is renewed before it is presented, so a tab\n * waking from sleep never presents a dead token. Concurrent renewals share\n * one in-flight request, and calls made while one is in flight wait for it\n * instead of presenting the token it is replacing. A renewal never hands one\n * account's token to a call made after the signed-in account changed.\n */\n getToken: (options?: { force?: boolean }) => Promise<string | null>;\n /** Called, with no arguments, whenever a different token becomes current. */\n subscribe: (listener: () => void) => () => void;\n /**\n * How many times the identity behind the token has changed: its JWT\n * subject, or the token itself when it is opaque. Renewals for the same\n * identity leave it alone. Long-lived connections key on it, so they keep\n * running across renewals and are rebuilt when someone else signs in,\n * including under a host `getToken`, whose identity is only known once a\n * token has been resolved. Subscribers hear when it moves.\n */\n identityEpoch: () => number;\n /** Tell subscribers the held token changed (the provider calls this when its inputs do). */\n notifyTokenChanged: () => void;\n}\n\n/** A token this close to its expiry is renewed before it is presented. */\nexport const TOKEN_EXPIRY_SKEW_MS = 30_000;\n\n/** The unverified claims of a JWT, or `null` for anything that is not one. */\nfunction jwtClaims(token: string): Record<string, unknown> | null {\n const payload = token.split('.')[1];\n if (!payload) return null;\n try {\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const claims: unknown = JSON.parse(atob(padded));\n return typeof claims === 'object' && claims !== null && !Array.isArray(claims)\n ? (claims as Record<string, unknown>)\n : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `exp` of a JWT, in milliseconds, or `null` for anything that is not a\n * JWT with a numeric `exp`. Read without verification: it only decides whether\n * to renew before presenting; the server still validates every token.\n */\nexport function jwtExpiresAtMs(token: string): number | null {\n const exp = jwtClaims(token)?.exp;\n return typeof exp === 'number' && Number.isFinite(exp) ? exp * 1000 : null;\n}\n\n/** The unverified `sub` of a JWT, or `null` for anything that is not a JWT with one. */\nexport function jwtSubject(token: string): string | null {\n const sub = jwtClaims(token)?.sub;\n return typeof sub === 'string' && sub.length > 0 ? sub : null;\n}\n\n/**\n * The unverified claims that scope a JWT to a workspace or org, as one stable\n * string, or `null` when it names none: Athena's `workspace_id` (an Office\n * session's token), `org_id` (an external sign-in's), and PropelAuth's\n * active-org `org_member_info.org_id`. Two of one person's tokens scoped to\n * different workspaces read differently; a renewal keeps it.\n */\nexport function jwtScope(token: string): string | null {\n const claims = jwtClaims(token);\n if (claims === null) return null;\n const orgMember = claims.org_member_info;\n const scope = [\n ['workspace_id', claims.workspace_id],\n ['org_id', claims.org_id],\n [\n 'org_member_info.org_id',\n typeof orgMember === 'object' && orgMember !== null\n ? (orgMember as Record<string, unknown>).org_id\n : undefined,\n ],\n ]\n .filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].length > 0)\n .map(([claim, value]) => `${claim}=${value}`);\n return scope.length > 0 ? scope.join('\\u0000') : null;\n}\n\n/** True for a JWT that has expired or expires within {@link TOKEN_EXPIRY_SKEW_MS}. */\nexport function isTokenExpiring(token: string, now: number = Date.now()): boolean {\n const expiresAt = jwtExpiresAtMs(token);\n return expiresAt !== null && expiresAt - now <= TOKEN_EXPIRY_SKEW_MS;\n}\n\nexport function createAthenaCredentialSource(\n read: () => AthenaCredentialInputs,\n): AthenaCredentialSource {\n const listeners = new Set<() => void>();\n // The renewal in flight, and whether the host's getter runs it (the host\n // owns identity for its getter) or the sign-in provider's / bridge's renewer.\n let renewal: { token: Promise<string | null>; byHost: boolean } | null = null;\n let lastResolved: string | null = null;\n let identity: string | null = null;\n let identityEpoch = 0;\n\n const notifyListeners = () => {\n for (const listener of [...listeners]) listener();\n };\n\n /** Note who a token acts as; true when that is someone other than before. */\n const observeIdentity = (token: string | null | undefined): boolean => {\n if (!token) return false;\n const next = jwtSubject(token) ?? token;\n if (next === identity) return false;\n const changed = identity !== null;\n identity = next;\n if (changed) identityEpoch += 1;\n return changed;\n };\n\n const notifyTokenChanged = () => {\n observeIdentity(read().token);\n notifyListeners();\n };\n\n /** Record what a resolve produced; a different token than the last one is news. */\n const settle = (token: string | null): string | null => {\n const identityChanged = observeIdentity(token);\n if (token !== null && token !== lastResolved) {\n const replaced = lastResolved !== null;\n lastResolved = token;\n if (replaced || identityChanged) notifyListeners();\n } else if (identityChanged) {\n notifyListeners();\n }\n return token;\n };\n\n const shareRenewal = (\n start: () => Promise<string | null>,\n byHost: boolean,\n ): { token: Promise<string | null>; byHost: boolean } => {\n if (!renewal) {\n const entry = {\n token: start().finally(() => {\n if (renewal === entry) renewal = null;\n }),\n byHost,\n };\n renewal = entry;\n }\n return renewal;\n };\n\n /**\n * What a finished renewal hands one caller. A host getter's token is\n * presented as is: the host owns identity for its getter. A sign-in\n * provider's or bridge's renewed token is presented only while it acts as\n * the account the provider holds now. When the account changed while the\n * renewal was in flight (its JWT subject differs from the held token's, or\n * the provider holds no token any more), the held token, the current\n * account's, is presented instead. A renewal that produced nothing keeps\n * the held token: the caller presents it and the host's refusal ends the\n * attempt.\n */\n const presentable = (\n { byHost }: { byHost: boolean },\n renewed: string | null,\n ): string | null => {\n const held = read().token ?? null;\n if (byHost) return renewed ?? held;\n if (!renewed || !held) return held;\n const heldSubject = jwtSubject(held);\n const renewedSubject = jwtSubject(renewed);\n return heldSubject && renewedSubject && heldSubject !== renewedSubject ? held : renewed;\n };\n\n const awaitRenewal = async (shared: {\n token: Promise<string | null>;\n byHost: boolean;\n }): Promise<string | null> => settle(presentable(shared, await shared.token));\n\n const getToken = async ({ force = false }: { force?: boolean } = {}): Promise<string | null> => {\n if (renewal) return awaitRenewal(renewal);\n const inputs = read();\n const hostGetToken = inputs.hostGetToken;\n if (hostGetToken) {\n if (!force) return settle((await hostGetToken({ force: false })) ?? read().token ?? null);\n return awaitRenewal(\n shareRenewal(async () => (await hostGetToken({ force: true })) ?? null, true),\n );\n }\n const current = inputs.token ?? null;\n const renew = inputs.renew;\n if (!renew) return settle(current);\n const stale = force || (current !== null && isTokenExpiring(current));\n if (!stale) return settle(current);\n return awaitRenewal(shareRenewal(() => renew({ force: true }), false));\n };\n\n return {\n getToken,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n notifyTokenChanged,\n identityEpoch: () => identityEpoch,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const runtimeCatalog = require("../agent-runtime/src/runtime-catalog.cjs");
3
4
  const statewireRunConfig = require("../runtime/statewire-run-config.cjs");
4
5
  function allowsAnyModel(value) {
5
6
  return value !== false;
@@ -23,19 +24,7 @@ function labeledModelsOf(value) {
23
24
  }
24
25
  }
25
26
  if (!Array.isArray(rows)) return [];
26
- const entries = [];
27
- for (const row of rows) {
28
- if (!isRecord(row)) continue;
29
- const model = nonBlank(row.model);
30
- if (model === null) continue;
31
- entries.push({
32
- id: typeof row.id === "string" && row.id !== "" ? row.id : model,
33
- model,
34
- label: nonBlank(row.label) ?? model,
35
- isDefault: row.isDefault === true
36
- });
37
- }
38
- return entries;
27
+ return runtimeCatalog.normalizeCatalogAgentModelEntries({ rows: Array.from(rows).filter(isRecord) });
39
28
  }
40
29
  function channelModelsOf(value) {
41
30
  if (!isRecord(value)) return {};
@@ -78,16 +67,19 @@ function isAutomaticModelLabel({
78
67
  }
79
68
  function agentDefaultModel({
80
69
  source,
81
- channel
70
+ channel,
71
+ refuseReplacements = false
82
72
  }) {
83
73
  const marked = source.models.find((entry) => entry.isDefault) ?? source.models[0];
84
- return (channel ? source.channelModels[channel] : void 0) ?? source.model ?? (marked == null ? void 0 : marked.model) ?? null;
74
+ const channelModel = channel && !refuseReplacements ? source.channelModels[channel] : void 0;
75
+ return channelModel ?? source.model ?? (marked == null ? void 0 : marked.model) ?? null;
85
76
  }
86
77
  function buildAgentModelOptions({
87
78
  source,
88
79
  channel,
89
80
  pinnedModel,
90
- modelLabel
81
+ modelLabel,
82
+ refuseReplacements = false
91
83
  }) {
92
84
  const options = [];
93
85
  const add = (model, label) => {
@@ -101,7 +93,7 @@ function buildAgentModelOptions({
101
93
  );
102
94
  }
103
95
  if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));
104
- const defaultModel = agentDefaultModel({ source, channel });
96
+ const defaultModel = agentDefaultModel({ source, channel, refuseReplacements });
105
97
  if (defaultModel !== null) {
106
98
  add(defaultModel, modelLabel(defaultModel));
107
99
  for (const option of options) option.isDefault = option.model === defaultModel;
@@ -112,8 +104,24 @@ function buildAgentModelOptions({
112
104
  }
113
105
  return { options, defaultModel };
114
106
  }
107
+ function agentModelOptionsWithoutDefault({
108
+ source,
109
+ channel,
110
+ pinnedModel,
111
+ modelLabel
112
+ }) {
113
+ const { options } = buildAgentModelOptions({
114
+ source,
115
+ channel,
116
+ pinnedModel,
117
+ modelLabel,
118
+ refuseReplacements: true
119
+ });
120
+ return { options: options.map((option) => ({ ...option, isDefault: false })), defaultModel: null };
121
+ }
115
122
  exports.agentAssetIdOf = agentAssetIdOf;
116
123
  exports.agentDefaultModel = agentDefaultModel;
124
+ exports.agentModelOptionsWithoutDefault = agentModelOptionsWithoutDefault;
117
125
  exports.allowsAnyModel = allowsAnyModel;
118
126
  exports.buildAgentModelOptions = buildAgentModelOptions;
119
127
  exports.isAutomaticModelLabel = isAutomaticModelLabel;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-model-options.cjs","sources":["../../src/catalog/agent-model-options.ts"],"sourcesContent":["import { parseCollabAgentRef } from '../runtime/statewire-run-config';\n\n/**\n * A collab agent's own model choices, from its published definition: exactly\n * the models its author listed (the agent editor's Models section), the\n * default marked. The rules are Athena's own composer's (Olympus) and the\n * Jevin app's, so every host offers the same choices for one agent:\n *\n * - the options are the agent's labeled models, by the author's labels; an\n * automatic name (empty, the editor's old `{ id: 'default', label: 'Default' }`,\n * or one that only restates the model) shows the model's catalog label;\n * - a legacy agent with no labeled list offers its single `model`;\n * - the session channel's model override (`channelOverrides[channel].model`)\n * is that channel's default, added when the list lacks it;\n * - a model the thread pins that is none of the agent's models is shown as it\n * is (`pinnedOutside`), never replaced.\n *\n * Restricting a picker to these choices is opt-in per agent: only an agent\n * whose \"Allow any workspace model\" switch is off (`allowAnyModel: false`)\n * does; every other agent keeps the workspace's models.\n *\n * Pure, for `useAthenaAgentModelOptions` and the tests.\n */\n\n/** One model choice of a collab agent. */\nexport interface AthenaAgentModelOption {\n /** The model id a send carries (`model`). */\n model: string;\n /** The author's label, or the model's catalog label for an automatic name. */\n label: string;\n /** The model the thread runs while it pins none (the agent's own, or its channel's). */\n isDefault: boolean;\n /**\n * A model the thread's server config pins that is none of the agent's models\n * (pinned by another client, or by an older picker): shown, never replaced.\n */\n pinnedOutside: boolean;\n}\n\n/** One entry of the agent's labeled model list. */\nexport interface AgentLabeledModel {\n id: string;\n model: string;\n label: string;\n isDefault: boolean;\n}\n\n/** What an agent's published definition says about its models. */\nexport interface AgentModelSource {\n /** The model the definition runs (`config.model`); `null` when it names none. */\n model: string | null;\n /** The labeled list (`config.models`), complete rows only, in the author's order. */\n models: readonly AgentLabeledModel[];\n /** Per-channel model overrides (`channelOverrides[<channel>].model`), by channel id. */\n channelModels: Readonly<Record<string, string>>;\n /**\n * Whether people may pick any workspace model (`config.allowAnyModel`, the\n * agent editor's \"Allow any workspace model\" switch; see\n * {@link allowsAnyModel}). Only `false` restricts a picker to the agent's\n * own models.\n */\n allowAnyModel: boolean;\n}\n\n/**\n * Whether a config's `allowAnyModel` value lets people pick any model: only an\n * explicit `false` restricts; absent, `null`, `true` and any non-boolean allow\n * (agora's `collab_agent_allows_any_model`), so every agent saved before the\n * switch existed behaves as it always has.\n */\nexport function allowsAnyModel(value: unknown): boolean {\n return value !== false;\n}\n\n/** The display label of a model id; the id itself when the catalog does not know it. */\nexport type ModelLabelOf = (model: string) => string;\n\n/** The row the agent editor used to create for an agent with no stored entries. */\nconst LEGACY_DEFAULT_ENTRY_ID = 'default';\nconst LEGACY_DEFAULT_ENTRY_LABEL = 'Default';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst nonBlank = (value: unknown): string | null =>\n typeof value === 'string' && value.trim() !== '' ? value.trim() : null;\n\n/**\n * The collab agent's asset id for an `agent` value: a `collab_agent:<id>` ref\n * (the provider's `agent` shape) or the bare asset id (the agent picker's\n * value). `null` for a blank value.\n */\nexport function agentAssetIdOf(agent: string | null | undefined): string | null {\n const trimmed = nonBlank(agent);\n if (trimmed === null) return null;\n return parseCollabAgentRef(trimmed) ?? (trimmed.includes(':') ? null : trimmed);\n}\n\n/** A stored `models` value: an array of rows, JSON-encoded or not; malformed rows dropped. */\nfunction labeledModelsOf(value: unknown): AgentLabeledModel[] {\n let rows: unknown = value;\n if (typeof rows === 'string') {\n try {\n rows = JSON.parse(rows);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(rows)) return [];\n const entries: AgentLabeledModel[] = [];\n for (const row of rows) {\n if (!isRecord(row)) continue;\n const model = nonBlank(row.model);\n if (model === null) continue;\n entries.push({\n id: typeof row.id === 'string' && row.id !== '' ? row.id : model,\n model,\n label: nonBlank(row.label) ?? model,\n isDefault: row.isDefault === true,\n });\n }\n return entries;\n}\n\n/** A stored `channelOverrides` value's per-channel models. */\nfunction channelModelsOf(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n const out: Record<string, string> = {};\n for (const [channel, override] of Object.entries(value)) {\n const model = isRecord(override) ? nonBlank(override.model) : null;\n if (model !== null) out[channel] = model;\n }\n return out;\n}\n\n/** Whether the agent asset's `athena_metadata` points at a published version. */\nexport function isPublishedAgent(athenaMetadata: unknown): boolean {\n if (!isRecord(athenaMetadata)) return false;\n const versionId = athenaMetadata.published_version_id;\n return (\n (typeof versionId === 'string' && versionId !== '') ||\n (typeof versionId === 'number' && Number.isFinite(versionId))\n );\n}\n\n/**\n * The model source of the agent's PUBLISHED definition, which every SDK run\n * compiles, from the agent asset's `athena_metadata` (publish writes\n * `published_config` beside `published_version_id`). `null` for an agent\n * that was never published, or whose published definition names no model\n * (not even a channel's).\n */\nexport function publishedAgentModelSource(athenaMetadata: unknown): AgentModelSource | null {\n if (!isRecord(athenaMetadata) || !isPublishedAgent(athenaMetadata)) return null;\n const config = athenaMetadata.published_config;\n if (!isRecord(config)) return null;\n const source: AgentModelSource = {\n model: nonBlank(config.model),\n models: labeledModelsOf(config.models),\n channelModels: channelModelsOf(config.channelOverrides),\n // The editor stores the field name; agora also accepts its snake-case alias.\n allowAnyModel: allowsAnyModel(\n Object.hasOwn(config, 'allowAnyModel') ? config.allowAnyModel : config.allow_any_model,\n ),\n };\n const namesAModel =\n source.model !== null ||\n source.models.length > 0 ||\n Object.keys(source.channelModels).length > 0;\n return namesAModel ? source : null;\n}\n\n/**\n * Whether an entry's name is automatic: empty, the editor's old \"Default\", or\n * a name that only restates the entry's model (its id or catalog label).\n */\nexport function isAutomaticModelLabel({\n entry,\n modelLabel,\n}: {\n entry: Pick<AgentLabeledModel, 'id' | 'model' | 'label'>;\n modelLabel: ModelLabelOf;\n}): boolean {\n const label = entry.label.trim();\n if (label === '') return true;\n if (entry.id === LEGACY_DEFAULT_ENTRY_ID && label === LEGACY_DEFAULT_ENTRY_LABEL) return true;\n return label === entry.model || label === modelLabel(entry.model);\n}\n\n/**\n * The model a thread runs while it pins none: the channel's override, else the\n * definition's `model`, else its marked (or first) labeled entry.\n */\nexport function agentDefaultModel({\n source,\n channel,\n}: {\n source: AgentModelSource;\n channel: string | null;\n}): string | null {\n const marked = source.models.find((entry) => entry.isDefault) ?? source.models[0];\n return (channel ? source.channelModels[channel] : undefined) ?? source.model ?? marked?.model ?? null;\n}\n\n/**\n * The agent's model choices and its default, by the rules above. Options keep\n * the author's order, one per model (the first entry of a model names it); the\n * default is marked, and appended when the list lacks it; a pinned model\n * outside the list comes last.\n */\nexport function buildAgentModelOptions({\n source,\n channel,\n pinnedModel,\n modelLabel,\n}: {\n source: AgentModelSource;\n channel: string | null;\n /** The model the thread's server config pins; `null` while it pins none (or is unknown). */\n pinnedModel: string | null;\n modelLabel: ModelLabelOf;\n}): { options: AthenaAgentModelOption[]; defaultModel: string | null } {\n const options: AthenaAgentModelOption[] = [];\n const add = (model: string, label: string) => {\n if (options.some((option) => option.model === model)) return;\n options.push({ model, label, isDefault: false, pinnedOutside: false });\n };\n for (const entry of source.models) {\n add(\n entry.model,\n isAutomaticModelLabel({ entry, modelLabel }) ? modelLabel(entry.model) : entry.label.trim(),\n );\n }\n if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));\n\n const defaultModel = agentDefaultModel({ source, channel });\n if (defaultModel !== null) {\n add(defaultModel, modelLabel(defaultModel));\n for (const option of options) option.isDefault = option.model === defaultModel;\n }\n\n const pinned = nonBlank(pinnedModel);\n if (pinned !== null && !options.some((option) => option.model === pinned)) {\n options.push({ model: pinned, label: modelLabel(pinned), isDefault: false, pinnedOutside: true });\n }\n return { options, defaultModel };\n}\n"],"names":["parseCollabAgentRef"],"mappings":"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"}
1
+ {"version":3,"file":"agent-model-options.cjs","sources":["../../src/catalog/agent-model-options.ts"],"sourcesContent":["import { normalizeCatalogAgentModelEntries } from '@athenaintel/agent-runtime/runtime-catalog';\nimport { parseCollabAgentRef } from '../runtime/statewire-run-config';\n\n/**\n * A collab agent's own model choices, from its published definition: exactly\n * the models its author listed (the agent editor's Models section), the\n * default marked. The rules are Athena's own composer's (Olympus) and the\n * Jevin app's, so every host offers the same choices for one agent:\n *\n * - the options are the agent's labeled models, by the author's labels; an\n * automatic name (empty, the editor's old `{ id: 'default', label: 'Default' }`,\n * or one that only restates the model) shows the model's catalog label;\n * - a legacy agent with no labeled list offers its single `model`;\n * - the session channel's model override (`channelOverrides[channel].model`)\n * is that channel's default, added when the list lacks it, until the\n * deployment's refusal day (identity v7 D12, `channel-refusal.ts`): from it\n * agora drops a channel's model at every compile, so the agent's own model is\n * the default on every channel;\n * - a model the thread pins that is none of the agent's models is shown as it\n * is (`pinnedOutside`), never replaced.\n *\n * Restricting a picker to these choices is opt-in per agent: only an agent\n * whose \"Allow any workspace model\" switch is off (`allowAnyModel: false`)\n * does; every other agent keeps the workspace's models.\n *\n * Pure, for `useAthenaAgentModelOptions` and the tests.\n */\n\n/** One model choice of a collab agent. */\nexport interface AthenaAgentModelOption {\n /** The model id a send carries (`model`). */\n model: string;\n /** The author's label, or the model's catalog label for an automatic name. */\n label: string;\n /**\n * The model the thread runs while it pins none: the agent's own, or its channel's until the\n * deployment's refusal day.\n */\n isDefault: boolean;\n /**\n * A model the thread's server config pins that is none of the agent's models\n * (pinned by another client, or by an older picker): shown, never replaced.\n */\n pinnedOutside: boolean;\n}\n\n/** One entry of the agent's labeled model list. */\nexport interface AgentLabeledModel {\n id: string;\n model: string;\n label: string;\n isDefault: boolean;\n}\n\n/** What an agent's published definition says about its models. */\nexport interface AgentModelSource {\n /** The model the definition runs (`config.model`); `null` when it names none. */\n model: string | null;\n /** The labeled list (`config.models`), complete rows only, in the author's order. */\n models: readonly AgentLabeledModel[];\n /** Per-channel model overrides (`channelOverrides[<channel>].model`), by channel id. */\n channelModels: Readonly<Record<string, string>>;\n /**\n * Whether people may pick any workspace model (`config.allowAnyModel`, the\n * agent editor's \"Allow any workspace model\" switch; see\n * {@link allowsAnyModel}). Only `false` restricts a picker to the agent's\n * own models.\n */\n allowAnyModel: boolean;\n}\n\n/**\n * Whether a config's `allowAnyModel` value lets people pick any model: only an\n * explicit `false` restricts; absent, `null`, `true` and any non-boolean allow\n * (agora's `collab_agent_allows_any_model`), so every agent saved before the\n * switch existed behaves as it always has.\n */\nexport function allowsAnyModel(value: unknown): boolean {\n return value !== false;\n}\n\n/** The display label of a model id; the id itself when the catalog does not know it. */\nexport type ModelLabelOf = (model: string) => string;\n\n/** The row the agent editor used to create for an agent with no stored entries. */\nconst LEGACY_DEFAULT_ENTRY_ID = 'default';\nconst LEGACY_DEFAULT_ENTRY_LABEL = 'Default';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst nonBlank = (value: unknown): string | null =>\n typeof value === 'string' && value.trim() !== '' ? value.trim() : null;\n\n/**\n * The collab agent's asset id for an `agent` value: a `collab_agent:<id>` ref\n * (the provider's `agent` shape) or the bare asset id (the agent picker's\n * value). `null` for a blank value.\n */\nexport function agentAssetIdOf(agent: string | null | undefined): string | null {\n const trimmed = nonBlank(agent);\n if (trimmed === null) return null;\n return parseCollabAgentRef(trimmed) ?? (trimmed.includes(':') ? null : trimmed);\n}\n\n/** A stored `models` value: an array of rows, JSON-encoded or not; malformed rows dropped. */\nfunction labeledModelsOf(value: unknown): AgentLabeledModel[] {\n let rows: unknown = value;\n if (typeof rows === 'string') {\n try {\n rows = JSON.parse(rows);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(rows)) return [];\n return normalizeCatalogAgentModelEntries({ rows: Array.from(rows).filter(isRecord) });\n}\n\n/** A stored `channelOverrides` value's per-channel models. */\nfunction channelModelsOf(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n const out: Record<string, string> = {};\n for (const [channel, override] of Object.entries(value)) {\n const model = isRecord(override) ? nonBlank(override.model) : null;\n if (model !== null) out[channel] = model;\n }\n return out;\n}\n\n/** Whether the agent asset's `athena_metadata` points at a published version. */\nexport function isPublishedAgent(athenaMetadata: unknown): boolean {\n if (!isRecord(athenaMetadata)) return false;\n const versionId = athenaMetadata.published_version_id;\n return (\n (typeof versionId === 'string' && versionId !== '') ||\n (typeof versionId === 'number' && Number.isFinite(versionId))\n );\n}\n\n/**\n * The model source of the agent's PUBLISHED definition, which every SDK run\n * compiles, from the agent asset's `athena_metadata` (publish writes\n * `published_config` beside `published_version_id`). `null` for an agent\n * that was never published, or whose published definition names no model\n * (not even a channel's).\n */\nexport function publishedAgentModelSource(athenaMetadata: unknown): AgentModelSource | null {\n if (!isRecord(athenaMetadata) || !isPublishedAgent(athenaMetadata)) return null;\n const config = athenaMetadata.published_config;\n if (!isRecord(config)) return null;\n const source: AgentModelSource = {\n model: nonBlank(config.model),\n models: labeledModelsOf(config.models),\n channelModels: channelModelsOf(config.channelOverrides),\n // The editor stores the field name; agora also accepts its snake-case alias.\n allowAnyModel: allowsAnyModel(\n Object.hasOwn(config, 'allowAnyModel') ? config.allowAnyModel : config.allow_any_model,\n ),\n };\n const namesAModel =\n source.model !== null ||\n source.models.length > 0 ||\n Object.keys(source.channelModels).length > 0;\n return namesAModel ? source : null;\n}\n\n/**\n * Whether an entry's name is automatic: empty, the editor's old \"Default\", or\n * a name that only restates the entry's model (its id or catalog label).\n */\nexport function isAutomaticModelLabel({\n entry,\n modelLabel,\n}: {\n entry: Pick<AgentLabeledModel, 'id' | 'model' | 'label'>;\n modelLabel: ModelLabelOf;\n}): boolean {\n const label = entry.label.trim();\n if (label === '') return true;\n if (entry.id === LEGACY_DEFAULT_ENTRY_ID && label === LEGACY_DEFAULT_ENTRY_LABEL) return true;\n return label === entry.model || label === modelLabel(entry.model);\n}\n\n/**\n * The model a thread runs while it pins none: the channel's override, else the\n * definition's `model`, else its marked (or first) labeled entry.\n *\n * From the deployment's refusal day (`refuseReplacements`, identity v7 D12) no\n * channel's model applies: agora's compile drops a layer's `model` whatever it\n * names (`refused_text_layer`), so the agent's own runs. That is what agora's\n * shared cases (`agora/agora/services/agent_config/channel_refusal_contract.json`)\n * say of every layer that names a model, and `channel-refusal-contract.test.ts`\n * holds this to them; which settings replace the agent stays the server's to say.\n */\nexport function agentDefaultModel({\n source,\n channel,\n refuseReplacements = false,\n}: {\n source: AgentModelSource;\n channel: string | null;\n /** The deployment's refusal day has come (`channelReplacementsRefusedOn`). */\n refuseReplacements?: boolean;\n}): string | null {\n const marked = source.models.find((entry) => entry.isDefault) ?? source.models[0];\n const channelModel = channel && !refuseReplacements ? source.channelModels[channel] : undefined;\n return channelModel ?? source.model ?? marked?.model ?? null;\n}\n\n/**\n * The agent's model choices and its default, by the rules above. Options keep\n * the author's order, one per model (the first entry of a model names it); the\n * default is marked, and appended when the list lacks it; a pinned model\n * outside the list comes last. From the refusal day (`refuseReplacements`) a\n * channel's model is offered only when the agent lists it, or the thread pins it.\n */\nexport function buildAgentModelOptions({\n source,\n channel,\n pinnedModel,\n modelLabel,\n refuseReplacements = false,\n}: {\n source: AgentModelSource;\n channel: string | null;\n /** The model the thread's server config pins; `null` while it pins none (or is unknown). */\n pinnedModel: string | null;\n modelLabel: ModelLabelOf;\n /** The deployment's refusal day has come (`channelReplacementsRefusedOn`). */\n refuseReplacements?: boolean;\n}): { options: AthenaAgentModelOption[]; defaultModel: string | null } {\n const options: AthenaAgentModelOption[] = [];\n const add = (model: string, label: string) => {\n if (options.some((option) => option.model === model)) return;\n options.push({ model, label, isDefault: false, pinnedOutside: false });\n };\n for (const entry of source.models) {\n add(\n entry.model,\n isAutomaticModelLabel({ entry, modelLabel }) ? modelLabel(entry.model) : entry.label.trim(),\n );\n }\n if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));\n\n const defaultModel = agentDefaultModel({ source, channel, refuseReplacements });\n if (defaultModel !== null) {\n add(defaultModel, modelLabel(defaultModel));\n for (const option of options) option.isDefault = option.model === defaultModel;\n }\n\n const pinned = nonBlank(pinnedModel);\n if (pinned !== null && !options.some((option) => option.model === pinned)) {\n options.push({ model: pinned, label: modelLabel(pinned), isDefault: false, pinnedOutside: true });\n }\n return { options, defaultModel };\n}\n\n/**\n * The agent's choices while its default can't be named yet (a chat on a channel, before the\n * deployment's refusal day is known): its own models only, the restriction they make kept, none\n * marked default and no default named. A channel's model joins only once the day says it still\n * applies; a model the thread pins is shown as always.\n */\nexport function agentModelOptionsWithoutDefault({\n source,\n channel,\n pinnedModel,\n modelLabel,\n}: {\n source: AgentModelSource;\n channel: string | null;\n pinnedModel: string | null;\n modelLabel: ModelLabelOf;\n}): { options: AthenaAgentModelOption[]; defaultModel: null } {\n const { options } = buildAgentModelOptions({\n source,\n channel,\n pinnedModel,\n modelLabel,\n refuseReplacements: true,\n });\n return { options: options.map((option) => ({ ...option, isDefault: false })), defaultModel: null };\n}\n"],"names":["parseCollabAgentRef","normalizeCatalogAgentModelEntries"],"mappings":";;;;AA6EO,SAAS,eAAe,OAAyB;AACtD,SAAO,UAAU;AACnB;AAMA,MAAM,0BAA0B;AAChC,MAAM,6BAA6B;AAEnC,MAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAErE,MAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,MAAM,KAAA,MAAW,KAAK,MAAM,KAAA,IAAS;AAO7D,SAAS,eAAe,OAAiD;AAC9E,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,YAAY,KAAM,QAAO;AAC7B,SAAOA,mBAAAA,oBAAoB,OAAO,MAAM,QAAQ,SAAS,GAAG,IAAI,OAAO;AACzE;AAGA,SAAS,gBAAgB,OAAqC;AAC5D,MAAI,OAAgB;AACpB,MAAI,OAAO,SAAS,UAAU;AAC5B,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN,aAAO,CAAA;AAAA,IACT;AAAA,EACF;AACA,MAAI,CAAC,MAAM,QAAQ,IAAI,UAAU,CAAA;AACjC,SAAOC,eAAAA,kCAAkC,EAAE,MAAM,MAAM,KAAK,IAAI,EAAE,OAAO,QAAQ,GAAG;AACtF;AAGA,SAAS,gBAAgB,OAAwC;AAC/D,MAAI,CAAC,SAAS,KAAK,UAAU,CAAA;AAC7B,QAAM,MAA8B,CAAA;AACpC,aAAW,CAAC,SAAS,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACvD,UAAM,QAAQ,SAAS,QAAQ,IAAI,SAAS,SAAS,KAAK,IAAI;AAC9D,QAAI,UAAU,KAAM,KAAI,OAAO,IAAI;AAAA,EACrC;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,gBAAkC;AACjE,MAAI,CAAC,SAAS,cAAc,EAAG,QAAO;AACtC,QAAM,YAAY,eAAe;AACjC,SACG,OAAO,cAAc,YAAY,cAAc,MAC/C,OAAO,cAAc,YAAY,OAAO,SAAS,SAAS;AAE/D;AASO,SAAS,0BAA0B,gBAAkD;AAC1F,MAAI,CAAC,SAAS,cAAc,KAAK,CAAC,iBAAiB,cAAc,EAAG,QAAO;AAC3E,QAAM,SAAS,eAAe;AAC9B,MAAI,CAAC,SAAS,MAAM,EAAG,QAAO;AAC9B,QAAM,SAA2B;AAAA,IAC/B,OAAO,SAAS,OAAO,KAAK;AAAA,IAC5B,QAAQ,gBAAgB,OAAO,MAAM;AAAA,IACrC,eAAe,gBAAgB,OAAO,gBAAgB;AAAA;AAAA,IAEtD,eAAe;AAAA,MACb,OAAO,OAAO,QAAQ,eAAe,IAAI,OAAO,gBAAgB,OAAO;AAAA,IAAA;AAAA,EACzE;AAEF,QAAM,cACJ,OAAO,UAAU,QACjB,OAAO,OAAO,SAAS,KACvB,OAAO,KAAK,OAAO,aAAa,EAAE,SAAS;AAC7C,SAAO,cAAc,SAAS;AAChC;AAMO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AACF,GAGY;AACV,QAAM,QAAQ,MAAM,MAAM,KAAA;AAC1B,MAAI,UAAU,GAAI,QAAO;AACzB,MAAI,MAAM,OAAO,2BAA2B,UAAU,2BAA4B,QAAO;AACzF,SAAO,UAAU,MAAM,SAAS,UAAU,WAAW,MAAM,KAAK;AAClE;AAaO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA,qBAAqB;AACvB,GAKkB;AAChB,QAAM,SAAS,OAAO,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,KAAK,OAAO,OAAO,CAAC;AAChF,QAAM,eAAe,WAAW,CAAC,qBAAqB,OAAO,cAAc,OAAO,IAAI;AACtF,SAAO,gBAAgB,OAAO,UAAS,iCAAQ,UAAS;AAC1D;AASO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,qBAAqB;AACvB,GAQuE;AACrE,QAAM,UAAoC,CAAA;AAC1C,QAAM,MAAM,CAAC,OAAe,UAAkB;AAC5C,QAAI,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK,EAAG;AACtD,YAAQ,KAAK,EAAE,OAAO,OAAO,WAAW,OAAO,eAAe,OAAO;AAAA,EACvE;AACA,aAAW,SAAS,OAAO,QAAQ;AACjC;AAAA,MACE,MAAM;AAAA,MACN,sBAAsB,EAAE,OAAO,WAAA,CAAY,IAAI,WAAW,MAAM,KAAK,IAAI,MAAM,MAAM,KAAA;AAAA,IAAK;AAAA,EAE9F;AACA,MAAI,QAAQ,WAAW,KAAK,OAAO,UAAU,KAAM,KAAI,OAAO,OAAO,WAAW,OAAO,KAAK,CAAC;AAE7F,QAAM,eAAe,kBAAkB,EAAE,QAAQ,SAAS,oBAAoB;AAC9E,MAAI,iBAAiB,MAAM;AACzB,QAAI,cAAc,WAAW,YAAY,CAAC;AAC1C,eAAW,UAAU,QAAS,QAAO,YAAY,OAAO,UAAU;AAAA,EACpE;AAEA,QAAM,SAAS,SAAS,WAAW;AACnC,MAAI,WAAW,QAAQ,CAAC,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,MAAM,GAAG;AACzE,YAAQ,KAAK,EAAE,OAAO,QAAQ,OAAO,WAAW,MAAM,GAAG,WAAW,OAAO,eAAe,KAAA,CAAM;AAAA,EAClG;AACA,SAAO,EAAE,SAAS,aAAA;AACpB;AAQO,SAAS,gCAAgC;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAK8D;AAC5D,QAAM,EAAE,QAAA,IAAY,uBAAuB;AAAA,IACzC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB;AAAA,EAAA,CACrB;AACD,SAAO,EAAE,SAAS,QAAQ,IAAI,CAAC,YAAY,EAAE,GAAG,QAAQ,WAAW,MAAA,EAAQ,GAAG,cAAc,KAAA;AAC9F;;;;;;;;;"}
@@ -9,7 +9,10 @@
9
9
  * or one that only restates the model) shows the model's catalog label;
10
10
  * - a legacy agent with no labeled list offers its single `model`;
11
11
  * - the session channel's model override (`channelOverrides[channel].model`)
12
- * is that channel's default, added when the list lacks it;
12
+ * is that channel's default, added when the list lacks it, until the
13
+ * deployment's refusal day (identity v7 D12, `channel-refusal.ts`): from it
14
+ * agora drops a channel's model at every compile, so the agent's own model is
15
+ * the default on every channel;
13
16
  * - a model the thread pins that is none of the agent's models is shown as it
14
17
  * is (`pinnedOutside`), never replaced.
15
18
  *
@@ -25,7 +28,10 @@ export interface AthenaAgentModelOption {
25
28
  model: string;
26
29
  /** The author's label, or the model's catalog label for an automatic name. */
27
30
  label: string;
28
- /** The model the thread runs while it pins none (the agent's own, or its channel's). */
31
+ /**
32
+ * The model the thread runs while it pins none: the agent's own, or its channel's until the
33
+ * deployment's refusal day.
34
+ */
29
35
  isDefault: boolean;
30
36
  /**
31
37
  * A model the thread's server config pins that is none of the agent's models
@@ -92,24 +98,51 @@ export declare function isAutomaticModelLabel({ entry, modelLabel, }: {
92
98
  /**
93
99
  * The model a thread runs while it pins none: the channel's override, else the
94
100
  * definition's `model`, else its marked (or first) labeled entry.
101
+ *
102
+ * From the deployment's refusal day (`refuseReplacements`, identity v7 D12) no
103
+ * channel's model applies: agora's compile drops a layer's `model` whatever it
104
+ * names (`refused_text_layer`), so the agent's own runs. That is what agora's
105
+ * shared cases (`agora/agora/services/agent_config/channel_refusal_contract.json`)
106
+ * say of every layer that names a model, and `channel-refusal-contract.test.ts`
107
+ * holds this to them; which settings replace the agent stays the server's to say.
95
108
  */
96
- export declare function agentDefaultModel({ source, channel, }: {
109
+ export declare function agentDefaultModel({ source, channel, refuseReplacements, }: {
97
110
  source: AgentModelSource;
98
111
  channel: string | null;
112
+ /** The deployment's refusal day has come (`channelReplacementsRefusedOn`). */
113
+ refuseReplacements?: boolean;
99
114
  }): string | null;
100
115
  /**
101
116
  * The agent's model choices and its default, by the rules above. Options keep
102
117
  * the author's order, one per model (the first entry of a model names it); the
103
118
  * default is marked, and appended when the list lacks it; a pinned model
104
- * outside the list comes last.
119
+ * outside the list comes last. From the refusal day (`refuseReplacements`) a
120
+ * channel's model is offered only when the agent lists it, or the thread pins it.
105
121
  */
106
- export declare function buildAgentModelOptions({ source, channel, pinnedModel, modelLabel, }: {
122
+ export declare function buildAgentModelOptions({ source, channel, pinnedModel, modelLabel, refuseReplacements, }: {
107
123
  source: AgentModelSource;
108
124
  channel: string | null;
109
125
  /** The model the thread's server config pins; `null` while it pins none (or is unknown). */
110
126
  pinnedModel: string | null;
111
127
  modelLabel: ModelLabelOf;
128
+ /** The deployment's refusal day has come (`channelReplacementsRefusedOn`). */
129
+ refuseReplacements?: boolean;
112
130
  }): {
113
131
  options: AthenaAgentModelOption[];
114
132
  defaultModel: string | null;
115
133
  };
134
+ /**
135
+ * The agent's choices while its default can't be named yet (a chat on a channel, before the
136
+ * deployment's refusal day is known): its own models only, the restriction they make kept, none
137
+ * marked default and no default named. A channel's model joins only once the day says it still
138
+ * applies; a model the thread pins is shown as always.
139
+ */
140
+ export declare function agentModelOptionsWithoutDefault({ source, channel, pinnedModel, modelLabel, }: {
141
+ source: AgentModelSource;
142
+ channel: string | null;
143
+ pinnedModel: string | null;
144
+ modelLabel: ModelLabelOf;
145
+ }): {
146
+ options: AthenaAgentModelOption[];
147
+ defaultModel: null;
148
+ };
@@ -1,3 +1,4 @@
1
+ import { normalizeCatalogAgentModelEntries } from "../agent-runtime/src/runtime-catalog.js";
1
2
  import { parseCollabAgentRef } from "../runtime/statewire-run-config.js";
2
3
  function allowsAnyModel(value) {
3
4
  return value !== false;
@@ -21,19 +22,7 @@ function labeledModelsOf(value) {
21
22
  }
22
23
  }
23
24
  if (!Array.isArray(rows)) return [];
24
- const entries = [];
25
- for (const row of rows) {
26
- if (!isRecord(row)) continue;
27
- const model = nonBlank(row.model);
28
- if (model === null) continue;
29
- entries.push({
30
- id: typeof row.id === "string" && row.id !== "" ? row.id : model,
31
- model,
32
- label: nonBlank(row.label) ?? model,
33
- isDefault: row.isDefault === true
34
- });
35
- }
36
- return entries;
25
+ return normalizeCatalogAgentModelEntries({ rows: Array.from(rows).filter(isRecord) });
37
26
  }
38
27
  function channelModelsOf(value) {
39
28
  if (!isRecord(value)) return {};
@@ -76,16 +65,19 @@ function isAutomaticModelLabel({
76
65
  }
77
66
  function agentDefaultModel({
78
67
  source,
79
- channel
68
+ channel,
69
+ refuseReplacements = false
80
70
  }) {
81
71
  const marked = source.models.find((entry) => entry.isDefault) ?? source.models[0];
82
- return (channel ? source.channelModels[channel] : void 0) ?? source.model ?? (marked == null ? void 0 : marked.model) ?? null;
72
+ const channelModel = channel && !refuseReplacements ? source.channelModels[channel] : void 0;
73
+ return channelModel ?? source.model ?? (marked == null ? void 0 : marked.model) ?? null;
83
74
  }
84
75
  function buildAgentModelOptions({
85
76
  source,
86
77
  channel,
87
78
  pinnedModel,
88
- modelLabel
79
+ modelLabel,
80
+ refuseReplacements = false
89
81
  }) {
90
82
  const options = [];
91
83
  const add = (model, label) => {
@@ -99,7 +91,7 @@ function buildAgentModelOptions({
99
91
  );
100
92
  }
101
93
  if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));
102
- const defaultModel = agentDefaultModel({ source, channel });
94
+ const defaultModel = agentDefaultModel({ source, channel, refuseReplacements });
103
95
  if (defaultModel !== null) {
104
96
  add(defaultModel, modelLabel(defaultModel));
105
97
  for (const option of options) option.isDefault = option.model === defaultModel;
@@ -110,9 +102,25 @@ function buildAgentModelOptions({
110
102
  }
111
103
  return { options, defaultModel };
112
104
  }
105
+ function agentModelOptionsWithoutDefault({
106
+ source,
107
+ channel,
108
+ pinnedModel,
109
+ modelLabel
110
+ }) {
111
+ const { options } = buildAgentModelOptions({
112
+ source,
113
+ channel,
114
+ pinnedModel,
115
+ modelLabel,
116
+ refuseReplacements: true
117
+ });
118
+ return { options: options.map((option) => ({ ...option, isDefault: false })), defaultModel: null };
119
+ }
113
120
  export {
114
121
  agentAssetIdOf,
115
122
  agentDefaultModel,
123
+ agentModelOptionsWithoutDefault,
116
124
  allowsAnyModel,
117
125
  buildAgentModelOptions,
118
126
  isAutomaticModelLabel,
@@ -1 +1 @@
1
- {"version":3,"file":"agent-model-options.js","sources":["../../src/catalog/agent-model-options.ts"],"sourcesContent":["import { parseCollabAgentRef } from '../runtime/statewire-run-config';\n\n/**\n * A collab agent's own model choices, from its published definition: exactly\n * the models its author listed (the agent editor's Models section), the\n * default marked. The rules are Athena's own composer's (Olympus) and the\n * Jevin app's, so every host offers the same choices for one agent:\n *\n * - the options are the agent's labeled models, by the author's labels; an\n * automatic name (empty, the editor's old `{ id: 'default', label: 'Default' }`,\n * or one that only restates the model) shows the model's catalog label;\n * - a legacy agent with no labeled list offers its single `model`;\n * - the session channel's model override (`channelOverrides[channel].model`)\n * is that channel's default, added when the list lacks it;\n * - a model the thread pins that is none of the agent's models is shown as it\n * is (`pinnedOutside`), never replaced.\n *\n * Restricting a picker to these choices is opt-in per agent: only an agent\n * whose \"Allow any workspace model\" switch is off (`allowAnyModel: false`)\n * does; every other agent keeps the workspace's models.\n *\n * Pure, for `useAthenaAgentModelOptions` and the tests.\n */\n\n/** One model choice of a collab agent. */\nexport interface AthenaAgentModelOption {\n /** The model id a send carries (`model`). */\n model: string;\n /** The author's label, or the model's catalog label for an automatic name. */\n label: string;\n /** The model the thread runs while it pins none (the agent's own, or its channel's). */\n isDefault: boolean;\n /**\n * A model the thread's server config pins that is none of the agent's models\n * (pinned by another client, or by an older picker): shown, never replaced.\n */\n pinnedOutside: boolean;\n}\n\n/** One entry of the agent's labeled model list. */\nexport interface AgentLabeledModel {\n id: string;\n model: string;\n label: string;\n isDefault: boolean;\n}\n\n/** What an agent's published definition says about its models. */\nexport interface AgentModelSource {\n /** The model the definition runs (`config.model`); `null` when it names none. */\n model: string | null;\n /** The labeled list (`config.models`), complete rows only, in the author's order. */\n models: readonly AgentLabeledModel[];\n /** Per-channel model overrides (`channelOverrides[<channel>].model`), by channel id. */\n channelModels: Readonly<Record<string, string>>;\n /**\n * Whether people may pick any workspace model (`config.allowAnyModel`, the\n * agent editor's \"Allow any workspace model\" switch; see\n * {@link allowsAnyModel}). Only `false` restricts a picker to the agent's\n * own models.\n */\n allowAnyModel: boolean;\n}\n\n/**\n * Whether a config's `allowAnyModel` value lets people pick any model: only an\n * explicit `false` restricts; absent, `null`, `true` and any non-boolean allow\n * (agora's `collab_agent_allows_any_model`), so every agent saved before the\n * switch existed behaves as it always has.\n */\nexport function allowsAnyModel(value: unknown): boolean {\n return value !== false;\n}\n\n/** The display label of a model id; the id itself when the catalog does not know it. */\nexport type ModelLabelOf = (model: string) => string;\n\n/** The row the agent editor used to create for an agent with no stored entries. */\nconst LEGACY_DEFAULT_ENTRY_ID = 'default';\nconst LEGACY_DEFAULT_ENTRY_LABEL = 'Default';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst nonBlank = (value: unknown): string | null =>\n typeof value === 'string' && value.trim() !== '' ? value.trim() : null;\n\n/**\n * The collab agent's asset id for an `agent` value: a `collab_agent:<id>` ref\n * (the provider's `agent` shape) or the bare asset id (the agent picker's\n * value). `null` for a blank value.\n */\nexport function agentAssetIdOf(agent: string | null | undefined): string | null {\n const trimmed = nonBlank(agent);\n if (trimmed === null) return null;\n return parseCollabAgentRef(trimmed) ?? (trimmed.includes(':') ? null : trimmed);\n}\n\n/** A stored `models` value: an array of rows, JSON-encoded or not; malformed rows dropped. */\nfunction labeledModelsOf(value: unknown): AgentLabeledModel[] {\n let rows: unknown = value;\n if (typeof rows === 'string') {\n try {\n rows = JSON.parse(rows);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(rows)) return [];\n const entries: AgentLabeledModel[] = [];\n for (const row of rows) {\n if (!isRecord(row)) continue;\n const model = nonBlank(row.model);\n if (model === null) continue;\n entries.push({\n id: typeof row.id === 'string' && row.id !== '' ? row.id : model,\n model,\n label: nonBlank(row.label) ?? model,\n isDefault: row.isDefault === true,\n });\n }\n return entries;\n}\n\n/** A stored `channelOverrides` value's per-channel models. */\nfunction channelModelsOf(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n const out: Record<string, string> = {};\n for (const [channel, override] of Object.entries(value)) {\n const model = isRecord(override) ? nonBlank(override.model) : null;\n if (model !== null) out[channel] = model;\n }\n return out;\n}\n\n/** Whether the agent asset's `athena_metadata` points at a published version. */\nexport function isPublishedAgent(athenaMetadata: unknown): boolean {\n if (!isRecord(athenaMetadata)) return false;\n const versionId = athenaMetadata.published_version_id;\n return (\n (typeof versionId === 'string' && versionId !== '') ||\n (typeof versionId === 'number' && Number.isFinite(versionId))\n );\n}\n\n/**\n * The model source of the agent's PUBLISHED definition, which every SDK run\n * compiles, from the agent asset's `athena_metadata` (publish writes\n * `published_config` beside `published_version_id`). `null` for an agent\n * that was never published, or whose published definition names no model\n * (not even a channel's).\n */\nexport function publishedAgentModelSource(athenaMetadata: unknown): AgentModelSource | null {\n if (!isRecord(athenaMetadata) || !isPublishedAgent(athenaMetadata)) return null;\n const config = athenaMetadata.published_config;\n if (!isRecord(config)) return null;\n const source: AgentModelSource = {\n model: nonBlank(config.model),\n models: labeledModelsOf(config.models),\n channelModels: channelModelsOf(config.channelOverrides),\n // The editor stores the field name; agora also accepts its snake-case alias.\n allowAnyModel: allowsAnyModel(\n Object.hasOwn(config, 'allowAnyModel') ? config.allowAnyModel : config.allow_any_model,\n ),\n };\n const namesAModel =\n source.model !== null ||\n source.models.length > 0 ||\n Object.keys(source.channelModels).length > 0;\n return namesAModel ? source : null;\n}\n\n/**\n * Whether an entry's name is automatic: empty, the editor's old \"Default\", or\n * a name that only restates the entry's model (its id or catalog label).\n */\nexport function isAutomaticModelLabel({\n entry,\n modelLabel,\n}: {\n entry: Pick<AgentLabeledModel, 'id' | 'model' | 'label'>;\n modelLabel: ModelLabelOf;\n}): boolean {\n const label = entry.label.trim();\n if (label === '') return true;\n if (entry.id === LEGACY_DEFAULT_ENTRY_ID && label === LEGACY_DEFAULT_ENTRY_LABEL) return true;\n return label === entry.model || label === modelLabel(entry.model);\n}\n\n/**\n * The model a thread runs while it pins none: the channel's override, else the\n * definition's `model`, else its marked (or first) labeled entry.\n */\nexport function agentDefaultModel({\n source,\n channel,\n}: {\n source: AgentModelSource;\n channel: string | null;\n}): string | null {\n const marked = source.models.find((entry) => entry.isDefault) ?? source.models[0];\n return (channel ? source.channelModels[channel] : undefined) ?? source.model ?? marked?.model ?? null;\n}\n\n/**\n * The agent's model choices and its default, by the rules above. Options keep\n * the author's order, one per model (the first entry of a model names it); the\n * default is marked, and appended when the list lacks it; a pinned model\n * outside the list comes last.\n */\nexport function buildAgentModelOptions({\n source,\n channel,\n pinnedModel,\n modelLabel,\n}: {\n source: AgentModelSource;\n channel: string | null;\n /** The model the thread's server config pins; `null` while it pins none (or is unknown). */\n pinnedModel: string | null;\n modelLabel: ModelLabelOf;\n}): { options: AthenaAgentModelOption[]; defaultModel: string | null } {\n const options: AthenaAgentModelOption[] = [];\n const add = (model: string, label: string) => {\n if (options.some((option) => option.model === model)) return;\n options.push({ model, label, isDefault: false, pinnedOutside: false });\n };\n for (const entry of source.models) {\n add(\n entry.model,\n isAutomaticModelLabel({ entry, modelLabel }) ? modelLabel(entry.model) : entry.label.trim(),\n );\n }\n if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));\n\n const defaultModel = agentDefaultModel({ source, channel });\n if (defaultModel !== null) {\n add(defaultModel, modelLabel(defaultModel));\n for (const option of options) option.isDefault = option.model === defaultModel;\n }\n\n const pinned = nonBlank(pinnedModel);\n if (pinned !== null && !options.some((option) => option.model === pinned)) {\n options.push({ model: pinned, label: modelLabel(pinned), isDefault: false, pinnedOutside: true });\n }\n return { options, defaultModel };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-model-options.js","sources":["../../src/catalog/agent-model-options.ts"],"sourcesContent":["import { normalizeCatalogAgentModelEntries } from '@athenaintel/agent-runtime/runtime-catalog';\nimport { parseCollabAgentRef } from '../runtime/statewire-run-config';\n\n/**\n * A collab agent's own model choices, from its published definition: exactly\n * the models its author listed (the agent editor's Models section), the\n * default marked. The rules are Athena's own composer's (Olympus) and the\n * Jevin app's, so every host offers the same choices for one agent:\n *\n * - the options are the agent's labeled models, by the author's labels; an\n * automatic name (empty, the editor's old `{ id: 'default', label: 'Default' }`,\n * or one that only restates the model) shows the model's catalog label;\n * - a legacy agent with no labeled list offers its single `model`;\n * - the session channel's model override (`channelOverrides[channel].model`)\n * is that channel's default, added when the list lacks it, until the\n * deployment's refusal day (identity v7 D12, `channel-refusal.ts`): from it\n * agora drops a channel's model at every compile, so the agent's own model is\n * the default on every channel;\n * - a model the thread pins that is none of the agent's models is shown as it\n * is (`pinnedOutside`), never replaced.\n *\n * Restricting a picker to these choices is opt-in per agent: only an agent\n * whose \"Allow any workspace model\" switch is off (`allowAnyModel: false`)\n * does; every other agent keeps the workspace's models.\n *\n * Pure, for `useAthenaAgentModelOptions` and the tests.\n */\n\n/** One model choice of a collab agent. */\nexport interface AthenaAgentModelOption {\n /** The model id a send carries (`model`). */\n model: string;\n /** The author's label, or the model's catalog label for an automatic name. */\n label: string;\n /**\n * The model the thread runs while it pins none: the agent's own, or its channel's until the\n * deployment's refusal day.\n */\n isDefault: boolean;\n /**\n * A model the thread's server config pins that is none of the agent's models\n * (pinned by another client, or by an older picker): shown, never replaced.\n */\n pinnedOutside: boolean;\n}\n\n/** One entry of the agent's labeled model list. */\nexport interface AgentLabeledModel {\n id: string;\n model: string;\n label: string;\n isDefault: boolean;\n}\n\n/** What an agent's published definition says about its models. */\nexport interface AgentModelSource {\n /** The model the definition runs (`config.model`); `null` when it names none. */\n model: string | null;\n /** The labeled list (`config.models`), complete rows only, in the author's order. */\n models: readonly AgentLabeledModel[];\n /** Per-channel model overrides (`channelOverrides[<channel>].model`), by channel id. */\n channelModels: Readonly<Record<string, string>>;\n /**\n * Whether people may pick any workspace model (`config.allowAnyModel`, the\n * agent editor's \"Allow any workspace model\" switch; see\n * {@link allowsAnyModel}). Only `false` restricts a picker to the agent's\n * own models.\n */\n allowAnyModel: boolean;\n}\n\n/**\n * Whether a config's `allowAnyModel` value lets people pick any model: only an\n * explicit `false` restricts; absent, `null`, `true` and any non-boolean allow\n * (agora's `collab_agent_allows_any_model`), so every agent saved before the\n * switch existed behaves as it always has.\n */\nexport function allowsAnyModel(value: unknown): boolean {\n return value !== false;\n}\n\n/** The display label of a model id; the id itself when the catalog does not know it. */\nexport type ModelLabelOf = (model: string) => string;\n\n/** The row the agent editor used to create for an agent with no stored entries. */\nconst LEGACY_DEFAULT_ENTRY_ID = 'default';\nconst LEGACY_DEFAULT_ENTRY_LABEL = 'Default';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst nonBlank = (value: unknown): string | null =>\n typeof value === 'string' && value.trim() !== '' ? value.trim() : null;\n\n/**\n * The collab agent's asset id for an `agent` value: a `collab_agent:<id>` ref\n * (the provider's `agent` shape) or the bare asset id (the agent picker's\n * value). `null` for a blank value.\n */\nexport function agentAssetIdOf(agent: string | null | undefined): string | null {\n const trimmed = nonBlank(agent);\n if (trimmed === null) return null;\n return parseCollabAgentRef(trimmed) ?? (trimmed.includes(':') ? null : trimmed);\n}\n\n/** A stored `models` value: an array of rows, JSON-encoded or not; malformed rows dropped. */\nfunction labeledModelsOf(value: unknown): AgentLabeledModel[] {\n let rows: unknown = value;\n if (typeof rows === 'string') {\n try {\n rows = JSON.parse(rows);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(rows)) return [];\n return normalizeCatalogAgentModelEntries({ rows: Array.from(rows).filter(isRecord) });\n}\n\n/** A stored `channelOverrides` value's per-channel models. */\nfunction channelModelsOf(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n const out: Record<string, string> = {};\n for (const [channel, override] of Object.entries(value)) {\n const model = isRecord(override) ? nonBlank(override.model) : null;\n if (model !== null) out[channel] = model;\n }\n return out;\n}\n\n/** Whether the agent asset's `athena_metadata` points at a published version. */\nexport function isPublishedAgent(athenaMetadata: unknown): boolean {\n if (!isRecord(athenaMetadata)) return false;\n const versionId = athenaMetadata.published_version_id;\n return (\n (typeof versionId === 'string' && versionId !== '') ||\n (typeof versionId === 'number' && Number.isFinite(versionId))\n );\n}\n\n/**\n * The model source of the agent's PUBLISHED definition, which every SDK run\n * compiles, from the agent asset's `athena_metadata` (publish writes\n * `published_config` beside `published_version_id`). `null` for an agent\n * that was never published, or whose published definition names no model\n * (not even a channel's).\n */\nexport function publishedAgentModelSource(athenaMetadata: unknown): AgentModelSource | null {\n if (!isRecord(athenaMetadata) || !isPublishedAgent(athenaMetadata)) return null;\n const config = athenaMetadata.published_config;\n if (!isRecord(config)) return null;\n const source: AgentModelSource = {\n model: nonBlank(config.model),\n models: labeledModelsOf(config.models),\n channelModels: channelModelsOf(config.channelOverrides),\n // The editor stores the field name; agora also accepts its snake-case alias.\n allowAnyModel: allowsAnyModel(\n Object.hasOwn(config, 'allowAnyModel') ? config.allowAnyModel : config.allow_any_model,\n ),\n };\n const namesAModel =\n source.model !== null ||\n source.models.length > 0 ||\n Object.keys(source.channelModels).length > 0;\n return namesAModel ? source : null;\n}\n\n/**\n * Whether an entry's name is automatic: empty, the editor's old \"Default\", or\n * a name that only restates the entry's model (its id or catalog label).\n */\nexport function isAutomaticModelLabel({\n entry,\n modelLabel,\n}: {\n entry: Pick<AgentLabeledModel, 'id' | 'model' | 'label'>;\n modelLabel: ModelLabelOf;\n}): boolean {\n const label = entry.label.trim();\n if (label === '') return true;\n if (entry.id === LEGACY_DEFAULT_ENTRY_ID && label === LEGACY_DEFAULT_ENTRY_LABEL) return true;\n return label === entry.model || label === modelLabel(entry.model);\n}\n\n/**\n * The model a thread runs while it pins none: the channel's override, else the\n * definition's `model`, else its marked (or first) labeled entry.\n *\n * From the deployment's refusal day (`refuseReplacements`, identity v7 D12) no\n * channel's model applies: agora's compile drops a layer's `model` whatever it\n * names (`refused_text_layer`), so the agent's own runs. That is what agora's\n * shared cases (`agora/agora/services/agent_config/channel_refusal_contract.json`)\n * say of every layer that names a model, and `channel-refusal-contract.test.ts`\n * holds this to them; which settings replace the agent stays the server's to say.\n */\nexport function agentDefaultModel({\n source,\n channel,\n refuseReplacements = false,\n}: {\n source: AgentModelSource;\n channel: string | null;\n /** The deployment's refusal day has come (`channelReplacementsRefusedOn`). */\n refuseReplacements?: boolean;\n}): string | null {\n const marked = source.models.find((entry) => entry.isDefault) ?? source.models[0];\n const channelModel = channel && !refuseReplacements ? source.channelModels[channel] : undefined;\n return channelModel ?? source.model ?? marked?.model ?? null;\n}\n\n/**\n * The agent's model choices and its default, by the rules above. Options keep\n * the author's order, one per model (the first entry of a model names it); the\n * default is marked, and appended when the list lacks it; a pinned model\n * outside the list comes last. From the refusal day (`refuseReplacements`) a\n * channel's model is offered only when the agent lists it, or the thread pins it.\n */\nexport function buildAgentModelOptions({\n source,\n channel,\n pinnedModel,\n modelLabel,\n refuseReplacements = false,\n}: {\n source: AgentModelSource;\n channel: string | null;\n /** The model the thread's server config pins; `null` while it pins none (or is unknown). */\n pinnedModel: string | null;\n modelLabel: ModelLabelOf;\n /** The deployment's refusal day has come (`channelReplacementsRefusedOn`). */\n refuseReplacements?: boolean;\n}): { options: AthenaAgentModelOption[]; defaultModel: string | null } {\n const options: AthenaAgentModelOption[] = [];\n const add = (model: string, label: string) => {\n if (options.some((option) => option.model === model)) return;\n options.push({ model, label, isDefault: false, pinnedOutside: false });\n };\n for (const entry of source.models) {\n add(\n entry.model,\n isAutomaticModelLabel({ entry, modelLabel }) ? modelLabel(entry.model) : entry.label.trim(),\n );\n }\n if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));\n\n const defaultModel = agentDefaultModel({ source, channel, refuseReplacements });\n if (defaultModel !== null) {\n add(defaultModel, modelLabel(defaultModel));\n for (const option of options) option.isDefault = option.model === defaultModel;\n }\n\n const pinned = nonBlank(pinnedModel);\n if (pinned !== null && !options.some((option) => option.model === pinned)) {\n options.push({ model: pinned, label: modelLabel(pinned), isDefault: false, pinnedOutside: true });\n }\n return { options, defaultModel };\n}\n\n/**\n * The agent's choices while its default can't be named yet (a chat on a channel, before the\n * deployment's refusal day is known): its own models only, the restriction they make kept, none\n * marked default and no default named. A channel's model joins only once the day says it still\n * applies; a model the thread pins is shown as always.\n */\nexport function agentModelOptionsWithoutDefault({\n source,\n channel,\n pinnedModel,\n modelLabel,\n}: {\n source: AgentModelSource;\n channel: string | null;\n pinnedModel: string | null;\n modelLabel: ModelLabelOf;\n}): { options: AthenaAgentModelOption[]; defaultModel: null } {\n const { options } = buildAgentModelOptions({\n source,\n channel,\n pinnedModel,\n modelLabel,\n refuseReplacements: true,\n });\n return { options: options.map((option) => ({ ...option, isDefault: false })), defaultModel: null };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const ATHENA_CHANNEL_REFUSAL_QUERY = `
4
+ query AthenaChannelReplacementsRefusedFrom {
5
+ featureGateEnv { channelReplacementsRefusedFrom }
6
+ }
7
+ `;
8
+ const DAY = /^\d{4}-\d{2}-\d{2}$/;
9
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
10
+ function refusalDayOf(data) {
11
+ const gate = isRecord(data) ? data.featureGateEnv : null;
12
+ const day = isRecord(gate) ? gate.channelReplacementsRefusedFrom : null;
13
+ return typeof day === "string" && DAY.test(day) ? day : null;
14
+ }
15
+ const UNKNOWN_REFUSAL_DAY_FIELD = /Cannot query field ["']?channelReplacementsRefusedFrom\b/;
16
+ function answersWithoutRefusalDay(message) {
17
+ return UNKNOWN_REFUSAL_DAY_FIELD.test(message);
18
+ }
19
+ function channelReplacementsRefusalStartsAt(refusedFrom) {
20
+ if (refusedFrom === null) return null;
21
+ const startsAt = Date.parse(`${refusedFrom}T00:00:00Z`);
22
+ return Number.isNaN(startsAt) ? null : startsAt;
23
+ }
24
+ function channelReplacementsRefusedOn({
25
+ refusedFrom,
26
+ now
27
+ }) {
28
+ const startsAt = channelReplacementsRefusalStartsAt(refusedFrom);
29
+ return startsAt !== null && now >= startsAt;
30
+ }
31
+ exports.ATHENA_CHANNEL_REFUSAL_QUERY = ATHENA_CHANNEL_REFUSAL_QUERY;
32
+ exports.answersWithoutRefusalDay = answersWithoutRefusalDay;
33
+ exports.channelReplacementsRefusalStartsAt = channelReplacementsRefusalStartsAt;
34
+ exports.channelReplacementsRefusedOn = channelReplacementsRefusedOn;
35
+ exports.refusalDayOf = refusalDayOf;
36
+ //# sourceMappingURL=channel-refusal.cjs.map