@athenaintel/react 0.30.0 → 0.35.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 (210) hide show
  1. package/README.md +160 -9
  2. package/dist/agent-runtime/src/athena-asset-drag.cjs +44 -0
  3. package/dist/agent-runtime/src/athena-asset-drag.cjs.map +1 -0
  4. package/dist/agent-runtime/src/athena-asset-drag.js +44 -0
  5. package/dist/agent-runtime/src/athena-asset-drag.js.map +1 -0
  6. package/dist/agent-runtime/src/composer-markdown.cjs.map +1 -1
  7. package/dist/agent-runtime/src/composer-markdown.js.map +1 -1
  8. package/dist/agent-runtime/src/config-mutations.cjs +59 -0
  9. package/dist/agent-runtime/src/config-mutations.cjs.map +1 -0
  10. package/dist/agent-runtime/src/config-mutations.js +59 -0
  11. package/dist/agent-runtime/src/config-mutations.js.map +1 -0
  12. package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs +2 -3
  13. package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
  14. package/dist/agent-runtime/src/deep-agent-transport-recipe.js +3 -4
  15. package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
  16. package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
  17. package/dist/agent-runtime/src/edit-command.js.map +1 -1
  18. package/dist/agent-runtime/src/generated/runconfig-contract.cjs +6 -0
  19. package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  20. package/dist/agent-runtime/src/generated/runconfig-contract.js +6 -0
  21. package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  22. package/dist/agent-runtime/src/messages.cjs +57 -2
  23. package/dist/agent-runtime/src/messages.cjs.map +1 -1
  24. package/dist/agent-runtime/src/messages.js +57 -2
  25. package/dist/agent-runtime/src/messages.js.map +1 -1
  26. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs +72 -3
  27. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  28. package/dist/agent-runtime/src/quota-safe-local-storage-session.js +73 -4
  29. package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  30. package/dist/agent-runtime/src/statewire-transport.cjs +4 -13
  31. package/dist/agent-runtime/src/statewire-transport.cjs.map +1 -1
  32. package/dist/agent-runtime/src/statewire-transport.js +5 -14
  33. package/dist/agent-runtime/src/statewire-transport.js.map +1 -1
  34. package/dist/agent-runtime/src/tool-metadata.cjs +26 -7
  35. package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
  36. package/dist/agent-runtime/src/tool-metadata.js +26 -7
  37. package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
  38. package/dist/agent-runtime/src/use-deep-agent-config.cjs +117 -1
  39. package/dist/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
  40. package/dist/agent-runtime/src/use-deep-agent-config.js +118 -2
  41. package/dist/agent-runtime/src/use-deep-agent-config.js.map +1 -1
  42. package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  43. package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  44. package/dist/assets/AthenaAssetEmbed.cjs +9 -2
  45. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  46. package/dist/assets/AthenaAssetEmbed.d.ts +9 -0
  47. package/dist/assets/AthenaAssetEmbed.js +11 -4
  48. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  49. package/dist/assets/asset-embed-messages.cjs +6 -0
  50. package/dist/assets/asset-embed-messages.cjs.map +1 -1
  51. package/dist/assets/asset-embed-messages.d.ts +7 -0
  52. package/dist/assets/asset-embed-messages.js +6 -0
  53. package/dist/assets/asset-embed-messages.js.map +1 -1
  54. package/dist/assets/use-asset-embed.cjs +71 -71
  55. package/dist/assets/use-asset-embed.cjs.map +1 -1
  56. package/dist/assets/use-asset-embed.d.ts +20 -7
  57. package/dist/assets/use-asset-embed.js +72 -72
  58. package/dist/assets/use-asset-embed.js.map +1 -1
  59. package/dist/catalog/agent-model-options.cjs +122 -0
  60. package/dist/catalog/agent-model-options.cjs.map +1 -0
  61. package/dist/catalog/agent-model-options.d.ts +115 -0
  62. package/dist/catalog/agent-model-options.js +122 -0
  63. package/dist/catalog/agent-model-options.js.map +1 -0
  64. package/dist/catalog/chat-pickers.cjs +184 -4
  65. package/dist/catalog/chat-pickers.cjs.map +1 -1
  66. package/dist/catalog/chat-pickers.d.ts +27 -3
  67. package/dist/catalog/chat-pickers.js +187 -7
  68. package/dist/catalog/chat-pickers.js.map +1 -1
  69. package/dist/catalog/runtime-catalog.cjs +117 -3
  70. package/dist/catalog/runtime-catalog.cjs.map +1 -1
  71. package/dist/catalog/runtime-catalog.d.ts +104 -0
  72. package/dist/catalog/runtime-catalog.js +119 -5
  73. package/dist/catalog/runtime-catalog.js.map +1 -1
  74. package/dist/chat/AthenaChat.cjs +41 -26
  75. package/dist/chat/AthenaChat.cjs.map +1 -1
  76. package/dist/chat/AthenaChat.d.ts +8 -7
  77. package/dist/chat/AthenaChat.js +41 -26
  78. package/dist/chat/AthenaChat.js.map +1 -1
  79. package/dist/chat/super-grouping/SuperGroupingCard.cjs +61 -14
  80. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  81. package/dist/chat/super-grouping/SuperGroupingCard.d.ts +26 -12
  82. package/dist/chat/super-grouping/SuperGroupingCard.js +63 -16
  83. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  84. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +80 -53
  85. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  86. package/dist/chat/super-grouping/SuperGroupingTranscript.d.ts +10 -10
  87. package/dist/chat/super-grouping/SuperGroupingTranscript.js +81 -54
  88. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  89. package/dist/chat/super-grouping/activity-timeline.cjs +19 -4
  90. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  91. package/dist/chat/super-grouping/activity-timeline.d.ts +24 -5
  92. package/dist/chat/super-grouping/activity-timeline.js +19 -4
  93. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  94. package/dist/chat/super-grouping/index.cjs +2 -0
  95. package/dist/chat/super-grouping/index.cjs.map +1 -1
  96. package/dist/chat/super-grouping/index.d.ts +7 -6
  97. package/dist/chat/super-grouping/index.js +3 -1
  98. package/dist/chat/super-grouping/toolkit-map.cjs +4 -1
  99. package/dist/chat/super-grouping/toolkit-map.cjs.map +1 -1
  100. package/dist/chat/super-grouping/toolkit-map.js +4 -1
  101. package/dist/chat/super-grouping/toolkit-map.js.map +1 -1
  102. package/dist/chat-ui/composer/tiptap-composer.cjs +40 -4
  103. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  104. package/dist/chat-ui/composer/tiptap-composer.js +40 -4
  105. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  106. package/dist/chat-ui/index.cjs +1 -0
  107. package/dist/chat-ui/index.cjs.map +1 -1
  108. package/dist/chat-ui/index.d.ts +1 -1
  109. package/dist/chat-ui/index.js +2 -1
  110. package/dist/chat-ui/mentions/mention-chip.cjs +29 -2
  111. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  112. package/dist/chat-ui/mentions/mention-chip.d.ts +2 -5
  113. package/dist/chat-ui/mentions/mention-chip.js +30 -3
  114. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  115. package/dist/chat-ui/mentions/mention-extension.cjs +1 -1
  116. package/dist/chat-ui/mentions/mention-extension.cjs.map +1 -1
  117. package/dist/chat-ui/mentions/mention-extension.js +1 -1
  118. package/dist/chat-ui/mentions/mention-extension.js.map +1 -1
  119. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +33 -2
  120. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
  121. package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +14 -0
  122. package/dist/chat-ui/mentions/suggestions/data/api-client.js +33 -2
  123. package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
  124. package/dist/chat-ui/mentions/use-account-workspace-id.cjs +61 -0
  125. package/dist/chat-ui/mentions/use-account-workspace-id.cjs.map +1 -0
  126. package/dist/chat-ui/mentions/use-account-workspace-id.d.ts +3 -0
  127. package/dist/chat-ui/mentions/use-account-workspace-id.js +61 -0
  128. package/dist/chat-ui/mentions/use-account-workspace-id.js.map +1 -0
  129. package/dist/chat-ui/mentions/use-member-avatar.cjs +75 -0
  130. package/dist/chat-ui/mentions/use-member-avatar.cjs.map +1 -0
  131. package/dist/chat-ui/mentions/use-member-avatar.d.ts +2 -0
  132. package/dist/chat-ui/mentions/use-member-avatar.js +75 -0
  133. package/dist/chat-ui/mentions/use-member-avatar.js.map +1 -0
  134. package/dist/collab/client.cjs +80 -19
  135. package/dist/collab/client.cjs.map +1 -1
  136. package/dist/collab/client.d.ts +22 -0
  137. package/dist/collab/client.js +80 -19
  138. package/dist/collab/client.js.map +1 -1
  139. package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
  140. package/dist/diagnostics/athena-diagnostics.d.ts +3 -1
  141. package/dist/diagnostics/athena-diagnostics.js.map +1 -1
  142. package/dist/index.cjs +5 -0
  143. package/dist/index.cjs.map +1 -1
  144. package/dist/index.d.ts +6 -3
  145. package/dist/index.js +7 -2
  146. package/dist/index.js.map +1 -1
  147. package/dist/lib/use-athena-rest-query.cjs +4 -0
  148. package/dist/lib/use-athena-rest-query.cjs.map +1 -1
  149. package/dist/lib/use-athena-rest-query.d.ts +6 -0
  150. package/dist/lib/use-athena-rest-query.js +4 -0
  151. package/dist/lib/use-athena-rest-query.js.map +1 -1
  152. package/dist/provider/AthenaProvider.cjs +57 -6
  153. package/dist/provider/AthenaProvider.cjs.map +1 -1
  154. package/dist/provider/AthenaProvider.d.ts +21 -5
  155. package/dist/provider/AthenaProvider.js +59 -8
  156. package/dist/provider/AthenaProvider.js.map +1 -1
  157. package/dist/provider/agent-selection-context.cjs +6 -0
  158. package/dist/provider/agent-selection-context.cjs.map +1 -0
  159. package/dist/provider/agent-selection-context.d.ts +14 -0
  160. package/dist/provider/agent-selection-context.js +6 -0
  161. package/dist/provider/agent-selection-context.js.map +1 -0
  162. package/dist/react/package.json.cjs +1 -1
  163. package/dist/react/package.json.js +1 -1
  164. package/dist/runtime/StatewireThreadConfigLaneSync.cjs +125 -0
  165. package/dist/runtime/StatewireThreadConfigLaneSync.cjs.map +1 -0
  166. package/dist/runtime/StatewireThreadConfigLaneSync.d.ts +54 -0
  167. package/dist/runtime/StatewireThreadConfigLaneSync.js +125 -0
  168. package/dist/runtime/StatewireThreadConfigLaneSync.js.map +1 -0
  169. package/dist/runtime/statewire-lifecycle.cjs +5 -2
  170. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  171. package/dist/runtime/statewire-lifecycle.d.ts +5 -0
  172. package/dist/runtime/statewire-lifecycle.js +5 -2
  173. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  174. package/dist/runtime/statewire-model-lane.cjs +16 -0
  175. package/dist/runtime/statewire-model-lane.cjs.map +1 -0
  176. package/dist/runtime/statewire-model-lane.d.ts +66 -0
  177. package/dist/runtime/statewire-model-lane.js +16 -0
  178. package/dist/runtime/statewire-model-lane.js.map +1 -0
  179. package/dist/runtime/statewire-run-config.cjs +16 -2
  180. package/dist/runtime/statewire-run-config.cjs.map +1 -1
  181. package/dist/runtime/statewire-run-config.d.ts +10 -0
  182. package/dist/runtime/statewire-run-config.js +16 -2
  183. package/dist/runtime/statewire-run-config.js.map +1 -1
  184. package/dist/runtime/statewire-server-lane-context.cjs +24 -0
  185. package/dist/runtime/statewire-server-lane-context.cjs.map +1 -0
  186. package/dist/runtime/statewire-server-lane-context.d.ts +30 -0
  187. package/dist/runtime/statewire-server-lane-context.js +24 -0
  188. package/dist/runtime/statewire-server-lane-context.js.map +1 -0
  189. package/dist/runtime/statewire-thread-config-lane.cjs +84 -0
  190. package/dist/runtime/statewire-thread-config-lane.cjs.map +1 -0
  191. package/dist/runtime/statewire-thread-config-lane.d.ts +94 -0
  192. package/dist/runtime/statewire-thread-config-lane.js +84 -0
  193. package/dist/runtime/statewire-thread-config-lane.js.map +1 -0
  194. package/dist/runtime/useAthenaStatewireRuntime.cjs +38 -18
  195. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  196. package/dist/runtime/useAthenaStatewireRuntime.d.ts +8 -1
  197. package/dist/runtime/useAthenaStatewireRuntime.js +39 -19
  198. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  199. package/dist/styles.css +1 -1
  200. package/dist/threads/api.cjs +2 -1
  201. package/dist/threads/api.cjs.map +1 -1
  202. package/dist/threads/api.d.ts +1 -0
  203. package/dist/threads/api.js +2 -1
  204. package/dist/threads/api.js.map +1 -1
  205. package/dist/threads/use-athena-thread-model.cjs +22 -0
  206. package/dist/threads/use-athena-thread-model.cjs.map +1 -0
  207. package/dist/threads/use-athena-thread-model.d.ts +27 -0
  208. package/dist/threads/use-athena-thread-model.js +22 -0
  209. package/dist/threads/use-athena-thread-model.js.map +1 -0
  210. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"use-asset-embed.cjs","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const heldToken = tokenRef.current;\n sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n }),\n )\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":["deriveAthenaAppUrl","useState","useRef","useEffect","sendWithCredentialRenewal"],"mappings":"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"}
