@athenaintel/react 0.52.1 → 0.53.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.
package/README.md CHANGED
@@ -951,6 +951,10 @@ Both use the same bearer / API key as session creation. The full recipe
951
951
  template skill `voice-session-guide.md`. `createAthenaVoiceSession` is also
952
952
  exported if you hold the bridge state yourself.
953
953
 
954
+ ## What's new in 0.53.0
955
+
956
+ **A chip names a chat by its own title.** Asset chips now look their assets up through Athena's `getAssetChipDetails`, which reads only what a chip draws (the asset's title and type) instead of each asset's whole row. A chip that links a chat session (`type=session`, an `asset_thread_…` id, or an anchor into a message or tool call) now shows the session's own title like every other asset, where it used to keep the label the model wrote, because a session's row was too heavy to read for a name. A repository file citation still reads as its file and lines. Against an Athena that predates the field, the SDK asks once per backend and then keeps each link's label, as before; nothing falls back to reading whole rows. Nothing to change in your app.
957
+
954
958
  ## What's new in 0.52.1
955
959
 
956
960
  **0.52.0 as published.** The 0.52.0 build bundled zod (reached through the handover cursor's message-id check) under `dist/node_modules`, which npm never packs, so it was never published. The check now lives in a zod-free module, and the build fails if `dist/node_modules` ever appears. Otherwise identical to 0.52.0 below.
@@ -98,17 +98,6 @@ function GitHubMarkIcon({ className }) {
98
98
  }
99
99
  );
100
100
  }
101
- const SESSION_ANCHORS = /* @__PURE__ */ new Set([
102
- "msg",
103
- "msg_range",
104
- "msg_block",
105
- "msg_section",
106
- "msg_text",
107
- "tool_call"
108
- ]);
109
- function isSessionLink(assetType, citation) {
110
- return (assetType == null ? void 0 : assetType.toLowerCase()) === "session" || citation !== void 0 && SESSION_ANCHORS.has(citation.anchorType);
111
- }
112
101
  const PLACE_FITS_CHARS = 20;