1
+ {"version":3,"file":"use-asset-embed.cjs","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useContext, useRef } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { keyPartFingerprint } from '../lib/use-athena-rest-query';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n /**\n * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, where the viewer\n * does not sign in: spreadsheets and Word documents, on an editable link (`readOnly: false`)\n * only. Its runs are billed to the asset's workspace under the link's creator, and each link has\n * its own hourly cap. The full mode's editors offer it to the signed-in person already. Defaults\n * to `false`.\n */\n allowQuickEdit?: boolean;\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n allowQuickEdit?: boolean;\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n // Sent only when asked: an agora that predates it is never handed an unknown field.\n ...(allowQuickEdit && { allow_quick_edit: true }),\n };\n}\n\n/**\n * The embed links' query client when the host provides none: one for the page, so every embed of\n * one asset, mode and identity shares one minted link until it expires.\n */\nconst embedQueryClient = new QueryClient();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint through TanStack Query (the host's\n * QueryClient when there is one, else the page's embed client), keyed on the\n * asset, the mode and the identity.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the query key. PropelAuth and similar providers refresh the token value\n * periodically (e.g. on window focus), but the underlying session is the\n * same — minting again on every token rotation would force the iframe to\n * reload unnecessarily. The link is minted again only when the identity\n * (user or tenant), the asset or the mode changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n // Keep the latest token in a ref so the query can read it without being\n // keyed on it: PropelAuth and similar providers rotate the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n const enabled = Boolean(assetId && backendUrl);\n\n const query = useQuery(\n {\n queryKey: [\n 'athena-sdk',\n 'asset-embed',\n assetId,\n readOnly,\n displayMode,\n allowQuickEdit,\n // The identity, never the credential: an opaque token or a key is part of `authContext`.\n keyPartFingerprint(authContext),\n backendUrl,\n appUrl ?? '',\n ],\n queryFn: async ({ signal }): Promise<{ url: string; expiresAt: number }> => {\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const heldToken = tokenRef.current;\n const res = await sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(`${apiBaseUrl}/api/embed/generate-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId: assetId ?? '',\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n }),\n ),\n signal,\n }),\n );\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n const data: { embed_url: string; expires_at: number } = await res.json();\n return {\n url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),\n expiresAt: data.expires_at,\n };\n },\n enabled,\n // A minted link is good until it expires; a new one would reload the frame, so it is never\n // minted again while the embed shows (no refetch on focus or reconnect) and never retried.\n staleTime: (cached) =>\n cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1000 - Date.now()) : 0,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n useContext(QueryClientContext) ?? embedQueryClient,\n );\n\n return {\n embedUrl: enabled ? (query.data?.url ?? null) : null,\n isLoading: enabled && query.isLoading,\n error: enabled && query.error ? query.error.message : null,\n };\n}\n"],"names":["QueryClient","deriveAthenaAppUrl","useRef","useQuery","keyPartFingerprint","sendWithCredentialRenewal","useContext","QueryClientContext"],"mappings":";;;;;;;AAuBO,MAAM,gCAAgC;AACtC,MAAM,uCAAuC;AAC7C,MAAM,mCAAmC;AAQzC,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,iBAAiB;AACnB,GAMG;AACD,SAAO;AAAA,IACL,UAAU;AAAA,IACV,WAAW;AAAA,IACX,oBAAoB;AAAA,IACpB,cAAc;AAAA;AAAA,IAEd,GAAI,kBAAkB,EAAE,kBAAkB,KAAA;AAAA,EAAK;AAEnD;AAMA,MAAM,mBAAmB,IAAIA,WAAAA,YAAA;AAE7B,MAAM,uBAAuB,oBAAI,IAAI,CAAC,uBAAuB,6BAA6B,CAAC;AAC3F,MAAM,iCAAiC;AACvC,MAAM,oCAAoC;AAC1C,MAAM,yBAAyB;AAE/B,MAAM,SAAS,CAAC,UAAsC;AACpD,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,IAAI,IAAI,KAAK;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,MAAM,mBAAmB,MAAqB;AAC5C,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,SAAS;AACzB;AAEA,MAAM,YAAY,CAAC,UAAA;;AAAyC,uBAAO,KAAK,MAAZ,mBAAe,WAAU;AAAA;AAErF,MAAM,yBAAyB,CAAC,QAA4B;AAC1D,QAAM,QAAQ,IAAI,SAAS,YAAA,EAAc,MAAM,iCAAiC;AAChF,UAAO,+BAAQ,OAAM;AACvB;AAEA,MAAM,2BAA2B,CAAC,QAAsB;AACtD,QAAM,WAAW,IAAI,SAAS,YAAA;AAE9B,MAAI,qBAAqB,IAAI,QAAQ,KAAK,SAAS,SAAS,8BAA8B,GAAG;AAC3F,WAAO;AAAA,EACT;AAEA,SAAO,uBAAuB,GAAG,MAAM;AACzC;AAEA,MAAM,iCAAiC,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,MAIe;AACb,MAAI,CAAC,eAAe;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,OAAO,MAAM;AACpC,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,uBAAuB,cAAc;AAC3D,MAAI,iBAAiB,kBAAkB,wBAAwB;AAC7D,WAAO;AAAA,EACT;AAEA,MAAI,yBAAyB,cAAc,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,QAAM,wBAAwB,UAAU,iBAAiB,iBAAA,CAAkB;AAC3E,SAAO,CAAC,CAAC,yBAAyB,eAAe,WAAW;AAC9D;AAEA,MAAM,qBAAqB,CAAC;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,MAIqB;AACnB,QAAM,gBAAgBC,WAAAA,mBAAmB,EAAE,YAAY;AAEvD,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,MAAI,+BAA+B,EAAE,QAAQ,eAAe,cAAA,CAAe,GAAG;AAC5E,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKW;AACT,MAAI;AACF,UAAM,mBAAmB,IAAI,IAAI,QAAQ;AACzC,QAAI,CAAC,iBAAiB,SAAS,WAAW,SAAS,GAAG;AACpD,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,mBAAmB;AAAA,MACrC;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AACD,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AAEA,UAAM,iBAAiB,IAAI,IAAI,WAAW;AAC1C,qBAAiB,WAAW,eAAe;AAC3C,qBAAiB,OAAO,eAAe;AACvC,WAAO,iBAAiB,SAAA;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,qBAAqB,EAAE,QAAQ,KAAK,SAAyE;AACpH,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GAAG;AACvD,WAAO,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,EAC/B;AACF;AAEA,SAAS,uBAAuB,EAAE,QAAQ,KAAK,SAAyE;AACtH,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,WAAO,IAAI,KAAK,KAAK;AAAA,EACvB;AACF;AAGA,MAAM,mBAAmB;AASzB,SAAS,oBAAoB,OAA+C;AAC1E,QAAM,aAAa,MAAM,QAAQ,GAAG;AACpC,QAAM,SAAS,eAAe,KAAK,QAAQ,MAAM,MAAM,aAAa,CAAC;AACrE,QAAM,QAAQ,CAAC,GAAG,IAAI,gBAAgB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,gBAAgB;AACzF,MAAI,MAAM,WAAW,KAAK,MAAM,KAAK,CAAC,CAAC,GAAG,MAAM,IAAI,WAAW,CAAC,EAAG,QAAO;AAC1E,SAAO;AACT;AAEA,SAAS,qBAAqB,EAAE,QAAQ,SAA2D;AACjG,QAAM,QAAQ,oBAAoB,KAAK;AACvC,MAAI,CAAC,MAAO;AAEZ,aAAW,CAAC,GAAG,KAAK,MAAO,QAAO,OAAO,GAAG;AAC5C,aAAW,CAAC,KAAK,KAAK,KAAK,MAAO,QAAO,OAAO,KAAK,KAAK;AAC5D;AAEA,SAAS,wBAAwB;AAAA,EAC/B;AAAA,EACA;AACF,GAGS;AACP,UAAQ,OAAO,MAAA;AAAA,IACb,KAAK;AACH,aAAO,IAAI,UAAU,OAAO,IAAI;AAChC,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,UAAU,SAAS,OAAO,OAAO;AAAA,MAAA,CACzC;AACD;AAAA,IACF,KAAK;AACH,aAAO,IAAI,UAAU,OAAO,IAAI;AAChC,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,eAAe,SAAS,OAAO,YAAY;AAAA,MAAA,CACnD;AACD,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,aAAa,SAAS,OAAO,UAAU;AAAA,MAAA,CAC/C;AACD;AAAA,IACF,KAAK;AACH,aAAO,IAAI,UAAU,OAAO,IAAI;AAChC,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,UAAU,SAAS,OAAO,OAAO;AAAA,MAAA,CACzC;AACD,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,OAAO,SAAS,OAAO,IAAI;AAAA,MAAA,CACnC;AACD,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,OAAO,SAAS,OAAO,IAAI;AAAA,MAAA,CACnC;AACD,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,WAAW,SAAS,OAAO,QAAQ;AAAA,MAAA,CAC3C;AACD,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,YAAY,SAAS,OAAO,SAAS;AAAA,MAAA,CAC7C;AACD,UACE,UAAU,WACT,OAAO,SAAS,aAAa,OAAO,SAAS,UAC9C;AACA,eAAO,IAAI,QAAQ,OAAO,IAAI;AAAA,MAChC;AACA;AAAA,IACF,KAAK;AACH,aAAO,IAAI,UAAU,OAAO,IAAI;AAChC,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,UAAU,SAAS,OAAO,OAAO;AAAA,MAAA,CACzC;AACD,UAAI,UAAU,UAAU,OAAO,OAAO,SAAS,UAAU;AACvD,eAAO,IAAI,QAAQ,OAAO,IAAI;AAAA,MAChC;AAIA,6BAAuB,EAAE,QAAQ,KAAK,UAAU,OAAO,YAAY,SAAS,OAAO,SAAS,OAAA,CAAW;AACvG,6BAAuB,EAAE,QAAQ,KAAK,UAAU,OAAO,YAAY,SAAS,OAAO,SAAS,OAAA,CAAW;AACvG,6BAAuB,EAAE,QAAQ,KAAK,QAAQ,OAAO,UAAU,SAAS,OAAO,OAAO,OAAA,CAAW;AACjG;AAAA;AAAA;AAAA,IAGF,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,IAAI,UAAU,OAAO,IAAI;AAChC,2BAAqB;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,OAAO,aAAa,SAAS,OAAO,UAAU;AAAA,MAAA,CAC/C;AACD,UAAI,UAAU,UAAU,OAAO,OAAO,SAAS,UAAU;AACvD,eAAO,IAAI,QAAQ,OAAO,IAAI;AAAA,MAChC;AACA,UAAI,WAAW,UAAU,OAAO,OAAO,UAAU,UAAU;AACzD,eAAO,IAAI,SAAS,OAAO,KAAK;AAAA,MAClC;AACA,UAAI,eAAe,UAAU,OAAO,OAAO,cAAc,UAAU;AACjE,eAAO,IAAI,cAAc,OAAO,SAAS;AAAA,MAC3C;AACA,UAAI,mBAAmB,UAAU,OAAO,OAAO,kBAAkB,UAAU;AACzE,eAAO,IAAI,kBAAkB,OAAO,aAAa;AAAA,MACnD;AACA;AAAA,EAAA;AAEN;AASO,SAAS,8BAA8B;AAAA,EAC5C;AAAA,EACA;AACF,GAGS;;AACP,QAAM,SAAS,uCAAW;AAC1B,MAAI,OAAO,WAAW,UAAU;AAC9B,yBAAqB,EAAE,QAAQ,OAAO,OAAA,CAAQ;AAAA,EAChD,WAAW,UAAU,OAAO,WAAW,YAAY,UAAU,UAAU,OAAO,OAAO,SAAS,UAAU;AACtG,4BAAwB,EAAE,QAAQ,QAAQ,EAAE,GAAG,QAAQ,MAAM,OAAO,KAAA,GAAQ;AAAA,EAC9E;AAEA,QAAM,WAAU,4CAAW,SAAX,mBAAiB;AACjC,MAAI,OAAO,YAAY,YAAY,SAAS;AAC1C,WAAO,IAAI,WAAW,OAAO;AAAA,EAC/B;AACF;AAEO,SAAS,sBAAsB,UAAkB,WAAkD;AACxG,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,QAAI,aAAa,IAAI,qBAAqB,GAAG;AAC7C,kCAA8B,EAAE,QAAQ,IAAI,cAAc,WAAW;AACrE,WAAO,IAAI,SAAA;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,MAAM,gBAAgB,CAAC,UAAkD;AACvE,MAAI;AACF,UAAM,iBAAiB,MAAM,MAAM,GAAG,EAAE,CAAC;AACzC,QAAI,CAAC,kBAAkB,OAAO,SAAS,WAAY,QAAO;AAC1D,UAAM,aAAa,eAAe,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACtE,UAAM,UAAmB,KAAK;AAAA,MAC5B,KAAK,WAAW,OAAO,WAAW,UAAW,IAAK,WAAW,SAAS,KAAM,GAAI,GAAG,CAAC;AAAA,IAAA;AAEtF,WAAO,WAAW,OAAO,YAAY,WAAY,UAAsC;AAAA,EACzF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,yBAAyB,EAAE,OAAO,UAA8D;AAC9G,MAAI,CAAC,MAAO,QAAO,SAAS,WAAW,MAAM,KAAK;AAElD,QAAM,UAAU,cAAc,KAAK;AACnC,QAAM,UAAS,mCAAS,aAAW,mCAAS;AAC5C,MAAI,OAAO,WAAW,YAAY,CAAC,OAAQ,QAAO,gBAAgB,KAAK;AAEvE,SAAO,OAAO,KAAK,UAAU;AAAA,IAC3B,SAAQ,mCAAS,QAAO;AAAA,IACxB;AAAA,IACA,cAAa,mCAAS,mBAAiB,mCAAS,WAAU;AAAA,IAC1D,cAAa,mCAAS,iBAAgB;AAAA,IACtC,iBAAgB,mCAAS,8BAA6B;AAAA,EAAA,CACvD,CAAC;AACJ;AAeO,SAAS,cACd,SACA,UAaI;AAAA,EACF,YAAY;AACd,GACqB;;AACrB,QAAM;AAAA,IACJ,WAAW;AAAA,IACX,mBAAmB,KAAK,KAAK,KAAK;AAAA,IAClC,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE;AAGJ,QAAM,WAAWC,MAAAA,OAAO,KAAK;AAC7B,WAAS,UAAU;AACnB,QAAM,cAAcA,MAAAA,OAAO,QAAQ;AACnC,cAAY,UAAU;AAItB,QAAM,cAAc,yBAAyB,EAAE,OAAO,QAAQ;AAC9D,QAAM,UAAU,QAAQ,WAAW,UAAU;AAE7C,QAAM,QAAQC,WAAAA;AAAAA,IACZ;AAAA,MACE,UAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAEAC,mBAAAA,mBAAmB,WAAW;AAAA,QAC9B;AAAA,QACA,UAAU;AAAA,MAAA;AAAA,MAEZ,SAAS,OAAO,EAAE,aAA0D;AAE1E,cAAM,aAAa,WAAW,QAAQ,2BAA2B,EAAE;AACnE,cAAM,YAAY,SAAS;AAC3B,cAAM,MAAM,MAAMC,kBAAAA;AAAAA,UAChB,EAAE,QAAQ,OAAO,WAAW,UAAU,YAAY,YAAY,UAAU,OAAA;AAAA,UACxE,CAAC,YACC,MAAM,GAAG,UAAU,6BAA6B;AAAA,YAC9C,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,QAAA;AAAA,YAClD,MAAM,KAAK;AAAA,cACT,2BAA2B;AAAA,gBACzB,SAAS,WAAW;AAAA,gBACpB;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cAAA,CACD;AAAA,YAAA;AAAA,YAEH;AAAA,UAAA,CACD;AAAA,QAAA;AAEL,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,OAAO,MAAM,IAAI,OAAO,MAAM,MAAM,EAAE;AAC5C,gBAAM,IAAI,MAAM,mCAAmC,IAAI,MAAM,MAAM,IAAI,EAAE;AAAA,QAC3E;AACA,cAAM,OAAkD,MAAM,IAAI,KAAA;AAClE,eAAO;AAAA,UACL,KAAK,qBAAqB,EAAE,UAAU,KAAK,WAAW,QAAQ,YAAY;AAAA,UAC1E,WAAW,KAAK;AAAA,QAAA;AAAA,MAEpB;AAAA,MACA;AAAA;AAAA;AAAA,MAGA,WAAW,CAAC,WACV,OAAO,MAAM,OAAO,KAAK,IAAI,GAAG,OAAO,MAAM,KAAK,YAAY,MAAO,KAAK,IAAA,CAAK,IAAI;AAAA,MACrF,sBAAsB;AAAA,MACtB,oBAAoB;AAAA,MACpB,OAAO;AAAA,IAAA;AAAA,IAETC,MAAAA,WAAWC,WAAAA,kBAAkB,KAAK;AAAA,EAAA;AAGpC,SAAO;AAAA,IACL,UAAU,YAAW,WAAM,SAAN,mBAAY,QAAO,OAAQ;AAAA,IAChD,WAAW,WAAW,MAAM;AAAA,IAC5B,OAAO,WAAW,MAAM,QAAQ,MAAM,MAAM,UAAU;AAAA,EAAA;AAE1D;;;;;;;;;;"}
@@ -5,24 +5,35 @@ export interface UseAssetEmbedOptions {
5
5
  expiresInSeconds?: number;
6
6
  /** Native Olympus surface rendered by the iframe. Defaults to `full`. */
7
7
  displayMode?: 'minimal' | 'full';
8
+ /**
9
+ * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, where the viewer
10
+ * does not sign in: spreadsheets and Word documents, on an editable link (`readOnly: false`)
11
+ * only. Its runs are billed to the asset's workspace under the link's creator, and each link has
12
+ * its own hourly cap. The full mode's editors offer it to the signed-in person already. Defaults
13
+ * to `false`.
14
+ */
15
+ allowQuickEdit?: boolean;
8
16
  }
9
17
  export declare const DEFAULT_ASSET_EMBED_READ_ONLY = false;
18
+ export declare const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;
10
19
  export declare const DEFAULT_ASSET_EMBED_DISPLAY_MODE: 'full';
11
20
  export interface UseAssetEmbedResult {
12
21
  embedUrl: string | null;
13
22
  isLoading: boolean;
14
23
  error: string | null;
15
24
  }
16
- export declare function buildAssetEmbedRequestBody({ assetId, readOnly, expiresInSeconds, displayMode, }: {
25
+ export declare function buildAssetEmbedRequestBody({ assetId, readOnly, expiresInSeconds, displayMode, allowQuickEdit, }: {
17
26
  assetId: string;
18
27
  readOnly: boolean;
19
28
  expiresInSeconds: number;
20
29
  displayMode: 'minimal' | 'full';
30
+ allowQuickEdit?: boolean;
21
31
  }): {
22
32
  asset_id: string;
23
33
  read_only: boolean;
24
34
  expires_in_seconds: number;
25
35
  display_mode: "full" | "minimal";
36
+ allow_quick_edit?: boolean | undefined;
26
37
  };
27
38
  export declare function resolveAssetEmbedUrl({ embedUrl, appUrl, backendUrl, currentOrigin, }: {
28
39
  embedUrl: string;
@@ -48,14 +59,16 @@ export declare function getAssetEmbedAuthContext({ token, apiKey }: {
48
59
  }): string;
49
60
  /**
50
61
  * Hook to generate an embed URL for rendering an asset in an iframe.
51
- * Calls the Athena embed token endpoint.
62
+ * Calls the Athena embed token endpoint through TanStack Query (the host's
63
+ * QueryClient when there is one, else the page's embed client), keyed on the
64
+ * asset, the mode and the identity.
52
65
  *
53
66
  * Note: The auth token is read via a ref and is intentionally NOT part
54
- * of the effect dependency array. PropelAuth and similar providers refresh
55
- * the token value periodically (e.g. on window focus), but the underlying
56
- * session is the same — re-running the effect on every token rotation
57
- * would force the iframe to reload unnecessarily. The effect only re-runs
58
- * when the auth _mode_ changes (auth vs anonymous) or the asset changes.
67
+ * of the query key. PropelAuth and similar providers refresh the token value
68
+ * periodically (e.g. on window focus), but the underlying session is the
69
+ * same — minting again on every token rotation would force the iframe to
70
+ * reload unnecessarily. The link is minted again only when the identity
71
+ * (user or tenant), the asset or the mode changes.
59
72
  */
60
73
  export declare function useAssetEmbed(assetId: string | null, options?: UseAssetEmbedOptions & {
61
74
  backendUrl: string;
@@ -1,22 +1,28 @@
1
- import { useState, useRef, useEffect } from "react";
1
+ import { useQuery, QueryClient, QueryClientContext } from "@tanstack/react-query";
2
+ import { useRef, useContext } from "react";
2
3
  import { deriveAthenaAppUrl } from "../lib/athena-urls.js";
4
+ import { keyPartFingerprint } from "../lib/use-athena-rest-query.js";
3
5
  import { sendWithCredentialRenewal } from "../runtime/credential-renewal.js";
4
6
  const DEFAULT_ASSET_EMBED_READ_ONLY = false;
7
+ const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;
5
8
  const DEFAULT_ASSET_EMBED_DISPLAY_MODE = "full";
6
9
  function buildAssetEmbedRequestBody({
7
10
  assetId,
8
11
  readOnly,
9
12
  expiresInSeconds,
10
- displayMode
13
+ displayMode,
14
+ allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT
11
15
  }) {
12
16
  return {
13
17
  asset_id: assetId,
14
18
  read_only: readOnly,
15
19
  expires_in_seconds: expiresInSeconds,
16
- display_mode: displayMode
20
+ display_mode: displayMode,
21
+ // Sent only when asked: an agora that predates it is never handed an unknown field.
22
+ ...allowQuickEdit && { allow_quick_edit: true }
17
23
  };
18
24
  }
19
- const embedCache = /* @__PURE__ */ new Map();
25
+ const embedQueryClient = new QueryClient();
20
26
  const ATHENA_APP_HOSTNAMES = /* @__PURE__ */ new Set(["app.athenaintel.com", "staging-app.athenaintel.com"]);
21
27
  const ATHENA_PREVIEW_HOSTNAME_SUFFIX = ".previews.athenaintel.com";
22
28
  const ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\d+)\.(?:app\.athenaintel\.com|staging-app\.athenaintel\.com)$/;
@@ -292,92 +298,86 @@ function getAssetEmbedAuthContext({ token, apiKey }) {
292
298
  function useAssetEmbed(assetId, options = {
293
299
  backendUrl: ""
294
300
  }) {
301
+ var _a;
295
302
  const {
296
303
  readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,
297
304
  expiresInSeconds = 60 * 60 * 24 * 30,
298
305
  displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,
306
+ allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,
299
307
  backendUrl,
300
308
  appUrl,
301
309
  apiKey,
302
310
  token,
303
311
  getToken
304
312
  } = options;
305
- const [embedUrl, setEmbedUrl] = useState(null);
306
- const [isLoading, setIsLoading] = useState(false);
307
- const [error, setError] = useState(null);
308
- const abortRef = useRef(null);
309
313
  const tokenRef = useRef(token);
310
314
  tokenRef.current = token;
311
315
  const getTokenRef = useRef(getToken);
312
316
  getTokenRef.current = getToken;
313
317
  const authContext = getAssetEmbedAuthContext({ token, apiKey });
314
- useEffect(() => {
315
- var _a;
316
- if (!assetId || !backendUrl) {
317
- setEmbedUrl(null);
318
- setIsLoading(false);
319
- setError(null);
320
- return;
321
- }
322
- const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ""}`;
323
- const cached = embedCache.get(cacheKey);
324
- if (cached && cached.expiresAt > Date.now() / 1e3) {
325
- setEmbedUrl(cached.url);
326
- setIsLoading(false);
327
- setError(null);
328
- return;
329
- }
330
- const apiBaseUrl = backendUrl.replace(/\/api\/assistant-ui\/?$/, "");
331
- const endpoint = `${apiBaseUrl}/api/embed/generate-token`;
332
- (_a = abortRef.current) == null ? void 0 : _a.abort();
333
- const controller = new AbortController();
334
- abortRef.current = controller;
335
- setIsLoading(true);
336
- setError(null);
337
- const heldToken = tokenRef.current;
338
- sendWithCredentialRenewal(
339
- { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : void 0 },
340
- (headers) => fetch(endpoint, {
341
- method: "POST",
342
- headers: { "Content-Type": "application/json", ...headers },
343
- body: JSON.stringify(
344
- buildAssetEmbedRequestBody({
345
- assetId,
346
- readOnly,
347
- expiresInSeconds,
348
- displayMode
318
+ const enabled = Boolean(assetId && backendUrl);
319
+ const query = useQuery(
320
+ {
321
+ queryKey: [
322
+ "athena-sdk",
323
+ "asset-embed",
324
+ assetId,
325
+ readOnly,
326
+ displayMode,
327
+ allowQuickEdit,
328
+ // The identity, never the credential: an opaque token or a key is part of `authContext`.
329
+ keyPartFingerprint(authContext),
330
+ backendUrl,
331
+ appUrl ?? ""
332
+ ],
333
+ queryFn: async ({ signal }) => {
334
+ const apiBaseUrl = backendUrl.replace(/\/api\/assistant-ui\/?$/, "");
335
+ const heldToken = tokenRef.current;
336
+ const res = await sendWithCredentialRenewal(
337
+ { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : void 0 },
338
+ (headers) => fetch(`${apiBaseUrl}/api/embed/generate-token`, {
339
+ method: "POST",
340
+ headers: { "Content-Type": "application/json", ...headers },
341
+ body: JSON.stringify(
342
+ buildAssetEmbedRequestBody({
343
+ assetId: assetId ?? "",
344
+ readOnly,
345
+ expiresInSeconds,
346
+ displayMode,
347
+ allowQuickEdit
348
+ })
349
+ ),
350
+ signal
349
351
  })
350
- ),
351
- signal: controller.signal
352
- })
353
- ).then(async (res) => {
354
- if (!res.ok) {
355
- const text = await res.text().catch(() => "");
356
- throw new Error(`Failed to generate embed token (${res.status}): ${text}`);
357
- }
358
- return res.json();
359
- }).then((data) => {
360
- const resolvedEmbedUrl = resolveAssetEmbedUrl({
361
- embedUrl: data.embed_url,
362
- appUrl,
363
- backendUrl
364
- });
365
- embedCache.set(cacheKey, {
366
- url: resolvedEmbedUrl,
367
- expiresAt: data.expires_at
368
- });
369
- setEmbedUrl(resolvedEmbedUrl);
370
- setIsLoading(false);
371
- }).catch((err) => {
372
- if (err.name === "AbortError") return;
373
- setError(err.message);
374
- setIsLoading(false);
375
- });
376
- return () => controller.abort();
377
- }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);
378
- return { embedUrl, isLoading, error };
352
+ );
353
+ if (!res.ok) {
354
+ const text = await res.text().catch(() => "");
355
+ throw new Error(`Failed to generate embed token (${res.status}): ${text}`);
356
+ }
357
+ const data = await res.json();
358
+ return {
359
+ url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),
360
+ expiresAt: data.expires_at
361
+ };
362
+ },
363
+ enabled,
364
+ // A minted link is good until it expires; a new one would reload the frame, so it is never
365
+ // minted again while the embed shows (no refetch on focus or reconnect) and never retried.
366
+ staleTime: (cached) => cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1e3 - Date.now()) : 0,
367
+ refetchOnWindowFocus: false,
368
+ refetchOnReconnect: false,
369
+ retry: false
370
+ },
371
+ useContext(QueryClientContext) ?? embedQueryClient
372
+ );
373
+ return {
374
+ embedUrl: enabled ? ((_a = query.data) == null ? void 0 : _a.url) ?? null : null,
375
+ isLoading: enabled && query.isLoading,
376
+ error: enabled && query.error ? query.error.message : null
377
+ };
379
378
  }
380
379
  export {
380
+ DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,
381
381
  DEFAULT_ASSET_EMBED_DISPLAY_MODE,
382
382
  DEFAULT_ASSET_EMBED_READ_ONLY,
383
383
  appendCitationReferenceParams,
@@ -1 +1 @@
1
- {"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const heldToken = tokenRef.current;\n sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n }),\n )\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useContext, useRef } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { keyPartFingerprint } from '../lib/use-athena-rest-query';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n /**\n * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, where the viewer\n * does not sign in: spreadsheets and Word documents, on an editable link (`readOnly: false`)\n * only. Its runs are billed to the asset's workspace under the link's creator, and each link has\n * its own hourly cap. The full mode's editors offer it to the signed-in person already. Defaults\n * to `false`.\n */\n allowQuickEdit?: boolean;\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n allowQuickEdit?: boolean;\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n // Sent only when asked: an agora that predates it is never handed an unknown field.\n ...(allowQuickEdit && { allow_quick_edit: true }),\n };\n}\n\n/**\n * The embed links' query client when the host provides none: one for the page, so every embed of\n * one asset, mode and identity shares one minted link until it expires.\n */\nconst embedQueryClient = new QueryClient();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint through TanStack Query (the host's\n * QueryClient when there is one, else the page's embed client), keyed on the\n * asset, the mode and the identity.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the query key. PropelAuth and similar providers refresh the token value\n * periodically (e.g. on window focus), but the underlying session is the\n * same — minting again on every token rotation would force the iframe to\n * reload unnecessarily. The link is minted again only when the identity\n * (user or tenant), the asset or the mode changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n // Keep the latest token in a ref so the query can read it without being\n // keyed on it: PropelAuth and similar providers rotate the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n const enabled = Boolean(assetId && backendUrl);\n\n const query = useQuery(\n {\n queryKey: [\n 'athena-sdk',\n 'asset-embed',\n assetId,\n readOnly,\n displayMode,\n allowQuickEdit,\n // The identity, never the credential: an opaque token or a key is part of `authContext`.\n keyPartFingerprint(authContext),\n backendUrl,\n appUrl ?? '',\n ],\n queryFn: async ({ signal }): Promise<{ url: string; expiresAt: number }> => {\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const heldToken = tokenRef.current;\n const res = await sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(`${apiBaseUrl}/api/embed/generate-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId: assetId ?? '',\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n }),\n ),\n signal,\n }),\n );\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n const data: { embed_url: string; expires_at: number } = await res.json();\n return {\n url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),\n expiresAt: data.expires_at,\n };\n },\n enabled,\n // A minted link is good until it expires; a new one would reload the frame, so it is never\n // minted again while the embed shows (no refetch on focus or reconnect) and never retried.\n staleTime: (cached) =>\n cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1000 - Date.now()) : 0,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n useContext(QueryClientContext) ?? embedQueryClient,\n );\n\n return {\n embedUrl: enabled ? (query.data?.url ?? null) : null,\n isLoading: enabled && query.isLoading,\n error: enabled && query.error ? query.error.message : null,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const statewireRunConfig = require("../runtime/statewire-run-config.cjs");
4
+ function allowsAnyModel(value) {
5
+ return value !== false;
6
+ }
7
+ const LEGACY_DEFAULT_ENTRY_ID = "default";
8
+ const LEGACY_DEFAULT_ENTRY_LABEL = "Default";
9
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
10
+ const nonBlank = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : null;
11
+ function agentAssetIdOf(agent) {
12
+ const trimmed = nonBlank(agent);
13
+ if (trimmed === null) return null;
14
+ return statewireRunConfig.parseCollabAgentRef(trimmed) ?? (trimmed.includes(":") ? null : trimmed);
15
+ }
16
+ function labeledModelsOf(value) {
17
+ let rows = value;
18
+ if (typeof rows === "string") {
19
+ try {
20
+ rows = JSON.parse(rows);
21
+ } catch {
22
+ return [];
23
+ }
24
+ }
25
+ 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;
39
+ }
40
+ function channelModelsOf(value) {
41
+ if (!isRecord(value)) return {};
42
+ const out = {};
43
+ for (const [channel, override] of Object.entries(value)) {
44
+ const model = isRecord(override) ? nonBlank(override.model) : null;
45
+ if (model !== null) out[channel] = model;
46
+ }
47
+ return out;
48
+ }
49
+ function isPublishedAgent(athenaMetadata) {
50
+ if (!isRecord(athenaMetadata)) return false;
51
+ const versionId = athenaMetadata.published_version_id;
52
+ return typeof versionId === "string" && versionId !== "" || typeof versionId === "number" && Number.isFinite(versionId);
53
+ }
54
+ function publishedAgentModelSource(athenaMetadata) {
55
+ if (!isRecord(athenaMetadata) || !isPublishedAgent(athenaMetadata)) return null;
56
+ const config = athenaMetadata.published_config;
57
+ if (!isRecord(config)) return null;
58
+ const source = {
59
+ model: nonBlank(config.model),
60
+ models: labeledModelsOf(config.models),
61
+ channelModels: channelModelsOf(config.channelOverrides),
62
+ // The editor stores the field name; agora also accepts its snake-case alias.
63
+ allowAnyModel: allowsAnyModel(
64
+ Object.hasOwn(config, "allowAnyModel") ? config.allowAnyModel : config.allow_any_model
65
+ )
66
+ };
67
+ const namesAModel = source.model !== null || source.models.length > 0 || Object.keys(source.channelModels).length > 0;
68
+ return namesAModel ? source : null;
69
+ }
70
+ function isAutomaticModelLabel({
71
+ entry,
72
+ modelLabel
73
+ }) {
74
+ const label = entry.label.trim();
75
+ if (label === "") return true;
76
+ if (entry.id === LEGACY_DEFAULT_ENTRY_ID && label === LEGACY_DEFAULT_ENTRY_LABEL) return true;
77
+ return label === entry.model || label === modelLabel(entry.model);
78
+ }
79
+ function agentDefaultModel({
80
+ source,
81
+ channel
82
+ }) {
83
+ 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;
85
+ }
86
+ function buildAgentModelOptions({
87
+ source,
88
+ channel,
89
+ pinnedModel,
90
+ modelLabel
91
+ }) {
92
+ const options = [];
93
+ const add = (model, label) => {
94
+ if (options.some((option) => option.model === model)) return;
95
+ options.push({ model, label, isDefault: false, pinnedOutside: false });
96
+ };
97
+ for (const entry of source.models) {
98
+ add(
99
+ entry.model,
100
+ isAutomaticModelLabel({ entry, modelLabel }) ? modelLabel(entry.model) : entry.label.trim()
101
+ );
102
+ }
103
+ if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));
104
+ const defaultModel = agentDefaultModel({ source, channel });
105
+ if (defaultModel !== null) {
106
+ add(defaultModel, modelLabel(defaultModel));
107
+ for (const option of options) option.isDefault = option.model === defaultModel;
108
+ }
109
+ const pinned = nonBlank(pinnedModel);
110
+ if (pinned !== null && !options.some((option) => option.model === pinned)) {
111
+ options.push({ model: pinned, label: modelLabel(pinned), isDefault: false, pinnedOutside: true });
112
+ }
113
+ return { options, defaultModel };
114
+ }
115
+ exports.agentAssetIdOf = agentAssetIdOf;
116
+ exports.agentDefaultModel = agentDefaultModel;
117
+ exports.allowsAnyModel = allowsAnyModel;
118
+ exports.buildAgentModelOptions = buildAgentModelOptions;
119
+ exports.isAutomaticModelLabel = isAutomaticModelLabel;
120
+ exports.isPublishedAgent = isPublishedAgent;
121
+ exports.publishedAgentModelSource = publishedAgentModelSource;
122
+ //# sourceMappingURL=agent-model-options.cjs.map