113
102
  function splitFileLines(label) {
114
103
  const colon = label.lastIndexOf(":");
@@ -227,7 +216,7 @@ const MentionChip = ({
227
216
  const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
228
217
  const repositoryFile = (citation == null ? void 0 : citation.anchorType) === "file_line_range" ? { ...splitFileLines(citation.label), label: citation.label } : null;
229
218
  const asset = useMentionAsset.useMentionAsset(
230
- type === "asset" && (assetIds == null ? void 0 : assetIds.length) === 1 && !repositoryFile && !isSessionLink(assetType, citation) ? assetIds[0] : void 0
219
+ type === "asset" && (assetIds == null ? void 0 : assetIds.length) === 1 && !repositoryFile ? assetIds[0] : void 0
231
220
  );
232
221
  const assetTitle = (asset == null ? void 0 : asset.title) || null;
233
222
  const chipName = (repositoryFile == null ? void 0 : repositoryFile.label) ?? assetTitle ?? name;
@@ -1 +1 @@
1
- {"version":3,"file":"mention-chip.cjs","sources":["../../../src/chat-ui/mentions/mention-chip.tsx"],"sourcesContent":["/**\n * Mention chip rendering for message display.\n *\n * Parses [@name](mention://type?...) markdown links and renders them\n * as styled inline chips matching the Olympus mention style.\n * Used in both user messages (inline text) and assistant messages (markdown links).\n */\n\nimport {\n isValidElement,\n useContext,\n useState,\n type FC,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport {\n type AssetReference,\n decodeFromAssetMentionUri,\n decodeFromAssetSpacesUrl,\n encodeToAssetSpacesUrl,\n formatReferenceAnchor,\n} from \"@athenaintel/references/asset-links\";\nimport {\n CrosshairIcon,\n DatabaseIcon,\n FileCodeIcon,\n FileIcon,\n FileTextIcon,\n FolderIcon,\n Globe2Icon,\n HeadphonesIcon,\n ImageIcon,\n type LucideIcon,\n MailIcon,\n MessageSquareIcon,\n MonitorIcon,\n PresentationIcon,\n SheetIcon,\n UserIcon,\n VideoIcon,\n} from \"lucide-react\";\nimport { cn } from \"../../lib/utils\";\nimport { createOlympusAssetUrl } from \"../lib/asset-urls\";\nimport { MentionAuthCtx } from \"./suggestions/data/api-client\";\nimport { assetTypeToThumb } from \"./suggestions/data/sources/asset-type-icons\";\nimport { useMemberAvatarUrl } from \"./use-member-avatar\";\nimport { useMentionAsset } from \"./use-mention-asset\";\n\nconst SPACES_MENTION_PATH = /\\/dashboard\\/spaces\\/?\\?/;\n\nfunction isSpacesMentionUrl(url: string): boolean {\n return SPACES_MENTION_PATH.test(url);\n}\n\nfunction getSpacesAssetType(url: URL): string | null {\n const assetType =\n url.searchParams.get(\"assetType\") ||\n url.searchParams.get(\"asset_type\") ||\n url.searchParams.get(\"type\");\n return assetType === \"sheet\" ? \"spreadsheet\" : assetType;\n}\n\n/**\n * Where in the asset a citation points, as its chip shows it. The anchor is\n * read and labelled by `@athenaintel/references` — the same parser and labels\n * Olympus's mention chips use — so a chip reads `jwt.py:12-25`, `p.5` or\n * `Slide 3` in every Athena chat.\n */\nexport interface MentionChipCitation {\n /** The anchor type (`page`, `file_line_range`, `slide`, …). */\n anchorType: string;\n /** The short label drawn after the name (`p.5`, `jwt.py:12-25`). */\n label: string;\n /**\n * The place spelled out (`Page 5`, `agora/…/jwt.py:12-25`), without the\n * excerpt: the tooltip when the link carries no excerpt, and part of the\n * chip's accessible name when it holds a word.\n */\n description: string;\n}\n\n/** The citation an asset link's anchor names, or undefined for a plain mention or an anchor nobody can read. */\nfunction citationOf(reference: AssetReference): MentionChipCitation | undefined {\n const { anchor, meta } = reference;\n if (!anchor) return undefined;\n const label = formatReferenceAnchor(anchor, \"short\", meta);\n if (!label) return undefined;\n const { excerpt: _excerpt, ...place } = meta ?? {};\n return {\n anchorType: anchor.type,\n label,\n description: formatReferenceAnchor(anchor, \"long\", place) ?? label,\n };\n}\n\n/**\n * The chip's icon for an asset type when the host serves no asset-type SVGs.\n * A compact set on purpose: the chip renders in every message (and Olympus's\n * main layout reaches it through the queued-message tray), so it carries a\n * dozen icons rather than the `@` menu's whole map.\n */\nconst ASSET_CHIP_ICONS: Record<string, LucideIcon> = {\n document: FileTextIcon,\n word_document: FileTextIcon,\n markdown: FileTextIcon,\n txt: FileTextIcon,\n pdf: FileTextIcon,\n spreadsheet: SheetIcon,\n table: SheetIcon,\n presentation: PresentationIcon,\n powerpoint_deck: PresentationIcon,\n presentation_pptx_studio: PresentationIcon,\n pptx: PresentationIcon,\n image: ImageIcon,\n figure: ImageIcon,\n canvas: ImageIcon,\n video: VideoIcon,\n meeting: VideoIcon,\n video_session: VideoIcon,\n audio: HeadphonesIcon,\n voice_memo: HeadphonesIcon,\n phone_call: HeadphonesIcon,\n email: MailIcon,\n email_message: MailIcon,\n live_email_thread: MailIcon,\n live_calendar_event: MailIcon,\n python: FileCodeIcon,\n javascript: FileCodeIcon,\n typescript: FileCodeIcon,\n json: FileCodeIcon,\n yaml: FileCodeIcon,\n database: DatabaseIcon,\n catalog: DatabaseIcon,\n drive: DatabaseIcon,\n dataset: DatabaseIcon,\n semantic_model: DatabaseIcon,\n query_snippet: DatabaseIcon,\n folder: FolderIcon,\n gdrive_folder: FolderIcon,\n project: FolderIcon,\n collection: FolderIcon,\n session: MessageSquareIcon,\n computer: MonitorIcon,\n desktop_session: MonitorIcon,\n user_interface: MonitorIcon,\n application: MonitorIcon,\n html: Globe2Icon,\n web_page: Globe2Icon,\n browse_session: Globe2Icon,\n};\n\nfunction assetChipIcon(assetType: string | undefined): LucideIcon {\n return (assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()]) || FileIcon;\n}\n\n/**\n * GitHub's mark (Octicons `mark-github`), drawn by a repository file's\n * citation. Lucide carries no brand icons and an SDK app serves none of\n * Olympus's, so the chip brings its own. Only a GitHub repository catalog\n * serves `file_line_range` anchors (`ASSET_ANCHOR_REGISTRY`; agora's\n * `_CATALOG_ANCHORS_BY_PROVIDER`), so the anchor alone names the provider.\n */\nfunction GitHubMarkIcon({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n className={className}\n data-chip-icon=\"github\"\n >\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z\" />\n </svg>\n );\n}\n\n/** Anchors only a session holds: its messages, blocks and tool calls. */\nconst SESSION_ANCHORS: ReadonlySet<string> = new Set([\n \"msg\",\n \"msg_range\",\n \"msg_block\",\n \"msg_section\",\n \"msg_text\",\n \"tool_call\",\n]);\n\n/** Whether a link names a session (its type, or an anchor only a session holds). */\nfunction isSessionLink(\n assetType: string | undefined,\n citation: MentionChipCitation | undefined,\n): boolean {\n return (\n assetType?.toLowerCase() === \"session\" ||\n (citation !== undefined && SESSION_ANCHORS.has(citation.anchorType))\n );\n}\n\n/** Past this many characters a place may not show whole in the chip, so the tooltip spells it out. */\nconst PLACE_FITS_CHARS = 20;\n\n/** `route.ts:18-24` as the file's name and its lines, so a long name shortens while its lines stay. */\nfunction splitFileLines(label: string): { file: string; lines: string } {\n const colon = label.lastIndexOf(\":\");\n return colon > 0\n ? { file: label.slice(0, colon), lines: label.slice(colon) }\n : { file: label, lines: \"\" };\n}\n\n/**\n * Whether a name already says where its citation points: the place's label or\n * its spelled-out form, or a name ending in the label (`Q4 report p.5`,\n * `olympus/…/route.ts:18-24`). The chip then draws the place once.\n */\nfunction restatesPlace(name: string, citation: MentionChipCitation): boolean {\n const said = name.trim().toLowerCase();\n const label = citation.label.toLowerCase();\n return said === label || said === citation.description.toLowerCase() || said.endsWith(label);\n}\n\n/**\n * The link's own label when it tells the reader something the chip does not\n * already show: not the place again (a path, `file:lines`), not the asset's\n * name. The tooltip keeps it, since the chip shows the asset and the place.\n */\nfunction informativeLabel({\n label,\n shown,\n citation,\n}: {\n label: string;\n shown: readonly (string | null | undefined)[];\n citation: MentionChipCitation | undefined;\n}): string | null {\n const trimmed = label.trim();\n if (!trimmed || !SPEAKABLE.test(trimmed)) return null;\n if (PLACEHOLDER_LABELS.has(trimmed.toLowerCase())) return null;\n if (citation && restatesPlace(trimmed, citation)) return null;\n // Every word of it already shown (`Tesla Q3 report` beside `Tesla Q3\n // Earnings Report`, `demo-app-monorepo` beside the repository's title).\n const shownWords = new Set(\n [...shown, citation?.description].flatMap((text) => (text ? wordsOf(text) : [])),\n );\n return wordsOf(trimmed).every((word) => shownWords.has(word)) ? null : trimmed;\n}\n\nfunction wordsOf(text: string): string[] {\n return text.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? [];\n}\n\n/** A label with a letter or digit in it; `§` or `•` alone says nothing read aloud. */\nconst SPEAKABLE = /[\\p{L}\\p{N}]/u;\n\n/** Labels that name nothing: agora's read_asset citations all read `Citation`. */\nconst PLACEHOLDER_LABELS: ReadonlySet<string> = new Set([\"citation\", \"source\", \"link\", \"here\"]);\n\n/** The asset reference an asset link decodes to, or null when it does not parse. */\nfunction decodeAssetLink(\n url: URL,\n decode: (url: URL) => AssetReference,\n): AssetReference | null {\n try {\n return decode(url);\n } catch {\n return null;\n }\n}\n\n// ── Mention link component for markdown renderer ──\n\n/**\n * Custom <a> renderer for MarkdownTextPrimitive.\n * Intercepts mention:// links and renders them as chips.\n * Falls through to a normal link for non-mention URLs.\n */\nfunction personEmailForMention(\n parsed: ReturnType<typeof parseMentionUrl>,\n): string | undefined {\n if (parsed.type !== \"person\") return undefined;\n return parsed.email || (parsed.id.includes(\"@\") ? parsed.id : undefined);\n}\n\n/**\n * The text a link's children read as. Markdown can hand a link several children, some of them\n * elements (an escaped `_` or a `*` in a URL splits its text), and stringifying an element gives\n * `[object Object]`.\n */\nfunction textOf(node: ReactNode): string {\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (isValidElement<{ children?: ReactNode }>(node)) return textOf(node.props.children);\n return \"\";\n}\n\nexport const MentionLink: FC<{\n href?: string;\n children?: ReactNode;\n interactive?: boolean;\n}> = ({ href, children, interactive = true }) => {\n if (\n href &&\n (href.startsWith(\"mention://\") || isSpacesMentionUrl(href))\n ) {\n const name = textOf(children).trim().replace(/^@/, \"\");\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : parsed.sourceHref}\n interactive={interactive}\n />\n );\n }\n\n if (href?.startsWith(\"chrome-extension://\")) {\n return <span>{children}</span>;\n }\n\n if (isScreenPointHref(href)) {\n return <ScreenPointChip href={href}>{children}</ScreenPointChip>;\n }\n\n // Normal link — render as-is\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n};\n\n// ── The chip pill ──\n\n/**\n * Olympus's mention pill (`mentionPillClassName`: 20px high, 16px icon, the\n * place in `text-xs`) in the provider's theme tokens, so a dark theme keeps it\n * legible, and `box-border` so its height holds without a host's CSS reset.\n * Every chat chip draws it: an asset or citation chip, a web source, a screen\n * point. Only stock Tailwind sizes: Olympus renders these chips (the queued\n * message tray) with a stylesheet that does not scan this package, so an\n * arbitrary value would reach it unstyled. `not-prose` matters: assistant\n * messages render inside Tailwind Typography's `prose`, whose\n * zero-specificity `img` margins (~1.7em top/bottom) would otherwise inflate\n * the chip's icon into a tall card that breaks the sentence flow around it.\n */\nexport const CHIP_PILL_CLASS =\n \"not-prose box-border inline-flex h-5 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-muted px-1 py-0.5 align-middle font-normal leading-none text-foreground no-underline\";\n/** A chip that opens something. */\nexport const CHIP_LINK_CLASS =\n \"cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\";\nexport const CHIP_ICON_CLASS = \"size-4 shrink-0\";\n\n// ── Inline screen-point chip ──\n\n/**\n * Renders an `athena-point://` link as a non-navigating coordinate chip.\n * Surfaces without screenshot context (extension side panel, SDK consumers)\n * get the label plus the coordinates; malformed points fall back to text.\n */\nexport const ScreenPointChip: FC<{\n href: string;\n children?: ReactNode;\n}> = ({ href, children }) => {\n const parsed = parseScreenPointHref(href);\n if (!parsed.ok) {\n return <span>{children}</span>;\n }\n const description = formatScreenPoint(parsed.point);\n return (\n <span\n className={cn(CHIP_PILL_CLASS, \"max-w-[260px]\")}\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n <span className=\"min-w-0 max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">\n {parsed.point.x}, {parsed.point.y}\n </span>\n </span>\n );\n};\n\n// ── Inline mention chip ──\n\nexport const MentionChip: FC<{\n name: string;\n type?: string;\n assetIds?: string[];\n /** The asset's type (`document`, `catalog`, …): picks the icon until (or unless) the asset is looked up. */\n assetType?: string;\n /** Where in the asset the link points: its label follows the name. */\n citation?: MentionChipCitation;\n personEmail?: string;\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({\n name,\n type,\n assetIds,\n assetType,\n citation,\n personEmail,\n thumbnailUrl,\n sourceHref,\n interactive = true,\n}) => {\n const auth = useContext(MentionAuthCtx);\n const memberAvatarUrl = useMemberAvatarUrl(personEmail);\n const [erroredAvatarUrl, setErroredAvatarUrl] = useState<string>();\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n // A repository file's lines read as the file (`route.ts:18-24`): the chip\n // opens that file, and every citation in a reply names the same repository,\n // whatever label the model wrote.\n const repositoryFile =\n citation?.anchorType === \"file_line_range\"\n ? { ...splitFileLines(citation.label), label: citation.label }\n : null;\n // The asset as agora has it, read the way Olympus's chips read it: its own\n // title and type. Until it answers, and for an asset the viewer cannot read,\n // the link's label and `type=` stand in. Asked only where it changes the\n // chip: a repository file reads as its file whatever the catalog is called,\n // and a session's row is too heavy to read for a name.\n const asset = useMentionAsset(\n type === \"asset\" &&\n assetIds?.length === 1 &&\n !repositoryFile &&\n !isSessionLink(assetType, citation)\n ? assetIds[0]\n : undefined,\n );\n const assetTitle = asset?.title || null;\n const chipName = repositoryFile?.label ?? assetTitle ?? name;\n // The place follows a name that does not already say it, as `[p.5]`.\n const placeLabel =\n citation && !repositoryFile && !restatesPlace(chipName, citation)\n ? citation.label\n : null;\n // The tooltip: what the model called it (when the chip does not show it),\n // the repository a file is in and the file's whole path, the cited passage.\n // The place spelled out stands in for a passage the citation does not carry,\n // and for a place too long to show whole (a long sheet name).\n const tooltip =\n citation || excerpt\n ? tooltipText({\n lines: [\n informativeLabel({ label: name, shown: [chipName, assetTitle], citation }),\n repositoryFile ? assetTitle : null,\n repositoryFile || !excerpt || (placeLabel?.length ?? 0) > PLACE_FITS_CHARS\n ? citation?.description\n : null,\n ],\n excerpt,\n })\n : null;\n const assetRootHref =\n type === \"asset\" && assetIds?.length\n ? createOlympusAssetUrl({\n appUrl: auth?.appUrl,\n backendUrl: auth?.backendUrl,\n assetIds,\n })\n : null;\n const href = (() => {\n if (!assetRootHref || !sourceHref) return assetRootHref;\n try {\n const source = new URL(sourceHref, \"https://app.athenaintel.com\");\n const destination = new URL(assetRootHref);\n for (const [key, value] of source.searchParams) {\n destination.searchParams.set(key, value);\n }\n destination.searchParams.set(\"asset_ids\", assetIds?.join(\",\") ?? \"\");\n return destination.toString();\n } catch {\n return assetRootHref;\n }\n })();\n const className = cn(CHIP_PILL_CLASS, \"max-w-[275px]\", href && CHIP_LINK_CLASS);\n // A host's asset-type SVG follows the looked-up type, when it serves them.\n const chipThumbnailUrl = repositoryFile\n ? undefined\n : (asset?.assetType && assetTypeToThumb(asset.assetType)) || thumbnailUrl;\n const AssetIcon = assetChipIcon(asset?.assetType || assetType);\n const contents = (\n <>\n {repositoryFile ? (\n <GitHubMarkIcon className={CHIP_ICON_CLASS} />\n ) : chipThumbnailUrl ? (\n <img\n src={chipThumbnailUrl}\n alt=\"\"\n className={CHIP_ICON_CLASS}\n draggable={false}\n />\n ) : type === \"asset\" || type === \"folder\" ? (\n // A host that bundles no asset-type SVGs (computer apps, most SDK\n // apps) still gets the type's icon.\n <AssetIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"webpage\" ? (\n <Globe2Icon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"person\" ? (\n memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? (\n <img\n src={memberAvatarUrl}\n alt=\"\"\n className={cn(CHIP_ICON_CLASS, \"rounded-full object-cover\")}\n draggable={false}\n onError={() => setErroredAvatarUrl(memberAvatarUrl)}\n />\n ) : (\n <UserIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n )\n ) : null}\n {repositoryFile ? (\n <span\n className=\"flex min-w-0 items-center\"\n data-citation-anchor=\"file_line_range\"\n >\n <span className=\"min-w-0 overflow-hidden text-ellipsis\">\n {repositoryFile.file}\n </span>\n <span className=\"shrink-0 text-muted-foreground\">{repositoryFile.lines}</span>\n </span>\n ) : (\n <span\n className={cn(\n \"max-w-[180px] overflow-hidden text-ellipsis\",\n // A long place may shorten the name, never make it vanish.\n placeLabel && \"min-w-8\",\n )}\n >\n {chipName}\n </span>\n )}\n {citation && placeLabel && (\n <>\n {/* Copied or read as text, the place stays a word apart from the name; a flex row draws no space. */}{\" \"}\n <span\n className=\"flex min-w-0 items-center text-xs text-muted-foreground\"\n data-citation-anchor={citation.anchorType}\n >\n [<span className=\"min-w-0 overflow-hidden text-ellipsis\">{placeLabel}</span>]\n </span>\n </>\n )}\n </>\n );\n\n if (href && interactive) {\n const speakablePlace = citation && SPEAKABLE.test(citation.description);\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={\n repositoryFile && citation\n ? `Open ${citation.description} in Spaces`\n : placeLabel && citation && speakablePlace\n ? `Open ${chipName} at ${citation.description} in Spaces`\n : `Open ${chipName} in Spaces`\n }\n title={tooltip ?? `Open ${chipName} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n // The asset without its place: what a click names the opened asset.\n data-mention-name={repositoryFile ? (assetTitle ?? name) : chipName}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={tooltip ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\n};\n\n/**\n * A chip's tooltip: the lines that say something, each once, then the cited\n * passage, quoted when it shares the tooltip with other lines.\n */\nfunction tooltipText({\n lines,\n excerpt,\n}: {\n lines: readonly (string | null | undefined)[];\n excerpt: string | null;\n}): string | null {\n const kept: string[] = [];\n for (const line of lines) {\n const trimmed = line?.trim();\n if (trimmed && !kept.includes(trimmed)) kept.push(trimmed);\n }\n if (excerpt) kept.push(kept.length ? `“${excerpt}”` : excerpt);\n return kept.length ? kept.join(\"\\n\") : null;\n}\n\n/** The `excerpt` a Spaces citation URL carries, or null. */\nfunction readSourceExcerpt(href: string): string | null {\n try {\n const excerpt = new URL(href, \"https://app.athenaintel.com\").searchParams\n .get(\"excerpt\")\n ?.trim();\n return excerpt ? excerpt : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The label an asset citation chip falls back to when the model wrote none\n * (`[](…/spaces/?asset_ids=…&page=5)`): the cited location, else \"Asset\" — a\n * chip must never render as an empty box.\n */\nfunction citationFallbackLabel(params: URLSearchParams): string {\n const start = params.get(\"start_page\") ?? params.get(\"page\");\n const end = params.get(\"end_page\");\n if (start && end && end !== start) return `Pages ${start}–${end}`;\n if (start) return `Page ${start}`;\n return \"Asset\";\n}\n\n// ── Parse mention:// URLs ──\n\nexport function parseMentionUrl(url: string): {\n type: string;\n name: string;\n id: string;\n email?: string;\n assetIds?: string[];\n assetType?: string;\n citation?: MentionChipCitation;\n /**\n * For a `mention://asset` citation, the Spaces link to its place (anchor and\n * excerpt), so the chip opens there; a Spaces mention is its own link.\n */\n sourceHref?: string;\n thumbnailUrl?: string;\n} {\n try {\n if (isSpacesMentionUrl(url)) {\n const parsed = new URL(url, \"https://app.athenaintel.com\");\n const assetIds = (parsed.searchParams.get(\"asset_ids\") ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const id = assetIds[0] ?? \"\";\n const assetType = getSpacesAssetType(parsed);\n // A link to several assets names no one place in any of them.\n const reference =\n assetIds.length === 1\n ? decodeAssetLink(parsed, decodeFromAssetSpacesUrl)\n : null;\n const citation = reference ? citationOf(reference) : undefined;\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : (citation?.description ??\n citationFallbackLabel(parsed.searchParams)),\n id,\n assetIds,\n assetType: assetType ?? undefined,\n citation,\n thumbnailUrl: assetTypeToThumb(assetType),\n };\n }\n // mention://tool?name=...&description=...\n // mention://asset?id=...&assetType=project&thumb=../icons/files/ic-project.svg\n const u = new URL(url);\n const type = u.hostname || u.pathname.replace(/^\\/\\//, \"\").split(\"?\")[0];\n const isWebpage = type === \"webpage\";\n const id = isWebpage\n ? u.searchParams.get(\"tab_id\") || \"\"\n : u.searchParams.get(\"id\") || u.searchParams.get(\"name\") || \"\";\n const name = isWebpage\n ? u.searchParams.get(\"title\") || \"This webpage\"\n : u.searchParams.get(\"name\") || id;\n\n // Get thumbnail: prefer explicit thumb param, then derive from assetType\n let thumbnailUrl = u.searchParams.get(\"thumb\") || undefined;\n if (!thumbnailUrl) {\n const assetType = u.searchParams.get(\"assetType\");\n if (assetType) {\n thumbnailUrl = assetTypeToThumb(assetType);\n } else if (type === \"asset\" || type === \"folder\") {\n thumbnailUrl = assetTypeToThumb(type === \"folder\" ? \"folder\" : null);\n }\n }\n\n const reference =\n type === \"asset\" ? decodeAssetLink(u, decodeFromAssetMentionUri) : null;\n\n return {\n type,\n name,\n id,\n email: type === \"person\" ? u.searchParams.get(\"email\") || undefined : undefined,\n assetIds: type === \"asset\" && id ? [id] : undefined,\n assetType:\n type === \"asset\" || type === \"folder\"\n ? u.searchParams.get(\"assetType\") ||\n reference?.meta?.assetType ||\n (type === \"folder\" ? \"folder\" : undefined)\n : undefined,\n citation: reference ? citationOf(reference) : undefined,\n sourceHref:\n reference && (reference.anchor || reference.meta?.excerpt)\n ? encodeToAssetSpacesUrl(reference)\n : undefined,\n thumbnailUrl,\n };\n } catch {\n return { type: \"unknown\", name: \"\", id: \"\" };\n }\n}\n\nfunction htmlAttribute(attributes: string, name: string): string | null {\n const escapedName = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const match = new RegExp(\n `(?:^|\\\\s)${escapedName}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`,\n \"i\",\n ).exec(attributes);\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n return value\n .replace(/&amp;/gi, \"&\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/gi, \"'\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\");\n}\n\n/** Turn complete model-emitted URL citation anchors into safe Markdown links. */\nexport function normalizeAthenaMentionHtml(text: string): string {\n return text.replace(\n /<a\\b([^>]*)>([\\s\\S]*?)<\\/a>/gi,\n (original, attributes: string, rawLabel: string) => {\n const className = htmlAttribute(attributes, \"class\") ?? \"\";\n const type = htmlAttribute(attributes, \"data-type\") ?? \"\";\n if (!className.split(/\\s+/).includes(\"mention\") || type !== \"url\") {\n return original;\n }\n\n const rawUrl =\n htmlAttribute(attributes, \"data-url\") ??\n htmlAttribute(attributes, \"href\");\n if (!rawUrl) return original;\n const url = decodeHtmlAttribute(rawUrl);\n let normalizedUrl: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return original;\n }\n normalizedUrl = parsed.toString();\n } catch {\n return original;\n }\n\n const label = decodeHtmlAttribute(rawLabel.replace(/<[^>]*>/g, \"\"))\n .trim()\n .replace(/([\\\\[\\]])/g, \"\\\\$1\");\n return label ? `[${label}](<${normalizedUrl}>)` : original;\n },\n );\n}\n// ── Parse mention markdown in plain text ──\n\n// The label may hold balanced brackets one level deep (`[@Plan [Q3]](…)`) or a lone `[`.\nconst MENTION_RE =\n /\\[@((?:\\[[^[\\]]*\\](?!\\((?:[a-z][a-z0-9+.-]*:\\/\\/|\\/))|[^\\]])*)\\]\\(((?:mention:\\/\\/|(?:https?:\\/\\/[^)\\s]+)?\\/dashboard\\/spaces\\/?\\?)[^)]+)\\)/g;\n\n/**\n * Parse text containing [@name](mention://...) patterns into\n * an array of string segments and MentionChip elements.\n */\nexport function parseMentionsInText(text: string): (string | ReactElement)[] {\n const parts: (string | ReactElement)[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n MENTION_RE.lastIndex = 0;\n\n while ((match = MENTION_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n\n const name = match[1];\n const url = match[2];\n const parsed = parseMentionUrl(url);\n\n parts.push(\n <MentionChip\n key={`mention-${match.index}`}\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : parsed.sourceHref}\n />\n );\n\n lastIndex = match.index + match[0].length;\n }\n\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return parts.length > 0 ? parts : [text];\n}\n\n/** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */\nexport function mentionsToPlainText(text: string): string {\n return text.replace(MENTION_RE, (_match, name: string, url: string) => {\n return `@${name || parseMentionUrl(url).name}`;\n });\n}\n"],"names":["formatReferenceAnchor","FileTextIcon","SheetIcon","PresentationIcon","ImageIcon","VideoIcon","HeadphonesIcon","MailIcon","FileCodeIcon","DatabaseIcon","FolderIcon","MessageSquareIcon","MonitorIcon","Globe2Icon","FileIcon","jsx","isValidElement","isScreenPointHref","parseScreenPointHref","formatScreenPoint","jsxs","cn","CrosshairIcon","useContext","MentionAuthCtx","useMemberAvatarUrl","useState","useMentionAsset","createOlympusAssetUrl","assetTypeToThumb","Fragment","UserIcon","id","reference","decodeFromAssetSpacesUrl","decodeFromAssetMentionUri","encodeToAssetSpacesUrl"],"mappings":"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"}
1
+ {"version":3,"file":"mention-chip.cjs","sources":["../../../src/chat-ui/mentions/mention-chip.tsx"],"sourcesContent":["/**\n * Mention chip rendering for message display.\n *\n * Parses [@name](mention://type?...) markdown links and renders them\n * as styled inline chips matching the Olympus mention style.\n * Used in both user messages (inline text) and assistant messages (markdown links).\n */\n\nimport {\n isValidElement,\n useContext,\n useState,\n type FC,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport {\n type AssetReference,\n decodeFromAssetMentionUri,\n decodeFromAssetSpacesUrl,\n encodeToAssetSpacesUrl,\n formatReferenceAnchor,\n} from \"@athenaintel/references/asset-links\";\nimport {\n CrosshairIcon,\n DatabaseIcon,\n FileCodeIcon,\n FileIcon,\n FileTextIcon,\n FolderIcon,\n Globe2Icon,\n HeadphonesIcon,\n ImageIcon,\n type LucideIcon,\n MailIcon,\n MessageSquareIcon,\n MonitorIcon,\n PresentationIcon,\n SheetIcon,\n UserIcon,\n VideoIcon,\n} from \"lucide-react\";\nimport { cn } from \"../../lib/utils\";\nimport { createOlympusAssetUrl } from \"../lib/asset-urls\";\nimport { MentionAuthCtx } from \"./suggestions/data/api-client\";\nimport { assetTypeToThumb } from \"./suggestions/data/sources/asset-type-icons\";\nimport { useMemberAvatarUrl } from \"./use-member-avatar\";\nimport { useMentionAsset } from \"./use-mention-asset\";\n\nconst SPACES_MENTION_PATH = /\\/dashboard\\/spaces\\/?\\?/;\n\nfunction isSpacesMentionUrl(url: string): boolean {\n return SPACES_MENTION_PATH.test(url);\n}\n\nfunction getSpacesAssetType(url: URL): string | null {\n const assetType =\n url.searchParams.get(\"assetType\") ||\n url.searchParams.get(\"asset_type\") ||\n url.searchParams.get(\"type\");\n return assetType === \"sheet\" ? \"spreadsheet\" : assetType;\n}\n\n/**\n * Where in the asset a citation points, as its chip shows it. The anchor is\n * read and labelled by `@athenaintel/references` — the same parser and labels\n * Olympus's mention chips use — so a chip reads `jwt.py:12-25`, `p.5` or\n * `Slide 3` in every Athena chat.\n */\nexport interface MentionChipCitation {\n /** The anchor type (`page`, `file_line_range`, `slide`, …). */\n anchorType: string;\n /** The short label drawn after the name (`p.5`, `jwt.py:12-25`). */\n label: string;\n /**\n * The place spelled out (`Page 5`, `agora/…/jwt.py:12-25`), without the\n * excerpt: the tooltip when the link carries no excerpt, and part of the\n * chip's accessible name when it holds a word.\n */\n description: string;\n}\n\n/** The citation an asset link's anchor names, or undefined for a plain mention or an anchor nobody can read. */\nfunction citationOf(reference: AssetReference): MentionChipCitation | undefined {\n const { anchor, meta } = reference;\n if (!anchor) return undefined;\n const label = formatReferenceAnchor(anchor, \"short\", meta);\n if (!label) return undefined;\n const { excerpt: _excerpt, ...place } = meta ?? {};\n return {\n anchorType: anchor.type,\n label,\n description: formatReferenceAnchor(anchor, \"long\", place) ?? label,\n };\n}\n\n/**\n * The chip's icon for an asset type when the host serves no asset-type SVGs.\n * A compact set on purpose: the chip renders in every message (and Olympus's\n * main layout reaches it through the queued-message tray), so it carries a\n * dozen icons rather than the `@` menu's whole map.\n */\nconst ASSET_CHIP_ICONS: Record<string, LucideIcon> = {\n document: FileTextIcon,\n word_document: FileTextIcon,\n markdown: FileTextIcon,\n txt: FileTextIcon,\n pdf: FileTextIcon,\n spreadsheet: SheetIcon,\n table: SheetIcon,\n presentation: PresentationIcon,\n powerpoint_deck: PresentationIcon,\n presentation_pptx_studio: PresentationIcon,\n pptx: PresentationIcon,\n image: ImageIcon,\n figure: ImageIcon,\n canvas: ImageIcon,\n video: VideoIcon,\n meeting: VideoIcon,\n video_session: VideoIcon,\n audio: HeadphonesIcon,\n voice_memo: HeadphonesIcon,\n phone_call: HeadphonesIcon,\n email: MailIcon,\n email_message: MailIcon,\n live_email_thread: MailIcon,\n live_calendar_event: MailIcon,\n python: FileCodeIcon,\n javascript: FileCodeIcon,\n typescript: FileCodeIcon,\n json: FileCodeIcon,\n yaml: FileCodeIcon,\n database: DatabaseIcon,\n catalog: DatabaseIcon,\n drive: DatabaseIcon,\n dataset: DatabaseIcon,\n semantic_model: DatabaseIcon,\n query_snippet: DatabaseIcon,\n folder: FolderIcon,\n gdrive_folder: FolderIcon,\n project: FolderIcon,\n collection: FolderIcon,\n session: MessageSquareIcon,\n computer: MonitorIcon,\n desktop_session: MonitorIcon,\n user_interface: MonitorIcon,\n application: MonitorIcon,\n html: Globe2Icon,\n web_page: Globe2Icon,\n browse_session: Globe2Icon,\n};\n\nfunction assetChipIcon(assetType: string | undefined): LucideIcon {\n return (assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()]) || FileIcon;\n}\n\n/**\n * GitHub's mark (Octicons `mark-github`), drawn by a repository file's\n * citation. Lucide carries no brand icons and an SDK app serves none of\n * Olympus's, so the chip brings its own. Only a GitHub repository catalog\n * serves `file_line_range` anchors (`ASSET_ANCHOR_REGISTRY`; agora's\n * `_CATALOG_ANCHORS_BY_PROVIDER`), so the anchor alone names the provider.\n */\nfunction GitHubMarkIcon({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n className={className}\n data-chip-icon=\"github\"\n >\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z\" />\n </svg>\n );\n}\n\n/** Past this many characters a place may not show whole in the chip, so the tooltip spells it out. */\nconst PLACE_FITS_CHARS = 20;\n\n/** `route.ts:18-24` as the file's name and its lines, so a long name shortens while its lines stay. */\nfunction splitFileLines(label: string): { file: string; lines: string } {\n const colon = label.lastIndexOf(\":\");\n return colon > 0\n ? { file: label.slice(0, colon), lines: label.slice(colon) }\n : { file: label, lines: \"\" };\n}\n\n/**\n * Whether a name already says where its citation points: the place's label or\n * its spelled-out form, or a name ending in the label (`Q4 report p.5`,\n * `olympus/…/route.ts:18-24`). The chip then draws the place once.\n */\nfunction restatesPlace(name: string, citation: MentionChipCitation): boolean {\n const said = name.trim().toLowerCase();\n const label = citation.label.toLowerCase();\n return said === label || said === citation.description.toLowerCase() || said.endsWith(label);\n}\n\n/**\n * The link's own label when it tells the reader something the chip does not\n * already show: not the place again (a path, `file:lines`), not the asset's\n * name. The tooltip keeps it, since the chip shows the asset and the place.\n */\nfunction informativeLabel({\n label,\n shown,\n citation,\n}: {\n label: string;\n shown: readonly (string | null | undefined)[];\n citation: MentionChipCitation | undefined;\n}): string | null {\n const trimmed = label.trim();\n if (!trimmed || !SPEAKABLE.test(trimmed)) return null;\n if (PLACEHOLDER_LABELS.has(trimmed.toLowerCase())) return null;\n if (citation && restatesPlace(trimmed, citation)) return null;\n // Every word of it already shown (`Tesla Q3 report` beside `Tesla Q3\n // Earnings Report`, `demo-app-monorepo` beside the repository's title).\n const shownWords = new Set(\n [...shown, citation?.description].flatMap((text) => (text ? wordsOf(text) : [])),\n );\n return wordsOf(trimmed).every((word) => shownWords.has(word)) ? null : trimmed;\n}\n\nfunction wordsOf(text: string): string[] {\n return text.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? [];\n}\n\n/** A label with a letter or digit in it; `§` or `•` alone says nothing read aloud. */\nconst SPEAKABLE = /[\\p{L}\\p{N}]/u;\n\n/** Labels that name nothing: agora's read_asset citations all read `Citation`. */\nconst PLACEHOLDER_LABELS: ReadonlySet<string> = new Set([\"citation\", \"source\", \"link\", \"here\"]);\n\n/** The asset reference an asset link decodes to, or null when it does not parse. */\nfunction decodeAssetLink(\n url: URL,\n decode: (url: URL) => AssetReference,\n): AssetReference | null {\n try {\n return decode(url);\n } catch {\n return null;\n }\n}\n\n// ── Mention link component for markdown renderer ──\n\n/**\n * Custom <a> renderer for MarkdownTextPrimitive.\n * Intercepts mention:// links and renders them as chips.\n * Falls through to a normal link for non-mention URLs.\n */\nfunction personEmailForMention(\n parsed: ReturnType<typeof parseMentionUrl>,\n): string | undefined {\n if (parsed.type !== \"person\") return undefined;\n return parsed.email || (parsed.id.includes(\"@\") ? parsed.id : undefined);\n}\n\n/**\n * The text a link's children read as. Markdown can hand a link several children, some of them\n * elements (an escaped `_` or a `*` in a URL splits its text), and stringifying an element gives\n * `[object Object]`.\n */\nfunction textOf(node: ReactNode): string {\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (isValidElement<{ children?: ReactNode }>(node)) return textOf(node.props.children);\n return \"\";\n}\n\nexport const MentionLink: FC<{\n href?: string;\n children?: ReactNode;\n interactive?: boolean;\n}> = ({ href, children, interactive = true }) => {\n if (\n href &&\n (href.startsWith(\"mention://\") || isSpacesMentionUrl(href))\n ) {\n const name = textOf(children).trim().replace(/^@/, \"\");\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : parsed.sourceHref}\n interactive={interactive}\n />\n );\n }\n\n if (href?.startsWith(\"chrome-extension://\")) {\n return <span>{children}</span>;\n }\n\n if (isScreenPointHref(href)) {\n return <ScreenPointChip href={href}>{children}</ScreenPointChip>;\n }\n\n // Normal link — render as-is\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n};\n\n// ── The chip pill ──\n\n/**\n * Olympus's mention pill (`mentionPillClassName`: 20px high, 16px icon, the\n * place in `text-xs`) in the provider's theme tokens, so a dark theme keeps it\n * legible, and `box-border` so its height holds without a host's CSS reset.\n * Every chat chip draws it: an asset or citation chip, a web source, a screen\n * point. Only stock Tailwind sizes: Olympus renders these chips (the queued\n * message tray) with a stylesheet that does not scan this package, so an\n * arbitrary value would reach it unstyled. `not-prose` matters: assistant\n * messages render inside Tailwind Typography's `prose`, whose\n * zero-specificity `img` margins (~1.7em top/bottom) would otherwise inflate\n * the chip's icon into a tall card that breaks the sentence flow around it.\n */\nexport const CHIP_PILL_CLASS =\n \"not-prose box-border inline-flex h-5 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-muted px-1 py-0.5 align-middle font-normal leading-none text-foreground no-underline\";\n/** A chip that opens something. */\nexport const CHIP_LINK_CLASS =\n \"cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\";\nexport const CHIP_ICON_CLASS = \"size-4 shrink-0\";\n\n// ── Inline screen-point chip ──\n\n/**\n * Renders an `athena-point://` link as a non-navigating coordinate chip.\n * Surfaces without screenshot context (extension side panel, SDK consumers)\n * get the label plus the coordinates; malformed points fall back to text.\n */\nexport const ScreenPointChip: FC<{\n href: string;\n children?: ReactNode;\n}> = ({ href, children }) => {\n const parsed = parseScreenPointHref(href);\n if (!parsed.ok) {\n return <span>{children}</span>;\n }\n const description = formatScreenPoint(parsed.point);\n return (\n <span\n className={cn(CHIP_PILL_CLASS, \"max-w-[260px]\")}\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n <span className=\"min-w-0 max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">\n {parsed.point.x}, {parsed.point.y}\n </span>\n </span>\n );\n};\n\n// ── Inline mention chip ──\n\nexport const MentionChip: FC<{\n name: string;\n type?: string;\n assetIds?: string[];\n /** The asset's type (`document`, `catalog`, …): picks the icon until (or unless) the asset is looked up. */\n assetType?: string;\n /** Where in the asset the link points: its label follows the name. */\n citation?: MentionChipCitation;\n personEmail?: string;\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({\n name,\n type,\n assetIds,\n assetType,\n citation,\n personEmail,\n thumbnailUrl,\n sourceHref,\n interactive = true,\n}) => {\n const auth = useContext(MentionAuthCtx);\n const memberAvatarUrl = useMemberAvatarUrl(personEmail);\n const [erroredAvatarUrl, setErroredAvatarUrl] = useState<string>();\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n // A repository file's lines read as the file (`route.ts:18-24`): the chip\n // opens that file, and every citation in a reply names the same repository,\n // whatever label the model wrote.\n const repositoryFile =\n citation?.anchorType === \"file_line_range\"\n ? { ...splitFileLines(citation.label), label: citation.label }\n : null;\n // The asset as agora has it, read the way Olympus's chips read it: its own\n // title and type. Until it answers, and for an asset the viewer cannot read,\n // the link's label and `type=` stand in. Not asked for a repository file,\n // which reads as its file whatever the catalog is called.\n const asset = useMentionAsset(\n type === \"asset\" && assetIds?.length === 1 && !repositoryFile ? assetIds[0] : undefined,\n );\n const assetTitle = asset?.title || null;\n const chipName = repositoryFile?.label ?? assetTitle ?? name;\n // The place follows a name that does not already say it, as `[p.5]`.\n const placeLabel =\n citation && !repositoryFile && !restatesPlace(chipName, citation)\n ? citation.label\n : null;\n // The tooltip: what the model called it (when the chip does not show it),\n // the repository a file is in and the file's whole path, the cited passage.\n // The place spelled out stands in for a passage the citation does not carry,\n // and for a place too long to show whole (a long sheet name).\n const tooltip =\n citation || excerpt\n ? tooltipText({\n lines: [\n informativeLabel({ label: name, shown: [chipName, assetTitle], citation }),\n repositoryFile ? assetTitle : null,\n repositoryFile || !excerpt || (placeLabel?.length ?? 0) > PLACE_FITS_CHARS\n ? citation?.description\n : null,\n ],\n excerpt,\n })\n : null;\n const assetRootHref =\n type === \"asset\" && assetIds?.length\n ? createOlympusAssetUrl({\n appUrl: auth?.appUrl,\n backendUrl: auth?.backendUrl,\n assetIds,\n })\n : null;\n const href = (() => {\n if (!assetRootHref || !sourceHref) return assetRootHref;\n try {\n const source = new URL(sourceHref, \"https://app.athenaintel.com\");\n const destination = new URL(assetRootHref);\n for (const [key, value] of source.searchParams) {\n destination.searchParams.set(key, value);\n }\n destination.searchParams.set(\"asset_ids\", assetIds?.join(\",\") ?? \"\");\n return destination.toString();\n } catch {\n return assetRootHref;\n }\n })();\n const className = cn(CHIP_PILL_CLASS, \"max-w-[275px]\", href && CHIP_LINK_CLASS);\n // A host's asset-type SVG follows the looked-up type, when it serves them.\n const chipThumbnailUrl = repositoryFile\n ? undefined\n : (asset?.assetType && assetTypeToThumb(asset.assetType)) || thumbnailUrl;\n const AssetIcon = assetChipIcon(asset?.assetType || assetType);\n const contents = (\n <>\n {repositoryFile ? (\n <GitHubMarkIcon className={CHIP_ICON_CLASS} />\n ) : chipThumbnailUrl ? (\n <img\n src={chipThumbnailUrl}\n alt=\"\"\n className={CHIP_ICON_CLASS}\n draggable={false}\n />\n ) : type === \"asset\" || type === \"folder\" ? (\n // A host that bundles no asset-type SVGs (computer apps, most SDK\n // apps) still gets the type's icon.\n <AssetIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"webpage\" ? (\n <Globe2Icon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"person\" ? (\n memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? (\n <img\n src={memberAvatarUrl}\n alt=\"\"\n className={cn(CHIP_ICON_CLASS, \"rounded-full object-cover\")}\n draggable={false}\n onError={() => setErroredAvatarUrl(memberAvatarUrl)}\n />\n ) : (\n <UserIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n )\n ) : null}\n {repositoryFile ? (\n <span\n className=\"flex min-w-0 items-center\"\n data-citation-anchor=\"file_line_range\"\n >\n <span className=\"min-w-0 overflow-hidden text-ellipsis\">\n {repositoryFile.file}\n </span>\n <span className=\"shrink-0 text-muted-foreground\">{repositoryFile.lines}</span>\n </span>\n ) : (\n <span\n className={cn(\n \"max-w-[180px] overflow-hidden text-ellipsis\",\n // A long place may shorten the name, never make it vanish.\n placeLabel && \"min-w-8\",\n )}\n >\n {chipName}\n </span>\n )}\n {citation && placeLabel && (\n <>\n {/* Copied or read as text, the place stays a word apart from the name; a flex row draws no space. */}{\" \"}\n <span\n className=\"flex min-w-0 items-center text-xs text-muted-foreground\"\n data-citation-anchor={citation.anchorType}\n >\n [<span className=\"min-w-0 overflow-hidden text-ellipsis\">{placeLabel}</span>]\n </span>\n </>\n )}\n </>\n );\n\n if (href && interactive) {\n const speakablePlace = citation && SPEAKABLE.test(citation.description);\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={\n repositoryFile && citation\n ? `Open ${citation.description} in Spaces`\n : placeLabel && citation && speakablePlace\n ? `Open ${chipName} at ${citation.description} in Spaces`\n : `Open ${chipName} in Spaces`\n }\n title={tooltip ?? `Open ${chipName} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n // The asset without its place: what a click names the opened asset.\n data-mention-name={repositoryFile ? (assetTitle ?? name) : chipName}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={tooltip ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\n};\n\n/**\n * A chip's tooltip: the lines that say something, each once, then the cited\n * passage, quoted when it shares the tooltip with other lines.\n */\nfunction tooltipText({\n lines,\n excerpt,\n}: {\n lines: readonly (string | null | undefined)[];\n excerpt: string | null;\n}): string | null {\n const kept: string[] = [];\n for (const line of lines) {\n const trimmed = line?.trim();\n if (trimmed && !kept.includes(trimmed)) kept.push(trimmed);\n }\n if (excerpt) kept.push(kept.length ? `“${excerpt}”` : excerpt);\n return kept.length ? kept.join(\"\\n\") : null;\n}\n\n/** The `excerpt` a Spaces citation URL carries, or null. */\nfunction readSourceExcerpt(href: string): string | null {\n try {\n const excerpt = new URL(href, \"https://app.athenaintel.com\").searchParams\n .get(\"excerpt\")\n ?.trim();\n return excerpt ? excerpt : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The label an asset citation chip falls back to when the model wrote none\n * (`[](…/spaces/?asset_ids=…&page=5)`): the cited location, else \"Asset\" — a\n * chip must never render as an empty box.\n */\nfunction citationFallbackLabel(params: URLSearchParams): string {\n const start = params.get(\"start_page\") ?? params.get(\"page\");\n const end = params.get(\"end_page\");\n if (start && end && end !== start) return `Pages ${start}–${end}`;\n if (start) return `Page ${start}`;\n return \"Asset\";\n}\n\n// ── Parse mention:// URLs ──\n\nexport function parseMentionUrl(url: string): {\n type: string;\n name: string;\n id: string;\n email?: string;\n assetIds?: string[];\n assetType?: string;\n citation?: MentionChipCitation;\n /**\n * For a `mention://asset` citation, the Spaces link to its place (anchor and\n * excerpt), so the chip opens there; a Spaces mention is its own link.\n */\n sourceHref?: string;\n thumbnailUrl?: string;\n} {\n try {\n if (isSpacesMentionUrl(url)) {\n const parsed = new URL(url, \"https://app.athenaintel.com\");\n const assetIds = (parsed.searchParams.get(\"asset_ids\") ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const id = assetIds[0] ?? \"\";\n const assetType = getSpacesAssetType(parsed);\n // A link to several assets names no one place in any of them.\n const reference =\n assetIds.length === 1\n ? decodeAssetLink(parsed, decodeFromAssetSpacesUrl)\n : null;\n const citation = reference ? citationOf(reference) : undefined;\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : (citation?.description ??\n citationFallbackLabel(parsed.searchParams)),\n id,\n assetIds,\n assetType: assetType ?? undefined,\n citation,\n thumbnailUrl: assetTypeToThumb(assetType),\n };\n }\n // mention://tool?name=...&description=...\n // mention://asset?id=...&assetType=project&thumb=../icons/files/ic-project.svg\n const u = new URL(url);\n const type = u.hostname || u.pathname.replace(/^\\/\\//, \"\").split(\"?\")[0];\n const isWebpage = type === \"webpage\";\n const id = isWebpage\n ? u.searchParams.get(\"tab_id\") || \"\"\n : u.searchParams.get(\"id\") || u.searchParams.get(\"name\") || \"\";\n const name = isWebpage\n ? u.searchParams.get(\"title\") || \"This webpage\"\n : u.searchParams.get(\"name\") || id;\n\n // Get thumbnail: prefer explicit thumb param, then derive from assetType\n let thumbnailUrl = u.searchParams.get(\"thumb\") || undefined;\n if (!thumbnailUrl) {\n const assetType = u.searchParams.get(\"assetType\");\n if (assetType) {\n thumbnailUrl = assetTypeToThumb(assetType);\n } else if (type === \"asset\" || type === \"folder\") {\n thumbnailUrl = assetTypeToThumb(type === \"folder\" ? \"folder\" : null);\n }\n }\n\n const reference =\n type === \"asset\" ? decodeAssetLink(u, decodeFromAssetMentionUri) : null;\n\n return {\n type,\n name,\n id,\n email: type === \"person\" ? u.searchParams.get(\"email\") || undefined : undefined,\n assetIds: type === \"asset\" && id ? [id] : undefined,\n assetType:\n type === \"asset\" || type === \"folder\"\n ? u.searchParams.get(\"assetType\") ||\n reference?.meta?.assetType ||\n (type === \"folder\" ? \"folder\" : undefined)\n : undefined,\n citation: reference ? citationOf(reference) : undefined,\n sourceHref:\n reference && (reference.anchor || reference.meta?.excerpt)\n ? encodeToAssetSpacesUrl(reference)\n : undefined,\n thumbnailUrl,\n };\n } catch {\n return { type: \"unknown\", name: \"\", id: \"\" };\n }\n}\n\nfunction htmlAttribute(attributes: string, name: string): string | null {\n const escapedName = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const match = new RegExp(\n `(?:^|\\\\s)${escapedName}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`,\n \"i\",\n ).exec(attributes);\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n return value\n .replace(/&amp;/gi, \"&\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/gi, \"'\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\");\n}\n\n/** Turn complete model-emitted URL citation anchors into safe Markdown links. */\nexport function normalizeAthenaMentionHtml(text: string): string {\n return text.replace(\n /<a\\b([^>]*)>([\\s\\S]*?)<\\/a>/gi,\n (original, attributes: string, rawLabel: string) => {\n const className = htmlAttribute(attributes, \"class\") ?? \"\";\n const type = htmlAttribute(attributes, \"data-type\") ?? \"\";\n if (!className.split(/\\s+/).includes(\"mention\") || type !== \"url\") {\n return original;\n }\n\n const rawUrl =\n htmlAttribute(attributes, \"data-url\") ??\n htmlAttribute(attributes, \"href\");\n if (!rawUrl) return original;\n const url = decodeHtmlAttribute(rawUrl);\n let normalizedUrl: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return original;\n }\n normalizedUrl = parsed.toString();\n } catch {\n return original;\n }\n\n const label = decodeHtmlAttribute(rawLabel.replace(/<[^>]*>/g, \"\"))\n .trim()\n .replace(/([\\\\[\\]])/g, \"\\\\$1\");\n return label ? `[${label}](<${normalizedUrl}>)` : original;\n },\n );\n}\n// ── Parse mention markdown in plain text ──\n\n// The label may hold balanced brackets one level deep (`[@Plan [Q3]](…)`) or a lone `[`.\nconst MENTION_RE =\n /\\[@((?:\\[[^[\\]]*\\](?!\\((?:[a-z][a-z0-9+.-]*:\\/\\/|\\/))|[^\\]])*)\\]\\(((?:mention:\\/\\/|(?:https?:\\/\\/[^)\\s]+)?\\/dashboard\\/spaces\\/?\\?)[^)]+)\\)/g;\n\n/**\n * Parse text containing [@name](mention://...) patterns into\n * an array of string segments and MentionChip elements.\n */\nexport function parseMentionsInText(text: string): (string | ReactElement)[] {\n const parts: (string | ReactElement)[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n MENTION_RE.lastIndex = 0;\n\n while ((match = MENTION_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n\n const name = match[1];\n const url = match[2];\n const parsed = parseMentionUrl(url);\n\n parts.push(\n <MentionChip\n key={`mention-${match.index}`}\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : parsed.sourceHref}\n />\n );\n\n lastIndex = match.index + match[0].length;\n }\n\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return parts.length > 0 ? parts : [text];\n}\n\n/** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */\nexport function mentionsToPlainText(text: string): string {\n return text.replace(MENTION_RE, (_match, name: string, url: string) => {\n return `@${name || parseMentionUrl(url).name}`;\n });\n}\n"],"names":["formatReferenceAnchor","FileTextIcon","SheetIcon","PresentationIcon","ImageIcon","VideoIcon","HeadphonesIcon","MailIcon","FileCodeIcon","DatabaseIcon","FolderIcon","MessageSquareIcon","MonitorIcon","Globe2Icon","FileIcon","jsx","isValidElement","isScreenPointHref","parseScreenPointHref","formatScreenPoint","jsxs","cn","CrosshairIcon","useContext","MentionAuthCtx","useMemberAvatarUrl","useState","useMentionAsset","createOlympusAssetUrl","assetTypeToThumb","Fragment","UserIcon","id","reference","decodeFromAssetSpacesUrl","decodeFromAssetMentionUri","encodeToAssetSpacesUrl"],"mappings":"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"}
@@ -96,17 +96,6 @@ function GitHubMarkIcon({ className }) {
96
96
  }
97
97
  );
98
98
  }
99
- const SESSION_ANCHORS = /* @__PURE__ */ new Set([
100
- "msg",
101
- "msg_range",
102
- "msg_block",
103
- "msg_section",
104
- "msg_text",
105
- "tool_call"
106
- ]);
107
- function isSessionLink(assetType, citation) {
108
- return (assetType == null ? void 0 : assetType.toLowerCase()) === "session" || citation !== void 0 && SESSION_ANCHORS.has(citation.anchorType);
109
- }
110
99
  const PLACE_FITS_CHARS = 20;
111
100
  function splitFileLines(label) {
112
101
  const colon = label.lastIndexOf(":");
@@ -225,7 +214,7 @@ const MentionChip = ({
225
214
  const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
226
215
  const repositoryFile = (citation == null ? void 0 : citation.anchorType) === "file_line_range" ? { ...splitFileLines(citation.label), label: citation.label } : null;
227
216
  const asset = useMentionAsset(
228
- type === "asset" && (assetIds == null ? void 0 : assetIds.length) === 1 && !repositoryFile && !isSessionLink(assetType, citation) ? assetIds[0] : void 0
217
+ type === "asset" && (assetIds == null ? void 0 : assetIds.length) === 1 && !repositoryFile ? assetIds[0] : void 0
229
218
  );
230
219
  const assetTitle = (asset == null ? void 0 : asset.title) || null;
231
220
  const chipName = (repositoryFile == null ? void 0 : repositoryFile.label) ?? assetTitle ?? name;
@@ -1 +1 @@
1
- {"version":3,"file":"mention-chip.js","sources":["../../../src/chat-ui/mentions/mention-chip.tsx"],"sourcesContent":["/**\n * Mention chip rendering for message display.\n *\n * Parses [@name](mention://type?...) markdown links and renders them\n * as styled inline chips matching the Olympus mention style.\n * Used in both user messages (inline text) and assistant messages (markdown links).\n */\n\nimport {\n isValidElement,\n useContext,\n useState,\n type FC,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport {\n type AssetReference,\n decodeFromAssetMentionUri,\n decodeFromAssetSpacesUrl,\n encodeToAssetSpacesUrl,\n formatReferenceAnchor,\n} from \"@athenaintel/references/asset-links\";\nimport {\n CrosshairIcon,\n DatabaseIcon,\n FileCodeIcon,\n FileIcon,\n FileTextIcon,\n FolderIcon,\n Globe2Icon,\n HeadphonesIcon,\n ImageIcon,\n type LucideIcon,\n MailIcon,\n MessageSquareIcon,\n MonitorIcon,\n PresentationIcon,\n SheetIcon,\n UserIcon,\n VideoIcon,\n} from \"lucide-react\";\nimport { cn } from \"../../lib/utils\";\nimport { createOlympusAssetUrl } from \"../lib/asset-urls\";\nimport { MentionAuthCtx } from \"./suggestions/data/api-client\";\nimport { assetTypeToThumb } from \"./suggestions/data/sources/asset-type-icons\";\nimport { useMemberAvatarUrl } from \"./use-member-avatar\";\nimport { useMentionAsset } from \"./use-mention-asset\";\n\nconst SPACES_MENTION_PATH = /\\/dashboard\\/spaces\\/?\\?/;\n\nfunction isSpacesMentionUrl(url: string): boolean {\n return SPACES_MENTION_PATH.test(url);\n}\n\nfunction getSpacesAssetType(url: URL): string | null {\n const assetType =\n url.searchParams.get(\"assetType\") ||\n url.searchParams.get(\"asset_type\") ||\n url.searchParams.get(\"type\");\n return assetType === \"sheet\" ? \"spreadsheet\" : assetType;\n}\n\n/**\n * Where in the asset a citation points, as its chip shows it. The anchor is\n * read and labelled by `@athenaintel/references` — the same parser and labels\n * Olympus's mention chips use — so a chip reads `jwt.py:12-25`, `p.5` or\n * `Slide 3` in every Athena chat.\n */\nexport interface MentionChipCitation {\n /** The anchor type (`page`, `file_line_range`, `slide`, …). */\n anchorType: string;\n /** The short label drawn after the name (`p.5`, `jwt.py:12-25`). */\n label: string;\n /**\n * The place spelled out (`Page 5`, `agora/…/jwt.py:12-25`), without the\n * excerpt: the tooltip when the link carries no excerpt, and part of the\n * chip's accessible name when it holds a word.\n */\n description: string;\n}\n\n/** The citation an asset link's anchor names, or undefined for a plain mention or an anchor nobody can read. */\nfunction citationOf(reference: AssetReference): MentionChipCitation | undefined {\n const { anchor, meta } = reference;\n if (!anchor) return undefined;\n const label = formatReferenceAnchor(anchor, \"short\", meta);\n if (!label) return undefined;\n const { excerpt: _excerpt, ...place } = meta ?? {};\n return {\n anchorType: anchor.type,\n label,\n description: formatReferenceAnchor(anchor, \"long\", place) ?? label,\n };\n}\n\n/**\n * The chip's icon for an asset type when the host serves no asset-type SVGs.\n * A compact set on purpose: the chip renders in every message (and Olympus's\n * main layout reaches it through the queued-message tray), so it carries a\n * dozen icons rather than the `@` menu's whole map.\n */\nconst ASSET_CHIP_ICONS: Record<string, LucideIcon> = {\n document: FileTextIcon,\n word_document: FileTextIcon,\n markdown: FileTextIcon,\n txt: FileTextIcon,\n pdf: FileTextIcon,\n spreadsheet: SheetIcon,\n table: SheetIcon,\n presentation: PresentationIcon,\n powerpoint_deck: PresentationIcon,\n presentation_pptx_studio: PresentationIcon,\n pptx: PresentationIcon,\n image: ImageIcon,\n figure: ImageIcon,\n canvas: ImageIcon,\n video: VideoIcon,\n meeting: VideoIcon,\n video_session: VideoIcon,\n audio: HeadphonesIcon,\n voice_memo: HeadphonesIcon,\n phone_call: HeadphonesIcon,\n email: MailIcon,\n email_message: MailIcon,\n live_email_thread: MailIcon,\n live_calendar_event: MailIcon,\n python: FileCodeIcon,\n javascript: FileCodeIcon,\n typescript: FileCodeIcon,\n json: FileCodeIcon,\n yaml: FileCodeIcon,\n database: DatabaseIcon,\n catalog: DatabaseIcon,\n drive: DatabaseIcon,\n dataset: DatabaseIcon,\n semantic_model: DatabaseIcon,\n query_snippet: DatabaseIcon,\n folder: FolderIcon,\n gdrive_folder: FolderIcon,\n project: FolderIcon,\n collection: FolderIcon,\n session: MessageSquareIcon,\n computer: MonitorIcon,\n desktop_session: MonitorIcon,\n user_interface: MonitorIcon,\n application: MonitorIcon,\n html: Globe2Icon,\n web_page: Globe2Icon,\n browse_session: Globe2Icon,\n};\n\nfunction assetChipIcon(assetType: string | undefined): LucideIcon {\n return (assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()]) || FileIcon;\n}\n\n/**\n * GitHub's mark (Octicons `mark-github`), drawn by a repository file's\n * citation. Lucide carries no brand icons and an SDK app serves none of\n * Olympus's, so the chip brings its own. Only a GitHub repository catalog\n * serves `file_line_range` anchors (`ASSET_ANCHOR_REGISTRY`; agora's\n * `_CATALOG_ANCHORS_BY_PROVIDER`), so the anchor alone names the provider.\n */\nfunction GitHubMarkIcon({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n className={className}\n data-chip-icon=\"github\"\n >\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z\" />\n </svg>\n );\n}\n\n/** Anchors only a session holds: its messages, blocks and tool calls. */\nconst SESSION_ANCHORS: ReadonlySet<string> = new Set([\n \"msg\",\n \"msg_range\",\n \"msg_block\",\n \"msg_section\",\n \"msg_text\",\n \"tool_call\",\n]);\n\n/** Whether a link names a session (its type, or an anchor only a session holds). */\nfunction isSessionLink(\n assetType: string | undefined,\n citation: MentionChipCitation | undefined,\n): boolean {\n return (\n assetType?.toLowerCase() === \"session\" ||\n (citation !== undefined && SESSION_ANCHORS.has(citation.anchorType))\n );\n}\n\n/** Past this many characters a place may not show whole in the chip, so the tooltip spells it out. */\nconst PLACE_FITS_CHARS = 20;\n\n/** `route.ts:18-24` as the file's name and its lines, so a long name shortens while its lines stay. */\nfunction splitFileLines(label: string): { file: string; lines: string } {\n const colon = label.lastIndexOf(\":\");\n return colon > 0\n ? { file: label.slice(0, colon), lines: label.slice(colon) }\n : { file: label, lines: \"\" };\n}\n\n/**\n * Whether a name already says where its citation points: the place's label or\n * its spelled-out form, or a name ending in the label (`Q4 report p.5`,\n * `olympus/…/route.ts:18-24`). The chip then draws the place once.\n */\nfunction restatesPlace(name: string, citation: MentionChipCitation): boolean {\n const said = name.trim().toLowerCase();\n const label = citation.label.toLowerCase();\n return said === label || said === citation.description.toLowerCase() || said.endsWith(label);\n}\n\n/**\n * The link's own label when it tells the reader something the chip does not\n * already show: not the place again (a path, `file:lines`), not the asset's\n * name. The tooltip keeps it, since the chip shows the asset and the place.\n */\nfunction informativeLabel({\n label,\n shown,\n citation,\n}: {\n label: string;\n shown: readonly (string | null | undefined)[];\n citation: MentionChipCitation | undefined;\n}): string | null {\n const trimmed = label.trim();\n if (!trimmed || !SPEAKABLE.test(trimmed)) return null;\n if (PLACEHOLDER_LABELS.has(trimmed.toLowerCase())) return null;\n if (citation && restatesPlace(trimmed, citation)) return null;\n // Every word of it already shown (`Tesla Q3 report` beside `Tesla Q3\n // Earnings Report`, `demo-app-monorepo` beside the repository's title).\n const shownWords = new Set(\n [...shown, citation?.description].flatMap((text) => (text ? wordsOf(text) : [])),\n );\n return wordsOf(trimmed).every((word) => shownWords.has(word)) ? null : trimmed;\n}\n\nfunction wordsOf(text: string): string[] {\n return text.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? [];\n}\n\n/** A label with a letter or digit in it; `§` or `•` alone says nothing read aloud. */\nconst SPEAKABLE = /[\\p{L}\\p{N}]/u;\n\n/** Labels that name nothing: agora's read_asset citations all read `Citation`. */\nconst PLACEHOLDER_LABELS: ReadonlySet<string> = new Set([\"citation\", \"source\", \"link\", \"here\"]);\n\n/** The asset reference an asset link decodes to, or null when it does not parse. */\nfunction decodeAssetLink(\n url: URL,\n decode: (url: URL) => AssetReference,\n): AssetReference | null {\n try {\n return decode(url);\n } catch {\n return null;\n }\n}\n\n// ── Mention link component for markdown renderer ──\n\n/**\n * Custom <a> renderer for MarkdownTextPrimitive.\n * Intercepts mention:// links and renders them as chips.\n * Falls through to a normal link for non-mention URLs.\n */\nfunction personEmailForMention(\n parsed: ReturnType<typeof parseMentionUrl>,\n): string | undefined {\n if (parsed.type !== \"person\") return undefined;\n return parsed.email || (parsed.id.includes(\"@\") ? parsed.id : undefined);\n}\n\n/**\n * The text a link's children read as. Markdown can hand a link several children, some of them\n * elements (an escaped `_` or a `*` in a URL splits its text), and stringifying an element gives\n * `[object Object]`.\n */\nfunction textOf(node: ReactNode): string {\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (isValidElement<{ children?: ReactNode }>(node)) return textOf(node.props.children);\n return \"\";\n}\n\nexport const MentionLink: FC<{\n href?: string;\n children?: ReactNode;\n interactive?: boolean;\n}> = ({ href, children, interactive = true }) => {\n if (\n href &&\n (href.startsWith(\"mention://\") || isSpacesMentionUrl(href))\n ) {\n const name = textOf(children).trim().replace(/^@/, \"\");\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : parsed.sourceHref}\n interactive={interactive}\n />\n );\n }\n\n if (href?.startsWith(\"chrome-extension://\")) {\n return <span>{children}</span>;\n }\n\n if (isScreenPointHref(href)) {\n return <ScreenPointChip href={href}>{children}</ScreenPointChip>;\n }\n\n // Normal link — render as-is\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n};\n\n// ── The chip pill ──\n\n/**\n * Olympus's mention pill (`mentionPillClassName`: 20px high, 16px icon, the\n * place in `text-xs`) in the provider's theme tokens, so a dark theme keeps it\n * legible, and `box-border` so its height holds without a host's CSS reset.\n * Every chat chip draws it: an asset or citation chip, a web source, a screen\n * point. Only stock Tailwind sizes: Olympus renders these chips (the queued\n * message tray) with a stylesheet that does not scan this package, so an\n * arbitrary value would reach it unstyled. `not-prose` matters: assistant\n * messages render inside Tailwind Typography's `prose`, whose\n * zero-specificity `img` margins (~1.7em top/bottom) would otherwise inflate\n * the chip's icon into a tall card that breaks the sentence flow around it.\n */\nexport const CHIP_PILL_CLASS =\n \"not-prose box-border inline-flex h-5 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-muted px-1 py-0.5 align-middle font-normal leading-none text-foreground no-underline\";\n/** A chip that opens something. */\nexport const CHIP_LINK_CLASS =\n \"cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\";\nexport const CHIP_ICON_CLASS = \"size-4 shrink-0\";\n\n// ── Inline screen-point chip ──\n\n/**\n * Renders an `athena-point://` link as a non-navigating coordinate chip.\n * Surfaces without screenshot context (extension side panel, SDK consumers)\n * get the label plus the coordinates; malformed points fall back to text.\n */\nexport const ScreenPointChip: FC<{\n href: string;\n children?: ReactNode;\n}> = ({ href, children }) => {\n const parsed = parseScreenPointHref(href);\n if (!parsed.ok) {\n return <span>{children}</span>;\n }\n const description = formatScreenPoint(parsed.point);\n return (\n <span\n className={cn(CHIP_PILL_CLASS, \"max-w-[260px]\")}\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n <span className=\"min-w-0 max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">\n {parsed.point.x}, {parsed.point.y}\n </span>\n </span>\n );\n};\n\n// ── Inline mention chip ──\n\nexport const MentionChip: FC<{\n name: string;\n type?: string;\n assetIds?: string[];\n /** The asset's type (`document`, `catalog`, …): picks the icon until (or unless) the asset is looked up. */\n assetType?: string;\n /** Where in the asset the link points: its label follows the name. */\n citation?: MentionChipCitation;\n personEmail?: string;\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({\n name,\n type,\n assetIds,\n assetType,\n citation,\n personEmail,\n thumbnailUrl,\n sourceHref,\n interactive = true,\n}) => {\n const auth = useContext(MentionAuthCtx);\n const memberAvatarUrl = useMemberAvatarUrl(personEmail);\n const [erroredAvatarUrl, setErroredAvatarUrl] = useState<string>();\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n // A repository file's lines read as the file (`route.ts:18-24`): the chip\n // opens that file, and every citation in a reply names the same repository,\n // whatever label the model wrote.\n const repositoryFile =\n citation?.anchorType === \"file_line_range\"\n ? { ...splitFileLines(citation.label), label: citation.label }\n : null;\n // The asset as agora has it, read the way Olympus's chips read it: its own\n // title and type. Until it answers, and for an asset the viewer cannot read,\n // the link's label and `type=` stand in. Asked only where it changes the\n // chip: a repository file reads as its file whatever the catalog is called,\n // and a session's row is too heavy to read for a name.\n const asset = useMentionAsset(\n type === \"asset\" &&\n assetIds?.length === 1 &&\n !repositoryFile &&\n !isSessionLink(assetType, citation)\n ? assetIds[0]\n : undefined,\n );\n const assetTitle = asset?.title || null;\n const chipName = repositoryFile?.label ?? assetTitle ?? name;\n // The place follows a name that does not already say it, as `[p.5]`.\n const placeLabel =\n citation && !repositoryFile && !restatesPlace(chipName, citation)\n ? citation.label\n : null;\n // The tooltip: what the model called it (when the chip does not show it),\n // the repository a file is in and the file's whole path, the cited passage.\n // The place spelled out stands in for a passage the citation does not carry,\n // and for a place too long to show whole (a long sheet name).\n const tooltip =\n citation || excerpt\n ? tooltipText({\n lines: [\n informativeLabel({ label: name, shown: [chipName, assetTitle], citation }),\n repositoryFile ? assetTitle : null,\n repositoryFile || !excerpt || (placeLabel?.length ?? 0) > PLACE_FITS_CHARS\n ? citation?.description\n : null,\n ],\n excerpt,\n })\n : null;\n const assetRootHref =\n type === \"asset\" && assetIds?.length\n ? createOlympusAssetUrl({\n appUrl: auth?.appUrl,\n backendUrl: auth?.backendUrl,\n assetIds,\n })\n : null;\n const href = (() => {\n if (!assetRootHref || !sourceHref) return assetRootHref;\n try {\n const source = new URL(sourceHref, \"https://app.athenaintel.com\");\n const destination = new URL(assetRootHref);\n for (const [key, value] of source.searchParams) {\n destination.searchParams.set(key, value);\n }\n destination.searchParams.set(\"asset_ids\", assetIds?.join(\",\") ?? \"\");\n return destination.toString();\n } catch {\n return assetRootHref;\n }\n })();\n const className = cn(CHIP_PILL_CLASS, \"max-w-[275px]\", href && CHIP_LINK_CLASS);\n // A host's asset-type SVG follows the looked-up type, when it serves them.\n const chipThumbnailUrl = repositoryFile\n ? undefined\n : (asset?.assetType && assetTypeToThumb(asset.assetType)) || thumbnailUrl;\n const AssetIcon = assetChipIcon(asset?.assetType || assetType);\n const contents = (\n <>\n {repositoryFile ? (\n <GitHubMarkIcon className={CHIP_ICON_CLASS} />\n ) : chipThumbnailUrl ? (\n <img\n src={chipThumbnailUrl}\n alt=\"\"\n className={CHIP_ICON_CLASS}\n draggable={false}\n />\n ) : type === \"asset\" || type === \"folder\" ? (\n // A host that bundles no asset-type SVGs (computer apps, most SDK\n // apps) still gets the type's icon.\n <AssetIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"webpage\" ? (\n <Globe2Icon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"person\" ? (\n memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? (\n <img\n src={memberAvatarUrl}\n alt=\"\"\n className={cn(CHIP_ICON_CLASS, \"rounded-full object-cover\")}\n draggable={false}\n onError={() => setErroredAvatarUrl(memberAvatarUrl)}\n />\n ) : (\n <UserIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n )\n ) : null}\n {repositoryFile ? (\n <span\n className=\"flex min-w-0 items-center\"\n data-citation-anchor=\"file_line_range\"\n >\n <span className=\"min-w-0 overflow-hidden text-ellipsis\">\n {repositoryFile.file}\n </span>\n <span className=\"shrink-0 text-muted-foreground\">{repositoryFile.lines}</span>\n </span>\n ) : (\n <span\n className={cn(\n \"max-w-[180px] overflow-hidden text-ellipsis\",\n // A long place may shorten the name, never make it vanish.\n placeLabel && \"min-w-8\",\n )}\n >\n {chipName}\n </span>\n )}\n {citation && placeLabel && (\n <>\n {/* Copied or read as text, the place stays a word apart from the name; a flex row draws no space. */}{\" \"}\n <span\n className=\"flex min-w-0 items-center text-xs text-muted-foreground\"\n data-citation-anchor={citation.anchorType}\n >\n [<span className=\"min-w-0 overflow-hidden text-ellipsis\">{placeLabel}</span>]\n </span>\n </>\n )}\n </>\n );\n\n if (href && interactive) {\n const speakablePlace = citation && SPEAKABLE.test(citation.description);\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={\n repositoryFile && citation\n ? `Open ${citation.description} in Spaces`\n : placeLabel && citation && speakablePlace\n ? `Open ${chipName} at ${citation.description} in Spaces`\n : `Open ${chipName} in Spaces`\n }\n title={tooltip ?? `Open ${chipName} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n // The asset without its place: what a click names the opened asset.\n data-mention-name={repositoryFile ? (assetTitle ?? name) : chipName}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={tooltip ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\n};\n\n/**\n * A chip's tooltip: the lines that say something, each once, then the cited\n * passage, quoted when it shares the tooltip with other lines.\n */\nfunction tooltipText({\n lines,\n excerpt,\n}: {\n lines: readonly (string | null | undefined)[];\n excerpt: string | null;\n}): string | null {\n const kept: string[] = [];\n for (const line of lines) {\n const trimmed = line?.trim();\n if (trimmed && !kept.includes(trimmed)) kept.push(trimmed);\n }\n if (excerpt) kept.push(kept.length ? `“${excerpt}”` : excerpt);\n return kept.length ? kept.join(\"\\n\") : null;\n}\n\n/** The `excerpt` a Spaces citation URL carries, or null. */\nfunction readSourceExcerpt(href: string): string | null {\n try {\n const excerpt = new URL(href, \"https://app.athenaintel.com\").searchParams\n .get(\"excerpt\")\n ?.trim();\n return excerpt ? excerpt : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The label an asset citation chip falls back to when the model wrote none\n * (`[](…/spaces/?asset_ids=…&page=5)`): the cited location, else \"Asset\" — a\n * chip must never render as an empty box.\n */\nfunction citationFallbackLabel(params: URLSearchParams): string {\n const start = params.get(\"start_page\") ?? params.get(\"page\");\n const end = params.get(\"end_page\");\n if (start && end && end !== start) return `Pages ${start}–${end}`;\n if (start) return `Page ${start}`;\n return \"Asset\";\n}\n\n// ── Parse mention:// URLs ──\n\nexport function parseMentionUrl(url: string): {\n type: string;\n name: string;\n id: string;\n email?: string;\n assetIds?: string[];\n assetType?: string;\n citation?: MentionChipCitation;\n /**\n * For a `mention://asset` citation, the Spaces link to its place (anchor and\n * excerpt), so the chip opens there; a Spaces mention is its own link.\n */\n sourceHref?: string;\n thumbnailUrl?: string;\n} {\n try {\n if (isSpacesMentionUrl(url)) {\n const parsed = new URL(url, \"https://app.athenaintel.com\");\n const assetIds = (parsed.searchParams.get(\"asset_ids\") ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const id = assetIds[0] ?? \"\";\n const assetType = getSpacesAssetType(parsed);\n // A link to several assets names no one place in any of them.\n const reference =\n assetIds.length === 1\n ? decodeAssetLink(parsed, decodeFromAssetSpacesUrl)\n : null;\n const citation = reference ? citationOf(reference) : undefined;\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : (citation?.description ??\n citationFallbackLabel(parsed.searchParams)),\n id,\n assetIds,\n assetType: assetType ?? undefined,\n citation,\n thumbnailUrl: assetTypeToThumb(assetType),\n };\n }\n // mention://tool?name=...&description=...\n // mention://asset?id=...&assetType=project&thumb=../icons/files/ic-project.svg\n const u = new URL(url);\n const type = u.hostname || u.pathname.replace(/^\\/\\//, \"\").split(\"?\")[0];\n const isWebpage = type === \"webpage\";\n const id = isWebpage\n ? u.searchParams.get(\"tab_id\") || \"\"\n : u.searchParams.get(\"id\") || u.searchParams.get(\"name\") || \"\";\n const name = isWebpage\n ? u.searchParams.get(\"title\") || \"This webpage\"\n : u.searchParams.get(\"name\") || id;\n\n // Get thumbnail: prefer explicit thumb param, then derive from assetType\n let thumbnailUrl = u.searchParams.get(\"thumb\") || undefined;\n if (!thumbnailUrl) {\n const assetType = u.searchParams.get(\"assetType\");\n if (assetType) {\n thumbnailUrl = assetTypeToThumb(assetType);\n } else if (type === \"asset\" || type === \"folder\") {\n thumbnailUrl = assetTypeToThumb(type === \"folder\" ? \"folder\" : null);\n }\n }\n\n const reference =\n type === \"asset\" ? decodeAssetLink(u, decodeFromAssetMentionUri) : null;\n\n return {\n type,\n name,\n id,\n email: type === \"person\" ? u.searchParams.get(\"email\") || undefined : undefined,\n assetIds: type === \"asset\" && id ? [id] : undefined,\n assetType:\n type === \"asset\" || type === \"folder\"\n ? u.searchParams.get(\"assetType\") ||\n reference?.meta?.assetType ||\n (type === \"folder\" ? \"folder\" : undefined)\n : undefined,\n citation: reference ? citationOf(reference) : undefined,\n sourceHref:\n reference && (reference.anchor || reference.meta?.excerpt)\n ? encodeToAssetSpacesUrl(reference)\n : undefined,\n thumbnailUrl,\n };\n } catch {\n return { type: \"unknown\", name: \"\", id: \"\" };\n }\n}\n\nfunction htmlAttribute(attributes: string, name: string): string | null {\n const escapedName = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const match = new RegExp(\n `(?:^|\\\\s)${escapedName}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`,\n \"i\",\n ).exec(attributes);\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n return value\n .replace(/&amp;/gi, \"&\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/gi, \"'\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\");\n}\n\n/** Turn complete model-emitted URL citation anchors into safe Markdown links. */\nexport function normalizeAthenaMentionHtml(text: string): string {\n return text.replace(\n /<a\\b([^>]*)>([\\s\\S]*?)<\\/a>/gi,\n (original, attributes: string, rawLabel: string) => {\n const className = htmlAttribute(attributes, \"class\") ?? \"\";\n const type = htmlAttribute(attributes, \"data-type\") ?? \"\";\n if (!className.split(/\\s+/).includes(\"mention\") || type !== \"url\") {\n return original;\n }\n\n const rawUrl =\n htmlAttribute(attributes, \"data-url\") ??\n htmlAttribute(attributes, \"href\");\n if (!rawUrl) return original;\n const url = decodeHtmlAttribute(rawUrl);\n let normalizedUrl: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return original;\n }\n normalizedUrl = parsed.toString();\n } catch {\n return original;\n }\n\n const label = decodeHtmlAttribute(rawLabel.replace(/<[^>]*>/g, \"\"))\n .trim()\n .replace(/([\\\\[\\]])/g, \"\\\\$1\");\n return label ? `[${label}](<${normalizedUrl}>)` : original;\n },\n );\n}\n// ── Parse mention markdown in plain text ──\n\n// The label may hold balanced brackets one level deep (`[@Plan [Q3]](…)`) or a lone `[`.\nconst MENTION_RE =\n /\\[@((?:\\[[^[\\]]*\\](?!\\((?:[a-z][a-z0-9+.-]*:\\/\\/|\\/))|[^\\]])*)\\]\\(((?:mention:\\/\\/|(?:https?:\\/\\/[^)\\s]+)?\\/dashboard\\/spaces\\/?\\?)[^)]+)\\)/g;\n\n/**\n * Parse text containing [@name](mention://...) patterns into\n * an array of string segments and MentionChip elements.\n */\nexport function parseMentionsInText(text: string): (string | ReactElement)[] {\n const parts: (string | ReactElement)[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n MENTION_RE.lastIndex = 0;\n\n while ((match = MENTION_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n\n const name = match[1];\n const url = match[2];\n const parsed = parseMentionUrl(url);\n\n parts.push(\n <MentionChip\n key={`mention-${match.index}`}\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : parsed.sourceHref}\n />\n );\n\n lastIndex = match.index + match[0].length;\n }\n\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return parts.length > 0 ? parts : [text];\n}\n\n/** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */\nexport function mentionsToPlainText(text: string): string {\n return text.replace(MENTION_RE, (_match, name: string, url: string) => {\n return `@${name || parseMentionUrl(url).name}`;\n });\n}\n"],"names":["id","reference"],"mappings":"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"}
1
+ {"version":3,"file":"mention-chip.js","sources":["../../../src/chat-ui/mentions/mention-chip.tsx"],"sourcesContent":["/**\n * Mention chip rendering for message display.\n *\n * Parses [@name](mention://type?...) markdown links and renders them\n * as styled inline chips matching the Olympus mention style.\n * Used in both user messages (inline text) and assistant messages (markdown links).\n */\n\nimport {\n isValidElement,\n useContext,\n useState,\n type FC,\n type ReactElement,\n type ReactNode,\n} from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport {\n type AssetReference,\n decodeFromAssetMentionUri,\n decodeFromAssetSpacesUrl,\n encodeToAssetSpacesUrl,\n formatReferenceAnchor,\n} from \"@athenaintel/references/asset-links\";\nimport {\n CrosshairIcon,\n DatabaseIcon,\n FileCodeIcon,\n FileIcon,\n FileTextIcon,\n FolderIcon,\n Globe2Icon,\n HeadphonesIcon,\n ImageIcon,\n type LucideIcon,\n MailIcon,\n MessageSquareIcon,\n MonitorIcon,\n PresentationIcon,\n SheetIcon,\n UserIcon,\n VideoIcon,\n} from \"lucide-react\";\nimport { cn } from \"../../lib/utils\";\nimport { createOlympusAssetUrl } from \"../lib/asset-urls\";\nimport { MentionAuthCtx } from \"./suggestions/data/api-client\";\nimport { assetTypeToThumb } from \"./suggestions/data/sources/asset-type-icons\";\nimport { useMemberAvatarUrl } from \"./use-member-avatar\";\nimport { useMentionAsset } from \"./use-mention-asset\";\n\nconst SPACES_MENTION_PATH = /\\/dashboard\\/spaces\\/?\\?/;\n\nfunction isSpacesMentionUrl(url: string): boolean {\n return SPACES_MENTION_PATH.test(url);\n}\n\nfunction getSpacesAssetType(url: URL): string | null {\n const assetType =\n url.searchParams.get(\"assetType\") ||\n url.searchParams.get(\"asset_type\") ||\n url.searchParams.get(\"type\");\n return assetType === \"sheet\" ? \"spreadsheet\" : assetType;\n}\n\n/**\n * Where in the asset a citation points, as its chip shows it. The anchor is\n * read and labelled by `@athenaintel/references` — the same parser and labels\n * Olympus's mention chips use — so a chip reads `jwt.py:12-25`, `p.5` or\n * `Slide 3` in every Athena chat.\n */\nexport interface MentionChipCitation {\n /** The anchor type (`page`, `file_line_range`, `slide`, …). */\n anchorType: string;\n /** The short label drawn after the name (`p.5`, `jwt.py:12-25`). */\n label: string;\n /**\n * The place spelled out (`Page 5`, `agora/…/jwt.py:12-25`), without the\n * excerpt: the tooltip when the link carries no excerpt, and part of the\n * chip's accessible name when it holds a word.\n */\n description: string;\n}\n\n/** The citation an asset link's anchor names, or undefined for a plain mention or an anchor nobody can read. */\nfunction citationOf(reference: AssetReference): MentionChipCitation | undefined {\n const { anchor, meta } = reference;\n if (!anchor) return undefined;\n const label = formatReferenceAnchor(anchor, \"short\", meta);\n if (!label) return undefined;\n const { excerpt: _excerpt, ...place } = meta ?? {};\n return {\n anchorType: anchor.type,\n label,\n description: formatReferenceAnchor(anchor, \"long\", place) ?? label,\n };\n}\n\n/**\n * The chip's icon for an asset type when the host serves no asset-type SVGs.\n * A compact set on purpose: the chip renders in every message (and Olympus's\n * main layout reaches it through the queued-message tray), so it carries a\n * dozen icons rather than the `@` menu's whole map.\n */\nconst ASSET_CHIP_ICONS: Record<string, LucideIcon> = {\n document: FileTextIcon,\n word_document: FileTextIcon,\n markdown: FileTextIcon,\n txt: FileTextIcon,\n pdf: FileTextIcon,\n spreadsheet: SheetIcon,\n table: SheetIcon,\n presentation: PresentationIcon,\n powerpoint_deck: PresentationIcon,\n presentation_pptx_studio: PresentationIcon,\n pptx: PresentationIcon,\n image: ImageIcon,\n figure: ImageIcon,\n canvas: ImageIcon,\n video: VideoIcon,\n meeting: VideoIcon,\n video_session: VideoIcon,\n audio: HeadphonesIcon,\n voice_memo: HeadphonesIcon,\n phone_call: HeadphonesIcon,\n email: MailIcon,\n email_message: MailIcon,\n live_email_thread: MailIcon,\n live_calendar_event: MailIcon,\n python: FileCodeIcon,\n javascript: FileCodeIcon,\n typescript: FileCodeIcon,\n json: FileCodeIcon,\n yaml: FileCodeIcon,\n database: DatabaseIcon,\n catalog: DatabaseIcon,\n drive: DatabaseIcon,\n dataset: DatabaseIcon,\n semantic_model: DatabaseIcon,\n query_snippet: DatabaseIcon,\n folder: FolderIcon,\n gdrive_folder: FolderIcon,\n project: FolderIcon,\n collection: FolderIcon,\n session: MessageSquareIcon,\n computer: MonitorIcon,\n desktop_session: MonitorIcon,\n user_interface: MonitorIcon,\n application: MonitorIcon,\n html: Globe2Icon,\n web_page: Globe2Icon,\n browse_session: Globe2Icon,\n};\n\nfunction assetChipIcon(assetType: string | undefined): LucideIcon {\n return (assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()]) || FileIcon;\n}\n\n/**\n * GitHub's mark (Octicons `mark-github`), drawn by a repository file's\n * citation. Lucide carries no brand icons and an SDK app serves none of\n * Olympus's, so the chip brings its own. Only a GitHub repository catalog\n * serves `file_line_range` anchors (`ASSET_ANCHOR_REGISTRY`; agora's\n * `_CATALOG_ANCHORS_BY_PROVIDER`), so the anchor alone names the provider.\n */\nfunction GitHubMarkIcon({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n className={className}\n data-chip-icon=\"github\"\n >\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z\" />\n </svg>\n );\n}\n\n/** Past this many characters a place may not show whole in the chip, so the tooltip spells it out. */\nconst PLACE_FITS_CHARS = 20;\n\n/** `route.ts:18-24` as the file's name and its lines, so a long name shortens while its lines stay. */\nfunction splitFileLines(label: string): { file: string; lines: string } {\n const colon = label.lastIndexOf(\":\");\n return colon > 0\n ? { file: label.slice(0, colon), lines: label.slice(colon) }\n : { file: label, lines: \"\" };\n}\n\n/**\n * Whether a name already says where its citation points: the place's label or\n * its spelled-out form, or a name ending in the label (`Q4 report p.5`,\n * `olympus/…/route.ts:18-24`). The chip then draws the place once.\n */\nfunction restatesPlace(name: string, citation: MentionChipCitation): boolean {\n const said = name.trim().toLowerCase();\n const label = citation.label.toLowerCase();\n return said === label || said === citation.description.toLowerCase() || said.endsWith(label);\n}\n\n/**\n * The link's own label when it tells the reader something the chip does not\n * already show: not the place again (a path, `file:lines`), not the asset's\n * name. The tooltip keeps it, since the chip shows the asset and the place.\n */\nfunction informativeLabel({\n label,\n shown,\n citation,\n}: {\n label: string;\n shown: readonly (string | null | undefined)[];\n citation: MentionChipCitation | undefined;\n}): string | null {\n const trimmed = label.trim();\n if (!trimmed || !SPEAKABLE.test(trimmed)) return null;\n if (PLACEHOLDER_LABELS.has(trimmed.toLowerCase())) return null;\n if (citation && restatesPlace(trimmed, citation)) return null;\n // Every word of it already shown (`Tesla Q3 report` beside `Tesla Q3\n // Earnings Report`, `demo-app-monorepo` beside the repository's title).\n const shownWords = new Set(\n [...shown, citation?.description].flatMap((text) => (text ? wordsOf(text) : [])),\n );\n return wordsOf(trimmed).every((word) => shownWords.has(word)) ? null : trimmed;\n}\n\nfunction wordsOf(text: string): string[] {\n return text.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? [];\n}\n\n/** A label with a letter or digit in it; `§` or `•` alone says nothing read aloud. */\nconst SPEAKABLE = /[\\p{L}\\p{N}]/u;\n\n/** Labels that name nothing: agora's read_asset citations all read `Citation`. */\nconst PLACEHOLDER_LABELS: ReadonlySet<string> = new Set([\"citation\", \"source\", \"link\", \"here\"]);\n\n/** The asset reference an asset link decodes to, or null when it does not parse. */\nfunction decodeAssetLink(\n url: URL,\n decode: (url: URL) => AssetReference,\n): AssetReference | null {\n try {\n return decode(url);\n } catch {\n return null;\n }\n}\n\n// ── Mention link component for markdown renderer ──\n\n/**\n * Custom <a> renderer for MarkdownTextPrimitive.\n * Intercepts mention:// links and renders them as chips.\n * Falls through to a normal link for non-mention URLs.\n */\nfunction personEmailForMention(\n parsed: ReturnType<typeof parseMentionUrl>,\n): string | undefined {\n if (parsed.type !== \"person\") return undefined;\n return parsed.email || (parsed.id.includes(\"@\") ? parsed.id : undefined);\n}\n\n/**\n * The text a link's children read as. Markdown can hand a link several children, some of them\n * elements (an escaped `_` or a `*` in a URL splits its text), and stringifying an element gives\n * `[object Object]`.\n */\nfunction textOf(node: ReactNode): string {\n if (typeof node === \"string\" || typeof node === \"number\") return String(node);\n if (Array.isArray(node)) return node.map(textOf).join(\"\");\n if (isValidElement<{ children?: ReactNode }>(node)) return textOf(node.props.children);\n return \"\";\n}\n\nexport const MentionLink: FC<{\n href?: string;\n children?: ReactNode;\n interactive?: boolean;\n}> = ({ href, children, interactive = true }) => {\n if (\n href &&\n (href.startsWith(\"mention://\") || isSpacesMentionUrl(href))\n ) {\n const name = textOf(children).trim().replace(/^@/, \"\");\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : parsed.sourceHref}\n interactive={interactive}\n />\n );\n }\n\n if (href?.startsWith(\"chrome-extension://\")) {\n return <span>{children}</span>;\n }\n\n if (isScreenPointHref(href)) {\n return <ScreenPointChip href={href}>{children}</ScreenPointChip>;\n }\n\n // Normal link — render as-is\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n};\n\n// ── The chip pill ──\n\n/**\n * Olympus's mention pill (`mentionPillClassName`: 20px high, 16px icon, the\n * place in `text-xs`) in the provider's theme tokens, so a dark theme keeps it\n * legible, and `box-border` so its height holds without a host's CSS reset.\n * Every chat chip draws it: an asset or citation chip, a web source, a screen\n * point. Only stock Tailwind sizes: Olympus renders these chips (the queued\n * message tray) with a stylesheet that does not scan this package, so an\n * arbitrary value would reach it unstyled. `not-prose` matters: assistant\n * messages render inside Tailwind Typography's `prose`, whose\n * zero-specificity `img` margins (~1.7em top/bottom) would otherwise inflate\n * the chip's icon into a tall card that breaks the sentence flow around it.\n */\nexport const CHIP_PILL_CLASS =\n \"not-prose box-border inline-flex h-5 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-muted px-1 py-0.5 align-middle font-normal leading-none text-foreground no-underline\";\n/** A chip that opens something. */\nexport const CHIP_LINK_CLASS =\n \"cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\";\nexport const CHIP_ICON_CLASS = \"size-4 shrink-0\";\n\n// ── Inline screen-point chip ──\n\n/**\n * Renders an `athena-point://` link as a non-navigating coordinate chip.\n * Surfaces without screenshot context (extension side panel, SDK consumers)\n * get the label plus the coordinates; malformed points fall back to text.\n */\nexport const ScreenPointChip: FC<{\n href: string;\n children?: ReactNode;\n}> = ({ href, children }) => {\n const parsed = parseScreenPointHref(href);\n if (!parsed.ok) {\n return <span>{children}</span>;\n }\n const description = formatScreenPoint(parsed.point);\n return (\n <span\n className={cn(CHIP_PILL_CLASS, \"max-w-[260px]\")}\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n <span className=\"min-w-0 max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">\n {parsed.point.x}, {parsed.point.y}\n </span>\n </span>\n );\n};\n\n// ── Inline mention chip ──\n\nexport const MentionChip: FC<{\n name: string;\n type?: string;\n assetIds?: string[];\n /** The asset's type (`document`, `catalog`, …): picks the icon until (or unless) the asset is looked up. */\n assetType?: string;\n /** Where in the asset the link points: its label follows the name. */\n citation?: MentionChipCitation;\n personEmail?: string;\n thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({\n name,\n type,\n assetIds,\n assetType,\n citation,\n personEmail,\n thumbnailUrl,\n sourceHref,\n interactive = true,\n}) => {\n const auth = useContext(MentionAuthCtx);\n const memberAvatarUrl = useMemberAvatarUrl(personEmail);\n const [erroredAvatarUrl, setErroredAvatarUrl] = useState<string>();\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;\n // A repository file's lines read as the file (`route.ts:18-24`): the chip\n // opens that file, and every citation in a reply names the same repository,\n // whatever label the model wrote.\n const repositoryFile =\n citation?.anchorType === \"file_line_range\"\n ? { ...splitFileLines(citation.label), label: citation.label }\n : null;\n // The asset as agora has it, read the way Olympus's chips read it: its own\n // title and type. Until it answers, and for an asset the viewer cannot read,\n // the link's label and `type=` stand in. Not asked for a repository file,\n // which reads as its file whatever the catalog is called.\n const asset = useMentionAsset(\n type === \"asset\" && assetIds?.length === 1 && !repositoryFile ? assetIds[0] : undefined,\n );\n const assetTitle = asset?.title || null;\n const chipName = repositoryFile?.label ?? assetTitle ?? name;\n // The place follows a name that does not already say it, as `[p.5]`.\n const placeLabel =\n citation && !repositoryFile && !restatesPlace(chipName, citation)\n ? citation.label\n : null;\n // The tooltip: what the model called it (when the chip does not show it),\n // the repository a file is in and the file's whole path, the cited passage.\n // The place spelled out stands in for a passage the citation does not carry,\n // and for a place too long to show whole (a long sheet name).\n const tooltip =\n citation || excerpt\n ? tooltipText({\n lines: [\n informativeLabel({ label: name, shown: [chipName, assetTitle], citation }),\n repositoryFile ? assetTitle : null,\n repositoryFile || !excerpt || (placeLabel?.length ?? 0) > PLACE_FITS_CHARS\n ? citation?.description\n : null,\n ],\n excerpt,\n })\n : null;\n const assetRootHref =\n type === \"asset\" && assetIds?.length\n ? createOlympusAssetUrl({\n appUrl: auth?.appUrl,\n backendUrl: auth?.backendUrl,\n assetIds,\n })\n : null;\n const href = (() => {\n if (!assetRootHref || !sourceHref) return assetRootHref;\n try {\n const source = new URL(sourceHref, \"https://app.athenaintel.com\");\n const destination = new URL(assetRootHref);\n for (const [key, value] of source.searchParams) {\n destination.searchParams.set(key, value);\n }\n destination.searchParams.set(\"asset_ids\", assetIds?.join(\",\") ?? \"\");\n return destination.toString();\n } catch {\n return assetRootHref;\n }\n })();\n const className = cn(CHIP_PILL_CLASS, \"max-w-[275px]\", href && CHIP_LINK_CLASS);\n // A host's asset-type SVG follows the looked-up type, when it serves them.\n const chipThumbnailUrl = repositoryFile\n ? undefined\n : (asset?.assetType && assetTypeToThumb(asset.assetType)) || thumbnailUrl;\n const AssetIcon = assetChipIcon(asset?.assetType || assetType);\n const contents = (\n <>\n {repositoryFile ? (\n <GitHubMarkIcon className={CHIP_ICON_CLASS} />\n ) : chipThumbnailUrl ? (\n <img\n src={chipThumbnailUrl}\n alt=\"\"\n className={CHIP_ICON_CLASS}\n draggable={false}\n />\n ) : type === \"asset\" || type === \"folder\" ? (\n // A host that bundles no asset-type SVGs (computer apps, most SDK\n // apps) still gets the type's icon.\n <AssetIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"webpage\" ? (\n <Globe2Icon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n ) : type === \"person\" ? (\n memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? (\n <img\n src={memberAvatarUrl}\n alt=\"\"\n className={cn(CHIP_ICON_CLASS, \"rounded-full object-cover\")}\n draggable={false}\n onError={() => setErroredAvatarUrl(memberAvatarUrl)}\n />\n ) : (\n <UserIcon className={CHIP_ICON_CLASS} aria-hidden=\"true\" />\n )\n ) : null}\n {repositoryFile ? (\n <span\n className=\"flex min-w-0 items-center\"\n data-citation-anchor=\"file_line_range\"\n >\n <span className=\"min-w-0 overflow-hidden text-ellipsis\">\n {repositoryFile.file}\n </span>\n <span className=\"shrink-0 text-muted-foreground\">{repositoryFile.lines}</span>\n </span>\n ) : (\n <span\n className={cn(\n \"max-w-[180px] overflow-hidden text-ellipsis\",\n // A long place may shorten the name, never make it vanish.\n placeLabel && \"min-w-8\",\n )}\n >\n {chipName}\n </span>\n )}\n {citation && placeLabel && (\n <>\n {/* Copied or read as text, the place stays a word apart from the name; a flex row draws no space. */}{\" \"}\n <span\n className=\"flex min-w-0 items-center text-xs text-muted-foreground\"\n data-citation-anchor={citation.anchorType}\n >\n [<span className=\"min-w-0 overflow-hidden text-ellipsis\">{placeLabel}</span>]\n </span>\n </>\n )}\n </>\n );\n\n if (href && interactive) {\n const speakablePlace = citation && SPEAKABLE.test(citation.description);\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={\n repositoryFile && citation\n ? `Open ${citation.description} in Spaces`\n : placeLabel && citation && speakablePlace\n ? `Open ${chipName} at ${citation.description} in Spaces`\n : `Open ${chipName} in Spaces`\n }\n title={tooltip ?? `Open ${chipName} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n // The asset without its place: what a click names the opened asset.\n data-mention-name={repositoryFile ? (assetTitle ?? name) : chipName}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={tooltip ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\n};\n\n/**\n * A chip's tooltip: the lines that say something, each once, then the cited\n * passage, quoted when it shares the tooltip with other lines.\n */\nfunction tooltipText({\n lines,\n excerpt,\n}: {\n lines: readonly (string | null | undefined)[];\n excerpt: string | null;\n}): string | null {\n const kept: string[] = [];\n for (const line of lines) {\n const trimmed = line?.trim();\n if (trimmed && !kept.includes(trimmed)) kept.push(trimmed);\n }\n if (excerpt) kept.push(kept.length ? `“${excerpt}”` : excerpt);\n return kept.length ? kept.join(\"\\n\") : null;\n}\n\n/** The `excerpt` a Spaces citation URL carries, or null. */\nfunction readSourceExcerpt(href: string): string | null {\n try {\n const excerpt = new URL(href, \"https://app.athenaintel.com\").searchParams\n .get(\"excerpt\")\n ?.trim();\n return excerpt ? excerpt : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The label an asset citation chip falls back to when the model wrote none\n * (`[](…/spaces/?asset_ids=…&page=5)`): the cited location, else \"Asset\" — a\n * chip must never render as an empty box.\n */\nfunction citationFallbackLabel(params: URLSearchParams): string {\n const start = params.get(\"start_page\") ?? params.get(\"page\");\n const end = params.get(\"end_page\");\n if (start && end && end !== start) return `Pages ${start}–${end}`;\n if (start) return `Page ${start}`;\n return \"Asset\";\n}\n\n// ── Parse mention:// URLs ──\n\nexport function parseMentionUrl(url: string): {\n type: string;\n name: string;\n id: string;\n email?: string;\n assetIds?: string[];\n assetType?: string;\n citation?: MentionChipCitation;\n /**\n * For a `mention://asset` citation, the Spaces link to its place (anchor and\n * excerpt), so the chip opens there; a Spaces mention is its own link.\n */\n sourceHref?: string;\n thumbnailUrl?: string;\n} {\n try {\n if (isSpacesMentionUrl(url)) {\n const parsed = new URL(url, \"https://app.athenaintel.com\");\n const assetIds = (parsed.searchParams.get(\"asset_ids\") ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const id = assetIds[0] ?? \"\";\n const assetType = getSpacesAssetType(parsed);\n // A link to several assets names no one place in any of them.\n const reference =\n assetIds.length === 1\n ? decodeAssetLink(parsed, decodeFromAssetSpacesUrl)\n : null;\n const citation = reference ? citationOf(reference) : undefined;\n return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : (citation?.description ??\n citationFallbackLabel(parsed.searchParams)),\n id,\n assetIds,\n assetType: assetType ?? undefined,\n citation,\n thumbnailUrl: assetTypeToThumb(assetType),\n };\n }\n // mention://tool?name=...&description=...\n // mention://asset?id=...&assetType=project&thumb=../icons/files/ic-project.svg\n const u = new URL(url);\n const type = u.hostname || u.pathname.replace(/^\\/\\//, \"\").split(\"?\")[0];\n const isWebpage = type === \"webpage\";\n const id = isWebpage\n ? u.searchParams.get(\"tab_id\") || \"\"\n : u.searchParams.get(\"id\") || u.searchParams.get(\"name\") || \"\";\n const name = isWebpage\n ? u.searchParams.get(\"title\") || \"This webpage\"\n : u.searchParams.get(\"name\") || id;\n\n // Get thumbnail: prefer explicit thumb param, then derive from assetType\n let thumbnailUrl = u.searchParams.get(\"thumb\") || undefined;\n if (!thumbnailUrl) {\n const assetType = u.searchParams.get(\"assetType\");\n if (assetType) {\n thumbnailUrl = assetTypeToThumb(assetType);\n } else if (type === \"asset\" || type === \"folder\") {\n thumbnailUrl = assetTypeToThumb(type === \"folder\" ? \"folder\" : null);\n }\n }\n\n const reference =\n type === \"asset\" ? decodeAssetLink(u, decodeFromAssetMentionUri) : null;\n\n return {\n type,\n name,\n id,\n email: type === \"person\" ? u.searchParams.get(\"email\") || undefined : undefined,\n assetIds: type === \"asset\" && id ? [id] : undefined,\n assetType:\n type === \"asset\" || type === \"folder\"\n ? u.searchParams.get(\"assetType\") ||\n reference?.meta?.assetType ||\n (type === \"folder\" ? \"folder\" : undefined)\n : undefined,\n citation: reference ? citationOf(reference) : undefined,\n sourceHref:\n reference && (reference.anchor || reference.meta?.excerpt)\n ? encodeToAssetSpacesUrl(reference)\n : undefined,\n thumbnailUrl,\n };\n } catch {\n return { type: \"unknown\", name: \"\", id: \"\" };\n }\n}\n\nfunction htmlAttribute(attributes: string, name: string): string | null {\n const escapedName = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const match = new RegExp(\n `(?:^|\\\\s)${escapedName}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`,\n \"i\",\n ).exec(attributes);\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n return value\n .replace(/&amp;/gi, \"&\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/gi, \"'\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\");\n}\n\n/** Turn complete model-emitted URL citation anchors into safe Markdown links. */\nexport function normalizeAthenaMentionHtml(text: string): string {\n return text.replace(\n /<a\\b([^>]*)>([\\s\\S]*?)<\\/a>/gi,\n (original, attributes: string, rawLabel: string) => {\n const className = htmlAttribute(attributes, \"class\") ?? \"\";\n const type = htmlAttribute(attributes, \"data-type\") ?? \"\";\n if (!className.split(/\\s+/).includes(\"mention\") || type !== \"url\") {\n return original;\n }\n\n const rawUrl =\n htmlAttribute(attributes, \"data-url\") ??\n htmlAttribute(attributes, \"href\");\n if (!rawUrl) return original;\n const url = decodeHtmlAttribute(rawUrl);\n let normalizedUrl: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return original;\n }\n normalizedUrl = parsed.toString();\n } catch {\n return original;\n }\n\n const label = decodeHtmlAttribute(rawLabel.replace(/<[^>]*>/g, \"\"))\n .trim()\n .replace(/([\\\\[\\]])/g, \"\\\\$1\");\n return label ? `[${label}](<${normalizedUrl}>)` : original;\n },\n );\n}\n// ── Parse mention markdown in plain text ──\n\n// The label may hold balanced brackets one level deep (`[@Plan [Q3]](…)`) or a lone `[`.\nconst MENTION_RE =\n /\\[@((?:\\[[^[\\]]*\\](?!\\((?:[a-z][a-z0-9+.-]*:\\/\\/|\\/))|[^\\]])*)\\]\\(((?:mention:\\/\\/|(?:https?:\\/\\/[^)\\s]+)?\\/dashboard\\/spaces\\/?\\?)[^)]+)\\)/g;\n\n/**\n * Parse text containing [@name](mention://...) patterns into\n * an array of string segments and MentionChip elements.\n */\nexport function parseMentionsInText(text: string): (string | ReactElement)[] {\n const parts: (string | ReactElement)[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n MENTION_RE.lastIndex = 0;\n\n while ((match = MENTION_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n\n const name = match[1];\n const url = match[2];\n const parsed = parseMentionUrl(url);\n\n parts.push(\n <MentionChip\n key={`mention-${match.index}`}\n name={name || parsed.name}\n type={parsed.type}\n personEmail={personEmailForMention(parsed)}\n assetIds={parsed.assetIds}\n assetType={parsed.assetType}\n citation={parsed.citation}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : parsed.sourceHref}\n />\n );\n\n lastIndex = match.index + match[0].length;\n }\n\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return parts.length > 0 ? parts : [text];\n}\n\n/** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */\nexport function mentionsToPlainText(text: string): string {\n return text.replace(MENTION_RE, (_match, name: string, url: string) => {\n return `@${name || parseMentionUrl(url).name}`;\n });\n}\n"],"names":["id","reference"],"mappings":"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"}
@@ -8,14 +8,18 @@ const MENTION_ASSET_BATCH_WINDOW_MS = 16;
8
8
  const MENTION_ASSET_STALE_TIME_MS = 6e4;
9
9
  const MENTION_ASSETS_QUERY = `
10
10
  query MentionChipAssets($assetIds: [String!]!) {
11
- getAssetsByIds(assetIds: $assetIds) {
11
+ getAssetChipDetails(assetIds: $assetIds) {
12
12
  id
13
13
  title
14
14
  athenaOriginalType
15
15
  }
16
16
  }
17
17
  `;
18
- const LOOKUP_ID = /^asset_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18
+ const LOOKUP_ID = /^asset_(?:thread_)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
19
+ const backendsWithoutChipDetails = /* @__PURE__ */ new Set();
20
+ function isMissingChipDetails(error) {
21
+ return error instanceof apiClient.AthenaGraphQLError && (error.status === 400 || error.message.includes("getAssetChipDetails"));
22
+ }
19
23
  function mentionAssetLookupId(assetId) {
20
24
  const baseId = (assetId == null ? void 0 : assetId.trim().split("@")[0]) ?? "";
21
25
  return LOOKUP_ID.test(baseId) ? baseId : null;
@@ -59,6 +63,10 @@ function sendBatch(batchKey) {
59
63
  if (!batch) return;
60
64
  pendingBatches.delete(batchKey);
61
65
  const ids = [...batch.waiters.keys()];
66
+ if (backendsWithoutChipDetails.has(batch.auth.backendUrl)) {
67
+ for (const id of ids) settle(batch.waiters.get(id), null);
68
+ return;
69
+ }
62
70
  for (let start = 0; start < ids.length; start += MENTION_ASSET_BATCH_SIZE) {
63
71
  const chunk = ids.slice(start, start + MENTION_ASSET_BATCH_SIZE);
64
72
  apiClient.fetchGraphQL(batch.auth, MENTION_ASSETS_QUERY, {
@@ -66,7 +74,9 @@ function sendBatch(batchKey) {
66
74
  }).then(
67
75
  (data) => {
68
76
  var _a, _b;
69
- const found = new Map((data.getAssetsByIds ?? []).map((asset) => [asset.id, asset]));
77
+ const found = new Map(
78
+ (data.getAssetChipDetails ?? []).map((asset) => [asset.id, asset])
79
+ );
70
80
  for (const id of chunk) {
71
81
  const asset = found.get(id);
72
82
  settle(
@@ -80,6 +90,11 @@ function sendBatch(batchKey) {
80
90
  }
81
91
  },
82
92
  (error) => {
93
+ if (isMissingChipDetails(error)) {
94
+ backendsWithoutChipDetails.add(batch.auth.backendUrl);
95
+ for (const id of chunk) settle(batch.waiters.get(id), null);
96
+ return;
97
+ }
83
98
  for (const id of chunk) fail(batch.waiters.get(id), error);
84
99
  }
85
100
  );
@@ -1 +1 @@
1
- {"version":3,"file":"use-mention-asset.cjs","sources":["../../../src/chat-ui/mentions/use-mention-asset.ts"],"sourcesContent":["/**\n * The asset an asset chip names, read the way Olympus's chips read it: its\n * title and type from agora's `getAssetsByIds`, which returns only the assets\n * the caller may read (an id they cannot read is left out of the answer, as is\n * another workspace's). A chip then shows the asset's own name and icon\n * instead of whatever label and `type=` its link carried.\n *\n * Chips that mount together ask together: one GraphQL request per batch of up\n * to {@link MENTION_ASSET_BATCH_SIZE} ids, gathered over\n * {@link MENTION_ASSET_BATCH_WINDOW_MS}. Each answer is a TanStack query keyed\n * by backend, the person asking and the id, fresh for a minute, so a\n * transcript full of citations to one repository asks for it once.\n */\nimport { QueryClient, QueryClientContext, useQuery } from \"@tanstack/react-query\";\nimport { useContext } from \"react\";\nimport {\n fetchGraphQL,\n type MentionAuthContext,\n MentionAuthCtx,\n} from \"./suggestions/data/api-client\";\n\n/** What a chip draws from the asset: its current title and its type. */\nexport interface MentionAsset {\n id: string;\n title: string;\n assetType: string;\n}\n\nexport const MENTION_ASSET_BATCH_SIZE = 50;\nexport const MENTION_ASSET_BATCH_WINDOW_MS = 16;\nconst MENTION_ASSET_STALE_TIME_MS = 60_000;\n\n/** No metadata: a session's carries its token ledgers, and a chip needs a name and a type. */\nconst MENTION_ASSETS_QUERY = `\n query MentionChipAssets($assetIds: [String!]!) {\n getAssetsByIds(assetIds: $assetIds) {\n id\n title\n athenaOriginalType\n }\n }\n`;\n\ninterface MentionAssetsResponse {\n getAssetsByIds?: Array<{\n id: string;\n title: string | null;\n athenaOriginalType: string | null;\n }> | null;\n}\n\n/**\n * The ids a lookup asks about: a stored asset. A versioned id (`asset_x@3`)\n * asks about its asset. A live provider file (`asset_<catalog>_<file>`) is not\n * a row `getAssetsByIds` holds, and a session (`asset_thread_<uuid>`) is never\n * asked about: agora reads the whole row, and a session's metadata carries\n * its transcript's ledgers (megabytes on a long session) — too much for a name.\n */\nconst LOOKUP_ID = /^asset_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport function mentionAssetLookupId(assetId: string | undefined): string | null {\n const baseId = assetId?.trim().split(\"@\")[0] ?? \"\";\n return LOOKUP_ID.test(baseId) ? baseId : null;\n}\n\n/** Whether a request can carry a credential (`mentionRequestInit` refuses one that cannot). */\nfunction canAuthenticate(auth: MentionAuthContext): boolean {\n return Boolean(auth.accessToken || auth.apiKey || auth.credentials || auth.getAccessToken);\n}\n\n/** A short, stable stand-in for a credential in a cache key; never the credential itself. */\nfunction fingerprint(credential: string): string {\n let hash = 0x811c9dc5;\n for (let index = 0; index < credential.length; index += 1) {\n hash ^= credential.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n\n/** The scope each credential with no stable identity of its own gets: its own auth object. */\nconst authScopes = new WeakMap<MentionAuthContext, string>();\nlet authScopeCount = 0;\n\n/**\n * Who is asking, as narrowly as the credential says: what `getAssetsByIds`\n * answers differs per person and per API key, and an answer (or a batch's\n * request) is only ever shared within one scope. A person's id and an API key\n * scope the cache across token rotations. A credential that names neither (a\n * session cookie, a token callback) shares nothing beyond its own auth object\n * and its current token, so a token swapped on the same object asks again.\n */\nfunction principalOf(auth: MentionAuthContext): string {\n const scope: string[] = [];\n if (auth.userId) scope.push(`user:${auth.userId}`);\n if (auth.apiKey) scope.push(`key:${fingerprint(auth.apiKey)}`);\n if (scope.length) return scope.join(\"|\");\n let own = authScopes.get(auth);\n if (!own) {\n authScopeCount += 1;\n own = `auth:${authScopeCount}`;\n authScopes.set(auth, own);\n }\n const token = auth.accessToken;\n return token ? `${own}|token:${fingerprint(token)}` : own;\n}\n\ninterface Waiter {\n resolve: (asset: MentionAsset | null) => void;\n reject: (error: unknown) => void;\n}\n\ninterface PendingBatch {\n auth: MentionAuthContext;\n waiters: Map<string, Waiter[]>;\n}\n\n/** The batch each backend and principal is gathering, until its window closes. */\nconst pendingBatches = new Map<string, PendingBatch>();\n\nfunction settle(waiters: Waiter[] | undefined, asset: MentionAsset | null): void {\n for (const waiter of waiters ?? []) waiter.resolve(asset);\n}\n\nfunction fail(waiters: Waiter[] | undefined, error: unknown): void {\n for (const waiter of waiters ?? []) waiter.reject(error);\n}\n\nfunction sendBatch(batchKey: string): void {\n const batch = pendingBatches.get(batchKey);\n if (!batch) return;\n pendingBatches.delete(batchKey);\n const ids = [...batch.waiters.keys()];\n for (let start = 0; start < ids.length; start += MENTION_ASSET_BATCH_SIZE) {\n const chunk = ids.slice(start, start + MENTION_ASSET_BATCH_SIZE);\n fetchGraphQL<MentionAssetsResponse>(batch.auth, MENTION_ASSETS_QUERY, {\n assetIds: chunk,\n }).then(\n (data) => {\n const found = new Map((data.getAssetsByIds ?? []).map((asset) => [asset.id, asset]));\n for (const id of chunk) {\n const asset = found.get(id);\n settle(\n batch.waiters.get(id),\n asset\n ? {\n id,\n title: asset.title?.trim() ?? \"\",\n assetType: asset.athenaOriginalType?.trim() ?? \"\",\n }\n : null,\n );\n }\n },\n (error: unknown) => {\n for (const id of chunk) fail(batch.waiters.get(id), error);\n },\n );\n }\n}\n\nfunction loadMentionAsset({\n auth,\n batchKey,\n assetId,\n}: {\n auth: MentionAuthContext;\n batchKey: string;\n assetId: string;\n}): Promise<MentionAsset | null> {\n return new Promise((resolve, reject) => {\n let batch = pendingBatches.get(batchKey);\n if (!batch) {\n batch = { auth, waiters: new Map() };\n pendingBatches.set(batchKey, batch);\n setTimeout(() => sendBatch(batchKey), MENTION_ASSET_BATCH_WINDOW_MS);\n }\n const waiters = batch.waiters.get(assetId) ?? [];\n waiters.push({ resolve, reject });\n batch.waiters.set(assetId, waiters);\n });\n}\n\n/** Chips rendered outside any query client (a host's own tray) still share one cache. */\nconst chipQueryClient = new QueryClient();\n\n/**\n * The asset an asset chip names: `undefined` while unknown (no lookup, still\n * loading, or the lookup failed), `null` when agora returned no asset the\n * viewer may read under that id, else its title and type.\n */\nexport function useMentionAsset(assetId: string | undefined): MentionAsset | null | undefined {\n const auth = useContext(MentionAuthCtx);\n const hostQueryClient = useContext(QueryClientContext);\n const lookupId = mentionAssetLookupId(assetId);\n const enabled = Boolean(auth && lookupId && canAuthenticate(auth));\n const backendUrl = auth?.backendUrl ?? \"\";\n const principal = auth ? principalOf(auth) : \"\";\n const { data } = useQuery(\n {\n queryKey: [\"athena-sdk\", \"mention-asset\", backendUrl, principal, lookupId ?? \"\"],\n queryFn: () => {\n if (!auth || !lookupId) return null;\n return loadMentionAsset({\n auth,\n batchKey: `${backendUrl}|${principal}`,\n assetId: lookupId,\n });\n },\n enabled,\n staleTime: MENTION_ASSET_STALE_TIME_MS,\n // Kept no longer than it is fresh: a title is shown again only after\n // agora answers again, never from a cache a minute past its check.\n gcTime: MENTION_ASSET_STALE_TIME_MS,\n retry: false,\n refetchOnWindowFocus: false,\n },\n hostQueryClient ?? chipQueryClient,\n );\n return enabled ? data : undefined;\n}\n\nexport function resetMentionAssetsForTests(): void {\n pendingBatches.clear();\n chipQueryClient.clear();\n}\n"],"names":["fetchGraphQL","QueryClient","useContext","MentionAuthCtx","QueryClientContext","useQuery"],"mappings":";;;;;AA4BO,MAAM,2BAA2B;AACjC,MAAM,gCAAgC;AAC7C,MAAM,8BAA8B;AAGpC,MAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyB7B,MAAM,YAAY;AAEX,SAAS,qBAAqB,SAA4C;AAC/E,QAAM,UAAS,mCAAS,OAAO,MAAM,KAAK,OAAM;AAChD,SAAO,UAAU,KAAK,MAAM,IAAI,SAAS;AAC3C;AAGA,SAAS,gBAAgB,MAAmC;AAC1D,SAAO,QAAQ,KAAK,eAAe,KAAK,UAAU,KAAK,eAAe,KAAK,cAAc;AAC3F;AAGA,SAAS,YAAY,YAA4B;AAC/C,MAAI,OAAO;AACX,WAAS,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS,GAAG;AACzD,YAAQ,WAAW,WAAW,KAAK;AACnC,WAAO,KAAK,KAAK,MAAM,QAAU,MAAM;AAAA,EACzC;AACA,SAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1C;AAGA,MAAM,iCAAiB,QAAA;AACvB,IAAI,iBAAiB;AAUrB,SAAS,YAAY,MAAkC;AACrD,QAAM,QAAkB,CAAA;AACxB,MAAI,KAAK,OAAQ,OAAM,KAAK,QAAQ,KAAK,MAAM,EAAE;AACjD,MAAI,KAAK,OAAQ,OAAM,KAAK,OAAO,YAAY,KAAK,MAAM,CAAC,EAAE;AAC7D,MAAI,MAAM,OAAQ,QAAO,MAAM,KAAK,GAAG;AACvC,MAAI,MAAM,WAAW,IAAI,IAAI;AAC7B,MAAI,CAAC,KAAK;AACR,sBAAkB;AAClB,UAAM,QAAQ,cAAc;AAC5B,eAAW,IAAI,MAAM,GAAG;AAAA,EAC1B;AACA,QAAM,QAAQ,KAAK;AACnB,SAAO,QAAQ,GAAG,GAAG,UAAU,YAAY,KAAK,CAAC,KAAK;AACxD;AAaA,MAAM,qCAAqB,IAAA;AAE3B,SAAS,OAAO,SAA+B,OAAkC;AAC/E,aAAW,UAAU,WAAW,CAAA,EAAI,QAAO,QAAQ,KAAK;AAC1D;AAEA,SAAS,KAAK,SAA+B,OAAsB;AACjE,aAAW,UAAU,WAAW,CAAA,EAAI,QAAO,OAAO,KAAK;AACzD;AAEA,SAAS,UAAU,UAAwB;AACzC,QAAM,QAAQ,eAAe,IAAI,QAAQ;AACzC,MAAI,CAAC,MAAO;AACZ,iBAAe,OAAO,QAAQ;AAC9B,QAAM,MAAM,CAAC,GAAG,MAAM,QAAQ,MAAM;AACpC,WAAS,QAAQ,GAAG,QAAQ,IAAI,QAAQ,SAAS,0BAA0B;AACzE,UAAM,QAAQ,IAAI,MAAM,OAAO,QAAQ,wBAAwB;AAC/DA,2BAAoC,MAAM,MAAM,sBAAsB;AAAA,MACpE,UAAU;AAAA,IAAA,CACX,EAAE;AAAA,MACD,CAAC,SAAS;;AACR,cAAM,QAAQ,IAAI,KAAK,KAAK,kBAAkB,CAAA,GAAI,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AACnF,mBAAW,MAAM,OAAO;AACtB,gBAAM,QAAQ,MAAM,IAAI,EAAE;AAC1B;AAAA,YACE,MAAM,QAAQ,IAAI,EAAE;AAAA,YACpB,QACI;AAAA,cACE;AAAA,cACA,SAAO,WAAM,UAAN,mBAAa,WAAU;AAAA,cAC9B,aAAW,WAAM,uBAAN,mBAA0B,WAAU;AAAA,YAAA,IAEjD;AAAA,UAAA;AAAA,QAER;AAAA,MACF;AAAA,MACA,CAAC,UAAmB;AAClB,mBAAW,MAAM,MAAO,MAAK,MAAM,QAAQ,IAAI,EAAE,GAAG,KAAK;AAAA,MAC3D;AAAA,IAAA;AAAA,EAEJ;AACF;AAEA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACF,GAIiC;AAC/B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,QAAQ,eAAe,IAAI,QAAQ;AACvC,QAAI,CAAC,OAAO;AACV,cAAQ,EAAE,MAAM,SAAS,oBAAI,MAAI;AACjC,qBAAe,IAAI,UAAU,KAAK;AAClC,iBAAW,MAAM,UAAU,QAAQ,GAAG,6BAA6B;AAAA,IACrE;AACA,UAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,KAAK,CAAA;AAC9C,YAAQ,KAAK,EAAE,SAAS,OAAA,CAAQ;AAChC,UAAM,QAAQ,IAAI,SAAS,OAAO;AAAA,EACpC,CAAC;AACH;AAGA,MAAM,kBAAkB,IAAIC,WAAAA,YAAA;AAOrB,SAAS,gBAAgB,SAA8D;AAC5F,QAAM,OAAOC,WAAAA,WAAWC,wBAAc;AACtC,QAAM,kBAAkBD,WAAAA,WAAWE,6BAAkB;AACrD,QAAM,WAAW,qBAAqB,OAAO;AAC7C,QAAM,UAAU,QAAQ,QAAQ,YAAY,gBAAgB,IAAI,CAAC;AACjE,QAAM,cAAa,6BAAM,eAAc;AACvC,QAAM,YAAY,OAAO,YAAY,IAAI,IAAI;AAC7C,QAAM,EAAE,SAASC,WAAAA;AAAAA,IACf;AAAA,MACE,UAAU,CAAC,cAAc,iBAAiB,YAAY,WAAW,YAAY,EAAE;AAAA,MAC/E,SAAS,MAAM;AACb,YAAI,CAAC,QAAQ,CAAC,SAAU,QAAO;AAC/B,eAAO,iBAAiB;AAAA,UACtB;AAAA,UACA,UAAU,GAAG,UAAU,IAAI,SAAS;AAAA,UACpC,SAAS;AAAA,QAAA,CACV;AAAA,MACH;AAAA,MACA;AAAA,MACA,WAAW;AAAA;AAAA;AAAA,MAGX,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,sBAAsB;AAAA,IAAA;AAAA,IAExB,mBAAmB;AAAA,EAAA;AAErB,SAAO,UAAU,OAAO;AAC1B;;;;;"}
1
+ {"version":3,"file":"use-mention-asset.cjs","sources":["../../../src/chat-ui/mentions/use-mention-asset.ts"],"sourcesContent":["/**\n * The asset an asset chip names, read the way Olympus's chips read it: its\n * title and type from agora's `getAssetChipDetails`, which reads only what a\n * chip draws (never an asset's whole row) and returns only the assets the\n * caller may read (an id they cannot read is left out of the answer, as is\n * another workspace's). A chip then shows the asset's own name and icon\n * instead of whatever label and `type=` its link carried. A backend that\n * predates the field is asked once: its chips keep their links' labels, and\n * nothing falls back to a whole-row read.\n *\n * Chips that mount together ask together: one GraphQL request per batch of up\n * to {@link MENTION_ASSET_BATCH_SIZE} ids, gathered over\n * {@link MENTION_ASSET_BATCH_WINDOW_MS}. Each answer is a TanStack query keyed\n * by backend, the person asking and the id, fresh for a minute, so a\n * transcript full of citations to one repository asks for it once.\n */\nimport { QueryClient, QueryClientContext, useQuery } from \"@tanstack/react-query\";\nimport { useContext } from \"react\";\nimport {\n AthenaGraphQLError,\n fetchGraphQL,\n type MentionAuthContext,\n MentionAuthCtx,\n} from \"./suggestions/data/api-client\";\n\n/** What a chip draws from the asset: its current title and its type. */\nexport interface MentionAsset {\n id: string;\n title: string;\n assetType: string;\n}\n\nexport const MENTION_ASSET_BATCH_SIZE = 50;\nexport const MENTION_ASSET_BATCH_WINDOW_MS = 16;\nconst MENTION_ASSET_STALE_TIME_MS = 60_000;\n\nconst MENTION_ASSETS_QUERY = `\n query MentionChipAssets($assetIds: [String!]!) {\n getAssetChipDetails(assetIds: $assetIds) {\n id\n title\n athenaOriginalType\n }\n }\n`;\n\ninterface MentionAssetsResponse {\n getAssetChipDetails?: Array<{\n id: string;\n title: string | null;\n athenaOriginalType: string | null;\n }> | null;\n}\n\n/**\n * The ids a lookup asks about: a stored asset, a session's\n * (`asset_thread_<uuid>`) included. A versioned id (`asset_x@3`) asks about its\n * asset. A live provider file (`asset_<catalog>_<file>`) is not asked about:\n * agora answers it from the provider, too slow for a name.\n */\nconst LOOKUP_ID =\n /^asset_(?:thread_)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Backends whose agora predates `getAssetChipDetails`, learned from their\n * answer: their chips are never asked about again.\n */\nconst backendsWithoutChipDetails = new Set<string>();\n\nfunction isMissingChipDetails(error: unknown): boolean {\n return (\n error instanceof AthenaGraphQLError &&\n (error.status === 400 || error.message.includes(\"getAssetChipDetails\"))\n );\n}\n\nexport function mentionAssetLookupId(assetId: string | undefined): string | null {\n const baseId = assetId?.trim().split(\"@\")[0] ?? \"\";\n return LOOKUP_ID.test(baseId) ? baseId : null;\n}\n\n/** Whether a request can carry a credential (`mentionRequestInit` refuses one that cannot). */\nfunction canAuthenticate(auth: MentionAuthContext): boolean {\n return Boolean(auth.accessToken || auth.apiKey || auth.credentials || auth.getAccessToken);\n}\n\n/** A short, stable stand-in for a credential in a cache key; never the credential itself. */\nfunction fingerprint(credential: string): string {\n let hash = 0x811c9dc5;\n for (let index = 0; index < credential.length; index += 1) {\n hash ^= credential.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n\n/** The scope each credential with no stable identity of its own gets: its own auth object. */\nconst authScopes = new WeakMap<MentionAuthContext, string>();\nlet authScopeCount = 0;\n\n/**\n * Who is asking, as narrowly as the credential says: what `getAssetsByIds`\n * answers differs per person and per API key, and an answer (or a batch's\n * request) is only ever shared within one scope. A person's id and an API key\n * scope the cache across token rotations. A credential that names neither (a\n * session cookie, a token callback) shares nothing beyond its own auth object\n * and its current token, so a token swapped on the same object asks again.\n */\nfunction principalOf(auth: MentionAuthContext): string {\n const scope: string[] = [];\n if (auth.userId) scope.push(`user:${auth.userId}`);\n if (auth.apiKey) scope.push(`key:${fingerprint(auth.apiKey)}`);\n if (scope.length) return scope.join(\"|\");\n let own = authScopes.get(auth);\n if (!own) {\n authScopeCount += 1;\n own = `auth:${authScopeCount}`;\n authScopes.set(auth, own);\n }\n const token = auth.accessToken;\n return token ? `${own}|token:${fingerprint(token)}` : own;\n}\n\ninterface Waiter {\n resolve: (asset: MentionAsset | null) => void;\n reject: (error: unknown) => void;\n}\n\ninterface PendingBatch {\n auth: MentionAuthContext;\n waiters: Map<string, Waiter[]>;\n}\n\n/** The batch each backend and principal is gathering, until its window closes. */\nconst pendingBatches = new Map<string, PendingBatch>();\n\nfunction settle(waiters: Waiter[] | undefined, asset: MentionAsset | null): void {\n for (const waiter of waiters ?? []) waiter.resolve(asset);\n}\n\nfunction fail(waiters: Waiter[] | undefined, error: unknown): void {\n for (const waiter of waiters ?? []) waiter.reject(error);\n}\n\nfunction sendBatch(batchKey: string): void {\n const batch = pendingBatches.get(batchKey);\n if (!batch) return;\n pendingBatches.delete(batchKey);\n const ids = [...batch.waiters.keys()];\n if (backendsWithoutChipDetails.has(batch.auth.backendUrl)) {\n for (const id of ids) settle(batch.waiters.get(id), null);\n return;\n }\n for (let start = 0; start < ids.length; start += MENTION_ASSET_BATCH_SIZE) {\n const chunk = ids.slice(start, start + MENTION_ASSET_BATCH_SIZE);\n fetchGraphQL<MentionAssetsResponse>(batch.auth, MENTION_ASSETS_QUERY, {\n assetIds: chunk,\n }).then(\n (data) => {\n const found = new Map(\n (data.getAssetChipDetails ?? []).map((asset) => [asset.id, asset]),\n );\n for (const id of chunk) {\n const asset = found.get(id);\n settle(\n batch.waiters.get(id),\n asset\n ? {\n id,\n title: asset.title?.trim() ?? \"\",\n assetType: asset.athenaOriginalType?.trim() ?? \"\",\n }\n : null,\n );\n }\n },\n (error: unknown) => {\n if (isMissingChipDetails(error)) {\n backendsWithoutChipDetails.add(batch.auth.backendUrl);\n for (const id of chunk) settle(batch.waiters.get(id), null);\n return;\n }\n for (const id of chunk) fail(batch.waiters.get(id), error);\n },\n );\n }\n}\n\nfunction loadMentionAsset({\n auth,\n batchKey,\n assetId,\n}: {\n auth: MentionAuthContext;\n batchKey: string;\n assetId: string;\n}): Promise<MentionAsset | null> {\n return new Promise((resolve, reject) => {\n let batch = pendingBatches.get(batchKey);\n if (!batch) {\n batch = { auth, waiters: new Map() };\n pendingBatches.set(batchKey, batch);\n setTimeout(() => sendBatch(batchKey), MENTION_ASSET_BATCH_WINDOW_MS);\n }\n const waiters = batch.waiters.get(assetId) ?? [];\n waiters.push({ resolve, reject });\n batch.waiters.set(assetId, waiters);\n });\n}\n\n/** Chips rendered outside any query client (a host's own tray) still share one cache. */\nconst chipQueryClient = new QueryClient();\n\n/**\n * The asset an asset chip names: `undefined` while unknown (no lookup, still\n * loading, or the lookup failed), `null` when agora returned no asset the\n * viewer may read under that id, else its title and type.\n */\nexport function useMentionAsset(assetId: string | undefined): MentionAsset | null | undefined {\n const auth = useContext(MentionAuthCtx);\n const hostQueryClient = useContext(QueryClientContext);\n const lookupId = mentionAssetLookupId(assetId);\n const enabled = Boolean(auth && lookupId && canAuthenticate(auth));\n const backendUrl = auth?.backendUrl ?? \"\";\n const principal = auth ? principalOf(auth) : \"\";\n const { data } = useQuery(\n {\n queryKey: [\"athena-sdk\", \"mention-asset\", backendUrl, principal, lookupId ?? \"\"],\n queryFn: () => {\n if (!auth || !lookupId) return null;\n return loadMentionAsset({\n auth,\n batchKey: `${backendUrl}|${principal}`,\n assetId: lookupId,\n });\n },\n enabled,\n staleTime: MENTION_ASSET_STALE_TIME_MS,\n // Kept no longer than it is fresh: a title is shown again only after\n // agora answers again, never from a cache a minute past its check.\n gcTime: MENTION_ASSET_STALE_TIME_MS,\n retry: false,\n refetchOnWindowFocus: false,\n },\n hostQueryClient ?? chipQueryClient,\n );\n return enabled ? data : undefined;\n}\n\nexport function resetMentionAssetsForTests(): void {\n pendingBatches.clear();\n backendsWithoutChipDetails.clear();\n chipQueryClient.clear();\n}\n"],"names":["AthenaGraphQLError","fetchGraphQL","QueryClient","useContext","MentionAuthCtx","QueryClientContext","useQuery"],"mappings":"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"}
@@ -1,19 +1,23 @@
1
1
  import { QueryClientContext, useQuery, QueryClient } from "@tanstack/react-query";
2
2
  import { useContext } from "react";
3
- import { MentionAuthCtx, fetchGraphQL } from "./suggestions/data/api-client.js";
3
+ import { MentionAuthCtx, fetchGraphQL, AthenaGraphQLError } from "./suggestions/data/api-client.js";
4
4
  const MENTION_ASSET_BATCH_SIZE = 50;
5
5
  const MENTION_ASSET_BATCH_WINDOW_MS = 16;
6
6
  const MENTION_ASSET_STALE_TIME_MS = 6e4;
7
7
  const MENTION_ASSETS_QUERY = `
8
8
  query MentionChipAssets($assetIds: [String!]!) {
9
- getAssetsByIds(assetIds: $assetIds) {
9
+ getAssetChipDetails(assetIds: $assetIds) {
10
10
  id
11
11
  title
12
12
  athenaOriginalType
13
13
  }
14
14
  }
15
15
  `;
16
- const LOOKUP_ID = /^asset_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
16
+ const LOOKUP_ID = /^asset_(?:thread_)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17
+ const backendsWithoutChipDetails = /* @__PURE__ */ new Set();
18
+ function isMissingChipDetails(error) {
19
+ return error instanceof AthenaGraphQLError && (error.status === 400 || error.message.includes("getAssetChipDetails"));
20
+ }
17
21
  function mentionAssetLookupId(assetId) {
18
22
  const baseId = (assetId == null ? void 0 : assetId.trim().split("@")[0]) ?? "";
19
23
  return LOOKUP_ID.test(baseId) ? baseId : null;
@@ -57,6 +61,10 @@ function sendBatch(batchKey) {
57
61
  if (!batch) return;
58
62
  pendingBatches.delete(batchKey);
59
63
  const ids = [...batch.waiters.keys()];
64
+ if (backendsWithoutChipDetails.has(batch.auth.backendUrl)) {
65
+ for (const id of ids) settle(batch.waiters.get(id), null);
66
+ return;
67
+ }
60
68
  for (let start = 0; start < ids.length; start += MENTION_ASSET_BATCH_SIZE) {
61
69
  const chunk = ids.slice(start, start + MENTION_ASSET_BATCH_SIZE);
62
70
  fetchGraphQL(batch.auth, MENTION_ASSETS_QUERY, {
@@ -64,7 +72,9 @@ function sendBatch(batchKey) {
64
72
  }).then(
65
73
  (data) => {
66
74
  var _a, _b;
67
- const found = new Map((data.getAssetsByIds ?? []).map((asset) => [asset.id, asset]));
75
+ const found = new Map(
76
+ (data.getAssetChipDetails ?? []).map((asset) => [asset.id, asset])
77
+ );
68
78
  for (const id of chunk) {
69
79
  const asset = found.get(id);
70
80
  settle(
@@ -78,6 +88,11 @@ function sendBatch(batchKey) {
78
88
  }
79
89
  },
80
90
  (error) => {
91
+ if (isMissingChipDetails(error)) {
92
+ backendsWithoutChipDetails.add(batch.auth.backendUrl);
93
+ for (const id of chunk) settle(batch.waiters.get(id), null);
94
+ return;
95
+ }
81
96
  for (const id of chunk) fail(batch.waiters.get(id), error);
82
97
  }
83
98
  );
@@ -1 +1 @@
1
- {"version":3,"file":"use-mention-asset.js","sources":["../../../src/chat-ui/mentions/use-mention-asset.ts"],"sourcesContent":["/**\n * The asset an asset chip names, read the way Olympus's chips read it: its\n * title and type from agora's `getAssetsByIds`, which returns only the assets\n * the caller may read (an id they cannot read is left out of the answer, as is\n * another workspace's). A chip then shows the asset's own name and icon\n * instead of whatever label and `type=` its link carried.\n *\n * Chips that mount together ask together: one GraphQL request per batch of up\n * to {@link MENTION_ASSET_BATCH_SIZE} ids, gathered over\n * {@link MENTION_ASSET_BATCH_WINDOW_MS}. Each answer is a TanStack query keyed\n * by backend, the person asking and the id, fresh for a minute, so a\n * transcript full of citations to one repository asks for it once.\n */\nimport { QueryClient, QueryClientContext, useQuery } from \"@tanstack/react-query\";\nimport { useContext } from \"react\";\nimport {\n fetchGraphQL,\n type MentionAuthContext,\n MentionAuthCtx,\n} from \"./suggestions/data/api-client\";\n\n/** What a chip draws from the asset: its current title and its type. */\nexport interface MentionAsset {\n id: string;\n title: string;\n assetType: string;\n}\n\nexport const MENTION_ASSET_BATCH_SIZE = 50;\nexport const MENTION_ASSET_BATCH_WINDOW_MS = 16;\nconst MENTION_ASSET_STALE_TIME_MS = 60_000;\n\n/** No metadata: a session's carries its token ledgers, and a chip needs a name and a type. */\nconst MENTION_ASSETS_QUERY = `\n query MentionChipAssets($assetIds: [String!]!) {\n getAssetsByIds(assetIds: $assetIds) {\n id\n title\n athenaOriginalType\n }\n }\n`;\n\ninterface MentionAssetsResponse {\n getAssetsByIds?: Array<{\n id: string;\n title: string | null;\n athenaOriginalType: string | null;\n }> | null;\n}\n\n/**\n * The ids a lookup asks about: a stored asset. A versioned id (`asset_x@3`)\n * asks about its asset. A live provider file (`asset_<catalog>_<file>`) is not\n * a row `getAssetsByIds` holds, and a session (`asset_thread_<uuid>`) is never\n * asked about: agora reads the whole row, and a session's metadata carries\n * its transcript's ledgers (megabytes on a long session) — too much for a name.\n */\nconst LOOKUP_ID = /^asset_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport function mentionAssetLookupId(assetId: string | undefined): string | null {\n const baseId = assetId?.trim().split(\"@\")[0] ?? \"\";\n return LOOKUP_ID.test(baseId) ? baseId : null;\n}\n\n/** Whether a request can carry a credential (`mentionRequestInit` refuses one that cannot). */\nfunction canAuthenticate(auth: MentionAuthContext): boolean {\n return Boolean(auth.accessToken || auth.apiKey || auth.credentials || auth.getAccessToken);\n}\n\n/** A short, stable stand-in for a credential in a cache key; never the credential itself. */\nfunction fingerprint(credential: string): string {\n let hash = 0x811c9dc5;\n for (let index = 0; index < credential.length; index += 1) {\n hash ^= credential.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n\n/** The scope each credential with no stable identity of its own gets: its own auth object. */\nconst authScopes = new WeakMap<MentionAuthContext, string>();\nlet authScopeCount = 0;\n\n/**\n * Who is asking, as narrowly as the credential says: what `getAssetsByIds`\n * answers differs per person and per API key, and an answer (or a batch's\n * request) is only ever shared within one scope. A person's id and an API key\n * scope the cache across token rotations. A credential that names neither (a\n * session cookie, a token callback) shares nothing beyond its own auth object\n * and its current token, so a token swapped on the same object asks again.\n */\nfunction principalOf(auth: MentionAuthContext): string {\n const scope: string[] = [];\n if (auth.userId) scope.push(`user:${auth.userId}`);\n if (auth.apiKey) scope.push(`key:${fingerprint(auth.apiKey)}`);\n if (scope.length) return scope.join(\"|\");\n let own = authScopes.get(auth);\n if (!own) {\n authScopeCount += 1;\n own = `auth:${authScopeCount}`;\n authScopes.set(auth, own);\n }\n const token = auth.accessToken;\n return token ? `${own}|token:${fingerprint(token)}` : own;\n}\n\ninterface Waiter {\n resolve: (asset: MentionAsset | null) => void;\n reject: (error: unknown) => void;\n}\n\ninterface PendingBatch {\n auth: MentionAuthContext;\n waiters: Map<string, Waiter[]>;\n}\n\n/** The batch each backend and principal is gathering, until its window closes. */\nconst pendingBatches = new Map<string, PendingBatch>();\n\nfunction settle(waiters: Waiter[] | undefined, asset: MentionAsset | null): void {\n for (const waiter of waiters ?? []) waiter.resolve(asset);\n}\n\nfunction fail(waiters: Waiter[] | undefined, error: unknown): void {\n for (const waiter of waiters ?? []) waiter.reject(error);\n}\n\nfunction sendBatch(batchKey: string): void {\n const batch = pendingBatches.get(batchKey);\n if (!batch) return;\n pendingBatches.delete(batchKey);\n const ids = [...batch.waiters.keys()];\n for (let start = 0; start < ids.length; start += MENTION_ASSET_BATCH_SIZE) {\n const chunk = ids.slice(start, start + MENTION_ASSET_BATCH_SIZE);\n fetchGraphQL<MentionAssetsResponse>(batch.auth, MENTION_ASSETS_QUERY, {\n assetIds: chunk,\n }).then(\n (data) => {\n const found = new Map((data.getAssetsByIds ?? []).map((asset) => [asset.id, asset]));\n for (const id of chunk) {\n const asset = found.get(id);\n settle(\n batch.waiters.get(id),\n asset\n ? {\n id,\n title: asset.title?.trim() ?? \"\",\n assetType: asset.athenaOriginalType?.trim() ?? \"\",\n }\n : null,\n );\n }\n },\n (error: unknown) => {\n for (const id of chunk) fail(batch.waiters.get(id), error);\n },\n );\n }\n}\n\nfunction loadMentionAsset({\n auth,\n batchKey,\n assetId,\n}: {\n auth: MentionAuthContext;\n batchKey: string;\n assetId: string;\n}): Promise<MentionAsset | null> {\n return new Promise((resolve, reject) => {\n let batch = pendingBatches.get(batchKey);\n if (!batch) {\n batch = { auth, waiters: new Map() };\n pendingBatches.set(batchKey, batch);\n setTimeout(() => sendBatch(batchKey), MENTION_ASSET_BATCH_WINDOW_MS);\n }\n const waiters = batch.waiters.get(assetId) ?? [];\n waiters.push({ resolve, reject });\n batch.waiters.set(assetId, waiters);\n });\n}\n\n/** Chips rendered outside any query client (a host's own tray) still share one cache. */\nconst chipQueryClient = new QueryClient();\n\n/**\n * The asset an asset chip names: `undefined` while unknown (no lookup, still\n * loading, or the lookup failed), `null` when agora returned no asset the\n * viewer may read under that id, else its title and type.\n */\nexport function useMentionAsset(assetId: string | undefined): MentionAsset | null | undefined {\n const auth = useContext(MentionAuthCtx);\n const hostQueryClient = useContext(QueryClientContext);\n const lookupId = mentionAssetLookupId(assetId);\n const enabled = Boolean(auth && lookupId && canAuthenticate(auth));\n const backendUrl = auth?.backendUrl ?? \"\";\n const principal = auth ? principalOf(auth) : \"\";\n const { data } = useQuery(\n {\n queryKey: [\"athena-sdk\", \"mention-asset\", backendUrl, principal, lookupId ?? \"\"],\n queryFn: () => {\n if (!auth || !lookupId) return null;\n return loadMentionAsset({\n auth,\n batchKey: `${backendUrl}|${principal}`,\n assetId: lookupId,\n });\n },\n enabled,\n staleTime: MENTION_ASSET_STALE_TIME_MS,\n // Kept no longer than it is fresh: a title is shown again only after\n // agora answers again, never from a cache a minute past its check.\n gcTime: MENTION_ASSET_STALE_TIME_MS,\n retry: false,\n refetchOnWindowFocus: false,\n },\n hostQueryClient ?? chipQueryClient,\n );\n return enabled ? data : undefined;\n}\n\nexport function resetMentionAssetsForTests(): void {\n pendingBatches.clear();\n chipQueryClient.clear();\n}\n"],"names":[],"mappings":";;;AA4BO,MAAM,2BAA2B;AACjC,MAAM,gCAAgC;AAC7C,MAAM,8BAA8B;AAGpC,MAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyB7B,MAAM,YAAY;AAEX,SAAS,qBAAqB,SAA4C;AAC/E,QAAM,UAAS,mCAAS,OAAO,MAAM,KAAK,OAAM;AAChD,SAAO,UAAU,KAAK,MAAM,IAAI,SAAS;AAC3C;AAGA,SAAS,gBAAgB,MAAmC;AAC1D,SAAO,QAAQ,KAAK,eAAe,KAAK,UAAU,KAAK,eAAe,KAAK,cAAc;AAC3F;AAGA,SAAS,YAAY,YAA4B;AAC/C,MAAI,OAAO;AACX,WAAS,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS,GAAG;AACzD,YAAQ,WAAW,WAAW,KAAK;AACnC,WAAO,KAAK,KAAK,MAAM,QAAU,MAAM;AAAA,EACzC;AACA,SAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1C;AAGA,MAAM,iCAAiB,QAAA;AACvB,IAAI,iBAAiB;AAUrB,SAAS,YAAY,MAAkC;AACrD,QAAM,QAAkB,CAAA;AACxB,MAAI,KAAK,OAAQ,OAAM,KAAK,QAAQ,KAAK,MAAM,EAAE;AACjD,MAAI,KAAK,OAAQ,OAAM,KAAK,OAAO,YAAY,KAAK,MAAM,CAAC,EAAE;AAC7D,MAAI,MAAM,OAAQ,QAAO,MAAM,KAAK,GAAG;AACvC,MAAI,MAAM,WAAW,IAAI,IAAI;AAC7B,MAAI,CAAC,KAAK;AACR,sBAAkB;AAClB,UAAM,QAAQ,cAAc;AAC5B,eAAW,IAAI,MAAM,GAAG;AAAA,EAC1B;AACA,QAAM,QAAQ,KAAK;AACnB,SAAO,QAAQ,GAAG,GAAG,UAAU,YAAY,KAAK,CAAC,KAAK;AACxD;AAaA,MAAM,qCAAqB,IAAA;AAE3B,SAAS,OAAO,SAA+B,OAAkC;AAC/E,aAAW,UAAU,WAAW,CAAA,EAAI,QAAO,QAAQ,KAAK;AAC1D;AAEA,SAAS,KAAK,SAA+B,OAAsB;AACjE,aAAW,UAAU,WAAW,CAAA,EAAI,QAAO,OAAO,KAAK;AACzD;AAEA,SAAS,UAAU,UAAwB;AACzC,QAAM,QAAQ,eAAe,IAAI,QAAQ;AACzC,MAAI,CAAC,MAAO;AACZ,iBAAe,OAAO,QAAQ;AAC9B,QAAM,MAAM,CAAC,GAAG,MAAM,QAAQ,MAAM;AACpC,WAAS,QAAQ,GAAG,QAAQ,IAAI,QAAQ,SAAS,0BAA0B;AACzE,UAAM,QAAQ,IAAI,MAAM,OAAO,QAAQ,wBAAwB;AAC/D,iBAAoC,MAAM,MAAM,sBAAsB;AAAA,MACpE,UAAU;AAAA,IAAA,CACX,EAAE;AAAA,MACD,CAAC,SAAS;;AACR,cAAM,QAAQ,IAAI,KAAK,KAAK,kBAAkB,CAAA,GAAI,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AACnF,mBAAW,MAAM,OAAO;AACtB,gBAAM,QAAQ,MAAM,IAAI,EAAE;AAC1B;AAAA,YACE,MAAM,QAAQ,IAAI,EAAE;AAAA,YACpB,QACI;AAAA,cACE;AAAA,cACA,SAAO,WAAM,UAAN,mBAAa,WAAU;AAAA,cAC9B,aAAW,WAAM,uBAAN,mBAA0B,WAAU;AAAA,YAAA,IAEjD;AAAA,UAAA;AAAA,QAER;AAAA,MACF;AAAA,MACA,CAAC,UAAmB;AAClB,mBAAW,MAAM,MAAO,MAAK,MAAM,QAAQ,IAAI,EAAE,GAAG,KAAK;AAAA,MAC3D;AAAA,IAAA;AAAA,EAEJ;AACF;AAEA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACF,GAIiC;AAC/B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,QAAQ,eAAe,IAAI,QAAQ;AACvC,QAAI,CAAC,OAAO;AACV,cAAQ,EAAE,MAAM,SAAS,oBAAI,MAAI;AACjC,qBAAe,IAAI,UAAU,KAAK;AAClC,iBAAW,MAAM,UAAU,QAAQ,GAAG,6BAA6B;AAAA,IACrE;AACA,UAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,KAAK,CAAA;AAC9C,YAAQ,KAAK,EAAE,SAAS,OAAA,CAAQ;AAChC,UAAM,QAAQ,IAAI,SAAS,OAAO;AAAA,EACpC,CAAC;AACH;AAGA,MAAM,kBAAkB,IAAI,YAAA;AAOrB,SAAS,gBAAgB,SAA8D;AAC5F,QAAM,OAAO,WAAW,cAAc;AACtC,QAAM,kBAAkB,WAAW,kBAAkB;AACrD,QAAM,WAAW,qBAAqB,OAAO;AAC7C,QAAM,UAAU,QAAQ,QAAQ,YAAY,gBAAgB,IAAI,CAAC;AACjE,QAAM,cAAa,6BAAM,eAAc;AACvC,QAAM,YAAY,OAAO,YAAY,IAAI,IAAI;AAC7C,QAAM,EAAE,SAAS;AAAA,IACf;AAAA,MACE,UAAU,CAAC,cAAc,iBAAiB,YAAY,WAAW,YAAY,EAAE;AAAA,MAC/E,SAAS,MAAM;AACb,YAAI,CAAC,QAAQ,CAAC,SAAU,QAAO;AAC/B,eAAO,iBAAiB;AAAA,UACtB;AAAA,UACA,UAAU,GAAG,UAAU,IAAI,SAAS;AAAA,UACpC,SAAS;AAAA,QAAA,CACV;AAAA,MACH;AAAA,MACA;AAAA,MACA,WAAW;AAAA;AAAA;AAAA,MAGX,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,sBAAsB;AAAA,IAAA;AAAA,IAExB,mBAAmB;AAAA,EAAA;AAErB,SAAO,UAAU,OAAO;AAC1B;"}
1
+ {"version":3,"file":"use-mention-asset.js","sources":["../../../src/chat-ui/mentions/use-mention-asset.ts"],"sourcesContent":["/**\n * The asset an asset chip names, read the way Olympus's chips read it: its\n * title and type from agora's `getAssetChipDetails`, which reads only what a\n * chip draws (never an asset's whole row) and returns only the assets the\n * caller may read (an id they cannot read is left out of the answer, as is\n * another workspace's). A chip then shows the asset's own name and icon\n * instead of whatever label and `type=` its link carried. A backend that\n * predates the field is asked once: its chips keep their links' labels, and\n * nothing falls back to a whole-row read.\n *\n * Chips that mount together ask together: one GraphQL request per batch of up\n * to {@link MENTION_ASSET_BATCH_SIZE} ids, gathered over\n * {@link MENTION_ASSET_BATCH_WINDOW_MS}. Each answer is a TanStack query keyed\n * by backend, the person asking and the id, fresh for a minute, so a\n * transcript full of citations to one repository asks for it once.\n */\nimport { QueryClient, QueryClientContext, useQuery } from \"@tanstack/react-query\";\nimport { useContext } from \"react\";\nimport {\n AthenaGraphQLError,\n fetchGraphQL,\n type MentionAuthContext,\n MentionAuthCtx,\n} from \"./suggestions/data/api-client\";\n\n/** What a chip draws from the asset: its current title and its type. */\nexport interface MentionAsset {\n id: string;\n title: string;\n assetType: string;\n}\n\nexport const MENTION_ASSET_BATCH_SIZE = 50;\nexport const MENTION_ASSET_BATCH_WINDOW_MS = 16;\nconst MENTION_ASSET_STALE_TIME_MS = 60_000;\n\nconst MENTION_ASSETS_QUERY = `\n query MentionChipAssets($assetIds: [String!]!) {\n getAssetChipDetails(assetIds: $assetIds) {\n id\n title\n athenaOriginalType\n }\n }\n`;\n\ninterface MentionAssetsResponse {\n getAssetChipDetails?: Array<{\n id: string;\n title: string | null;\n athenaOriginalType: string | null;\n }> | null;\n}\n\n/**\n * The ids a lookup asks about: a stored asset, a session's\n * (`asset_thread_<uuid>`) included. A versioned id (`asset_x@3`) asks about its\n * asset. A live provider file (`asset_<catalog>_<file>`) is not asked about:\n * agora answers it from the provider, too slow for a name.\n */\nconst LOOKUP_ID =\n /^asset_(?:thread_)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Backends whose agora predates `getAssetChipDetails`, learned from their\n * answer: their chips are never asked about again.\n */\nconst backendsWithoutChipDetails = new Set<string>();\n\nfunction isMissingChipDetails(error: unknown): boolean {\n return (\n error instanceof AthenaGraphQLError &&\n (error.status === 400 || error.message.includes(\"getAssetChipDetails\"))\n );\n}\n\nexport function mentionAssetLookupId(assetId: string | undefined): string | null {\n const baseId = assetId?.trim().split(\"@\")[0] ?? \"\";\n return LOOKUP_ID.test(baseId) ? baseId : null;\n}\n\n/** Whether a request can carry a credential (`mentionRequestInit` refuses one that cannot). */\nfunction canAuthenticate(auth: MentionAuthContext): boolean {\n return Boolean(auth.accessToken || auth.apiKey || auth.credentials || auth.getAccessToken);\n}\n\n/** A short, stable stand-in for a credential in a cache key; never the credential itself. */\nfunction fingerprint(credential: string): string {\n let hash = 0x811c9dc5;\n for (let index = 0; index < credential.length; index += 1) {\n hash ^= credential.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n\n/** The scope each credential with no stable identity of its own gets: its own auth object. */\nconst authScopes = new WeakMap<MentionAuthContext, string>();\nlet authScopeCount = 0;\n\n/**\n * Who is asking, as narrowly as the credential says: what `getAssetsByIds`\n * answers differs per person and per API key, and an answer (or a batch's\n * request) is only ever shared within one scope. A person's id and an API key\n * scope the cache across token rotations. A credential that names neither (a\n * session cookie, a token callback) shares nothing beyond its own auth object\n * and its current token, so a token swapped on the same object asks again.\n */\nfunction principalOf(auth: MentionAuthContext): string {\n const scope: string[] = [];\n if (auth.userId) scope.push(`user:${auth.userId}`);\n if (auth.apiKey) scope.push(`key:${fingerprint(auth.apiKey)}`);\n if (scope.length) return scope.join(\"|\");\n let own = authScopes.get(auth);\n if (!own) {\n authScopeCount += 1;\n own = `auth:${authScopeCount}`;\n authScopes.set(auth, own);\n }\n const token = auth.accessToken;\n return token ? `${own}|token:${fingerprint(token)}` : own;\n}\n\ninterface Waiter {\n resolve: (asset: MentionAsset | null) => void;\n reject: (error: unknown) => void;\n}\n\ninterface PendingBatch {\n auth: MentionAuthContext;\n waiters: Map<string, Waiter[]>;\n}\n\n/** The batch each backend and principal is gathering, until its window closes. */\nconst pendingBatches = new Map<string, PendingBatch>();\n\nfunction settle(waiters: Waiter[] | undefined, asset: MentionAsset | null): void {\n for (const waiter of waiters ?? []) waiter.resolve(asset);\n}\n\nfunction fail(waiters: Waiter[] | undefined, error: unknown): void {\n for (const waiter of waiters ?? []) waiter.reject(error);\n}\n\nfunction sendBatch(batchKey: string): void {\n const batch = pendingBatches.get(batchKey);\n if (!batch) return;\n pendingBatches.delete(batchKey);\n const ids = [...batch.waiters.keys()];\n if (backendsWithoutChipDetails.has(batch.auth.backendUrl)) {\n for (const id of ids) settle(batch.waiters.get(id), null);\n return;\n }\n for (let start = 0; start < ids.length; start += MENTION_ASSET_BATCH_SIZE) {\n const chunk = ids.slice(start, start + MENTION_ASSET_BATCH_SIZE);\n fetchGraphQL<MentionAssetsResponse>(batch.auth, MENTION_ASSETS_QUERY, {\n assetIds: chunk,\n }).then(\n (data) => {\n const found = new Map(\n (data.getAssetChipDetails ?? []).map((asset) => [asset.id, asset]),\n );\n for (const id of chunk) {\n const asset = found.get(id);\n settle(\n batch.waiters.get(id),\n asset\n ? {\n id,\n title: asset.title?.trim() ?? \"\",\n assetType: asset.athenaOriginalType?.trim() ?? \"\",\n }\n : null,\n );\n }\n },\n (error: unknown) => {\n if (isMissingChipDetails(error)) {\n backendsWithoutChipDetails.add(batch.auth.backendUrl);\n for (const id of chunk) settle(batch.waiters.get(id), null);\n return;\n }\n for (const id of chunk) fail(batch.waiters.get(id), error);\n },\n );\n }\n}\n\nfunction loadMentionAsset({\n auth,\n batchKey,\n assetId,\n}: {\n auth: MentionAuthContext;\n batchKey: string;\n assetId: string;\n}): Promise<MentionAsset | null> {\n return new Promise((resolve, reject) => {\n let batch = pendingBatches.get(batchKey);\n if (!batch) {\n batch = { auth, waiters: new Map() };\n pendingBatches.set(batchKey, batch);\n setTimeout(() => sendBatch(batchKey), MENTION_ASSET_BATCH_WINDOW_MS);\n }\n const waiters = batch.waiters.get(assetId) ?? [];\n waiters.push({ resolve, reject });\n batch.waiters.set(assetId, waiters);\n });\n}\n\n/** Chips rendered outside any query client (a host's own tray) still share one cache. */\nconst chipQueryClient = new QueryClient();\n\n/**\n * The asset an asset chip names: `undefined` while unknown (no lookup, still\n * loading, or the lookup failed), `null` when agora returned no asset the\n * viewer may read under that id, else its title and type.\n */\nexport function useMentionAsset(assetId: string | undefined): MentionAsset | null | undefined {\n const auth = useContext(MentionAuthCtx);\n const hostQueryClient = useContext(QueryClientContext);\n const lookupId = mentionAssetLookupId(assetId);\n const enabled = Boolean(auth && lookupId && canAuthenticate(auth));\n const backendUrl = auth?.backendUrl ?? \"\";\n const principal = auth ? principalOf(auth) : \"\";\n const { data } = useQuery(\n {\n queryKey: [\"athena-sdk\", \"mention-asset\", backendUrl, principal, lookupId ?? \"\"],\n queryFn: () => {\n if (!auth || !lookupId) return null;\n return loadMentionAsset({\n auth,\n batchKey: `${backendUrl}|${principal}`,\n assetId: lookupId,\n });\n },\n enabled,\n staleTime: MENTION_ASSET_STALE_TIME_MS,\n // Kept no longer than it is fresh: a title is shown again only after\n // agora answers again, never from a cache a minute past its check.\n gcTime: MENTION_ASSET_STALE_TIME_MS,\n retry: false,\n refetchOnWindowFocus: false,\n },\n hostQueryClient ?? chipQueryClient,\n );\n return enabled ? data : undefined;\n}\n\nexport function resetMentionAssetsForTests(): void {\n pendingBatches.clear();\n backendsWithoutChipDetails.clear();\n chipQueryClient.clear();\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const version = "0.52.1";
3
+ const version = "0.53.0";
4
4
  const packageJson = {
5
5
  version
6
6
  };
@@ -1,4 +1,4 @@
1
- const version = "0.52.1";
1
+ const version = "0.53.0";
2
2
  const packageJson = {
3
3
  version
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@athenaintel/react",
3
- "version": "0.52.1",
3
+ "version": "0.53.0",
4
4
  "description": "Athena React SDK — Build AI-powered chat applications with the Athena platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",