@athenaintel/react 0.26.5 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +19 -0
  2. package/dist/chat/AthenaChat.cjs +1 -1
  3. package/dist/chat/AthenaChat.cjs.map +1 -1
  4. package/dist/chat/AthenaChat.js +1 -1
  5. package/dist/chat/AthenaChat.js.map +1 -1
  6. package/dist/chat/StatewireQueuedMessages.cjs +44 -1
  7. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  8. package/dist/chat/StatewireQueuedMessages.js +45 -2
  9. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  10. package/dist/chat-ui/mentions/mention-chip.cjs +6 -0
  11. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  12. package/dist/chat-ui/mentions/mention-chip.d.ts +2 -0
  13. package/dist/chat-ui/mentions/mention-chip.js +6 -0
  14. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  15. package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
  16. package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
  17. package/dist/diagnostics/athena-diagnostics.js.map +1 -1
  18. package/dist/diagnostics/errors.cjs +11 -2
  19. package/dist/diagnostics/errors.cjs.map +1 -1
  20. package/dist/diagnostics/errors.d.ts +11 -2
  21. package/dist/diagnostics/errors.js +11 -2
  22. package/dist/diagnostics/errors.js.map +1 -1
  23. package/dist/packages/agent-runtime/src/tool-metadata.cjs +15 -0
  24. package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
  25. package/dist/packages/agent-runtime/src/tool-metadata.js +15 -0
  26. package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
  27. package/dist/packages/react/package.json.cjs +1 -1
  28. package/dist/packages/react/package.json.js +1 -1
  29. package/dist/provider/AthenaProvider.cjs +3 -0
  30. package/dist/provider/AthenaProvider.cjs.map +1 -1
  31. package/dist/provider/AthenaProvider.js +3 -0
  32. package/dist/provider/AthenaProvider.js.map +1 -1
  33. package/dist/runtime/auth.cjs +219 -104
  34. package/dist/runtime/auth.cjs.map +1 -1
  35. package/dist/runtime/auth.d.ts +79 -5
  36. package/dist/runtime/auth.js +219 -104
  37. package/dist/runtime/auth.js.map +1 -1
  38. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"StatewireQueuedMessages.cjs","sources":["../../src/chat/StatewireQueuedMessages.tsx"],"sourcesContent":["import { useAui, useAuiState } from '@assistant-ui/react';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport {\n useStatewireRuns,\n useStatewireSendCommand,\n useStatewireState,\n} from '@assistant-ui/react-statewire';\nimport {\n buildQueueEditCommand,\n buildStatewireQueueRows,\n type DeepAgentThreadStatus,\n describeQueueSendFailure,\n hasStatewireThreadExtras,\n isFailedSendEntryRace,\n isQueueEntryUnsettled,\n isServerUpdateStopSource,\n QUEUE_AUTO_CONTINUE_DELAY_MS,\n queueEntryText,\n queueLaneFlags,\n readDeepAgentStreamValues,\n readPrioritizedQueueIds,\n readRetryableRunRefusal,\n readRunMarkerFromSourceMessages,\n requireStatewireFailedActions,\n showQueueRowSendNow,\n type StatewireFailedActions,\n useDeepAgentThreadStatus,\n userStopBlocksQueueAutoContinue,\n} from '@athenaintel/agent-runtime';\nimport {\n AlertCircleIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronRightIcon,\n CircleIcon,\n Loader2Icon,\n PencilIcon,\n RotateCcwIcon,\n SendIcon,\n Trash2Icon,\n XIcon,\n ZapIcon,\n} from 'lucide-react';\nimport {\n type FC,\n type KeyboardEvent,\n type MutableRefObject,\n type ReactNode,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { ContinuePill } from '../chat-ui/messages/continue-pill';\nimport { cn } from '../lib/utils';\n\ndeclare module '@assistant-ui/core' {\n namespace Assistant {\n interface Commands {\n /**\n * The ⚡ on a queued row (`DeepAgentThread.run_prioritize`). With\n * `priority: true` the host moves the entry to the head of the queue\n * lane (behind entries prioritized before it), records it durably, and\n * the live run ends at its next model-call boundary — after the\n * in-flight tool chain, never mid-tool — so the settle delivers the\n * entry on top of the finished tool results instead of after the whole\n * turn. `priority: false` withdraws the request and leaves the entry\n * where it is.\n */\n runPrioritize: {\n type: 'run/prioritize';\n runId: string;\n messageId: string;\n priority: boolean;\n };\n }\n }\n}\n\n/**\n * The one queued-messages tray for the server-owned statewire queue and steer\n * lanes, mounted by the SDK's `AthenaChat`, the Chrome side panel, the Office\n * task pane, and Olympus.\n *\n * A message sent mid-run queues behind the active run. Rows offer \"Send now\"\n * (a steer-lane move — the host stops the run and dispatches it once the run\n * settles; on a stopped/errored thread the same move dispatches immediately),\n * \"Prioritize\" (`run/prioritize`, the ⚡ — the non-interrupting analog: the\n * live run ends at its next model-call boundary so the entry lands on top of\n * the finished tool results), inline edit (re-sent under the entry's own id\n * on its own lane, which the host folds as an in-place content update), and\n * remove (`run/dequeue`). While the thread rests stopped/errored, stranded\n * steer-lane entries list ahead of the queue (they dispatch first, as one\n * batch, so they carry Remove but no per-row Send now) and a Continue pill\n * steer-promotes the queue head — a steer entry moved onto its own lane is a\n * no-op, so a steer-only rest state gets no dead button.\n *\n * A row whose send has not settled yet (`isQueueEntryUnsettled` — this\n * client's optimistic fold, not a host-admitted lane entry) renders as\n * \"Sending…\" with no acting affordances: admission is server-side and can\n * still refuse the message. Failed run sends (`runs.failed`) list after the\n * lanes with Retry (`retryFailed`) and Discard (`discardFailed`).\n *\n * With `autoContinue`, an ordinary stranded settle promotes the queue head\n * once after `QUEUE_AUTO_CONTINUE_DELAY_MS`. User stops remain manual;\n * infrastructure pauses and retryable dispatch refusals recover on the server.\n *\n * Renders nothing on the legacy transport or when every lane is empty.\n */\n\nexport type StatewireQueueEvent =\n | {\n type: 'auto-continued';\n threadId: string | null;\n status: DeepAgentThreadStatus;\n headEntryId: string;\n }\n | {\n type: 'failed-send-action-raced';\n threadId: string | null;\n messageId: string;\n action: 'retry' | 'discard';\n error: string;\n };\n\nexport interface StatewireQueueRowContentContext {\n text: string;\n index: number;\n item: StatewireThread.RunsQueueEntry;\n}\n\nexport interface StatewireQueueEditorContext {\n value: string;\n onChange: (value: string) => void;\n /** Save the edit; a blank or unchanged text sends nothing but closes the editor. */\n onSave: (text: string) => void;\n onCancel: () => void;\n}\n\nexport interface StatewireQueuedMessagesProps {\n /** Surface-styling overrides merged onto the default classes. */\n classNames?: {\n /** The tray container. */\n root?: string;\n /** The row list. */\n list?: string;\n };\n /**\n * Steer-promote a stranded queue head automatically after a settle (see the\n * component docs). Off by default: a host opts in to dispatching runs on the\n * user's behalf. Pass `{ delayMs }` to tune the grace before the promotion\n * (defaults to `QUEUE_AUTO_CONTINUE_DELAY_MS`).\n */\n autoContinue?: boolean | { delayMs?: number };\n /** Identifies the thread for once-per-entry auto-continue attempts and events. */\n threadId?: string | null;\n /** Telemetry sink for the tray's notable moments. */\n onEvent?: (event: StatewireQueueEvent) => void;\n /** Replace the plain-text row body (e.g. a mention-aware renderer). */\n renderContent?: (context: StatewireQueueRowContentContext) => ReactNode;\n /** Replace the plain textarea inline editor (e.g. the host's composer editor). */\n renderEditor?: (context: StatewireQueueEditorContext) => ReactNode;\n}\n\nexport const StatewireQueuedMessages: FC<StatewireQueuedMessagesProps> = (props) => {\n const extras = useAuiState((s) => s.thread.extras);\n if (!hasStatewireThreadExtras(extras)) return null;\n return <StatewireQueuedMessagesInner {...props} />;\n};\n\nconst StatewireQueuedMessagesInner: FC<StatewireQueuedMessagesProps> = ({\n classNames,\n autoContinue = false,\n threadId = null,\n onEvent,\n renderContent,\n renderEditor,\n}) => {\n // The QUEUE lane only: composer.queue prepends the steer lane, whose entries\n // already render in the message list. A non-empty queue implies a live run\n // entry (the host retires runId only once both lanes drain), so the row\n // controls can always send.\n const runs = useStatewireRuns();\n const { queue, steerQueue, runId } = runs;\n // `failed` joined the runs projection in react-statewire 0.24; a projection\n // without it (an older lib, a stubbed runs read) must not crash the tray.\n const failed = runs.failed ?? [];\n // The host's durable prioritized-id record: a raw path read (the selector\n // must return a stable reference), with the lookup set derived below.\n const rawPrioritizedQueueIds = useStatewireState(\n (state) => readDeepAgentStreamValues(state).prioritizedQueueIds,\n );\n const prioritizedQueueIds = useMemo(\n () => new Set(readPrioritizedQueueIds(rawPrioritizedQueueIds)),\n [rawPrioritizedQueueIds],\n );\n const status = useDeepAgentThreadStatus();\n const { isRunning, isContinuable } = queueLaneFlags(status);\n const aui = useAui();\n const sendCommand = useStatewireSendCommand();\n const listId = useId();\n const [expanded, setExpanded] = useState(true);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editingText, setEditingText] = useState('');\n // Head entries already auto-promoted once on this mount: an attempt whose\n // continue run dies and re-strands the SAME entry must not loop — the manual\n // Continue pill is the fallback for a repeatedly failing dispatch. Kept on\n // the tray (not the auto-continue child) so it survives the empty-lane\n // renders between a promotion and its outcome.\n const autoContinuedRef = useRef<Set<string>>(new Set());\n const onEventRef = useRef(onEvent);\n onEventRef.current = onEvent;\n\n const rows = buildStatewireQueueRows({ queue, steerQueue, isContinuable });\n // Continue promotes the QUEUE head only: a queue→steer move dispatches the\n // stranded steer lane plus this entry, while a steer entry moved onto its\n // own lane is a no-op.\n const queueHeadId = queue[0]?.id ?? null;\n const retryableRefusal = readRetryableRunRefusal(runs.error) !== null;\n const autoContinueOptions = autoContinue === true ? {} : autoContinue || null;\n\n /**\n * Re-send the entry under its own id on its own lane: the host replaces the\n * queued message's content in place, leaving its queue position and lane\n * untouched (a same-id send never dispatches).\n */\n const saveEdit = (\n item: StatewireThread.RunsQueueEntry,\n lane: 'queue' | 'steer',\n text: string,\n ): void => {\n setEditingId(null);\n setEditingText('');\n const command = buildQueueEditCommand({ item, lane, runId, text });\n if (command !== null) sendCommand(command);\n };\n\n /**\n * Retry/Discard act on a rendered lane snapshot that can trail the session\n * record; acting on an entry statewire already dropped throws. Nothing is\n * lost in that race, so it is recorded instead of crashing the click.\n */\n const runFailedSendAction = ({\n action,\n messageId,\n run,\n }: {\n action: 'retry' | 'discard';\n messageId: string;\n run: (actions: StatewireFailedActions) => void;\n }): void => {\n try {\n run(requireStatewireFailedActions(aui.thread().getState().extras));\n } catch (error) {\n if (!(error instanceof Error) || !isFailedSendEntryRace(error)) throw error;\n onEvent?.({\n type: 'failed-send-action-raced',\n threadId,\n messageId,\n action,\n error: error.message,\n });\n }\n };\n\n if (rows.length === 0 && failed.length === 0) return null;\n\n const renderRowContent = (context: StatewireQueueRowContentContext): ReactNode =>\n renderContent ? (\n renderContent(context)\n ) : (\n <p className=\"min-w-0 flex-1 whitespace-pre-wrap break-words text-foreground text-xs\">\n {context.text || 'Queued attachment'}\n </p>\n );\n\n return (\n <section\n aria-label=\"Queued messages\"\n className={cn(\n 'aui-statewire-queue w-full rounded-xl border border-border/70 bg-muted/30 px-2 py-1.5',\n classNames?.root,\n )}\n >\n {autoContinueOptions ? (\n <QueueAutoContinue\n delayMs={autoContinueOptions.delayMs ?? QUEUE_AUTO_CONTINUE_DELAY_MS}\n queueHeadId={queueHeadId}\n retryableRefusal={retryableRefusal}\n isContinuable={isContinuable}\n status={status}\n threadId={threadId}\n editing={editingId !== null}\n continuedRef={autoContinuedRef}\n onEventRef={onEventRef}\n />\n ) : null}\n <div className=\"flex items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n aria-expanded={expanded}\n aria-controls={listId}\n className=\"flex items-center gap-1 rounded px-1 py-1 font-medium text-muted-foreground text-xs hover:text-foreground\"\n >\n {expanded ? (\n <ChevronDownIcon className=\"size-3.5\" />\n ) : (\n <ChevronRightIcon className=\"size-3.5\" />\n )}\n {rows.length + failed.length} queued\n </button>\n {isContinuable && queueHeadId !== null ? (\n <ContinuePill\n ariaLabel=\"Continue with queued messages\"\n onClick={() => aui.composer.queueItem({ id: queueHeadId }).move({ lane: 'steer' })}\n />\n ) : null}\n </div>\n {expanded ? (\n <ul\n id={listId}\n className={cn('max-h-40 overflow-y-auto', classNames?.list)}\n data-testid=\"athena-statewire-queue\"\n >\n {rows.map(({ item, lane }, index) => {\n const text = queueEntryText(item.parts);\n const isUnsettled = isQueueEntryUnsettled(item);\n const isEditing = editingId === item.id;\n const showSendNow =\n !isUnsettled && showQueueRowSendNow({ lane, index, isRunning, isContinuable });\n const showPrioritize = !isUnsettled && lane === 'queue' && isRunning && runId !== null;\n const isPrioritized = prioritizedQueueIds.has(item.id);\n const editorContext: StatewireQueueEditorContext = {\n value: editingText,\n onChange: setEditingText,\n onSave: (next) => saveEdit(item, lane, next),\n onCancel: () => {\n setEditingId(null);\n setEditingText('');\n },\n };\n return (\n <li\n key={item.id}\n className=\"group flex items-start gap-2 rounded-md px-1 py-1.5 hover:bg-background\"\n >\n {isUnsettled ? (\n <Loader2Icon className=\"mt-0.5 size-3.5 shrink-0 animate-spin text-muted-foreground/60\" />\n ) : (\n <CircleIcon className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground/40\" />\n )}\n {isEditing ? (\n renderEditor ? (\n renderEditor(editorContext)\n ) : (\n <DefaultQueueEditor {...editorContext} />\n )\n ) : (\n renderRowContent({ text, index, item })\n )}\n <div\n className={cn('flex shrink-0 items-center gap-1', isEditing && 'hidden')}\n data-testid=\"statewire-queue-row-actions\"\n >\n {isUnsettled ? (\n <span\n className=\"px-1 text-[10px] text-muted-foreground\"\n data-testid=\"statewire-queued-message-sending\"\n >\n Sending…\n </span>\n ) : null}\n {showSendNow ? (\n <button\n type=\"button\"\n aria-label=\"Send now\"\n onClick={() =>\n aui.composer.queueItem({ id: item.id }).move({ lane: 'steer' })\n }\n className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 px-2 py-1 font-medium text-[10px] text-primary hover:bg-primary/5\"\n >\n <SendIcon className=\"size-2.5\" />\n Send now\n </button>\n ) : null}\n {showPrioritize ? (\n <button\n type=\"button\"\n aria-label={isPrioritized ? 'Remove priority' : 'Prioritize'}\n aria-pressed={isPrioritized}\n title={\n isPrioritized\n ? 'Remove priority'\n : 'Prioritize this message. Delivered as soon as the current step ends.'\n }\n onClick={() => {\n if (runId === null) return;\n sendCommand({\n type: 'run/prioritize',\n runId,\n messageId: item.id,\n priority: !isPrioritized,\n });\n }}\n className={cn(\n 'rounded p-1 transition-colors',\n isPrioritized\n ? 'text-amber-500 hover:text-amber-600'\n : 'text-muted-foreground hover:text-amber-500',\n )}\n >\n <ZapIcon className=\"size-3\" />\n </button>\n ) : null}\n {!isUnsettled ? (\n <button\n type=\"button\"\n aria-label=\"Edit queued message\"\n onClick={() => {\n setEditingId(item.id);\n setEditingText(text);\n }}\n className=\"rounded p-1 text-muted-foreground transition-colors hover:text-primary\"\n >\n <PencilIcon className=\"size-3\" />\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label=\"Remove queued message\"\n onClick={() => aui.composer.queueItem({ id: item.id }).remove()}\n className={cn(\n 'rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive',\n !showSendNow &&\n 'opacity-0 focus-visible:opacity-100 group-hover:opacity-100',\n )}\n >\n <Trash2Icon className=\"size-3\" />\n </button>\n </div>\n </li>\n );\n })}\n {failed.map((item, index) => (\n <li\n key={item.id}\n className=\"group flex items-start gap-2 rounded-md px-1 py-1.5 hover:bg-background\"\n >\n <AlertCircleIcon className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" />\n {renderRowContent({\n text: queueEntryText(item.parts),\n index: rows.length + index,\n item,\n })}\n <div\n className=\"flex shrink-0 items-center gap-1\"\n data-testid=\"statewire-queue-row-actions\"\n >\n <span\n className=\"px-1 text-[10px] text-destructive\"\n title={describeQueueSendFailure(item.sendFailure)}\n >\n Failed to send\n </span>\n <button\n type=\"button\"\n aria-label=\"Retry failed message\"\n onClick={() =>\n runFailedSendAction({\n action: 'retry',\n messageId: item.id,\n run: (actions) => actions.retryFailed(item.id),\n })\n }\n className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 px-2 py-1 font-medium text-[10px] text-primary hover:bg-primary/5\"\n >\n <RotateCcwIcon className=\"size-2.5\" />\n Retry\n </button>\n <button\n type=\"button\"\n aria-label=\"Discard failed message\"\n onClick={() =>\n runFailedSendAction({\n action: 'discard',\n messageId: item.id,\n run: (actions) => actions.discardFailed(item.id),\n })\n }\n className=\"rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2Icon className=\"size-3\" />\n </button>\n </div>\n </li>\n ))}\n </ul>\n ) : null}\n </section>\n );\n};\n\n/** Promotes ordinary stranded queue heads once; infrastructure recovery stays server-owned. */\nconst QueueAutoContinue: FC<{\n delayMs: number;\n queueHeadId: string | null;\n retryableRefusal: boolean;\n isContinuable: boolean;\n status: DeepAgentThreadStatus;\n threadId: string | null;\n editing: boolean;\n continuedRef: MutableRefObject<Set<string>>;\n onEventRef: MutableRefObject<((event: StatewireQueueEvent) => void) | undefined>;\n}> = ({\n delayMs,\n queueHeadId,\n retryableRefusal,\n isContinuable,\n status,\n threadId,\n editing,\n continuedRef,\n onEventRef,\n}) => {\n const aui = useAui();\n // The settle that stranded the queue is described by the LAST message's run\n // marker (a marker deeper in history was superseded by a later send). The\n // selectors return primitives so the subscriptions stay shallow-stable.\n const lastMessageMarker = useAuiState((s) => {\n const last = s.thread.messages.at(-1);\n return last ? readRunMarkerFromSourceMessages(last)?.marker : undefined;\n });\n const lastMessageMarkerSource = useAuiState((s) => {\n const last = s.thread.messages.at(-1);\n return last ? readRunMarkerFromSourceMessages(last)?.source : undefined;\n });\n const userStopBlocks = userStopBlocksQueueAutoContinue({\n marker: lastMessageMarker,\n source: lastMessageMarkerSource,\n });\n\n useEffect(() => {\n if (\n !isContinuable ||\n userStopBlocks ||\n retryableRefusal ||\n isServerUpdateStopSource(lastMessageMarkerSource)\n ) return;\n // An open inline edit means the user is mid-revision on some row —\n // dispatching underneath them could send text they are about to change.\n if (editing) return;\n if (queueHeadId === null) return;\n const attemptKey = `${threadId ?? 'unknown'}:${queueHeadId}`;\n if (continuedRef.current.has(attemptKey)) return;\n const timeoutId = setTimeout(() => {\n continuedRef.current.add(attemptKey);\n onEventRef.current?.({ type: 'auto-continued', threadId, status, headEntryId: queueHeadId });\n aui.composer.queueItem({ id: queueHeadId }).move({ lane: 'steer' });\n }, delayMs);\n return () => clearTimeout(timeoutId);\n }, [\n continuedRef,\n aui,\n delayMs,\n editing,\n isContinuable,\n onEventRef,\n queueHeadId,\n retryableRefusal,\n lastMessageMarkerSource,\n status,\n threadId,\n userStopBlocks,\n ]);\n\n return null;\n};\n\n/** Plain textarea editor: Enter saves, Shift+Enter breaks a line, Escape cancels. */\nconst DefaultQueueEditor: FC<StatewireQueueEditorContext> = ({\n value,\n onChange,\n onSave,\n onCancel,\n}) => {\n const canSave = value.trim().length > 0;\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n if (canSave) onSave(value);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n onCancel();\n }\n };\n return (\n <div className=\"flex min-w-0 flex-1 items-start gap-1\">\n <textarea\n autoFocus\n aria-label=\"Edit message\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={Math.min(6, Math.max(1, value.split('\\n').length))}\n className=\"min-w-0 flex-1 resize-none rounded-md border border-border bg-background px-2 py-1 text-foreground text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring/50\"\n />\n <button\n type=\"button\"\n aria-label=\"Save edit\"\n disabled={!canSave}\n onClick={() => onSave(value)}\n className={cn(\n 'rounded p-1 text-primary transition-colors hover:bg-primary/10',\n !canSave && 'cursor-not-allowed opacity-40',\n )}\n >\n <CheckIcon className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Cancel edit\"\n onClick={onCancel}\n className=\"rounded p-1 text-muted-foreground transition-colors hover:bg-muted\"\n >\n <XIcon className=\"size-3.5\" />\n </button>\n </div>\n );\n};\n"],"names":["useAuiState","hasStatewireThreadExtras","jsx","useStatewireRuns","queue","useStatewireState","readDeepAgentStreamValues","useMemo","readPrioritizedQueueIds","useDeepAgentThreadStatus","queueLaneFlags","useAui","useStatewireSendCommand","useId","useState","useRef","buildStatewireQueueRows","readRetryableRunRefusal","buildQueueEditCommand","requireStatewireFailedActions","isFailedSendEntryRace","jsxs","cn","QUEUE_AUTO_CONTINUE_DELAY_MS","ChevronDownIcon","ChevronRightIcon","ContinuePill","queueEntryText","isQueueEntryUnsettled","showQueueRowSendNow","Loader2Icon","CircleIcon","SendIcon","ZapIcon","PencilIcon","Trash2Icon","AlertCircleIcon","describeQueueSendFailure","RotateCcwIcon","readRunMarkerFromSourceMessages","userStopBlocksQueueAutoContinue","useEffect","isServerUpdateStopSource","CheckIcon","XIcon"],"mappings":"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"}
1
+ {"version":3,"file":"StatewireQueuedMessages.cjs","sources":["../../src/chat/StatewireQueuedMessages.tsx"],"sourcesContent":["import { useAui, useAuiState } from '@assistant-ui/react';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport {\n useStatewireRuns,\n useStatewireSendCommand,\n useStatewireState,\n} from '@assistant-ui/react-statewire';\nimport {\n buildQueueEditCommand,\n buildStatewireQueueRows,\n type DeepAgentThreadStatus,\n describeQueueSendFailure,\n hasStatewireThreadExtras,\n isFailedSendEntryRace,\n isQueueEntryUnsettled,\n isServerUpdateStopSource,\n QUEUE_AUTO_CONTINUE_DELAY_MS,\n queueEntryText,\n queueLaneFlags,\n readDeepAgentStreamValues,\n readPrioritizedQueueIds,\n readRetryableRunRefusal,\n readRunMarkerFromSourceMessages,\n requireStatewireFailedActions,\n showQueueRowSendNow,\n type StatewireFailedActions,\n useDeepAgentThreadStatus,\n userStopBlocksQueueAutoContinue,\n} from '@athenaintel/agent-runtime';\nimport {\n AlertCircleIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronRightIcon,\n CircleIcon,\n Loader2Icon,\n PencilIcon,\n RotateCcwIcon,\n SendIcon,\n Trash2Icon,\n XIcon,\n ZapIcon,\n} from 'lucide-react';\nimport {\n type FC,\n type KeyboardEvent,\n type MutableRefObject,\n type ReactNode,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { ContinuePill } from '../chat-ui/messages/continue-pill';\nimport { mentionsToPlainText, parseMentionsInText } from '../chat-ui/mentions/mention-chip';\nimport { cn } from '../lib/utils';\n\ndeclare module '@assistant-ui/core' {\n namespace Assistant {\n interface Commands {\n /**\n * The ⚡ on a queued row (`DeepAgentThread.run_prioritize`). With\n * `priority: true` the host moves the entry to the head of the queue\n * lane (behind entries prioritized before it), records it durably, and\n * the live run ends at its next model-call boundary — after the\n * in-flight tool chain, never mid-tool — so the settle delivers the\n * entry on top of the finished tool results instead of after the whole\n * turn. `priority: false` withdraws the request and leaves the entry\n * where it is.\n */\n runPrioritize: {\n type: 'run/prioritize';\n runId: string;\n messageId: string;\n priority: boolean;\n };\n }\n }\n}\n\n/**\n * The one queued-messages tray for the server-owned statewire queue and steer\n * lanes, mounted by the SDK's `AthenaChat`, the Chrome side panel, the Office\n * task pane, and Olympus.\n *\n * A message sent mid-run queues behind the active run. Rows offer \"Send now\"\n * (a steer-lane move — the host stops the run and dispatches it once the run\n * settles; on a stopped/errored thread the same move dispatches immediately),\n * \"Prioritize\" (`run/prioritize`, the ⚡ — the non-interrupting analog: the\n * live run ends at its next model-call boundary so the entry lands on top of\n * the finished tool results), inline edit (re-sent under the entry's own id\n * on its own lane, which the host folds as an in-place content update), and\n * remove (`run/dequeue`). While the thread rests stopped/errored, stranded\n * steer-lane entries list ahead of the queue (they dispatch first, as one\n * batch, so they carry Remove but no per-row Send now) and a Continue pill\n * steer-promotes the queue head — a steer entry moved onto its own lane is a\n * no-op, so a steer-only rest state gets no dead button.\n *\n * A row whose send has not settled yet (`isQueueEntryUnsettled` — this\n * client's optimistic fold, not a host-admitted lane entry) renders as\n * \"Sending…\" with no acting affordances: admission is server-side and can\n * still refuse the message. Failed run sends (`runs.failed`) list after the\n * lanes with Retry (`retryFailed`) and Discard (`discardFailed`).\n *\n * With `autoContinue`, an ordinary stranded settle promotes the queue head\n * once after `QUEUE_AUTO_CONTINUE_DELAY_MS`. User stops remain manual;\n * infrastructure pauses and retryable dispatch refusals recover on the server.\n *\n * Renders nothing on the legacy transport or when every lane is empty.\n */\n\nexport type StatewireQueueEvent =\n | {\n type: 'auto-continued';\n threadId: string | null;\n status: DeepAgentThreadStatus;\n headEntryId: string;\n }\n | {\n type: 'failed-send-action-raced';\n threadId: string | null;\n messageId: string;\n action: 'retry' | 'discard';\n error: string;\n };\n\nexport interface StatewireQueueRowContentContext {\n text: string;\n index: number;\n item: StatewireThread.RunsQueueEntry;\n}\n\nexport interface StatewireQueueEditorContext {\n value: string;\n onChange: (value: string) => void;\n /** Save the edit; a blank or unchanged text sends nothing but closes the editor. */\n onSave: (text: string) => void;\n onCancel: () => void;\n}\n\nexport interface StatewireQueuedMessagesProps {\n /** Surface-styling overrides merged onto the default classes. */\n classNames?: {\n /** The tray container. */\n root?: string;\n /** The row list. */\n list?: string;\n };\n /**\n * Steer-promote a stranded queue head automatically after a settle (see the\n * component docs). Off by default: a host opts in to dispatching runs on the\n * user's behalf. Pass `{ delayMs }` to tune the grace before the promotion\n * (defaults to `QUEUE_AUTO_CONTINUE_DELAY_MS`).\n */\n autoContinue?: boolean | { delayMs?: number };\n /** Identifies the thread for once-per-entry auto-continue attempts and events. */\n threadId?: string | null;\n /** Telemetry sink for the tray's notable moments. */\n onEvent?: (event: StatewireQueueEvent) => void;\n /** Replace the plain-text row body (e.g. a mention-aware renderer). */\n renderContent?: (context: StatewireQueueRowContentContext) => ReactNode;\n /** Replace the plain textarea inline editor (e.g. the host's composer editor). */\n renderEditor?: (context: StatewireQueueEditorContext) => ReactNode;\n}\n\nexport const StatewireQueuedMessages: FC<StatewireQueuedMessagesProps> = (props) => {\n const extras = useAuiState((s) => s.thread.extras);\n if (!hasStatewireThreadExtras(extras)) return null;\n return <StatewireQueuedMessagesInner {...props} />;\n};\n\nconst StatewireQueuedMessagesInner: FC<StatewireQueuedMessagesProps> = ({\n classNames,\n autoContinue = false,\n threadId = null,\n onEvent,\n renderContent,\n renderEditor,\n}) => {\n // The QUEUE lane only: composer.queue prepends the steer lane, whose entries\n // already render in the message list. A non-empty queue implies a live run\n // entry (the host retires runId only once both lanes drain), so the row\n // controls can always send.\n const runs = useStatewireRuns();\n const { queue, steerQueue, runId } = runs;\n // `failed` joined the runs projection in react-statewire 0.24; a projection\n // without it (an older lib, a stubbed runs read) must not crash the tray.\n const failed = runs.failed ?? [];\n // The host's durable prioritized-id record: a raw path read (the selector\n // must return a stable reference), with the lookup set derived below.\n const rawPrioritizedQueueIds = useStatewireState(\n (state) => readDeepAgentStreamValues(state).prioritizedQueueIds,\n );\n const prioritizedQueueIds = useMemo(\n () => new Set(readPrioritizedQueueIds(rawPrioritizedQueueIds)),\n [rawPrioritizedQueueIds],\n );\n const status = useDeepAgentThreadStatus();\n const { isRunning, isContinuable } = queueLaneFlags(status);\n const aui = useAui();\n const sendCommand = useStatewireSendCommand();\n const listId = useId();\n const [expanded, setExpanded] = useState(true);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editingText, setEditingText] = useState('');\n // Head entries already auto-promoted once on this mount: an attempt whose\n // continue run dies and re-strands the SAME entry must not loop — the manual\n // Continue pill is the fallback for a repeatedly failing dispatch. Kept on\n // the tray (not the auto-continue child) so it survives the empty-lane\n // renders between a promotion and its outcome.\n const autoContinuedRef = useRef<Set<string>>(new Set());\n const onEventRef = useRef(onEvent);\n onEventRef.current = onEvent;\n\n const rows = buildStatewireQueueRows({ queue, steerQueue, isContinuable });\n // Continue promotes the QUEUE head only: a queue→steer move dispatches the\n // stranded steer lane plus this entry, while a steer entry moved onto its\n // own lane is a no-op.\n const queueHeadId = queue[0]?.id ?? null;\n const retryableRefusal = readRetryableRunRefusal(runs.error) !== null;\n const autoContinueOptions = autoContinue === true ? {} : autoContinue || null;\n\n /**\n * Re-send the entry under its own id on its own lane: the host replaces the\n * queued message's content in place, leaving its queue position and lane\n * untouched (a same-id send never dispatches).\n */\n const saveEdit = (\n item: StatewireThread.RunsQueueEntry,\n lane: 'queue' | 'steer',\n text: string,\n ): void => {\n setEditingId(null);\n setEditingText('');\n const command = buildQueueEditCommand({ item, lane, runId, text });\n if (command !== null) sendCommand(command);\n };\n\n /**\n * Retry/Discard act on a rendered lane snapshot that can trail the session\n * record; acting on an entry statewire already dropped throws. Nothing is\n * lost in that race, so it is recorded instead of crashing the click.\n */\n const runFailedSendAction = ({\n action,\n messageId,\n run,\n }: {\n action: 'retry' | 'discard';\n messageId: string;\n run: (actions: StatewireFailedActions) => void;\n }): void => {\n try {\n run(requireStatewireFailedActions(aui.thread().getState().extras));\n } catch (error) {\n if (!(error instanceof Error) || !isFailedSendEntryRace(error)) throw error;\n onEvent?.({\n type: 'failed-send-action-raced',\n threadId,\n messageId,\n action,\n error: error.message,\n });\n }\n };\n\n if (rows.length === 0 && failed.length === 0) return null;\n\n const renderRowContent = (context: StatewireQueueRowContentContext): ReactNode =>\n renderContent ? renderContent(context) : <QueueRowText text={context.text} />;\n\n return (\n <section\n aria-label=\"Queued messages\"\n className={cn(\n 'aui-statewire-queue w-full rounded-xl border border-border/70 bg-muted/30 px-2 py-1.5',\n classNames?.root,\n )}\n >\n {autoContinueOptions ? (\n <QueueAutoContinue\n delayMs={autoContinueOptions.delayMs ?? QUEUE_AUTO_CONTINUE_DELAY_MS}\n queueHeadId={queueHeadId}\n retryableRefusal={retryableRefusal}\n isContinuable={isContinuable}\n status={status}\n threadId={threadId}\n editing={editingId !== null}\n continuedRef={autoContinuedRef}\n onEventRef={onEventRef}\n />\n ) : null}\n <div className=\"flex items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n aria-expanded={expanded}\n aria-controls={listId}\n className=\"flex items-center gap-1 rounded px-1 py-1 font-medium text-muted-foreground text-xs hover:text-foreground\"\n >\n {expanded ? (\n <ChevronDownIcon className=\"size-3.5\" />\n ) : (\n <ChevronRightIcon className=\"size-3.5\" />\n )}\n {rows.length + failed.length} queued\n </button>\n {isContinuable && queueHeadId !== null ? (\n <ContinuePill\n ariaLabel=\"Continue with queued messages\"\n onClick={() => aui.composer.queueItem({ id: queueHeadId }).move({ lane: 'steer' })}\n />\n ) : null}\n </div>\n {expanded ? (\n <ul\n id={listId}\n className={cn('max-h-40 overflow-y-auto', classNames?.list)}\n data-testid=\"athena-statewire-queue\"\n >\n {rows.map(({ item, lane }, index) => {\n const text = queueEntryText(item.parts);\n const isUnsettled = isQueueEntryUnsettled(item);\n const isEditing = editingId === item.id;\n const showSendNow =\n !isUnsettled && showQueueRowSendNow({ lane, index, isRunning, isContinuable });\n const showPrioritize = !isUnsettled && lane === 'queue' && isRunning && runId !== null;\n const isPrioritized = prioritizedQueueIds.has(item.id);\n const editorContext: StatewireQueueEditorContext = {\n value: editingText,\n onChange: setEditingText,\n onSave: (next) => saveEdit(item, lane, next),\n onCancel: () => {\n setEditingId(null);\n setEditingText('');\n },\n };\n return (\n <li\n key={item.id}\n className=\"group flex items-start gap-2 rounded-md px-1 py-1.5 hover:bg-background\"\n >\n {isUnsettled ? (\n <Loader2Icon className=\"mt-0.5 size-3.5 shrink-0 animate-spin text-muted-foreground/60\" />\n ) : (\n <CircleIcon className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground/40\" />\n )}\n {isEditing ? (\n renderEditor ? (\n renderEditor(editorContext)\n ) : (\n <DefaultQueueEditor {...editorContext} />\n )\n ) : (\n renderRowContent({ text, index, item })\n )}\n <div\n className={cn('flex shrink-0 items-center gap-1', isEditing && 'hidden')}\n data-testid=\"statewire-queue-row-actions\"\n >\n {isUnsettled ? (\n <span\n className=\"px-1 text-[10px] text-muted-foreground\"\n data-testid=\"statewire-queued-message-sending\"\n >\n Sending…\n </span>\n ) : null}\n {showSendNow ? (\n <button\n type=\"button\"\n aria-label=\"Send now\"\n onClick={() =>\n aui.composer.queueItem({ id: item.id }).move({ lane: 'steer' })\n }\n className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 px-2 py-1 font-medium text-[10px] text-primary hover:bg-primary/5\"\n >\n <SendIcon className=\"size-2.5\" />\n Send now\n </button>\n ) : null}\n {showPrioritize ? (\n <button\n type=\"button\"\n aria-label={isPrioritized ? 'Remove priority' : 'Prioritize'}\n aria-pressed={isPrioritized}\n title={\n isPrioritized\n ? 'Remove priority'\n : 'Prioritize this message. Delivered as soon as the current step ends.'\n }\n onClick={() => {\n if (runId === null) return;\n sendCommand({\n type: 'run/prioritize',\n runId,\n messageId: item.id,\n priority: !isPrioritized,\n });\n }}\n className={cn(\n 'rounded p-1 transition-colors',\n isPrioritized\n ? 'text-amber-500 hover:text-amber-600'\n : 'text-muted-foreground hover:text-amber-500',\n )}\n >\n <ZapIcon className=\"size-3\" />\n </button>\n ) : null}\n {!isUnsettled ? (\n <button\n type=\"button\"\n aria-label=\"Edit queued message\"\n onClick={() => {\n setEditingId(item.id);\n setEditingText(text);\n }}\n className=\"rounded p-1 text-muted-foreground transition-colors hover:text-primary\"\n >\n <PencilIcon className=\"size-3\" />\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label=\"Remove queued message\"\n onClick={() => aui.composer.queueItem({ id: item.id }).remove()}\n className={cn(\n 'rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive',\n !showSendNow &&\n 'opacity-0 focus-visible:opacity-100 group-hover:opacity-100',\n )}\n >\n <Trash2Icon className=\"size-3\" />\n </button>\n </div>\n </li>\n );\n })}\n {failed.map((item, index) => (\n <li\n key={item.id}\n className=\"group flex items-start gap-2 rounded-md px-1 py-1.5 hover:bg-background\"\n >\n <AlertCircleIcon className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" />\n {renderRowContent({\n text: queueEntryText(item.parts),\n index: rows.length + index,\n item,\n })}\n <div\n className=\"flex shrink-0 items-center gap-1\"\n data-testid=\"statewire-queue-row-actions\"\n >\n <span\n className=\"px-1 text-[10px] text-destructive\"\n title={describeQueueSendFailure(item.sendFailure)}\n >\n Failed to send\n </span>\n <button\n type=\"button\"\n aria-label=\"Retry failed message\"\n onClick={() =>\n runFailedSendAction({\n action: 'retry',\n messageId: item.id,\n run: (actions) => actions.retryFailed(item.id),\n })\n }\n className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 px-2 py-1 font-medium text-[10px] text-primary hover:bg-primary/5\"\n >\n <RotateCcwIcon className=\"size-2.5\" />\n Retry\n </button>\n <button\n type=\"button\"\n aria-label=\"Discard failed message\"\n onClick={() =>\n runFailedSendAction({\n action: 'discard',\n messageId: item.id,\n run: (actions) => actions.discardFailed(item.id),\n })\n }\n className=\"rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2Icon className=\"size-3\" />\n </button>\n </div>\n </li>\n ))}\n </ul>\n ) : null}\n </section>\n );\n};\n\n/** Promotes ordinary stranded queue heads once; infrastructure recovery stays server-owned. */\nconst QueueAutoContinue: FC<{\n delayMs: number;\n queueHeadId: string | null;\n retryableRefusal: boolean;\n isContinuable: boolean;\n status: DeepAgentThreadStatus;\n threadId: string | null;\n editing: boolean;\n continuedRef: MutableRefObject<Set<string>>;\n onEventRef: MutableRefObject<((event: StatewireQueueEvent) => void) | undefined>;\n}> = ({\n delayMs,\n queueHeadId,\n retryableRefusal,\n isContinuable,\n status,\n threadId,\n editing,\n continuedRef,\n onEventRef,\n}) => {\n const aui = useAui();\n // The settle that stranded the queue is described by the LAST message's run\n // marker (a marker deeper in history was superseded by a later send). The\n // selectors return primitives so the subscriptions stay shallow-stable.\n const lastMessageMarker = useAuiState((s) => {\n const last = s.thread.messages.at(-1);\n return last ? readRunMarkerFromSourceMessages(last)?.marker : undefined;\n });\n const lastMessageMarkerSource = useAuiState((s) => {\n const last = s.thread.messages.at(-1);\n return last ? readRunMarkerFromSourceMessages(last)?.source : undefined;\n });\n const userStopBlocks = userStopBlocksQueueAutoContinue({\n marker: lastMessageMarker,\n source: lastMessageMarkerSource,\n });\n\n useEffect(() => {\n if (\n !isContinuable ||\n userStopBlocks ||\n retryableRefusal ||\n isServerUpdateStopSource(lastMessageMarkerSource)\n ) return;\n // An open inline edit means the user is mid-revision on some row —\n // dispatching underneath them could send text they are about to change.\n if (editing) return;\n if (queueHeadId === null) return;\n const attemptKey = `${threadId ?? 'unknown'}:${queueHeadId}`;\n if (continuedRef.current.has(attemptKey)) return;\n const timeoutId = setTimeout(() => {\n continuedRef.current.add(attemptKey);\n onEventRef.current?.({ type: 'auto-continued', threadId, status, headEntryId: queueHeadId });\n aui.composer.queueItem({ id: queueHeadId }).move({ lane: 'steer' });\n }, delayMs);\n return () => clearTimeout(timeoutId);\n }, [\n continuedRef,\n aui,\n delayMs,\n editing,\n isContinuable,\n onEventRef,\n queueHeadId,\n retryableRefusal,\n lastMessageMarkerSource,\n status,\n threadId,\n userStopBlocks,\n ]);\n\n return null;\n};\n\n/**\n * The default row body: `[@name](…)` mentions render as chips, not raw\n * markdown links, and the body clamps to two lines. Show more / Show less\n * appears only when the rendered text overflows those two lines, so a short\n * message that wraps in a narrow tray is still caught and a short one gets no\n * dead control.\n */\nconst QueueRowText: FC<{ text: string }> = ({ text }) => {\n const bodyRef = useRef<HTMLParagraphElement>(null);\n const [expanded, setExpanded] = useState(false);\n const [overflows, setOverflows] = useState(false);\n\n useLayoutEffect(() => {\n const body = bodyRef.current;\n // Expanded text has no clamp to overflow; the toggle stays up to collapse it.\n if (!body || expanded) return;\n const measure = (): void => setOverflows(body.scrollHeight > body.clientHeight + 1);\n measure();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(measure);\n observer.observe(body);\n return () => observer.disconnect();\n }, [text, expanded]);\n\n if (!text) {\n return <p className=\"min-w-0 flex-1 text-foreground text-xs\">Queued attachment</p>;\n }\n return (\n <div className=\"min-w-0 flex-1\">\n <p\n ref={bodyRef}\n className={cn(\n 'whitespace-pre-wrap break-words text-foreground text-xs leading-relaxed',\n !expanded && 'line-clamp-2',\n )}\n title={overflows && !expanded ? mentionsToPlainText(text) : undefined}\n >\n {parseMentionsInText(text)}\n </p>\n {overflows || expanded ? (\n <button\n type=\"button\"\n aria-expanded={expanded}\n onClick={() => setExpanded((value) => !value)}\n className=\"mt-0.5 font-medium text-[10px] text-muted-foreground hover:text-foreground\"\n >\n {expanded ? 'Show less' : 'Show more'}\n </button>\n ) : null}\n </div>\n );\n};\n\n/** Plain textarea editor: Enter saves, Shift+Enter breaks a line, Escape cancels. */\nconst DefaultQueueEditor: FC<StatewireQueueEditorContext> = ({\n value,\n onChange,\n onSave,\n onCancel,\n}) => {\n const canSave = value.trim().length > 0;\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n if (canSave) onSave(value);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n onCancel();\n }\n };\n return (\n <div className=\"flex min-w-0 flex-1 items-start gap-1\">\n <textarea\n autoFocus\n aria-label=\"Edit message\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={Math.min(6, Math.max(1, value.split('\\n').length))}\n className=\"min-w-0 flex-1 resize-none rounded-md border border-border bg-background px-2 py-1 text-foreground text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring/50\"\n />\n <button\n type=\"button\"\n aria-label=\"Save edit\"\n disabled={!canSave}\n onClick={() => onSave(value)}\n className={cn(\n 'rounded p-1 text-primary transition-colors hover:bg-primary/10',\n !canSave && 'cursor-not-allowed opacity-40',\n )}\n >\n <CheckIcon className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Cancel edit\"\n onClick={onCancel}\n className=\"rounded p-1 text-muted-foreground transition-colors hover:bg-muted\"\n >\n <XIcon className=\"size-3.5\" />\n </button>\n </div>\n );\n};\n"],"names":["useAuiState","hasStatewireThreadExtras","jsx","useStatewireRuns","queue","useStatewireState","readDeepAgentStreamValues","useMemo","readPrioritizedQueueIds","useDeepAgentThreadStatus","queueLaneFlags","useAui","useStatewireSendCommand","useId","useState","useRef","buildStatewireQueueRows","readRetryableRunRefusal","buildQueueEditCommand","requireStatewireFailedActions","isFailedSendEntryRace","jsxs","cn","QUEUE_AUTO_CONTINUE_DELAY_MS","ChevronDownIcon","ChevronRightIcon","ContinuePill","queueEntryText","isQueueEntryUnsettled","showQueueRowSendNow","Loader2Icon","CircleIcon","SendIcon","ZapIcon","PencilIcon","Trash2Icon","AlertCircleIcon","describeQueueSendFailure","RotateCcwIcon","readRunMarkerFromSourceMessages","userStopBlocksQueueAutoContinue","useEffect","isServerUpdateStopSource","useLayoutEffect","mentionsToPlainText","CheckIcon","XIcon"],"mappings":"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"}
@@ -2,8 +2,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useAuiState, useAui } from "@assistant-ui/react";
3
3
  import { useStatewireRuns, useStatewireState, useStatewireSendCommand } from "@assistant-ui/react-statewire";
4
4
  import { ChevronDownIcon, ChevronRightIcon, Loader2Icon, CircleIcon, SendIcon, ZapIcon, PencilIcon, Trash2Icon, AlertCircleIcon, RotateCcwIcon, CheckIcon, XIcon } from "lucide-react";
5
- import { useMemo, useId, useState, useRef, useEffect } from "react";
5
+ import { useMemo, useId, useState, useRef, useEffect, useLayoutEffect } from "react";
6
6
  import { ContinuePill } from "../chat-ui/messages/continue-pill.js";
7
+ import { parseMentionsInText, mentionsToPlainText } from "../chat-ui/mentions/mention-chip.js";
7
8
  import { hasStatewireThreadExtras, requireStatewireFailedActions } from "../packages/agent-runtime/src/thread-extras.js";
8
9
  import { readDeepAgentStreamValues } from "../packages/agent-runtime/src/statewire-state.js";
9
10
  import { readPrioritizedQueueIds, buildStatewireQueueRows, QUEUE_AUTO_CONTINUE_DELAY_MS, queueEntryText, isQueueEntryUnsettled, describeQueueSendFailure, queueLaneFlags, showQueueRowSendNow, isFailedSendEntryRace, userStopBlocksQueueAutoContinue, buildQueueEditCommand } from "../packages/agent-runtime/src/queue.js";
@@ -75,7 +76,7 @@ const StatewireQueuedMessagesInner = ({
75
76
  }
76
77
  };
77
78
  if (rows.length === 0 && failed.length === 0) return null;
78
- const renderRowContent = (context) => renderContent ? renderContent(context) : /* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 whitespace-pre-wrap break-words text-foreground text-xs", children: context.text || "Queued attachment" });
79
+ const renderRowContent = (context) => renderContent ? renderContent(context) : /* @__PURE__ */ jsx(QueueRowText, { text: context.text });
79
80
  return /* @__PURE__ */ jsxs(
80
81
  "section",
81
82
  {
@@ -362,6 +363,48 @@ const QueueAutoContinue = ({
362
363
  ]);
363
364
  return null;
364
365
  };
366
+ const QueueRowText = ({ text }) => {
367
+ const bodyRef = useRef(null);
368
+ const [expanded, setExpanded] = useState(false);
369
+ const [overflows, setOverflows] = useState(false);
370
+ useLayoutEffect(() => {
371
+ const body = bodyRef.current;
372
+ if (!body || expanded) return;
373
+ const measure = () => setOverflows(body.scrollHeight > body.clientHeight + 1);
374
+ measure();
375
+ if (typeof ResizeObserver === "undefined") return;
376
+ const observer = new ResizeObserver(measure);
377
+ observer.observe(body);
378
+ return () => observer.disconnect();
379
+ }, [text, expanded]);
380
+ if (!text) {
381
+ return /* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 text-foreground text-xs", children: "Queued attachment" });
382
+ }
383
+ return /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
384
+ /* @__PURE__ */ jsx(
385
+ "p",
386
+ {
387
+ ref: bodyRef,
388
+ className: cn(
389
+ "whitespace-pre-wrap break-words text-foreground text-xs leading-relaxed",
390
+ !expanded && "line-clamp-2"
391
+ ),
392
+ title: overflows && !expanded ? mentionsToPlainText(text) : void 0,
393
+ children: parseMentionsInText(text)
394
+ }
395
+ ),
396
+ overflows || expanded ? /* @__PURE__ */ jsx(
397
+ "button",
398
+ {
399
+ type: "button",
400
+ "aria-expanded": expanded,
401
+ onClick: () => setExpanded((value) => !value),
402
+ className: "mt-0.5 font-medium text-[10px] text-muted-foreground hover:text-foreground",
403
+ children: expanded ? "Show less" : "Show more"
404
+ }
405
+ ) : null
406
+ ] });
407
+ };
365
408
  const DefaultQueueEditor = ({
366
409
  value,
367
410
  onChange,
@@ -1 +1 @@
1
- {"version":3,"file":"StatewireQueuedMessages.js","sources":["../../src/chat/StatewireQueuedMessages.tsx"],"sourcesContent":["import { useAui, useAuiState } from '@assistant-ui/react';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport {\n useStatewireRuns,\n useStatewireSendCommand,\n useStatewireState,\n} from '@assistant-ui/react-statewire';\nimport {\n buildQueueEditCommand,\n buildStatewireQueueRows,\n type DeepAgentThreadStatus,\n describeQueueSendFailure,\n hasStatewireThreadExtras,\n isFailedSendEntryRace,\n isQueueEntryUnsettled,\n isServerUpdateStopSource,\n QUEUE_AUTO_CONTINUE_DELAY_MS,\n queueEntryText,\n queueLaneFlags,\n readDeepAgentStreamValues,\n readPrioritizedQueueIds,\n readRetryableRunRefusal,\n readRunMarkerFromSourceMessages,\n requireStatewireFailedActions,\n showQueueRowSendNow,\n type StatewireFailedActions,\n useDeepAgentThreadStatus,\n userStopBlocksQueueAutoContinue,\n} from '@athenaintel/agent-runtime';\nimport {\n AlertCircleIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronRightIcon,\n CircleIcon,\n Loader2Icon,\n PencilIcon,\n RotateCcwIcon,\n SendIcon,\n Trash2Icon,\n XIcon,\n ZapIcon,\n} from 'lucide-react';\nimport {\n type FC,\n type KeyboardEvent,\n type MutableRefObject,\n type ReactNode,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { ContinuePill } from '../chat-ui/messages/continue-pill';\nimport { cn } from '../lib/utils';\n\ndeclare module '@assistant-ui/core' {\n namespace Assistant {\n interface Commands {\n /**\n * The ⚡ on a queued row (`DeepAgentThread.run_prioritize`). With\n * `priority: true` the host moves the entry to the head of the queue\n * lane (behind entries prioritized before it), records it durably, and\n * the live run ends at its next model-call boundary — after the\n * in-flight tool chain, never mid-tool — so the settle delivers the\n * entry on top of the finished tool results instead of after the whole\n * turn. `priority: false` withdraws the request and leaves the entry\n * where it is.\n */\n runPrioritize: {\n type: 'run/prioritize';\n runId: string;\n messageId: string;\n priority: boolean;\n };\n }\n }\n}\n\n/**\n * The one queued-messages tray for the server-owned statewire queue and steer\n * lanes, mounted by the SDK's `AthenaChat`, the Chrome side panel, the Office\n * task pane, and Olympus.\n *\n * A message sent mid-run queues behind the active run. Rows offer \"Send now\"\n * (a steer-lane move — the host stops the run and dispatches it once the run\n * settles; on a stopped/errored thread the same move dispatches immediately),\n * \"Prioritize\" (`run/prioritize`, the ⚡ — the non-interrupting analog: the\n * live run ends at its next model-call boundary so the entry lands on top of\n * the finished tool results), inline edit (re-sent under the entry's own id\n * on its own lane, which the host folds as an in-place content update), and\n * remove (`run/dequeue`). While the thread rests stopped/errored, stranded\n * steer-lane entries list ahead of the queue (they dispatch first, as one\n * batch, so they carry Remove but no per-row Send now) and a Continue pill\n * steer-promotes the queue head — a steer entry moved onto its own lane is a\n * no-op, so a steer-only rest state gets no dead button.\n *\n * A row whose send has not settled yet (`isQueueEntryUnsettled` — this\n * client's optimistic fold, not a host-admitted lane entry) renders as\n * \"Sending…\" with no acting affordances: admission is server-side and can\n * still refuse the message. Failed run sends (`runs.failed`) list after the\n * lanes with Retry (`retryFailed`) and Discard (`discardFailed`).\n *\n * With `autoContinue`, an ordinary stranded settle promotes the queue head\n * once after `QUEUE_AUTO_CONTINUE_DELAY_MS`. User stops remain manual;\n * infrastructure pauses and retryable dispatch refusals recover on the server.\n *\n * Renders nothing on the legacy transport or when every lane is empty.\n */\n\nexport type StatewireQueueEvent =\n | {\n type: 'auto-continued';\n threadId: string | null;\n status: DeepAgentThreadStatus;\n headEntryId: string;\n }\n | {\n type: 'failed-send-action-raced';\n threadId: string | null;\n messageId: string;\n action: 'retry' | 'discard';\n error: string;\n };\n\nexport interface StatewireQueueRowContentContext {\n text: string;\n index: number;\n item: StatewireThread.RunsQueueEntry;\n}\n\nexport interface StatewireQueueEditorContext {\n value: string;\n onChange: (value: string) => void;\n /** Save the edit; a blank or unchanged text sends nothing but closes the editor. */\n onSave: (text: string) => void;\n onCancel: () => void;\n}\n\nexport interface StatewireQueuedMessagesProps {\n /** Surface-styling overrides merged onto the default classes. */\n classNames?: {\n /** The tray container. */\n root?: string;\n /** The row list. */\n list?: string;\n };\n /**\n * Steer-promote a stranded queue head automatically after a settle (see the\n * component docs). Off by default: a host opts in to dispatching runs on the\n * user's behalf. Pass `{ delayMs }` to tune the grace before the promotion\n * (defaults to `QUEUE_AUTO_CONTINUE_DELAY_MS`).\n */\n autoContinue?: boolean | { delayMs?: number };\n /** Identifies the thread for once-per-entry auto-continue attempts and events. */\n threadId?: string | null;\n /** Telemetry sink for the tray's notable moments. */\n onEvent?: (event: StatewireQueueEvent) => void;\n /** Replace the plain-text row body (e.g. a mention-aware renderer). */\n renderContent?: (context: StatewireQueueRowContentContext) => ReactNode;\n /** Replace the plain textarea inline editor (e.g. the host's composer editor). */\n renderEditor?: (context: StatewireQueueEditorContext) => ReactNode;\n}\n\nexport const StatewireQueuedMessages: FC<StatewireQueuedMessagesProps> = (props) => {\n const extras = useAuiState((s) => s.thread.extras);\n if (!hasStatewireThreadExtras(extras)) return null;\n return <StatewireQueuedMessagesInner {...props} />;\n};\n\nconst StatewireQueuedMessagesInner: FC<StatewireQueuedMessagesProps> = ({\n classNames,\n autoContinue = false,\n threadId = null,\n onEvent,\n renderContent,\n renderEditor,\n}) => {\n // The QUEUE lane only: composer.queue prepends the steer lane, whose entries\n // already render in the message list. A non-empty queue implies a live run\n // entry (the host retires runId only once both lanes drain), so the row\n // controls can always send.\n const runs = useStatewireRuns();\n const { queue, steerQueue, runId } = runs;\n // `failed` joined the runs projection in react-statewire 0.24; a projection\n // without it (an older lib, a stubbed runs read) must not crash the tray.\n const failed = runs.failed ?? [];\n // The host's durable prioritized-id record: a raw path read (the selector\n // must return a stable reference), with the lookup set derived below.\n const rawPrioritizedQueueIds = useStatewireState(\n (state) => readDeepAgentStreamValues(state).prioritizedQueueIds,\n );\n const prioritizedQueueIds = useMemo(\n () => new Set(readPrioritizedQueueIds(rawPrioritizedQueueIds)),\n [rawPrioritizedQueueIds],\n );\n const status = useDeepAgentThreadStatus();\n const { isRunning, isContinuable } = queueLaneFlags(status);\n const aui = useAui();\n const sendCommand = useStatewireSendCommand();\n const listId = useId();\n const [expanded, setExpanded] = useState(true);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editingText, setEditingText] = useState('');\n // Head entries already auto-promoted once on this mount: an attempt whose\n // continue run dies and re-strands the SAME entry must not loop — the manual\n // Continue pill is the fallback for a repeatedly failing dispatch. Kept on\n // the tray (not the auto-continue child) so it survives the empty-lane\n // renders between a promotion and its outcome.\n const autoContinuedRef = useRef<Set<string>>(new Set());\n const onEventRef = useRef(onEvent);\n onEventRef.current = onEvent;\n\n const rows = buildStatewireQueueRows({ queue, steerQueue, isContinuable });\n // Continue promotes the QUEUE head only: a queue→steer move dispatches the\n // stranded steer lane plus this entry, while a steer entry moved onto its\n // own lane is a no-op.\n const queueHeadId = queue[0]?.id ?? null;\n const retryableRefusal = readRetryableRunRefusal(runs.error) !== null;\n const autoContinueOptions = autoContinue === true ? {} : autoContinue || null;\n\n /**\n * Re-send the entry under its own id on its own lane: the host replaces the\n * queued message's content in place, leaving its queue position and lane\n * untouched (a same-id send never dispatches).\n */\n const saveEdit = (\n item: StatewireThread.RunsQueueEntry,\n lane: 'queue' | 'steer',\n text: string,\n ): void => {\n setEditingId(null);\n setEditingText('');\n const command = buildQueueEditCommand({ item, lane, runId, text });\n if (command !== null) sendCommand(command);\n };\n\n /**\n * Retry/Discard act on a rendered lane snapshot that can trail the session\n * record; acting on an entry statewire already dropped throws. Nothing is\n * lost in that race, so it is recorded instead of crashing the click.\n */\n const runFailedSendAction = ({\n action,\n messageId,\n run,\n }: {\n action: 'retry' | 'discard';\n messageId: string;\n run: (actions: StatewireFailedActions) => void;\n }): void => {\n try {\n run(requireStatewireFailedActions(aui.thread().getState().extras));\n } catch (error) {\n if (!(error instanceof Error) || !isFailedSendEntryRace(error)) throw error;\n onEvent?.({\n type: 'failed-send-action-raced',\n threadId,\n messageId,\n action,\n error: error.message,\n });\n }\n };\n\n if (rows.length === 0 && failed.length === 0) return null;\n\n const renderRowContent = (context: StatewireQueueRowContentContext): ReactNode =>\n renderContent ? (\n renderContent(context)\n ) : (\n <p className=\"min-w-0 flex-1 whitespace-pre-wrap break-words text-foreground text-xs\">\n {context.text || 'Queued attachment'}\n </p>\n );\n\n return (\n <section\n aria-label=\"Queued messages\"\n className={cn(\n 'aui-statewire-queue w-full rounded-xl border border-border/70 bg-muted/30 px-2 py-1.5',\n classNames?.root,\n )}\n >\n {autoContinueOptions ? (\n <QueueAutoContinue\n delayMs={autoContinueOptions.delayMs ?? QUEUE_AUTO_CONTINUE_DELAY_MS}\n queueHeadId={queueHeadId}\n retryableRefusal={retryableRefusal}\n isContinuable={isContinuable}\n status={status}\n threadId={threadId}\n editing={editingId !== null}\n continuedRef={autoContinuedRef}\n onEventRef={onEventRef}\n />\n ) : null}\n <div className=\"flex items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n aria-expanded={expanded}\n aria-controls={listId}\n className=\"flex items-center gap-1 rounded px-1 py-1 font-medium text-muted-foreground text-xs hover:text-foreground\"\n >\n {expanded ? (\n <ChevronDownIcon className=\"size-3.5\" />\n ) : (\n <ChevronRightIcon className=\"size-3.5\" />\n )}\n {rows.length + failed.length} queued\n </button>\n {isContinuable && queueHeadId !== null ? (\n <ContinuePill\n ariaLabel=\"Continue with queued messages\"\n onClick={() => aui.composer.queueItem({ id: queueHeadId }).move({ lane: 'steer' })}\n />\n ) : null}\n </div>\n {expanded ? (\n <ul\n id={listId}\n className={cn('max-h-40 overflow-y-auto', classNames?.list)}\n data-testid=\"athena-statewire-queue\"\n >\n {rows.map(({ item, lane }, index) => {\n const text = queueEntryText(item.parts);\n const isUnsettled = isQueueEntryUnsettled(item);\n const isEditing = editingId === item.id;\n const showSendNow =\n !isUnsettled && showQueueRowSendNow({ lane, index, isRunning, isContinuable });\n const showPrioritize = !isUnsettled && lane === 'queue' && isRunning && runId !== null;\n const isPrioritized = prioritizedQueueIds.has(item.id);\n const editorContext: StatewireQueueEditorContext = {\n value: editingText,\n onChange: setEditingText,\n onSave: (next) => saveEdit(item, lane, next),\n onCancel: () => {\n setEditingId(null);\n setEditingText('');\n },\n };\n return (\n <li\n key={item.id}\n className=\"group flex items-start gap-2 rounded-md px-1 py-1.5 hover:bg-background\"\n >\n {isUnsettled ? (\n <Loader2Icon className=\"mt-0.5 size-3.5 shrink-0 animate-spin text-muted-foreground/60\" />\n ) : (\n <CircleIcon className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground/40\" />\n )}\n {isEditing ? (\n renderEditor ? (\n renderEditor(editorContext)\n ) : (\n <DefaultQueueEditor {...editorContext} />\n )\n ) : (\n renderRowContent({ text, index, item })\n )}\n <div\n className={cn('flex shrink-0 items-center gap-1', isEditing && 'hidden')}\n data-testid=\"statewire-queue-row-actions\"\n >\n {isUnsettled ? (\n <span\n className=\"px-1 text-[10px] text-muted-foreground\"\n data-testid=\"statewire-queued-message-sending\"\n >\n Sending…\n </span>\n ) : null}\n {showSendNow ? (\n <button\n type=\"button\"\n aria-label=\"Send now\"\n onClick={() =>\n aui.composer.queueItem({ id: item.id }).move({ lane: 'steer' })\n }\n className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 px-2 py-1 font-medium text-[10px] text-primary hover:bg-primary/5\"\n >\n <SendIcon className=\"size-2.5\" />\n Send now\n </button>\n ) : null}\n {showPrioritize ? (\n <button\n type=\"button\"\n aria-label={isPrioritized ? 'Remove priority' : 'Prioritize'}\n aria-pressed={isPrioritized}\n title={\n isPrioritized\n ? 'Remove priority'\n : 'Prioritize this message. Delivered as soon as the current step ends.'\n }\n onClick={() => {\n if (runId === null) return;\n sendCommand({\n type: 'run/prioritize',\n runId,\n messageId: item.id,\n priority: !isPrioritized,\n });\n }}\n className={cn(\n 'rounded p-1 transition-colors',\n isPrioritized\n ? 'text-amber-500 hover:text-amber-600'\n : 'text-muted-foreground hover:text-amber-500',\n )}\n >\n <ZapIcon className=\"size-3\" />\n </button>\n ) : null}\n {!isUnsettled ? (\n <button\n type=\"button\"\n aria-label=\"Edit queued message\"\n onClick={() => {\n setEditingId(item.id);\n setEditingText(text);\n }}\n className=\"rounded p-1 text-muted-foreground transition-colors hover:text-primary\"\n >\n <PencilIcon className=\"size-3\" />\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label=\"Remove queued message\"\n onClick={() => aui.composer.queueItem({ id: item.id }).remove()}\n className={cn(\n 'rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive',\n !showSendNow &&\n 'opacity-0 focus-visible:opacity-100 group-hover:opacity-100',\n )}\n >\n <Trash2Icon className=\"size-3\" />\n </button>\n </div>\n </li>\n );\n })}\n {failed.map((item, index) => (\n <li\n key={item.id}\n className=\"group flex items-start gap-2 rounded-md px-1 py-1.5 hover:bg-background\"\n >\n <AlertCircleIcon className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" />\n {renderRowContent({\n text: queueEntryText(item.parts),\n index: rows.length + index,\n item,\n })}\n <div\n className=\"flex shrink-0 items-center gap-1\"\n data-testid=\"statewire-queue-row-actions\"\n >\n <span\n className=\"px-1 text-[10px] text-destructive\"\n title={describeQueueSendFailure(item.sendFailure)}\n >\n Failed to send\n </span>\n <button\n type=\"button\"\n aria-label=\"Retry failed message\"\n onClick={() =>\n runFailedSendAction({\n action: 'retry',\n messageId: item.id,\n run: (actions) => actions.retryFailed(item.id),\n })\n }\n className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 px-2 py-1 font-medium text-[10px] text-primary hover:bg-primary/5\"\n >\n <RotateCcwIcon className=\"size-2.5\" />\n Retry\n </button>\n <button\n type=\"button\"\n aria-label=\"Discard failed message\"\n onClick={() =>\n runFailedSendAction({\n action: 'discard',\n messageId: item.id,\n run: (actions) => actions.discardFailed(item.id),\n })\n }\n className=\"rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2Icon className=\"size-3\" />\n </button>\n </div>\n </li>\n ))}\n </ul>\n ) : null}\n </section>\n );\n};\n\n/** Promotes ordinary stranded queue heads once; infrastructure recovery stays server-owned. */\nconst QueueAutoContinue: FC<{\n delayMs: number;\n queueHeadId: string | null;\n retryableRefusal: boolean;\n isContinuable: boolean;\n status: DeepAgentThreadStatus;\n threadId: string | null;\n editing: boolean;\n continuedRef: MutableRefObject<Set<string>>;\n onEventRef: MutableRefObject<((event: StatewireQueueEvent) => void) | undefined>;\n}> = ({\n delayMs,\n queueHeadId,\n retryableRefusal,\n isContinuable,\n status,\n threadId,\n editing,\n continuedRef,\n onEventRef,\n}) => {\n const aui = useAui();\n // The settle that stranded the queue is described by the LAST message's run\n // marker (a marker deeper in history was superseded by a later send). The\n // selectors return primitives so the subscriptions stay shallow-stable.\n const lastMessageMarker = useAuiState((s) => {\n const last = s.thread.messages.at(-1);\n return last ? readRunMarkerFromSourceMessages(last)?.marker : undefined;\n });\n const lastMessageMarkerSource = useAuiState((s) => {\n const last = s.thread.messages.at(-1);\n return last ? readRunMarkerFromSourceMessages(last)?.source : undefined;\n });\n const userStopBlocks = userStopBlocksQueueAutoContinue({\n marker: lastMessageMarker,\n source: lastMessageMarkerSource,\n });\n\n useEffect(() => {\n if (\n !isContinuable ||\n userStopBlocks ||\n retryableRefusal ||\n isServerUpdateStopSource(lastMessageMarkerSource)\n ) return;\n // An open inline edit means the user is mid-revision on some row —\n // dispatching underneath them could send text they are about to change.\n if (editing) return;\n if (queueHeadId === null) return;\n const attemptKey = `${threadId ?? 'unknown'}:${queueHeadId}`;\n if (continuedRef.current.has(attemptKey)) return;\n const timeoutId = setTimeout(() => {\n continuedRef.current.add(attemptKey);\n onEventRef.current?.({ type: 'auto-continued', threadId, status, headEntryId: queueHeadId });\n aui.composer.queueItem({ id: queueHeadId }).move({ lane: 'steer' });\n }, delayMs);\n return () => clearTimeout(timeoutId);\n }, [\n continuedRef,\n aui,\n delayMs,\n editing,\n isContinuable,\n onEventRef,\n queueHeadId,\n retryableRefusal,\n lastMessageMarkerSource,\n status,\n threadId,\n userStopBlocks,\n ]);\n\n return null;\n};\n\n/** Plain textarea editor: Enter saves, Shift+Enter breaks a line, Escape cancels. */\nconst DefaultQueueEditor: FC<StatewireQueueEditorContext> = ({\n value,\n onChange,\n onSave,\n onCancel,\n}) => {\n const canSave = value.trim().length > 0;\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n if (canSave) onSave(value);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n onCancel();\n }\n };\n return (\n <div className=\"flex min-w-0 flex-1 items-start gap-1\">\n <textarea\n autoFocus\n aria-label=\"Edit message\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={Math.min(6, Math.max(1, value.split('\\n').length))}\n className=\"min-w-0 flex-1 resize-none rounded-md border border-border bg-background px-2 py-1 text-foreground text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring/50\"\n />\n <button\n type=\"button\"\n aria-label=\"Save edit\"\n disabled={!canSave}\n onClick={() => onSave(value)}\n className={cn(\n 'rounded p-1 text-primary transition-colors hover:bg-primary/10',\n !canSave && 'cursor-not-allowed opacity-40',\n )}\n >\n <CheckIcon className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Cancel edit\"\n onClick={onCancel}\n className=\"rounded p-1 text-muted-foreground transition-colors hover:bg-muted\"\n >\n <XIcon className=\"size-3.5\" />\n </button>\n </div>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StatewireQueuedMessages.js","sources":["../../src/chat/StatewireQueuedMessages.tsx"],"sourcesContent":["import { useAui, useAuiState } from '@assistant-ui/react';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport {\n useStatewireRuns,\n useStatewireSendCommand,\n useStatewireState,\n} from '@assistant-ui/react-statewire';\nimport {\n buildQueueEditCommand,\n buildStatewireQueueRows,\n type DeepAgentThreadStatus,\n describeQueueSendFailure,\n hasStatewireThreadExtras,\n isFailedSendEntryRace,\n isQueueEntryUnsettled,\n isServerUpdateStopSource,\n QUEUE_AUTO_CONTINUE_DELAY_MS,\n queueEntryText,\n queueLaneFlags,\n readDeepAgentStreamValues,\n readPrioritizedQueueIds,\n readRetryableRunRefusal,\n readRunMarkerFromSourceMessages,\n requireStatewireFailedActions,\n showQueueRowSendNow,\n type StatewireFailedActions,\n useDeepAgentThreadStatus,\n userStopBlocksQueueAutoContinue,\n} from '@athenaintel/agent-runtime';\nimport {\n AlertCircleIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronRightIcon,\n CircleIcon,\n Loader2Icon,\n PencilIcon,\n RotateCcwIcon,\n SendIcon,\n Trash2Icon,\n XIcon,\n ZapIcon,\n} from 'lucide-react';\nimport {\n type FC,\n type KeyboardEvent,\n type MutableRefObject,\n type ReactNode,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { ContinuePill } from '../chat-ui/messages/continue-pill';\nimport { mentionsToPlainText, parseMentionsInText } from '../chat-ui/mentions/mention-chip';\nimport { cn } from '../lib/utils';\n\ndeclare module '@assistant-ui/core' {\n namespace Assistant {\n interface Commands {\n /**\n * The ⚡ on a queued row (`DeepAgentThread.run_prioritize`). With\n * `priority: true` the host moves the entry to the head of the queue\n * lane (behind entries prioritized before it), records it durably, and\n * the live run ends at its next model-call boundary — after the\n * in-flight tool chain, never mid-tool — so the settle delivers the\n * entry on top of the finished tool results instead of after the whole\n * turn. `priority: false` withdraws the request and leaves the entry\n * where it is.\n */\n runPrioritize: {\n type: 'run/prioritize';\n runId: string;\n messageId: string;\n priority: boolean;\n };\n }\n }\n}\n\n/**\n * The one queued-messages tray for the server-owned statewire queue and steer\n * lanes, mounted by the SDK's `AthenaChat`, the Chrome side panel, the Office\n * task pane, and Olympus.\n *\n * A message sent mid-run queues behind the active run. Rows offer \"Send now\"\n * (a steer-lane move — the host stops the run and dispatches it once the run\n * settles; on a stopped/errored thread the same move dispatches immediately),\n * \"Prioritize\" (`run/prioritize`, the ⚡ — the non-interrupting analog: the\n * live run ends at its next model-call boundary so the entry lands on top of\n * the finished tool results), inline edit (re-sent under the entry's own id\n * on its own lane, which the host folds as an in-place content update), and\n * remove (`run/dequeue`). While the thread rests stopped/errored, stranded\n * steer-lane entries list ahead of the queue (they dispatch first, as one\n * batch, so they carry Remove but no per-row Send now) and a Continue pill\n * steer-promotes the queue head — a steer entry moved onto its own lane is a\n * no-op, so a steer-only rest state gets no dead button.\n *\n * A row whose send has not settled yet (`isQueueEntryUnsettled` — this\n * client's optimistic fold, not a host-admitted lane entry) renders as\n * \"Sending…\" with no acting affordances: admission is server-side and can\n * still refuse the message. Failed run sends (`runs.failed`) list after the\n * lanes with Retry (`retryFailed`) and Discard (`discardFailed`).\n *\n * With `autoContinue`, an ordinary stranded settle promotes the queue head\n * once after `QUEUE_AUTO_CONTINUE_DELAY_MS`. User stops remain manual;\n * infrastructure pauses and retryable dispatch refusals recover on the server.\n *\n * Renders nothing on the legacy transport or when every lane is empty.\n */\n\nexport type StatewireQueueEvent =\n | {\n type: 'auto-continued';\n threadId: string | null;\n status: DeepAgentThreadStatus;\n headEntryId: string;\n }\n | {\n type: 'failed-send-action-raced';\n threadId: string | null;\n messageId: string;\n action: 'retry' | 'discard';\n error: string;\n };\n\nexport interface StatewireQueueRowContentContext {\n text: string;\n index: number;\n item: StatewireThread.RunsQueueEntry;\n}\n\nexport interface StatewireQueueEditorContext {\n value: string;\n onChange: (value: string) => void;\n /** Save the edit; a blank or unchanged text sends nothing but closes the editor. */\n onSave: (text: string) => void;\n onCancel: () => void;\n}\n\nexport interface StatewireQueuedMessagesProps {\n /** Surface-styling overrides merged onto the default classes. */\n classNames?: {\n /** The tray container. */\n root?: string;\n /** The row list. */\n list?: string;\n };\n /**\n * Steer-promote a stranded queue head automatically after a settle (see the\n * component docs). Off by default: a host opts in to dispatching runs on the\n * user's behalf. Pass `{ delayMs }` to tune the grace before the promotion\n * (defaults to `QUEUE_AUTO_CONTINUE_DELAY_MS`).\n */\n autoContinue?: boolean | { delayMs?: number };\n /** Identifies the thread for once-per-entry auto-continue attempts and events. */\n threadId?: string | null;\n /** Telemetry sink for the tray's notable moments. */\n onEvent?: (event: StatewireQueueEvent) => void;\n /** Replace the plain-text row body (e.g. a mention-aware renderer). */\n renderContent?: (context: StatewireQueueRowContentContext) => ReactNode;\n /** Replace the plain textarea inline editor (e.g. the host's composer editor). */\n renderEditor?: (context: StatewireQueueEditorContext) => ReactNode;\n}\n\nexport const StatewireQueuedMessages: FC<StatewireQueuedMessagesProps> = (props) => {\n const extras = useAuiState((s) => s.thread.extras);\n if (!hasStatewireThreadExtras(extras)) return null;\n return <StatewireQueuedMessagesInner {...props} />;\n};\n\nconst StatewireQueuedMessagesInner: FC<StatewireQueuedMessagesProps> = ({\n classNames,\n autoContinue = false,\n threadId = null,\n onEvent,\n renderContent,\n renderEditor,\n}) => {\n // The QUEUE lane only: composer.queue prepends the steer lane, whose entries\n // already render in the message list. A non-empty queue implies a live run\n // entry (the host retires runId only once both lanes drain), so the row\n // controls can always send.\n const runs = useStatewireRuns();\n const { queue, steerQueue, runId } = runs;\n // `failed` joined the runs projection in react-statewire 0.24; a projection\n // without it (an older lib, a stubbed runs read) must not crash the tray.\n const failed = runs.failed ?? [];\n // The host's durable prioritized-id record: a raw path read (the selector\n // must return a stable reference), with the lookup set derived below.\n const rawPrioritizedQueueIds = useStatewireState(\n (state) => readDeepAgentStreamValues(state).prioritizedQueueIds,\n );\n const prioritizedQueueIds = useMemo(\n () => new Set(readPrioritizedQueueIds(rawPrioritizedQueueIds)),\n [rawPrioritizedQueueIds],\n );\n const status = useDeepAgentThreadStatus();\n const { isRunning, isContinuable } = queueLaneFlags(status);\n const aui = useAui();\n const sendCommand = useStatewireSendCommand();\n const listId = useId();\n const [expanded, setExpanded] = useState(true);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editingText, setEditingText] = useState('');\n // Head entries already auto-promoted once on this mount: an attempt whose\n // continue run dies and re-strands the SAME entry must not loop — the manual\n // Continue pill is the fallback for a repeatedly failing dispatch. Kept on\n // the tray (not the auto-continue child) so it survives the empty-lane\n // renders between a promotion and its outcome.\n const autoContinuedRef = useRef<Set<string>>(new Set());\n const onEventRef = useRef(onEvent);\n onEventRef.current = onEvent;\n\n const rows = buildStatewireQueueRows({ queue, steerQueue, isContinuable });\n // Continue promotes the QUEUE head only: a queue→steer move dispatches the\n // stranded steer lane plus this entry, while a steer entry moved onto its\n // own lane is a no-op.\n const queueHeadId = queue[0]?.id ?? null;\n const retryableRefusal = readRetryableRunRefusal(runs.error) !== null;\n const autoContinueOptions = autoContinue === true ? {} : autoContinue || null;\n\n /**\n * Re-send the entry under its own id on its own lane: the host replaces the\n * queued message's content in place, leaving its queue position and lane\n * untouched (a same-id send never dispatches).\n */\n const saveEdit = (\n item: StatewireThread.RunsQueueEntry,\n lane: 'queue' | 'steer',\n text: string,\n ): void => {\n setEditingId(null);\n setEditingText('');\n const command = buildQueueEditCommand({ item, lane, runId, text });\n if (command !== null) sendCommand(command);\n };\n\n /**\n * Retry/Discard act on a rendered lane snapshot that can trail the session\n * record; acting on an entry statewire already dropped throws. Nothing is\n * lost in that race, so it is recorded instead of crashing the click.\n */\n const runFailedSendAction = ({\n action,\n messageId,\n run,\n }: {\n action: 'retry' | 'discard';\n messageId: string;\n run: (actions: StatewireFailedActions) => void;\n }): void => {\n try {\n run(requireStatewireFailedActions(aui.thread().getState().extras));\n } catch (error) {\n if (!(error instanceof Error) || !isFailedSendEntryRace(error)) throw error;\n onEvent?.({\n type: 'failed-send-action-raced',\n threadId,\n messageId,\n action,\n error: error.message,\n });\n }\n };\n\n if (rows.length === 0 && failed.length === 0) return null;\n\n const renderRowContent = (context: StatewireQueueRowContentContext): ReactNode =>\n renderContent ? renderContent(context) : <QueueRowText text={context.text} />;\n\n return (\n <section\n aria-label=\"Queued messages\"\n className={cn(\n 'aui-statewire-queue w-full rounded-xl border border-border/70 bg-muted/30 px-2 py-1.5',\n classNames?.root,\n )}\n >\n {autoContinueOptions ? (\n <QueueAutoContinue\n delayMs={autoContinueOptions.delayMs ?? QUEUE_AUTO_CONTINUE_DELAY_MS}\n queueHeadId={queueHeadId}\n retryableRefusal={retryableRefusal}\n isContinuable={isContinuable}\n status={status}\n threadId={threadId}\n editing={editingId !== null}\n continuedRef={autoContinuedRef}\n onEventRef={onEventRef}\n />\n ) : null}\n <div className=\"flex items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n aria-expanded={expanded}\n aria-controls={listId}\n className=\"flex items-center gap-1 rounded px-1 py-1 font-medium text-muted-foreground text-xs hover:text-foreground\"\n >\n {expanded ? (\n <ChevronDownIcon className=\"size-3.5\" />\n ) : (\n <ChevronRightIcon className=\"size-3.5\" />\n )}\n {rows.length + failed.length} queued\n </button>\n {isContinuable && queueHeadId !== null ? (\n <ContinuePill\n ariaLabel=\"Continue with queued messages\"\n onClick={() => aui.composer.queueItem({ id: queueHeadId }).move({ lane: 'steer' })}\n />\n ) : null}\n </div>\n {expanded ? (\n <ul\n id={listId}\n className={cn('max-h-40 overflow-y-auto', classNames?.list)}\n data-testid=\"athena-statewire-queue\"\n >\n {rows.map(({ item, lane }, index) => {\n const text = queueEntryText(item.parts);\n const isUnsettled = isQueueEntryUnsettled(item);\n const isEditing = editingId === item.id;\n const showSendNow =\n !isUnsettled && showQueueRowSendNow({ lane, index, isRunning, isContinuable });\n const showPrioritize = !isUnsettled && lane === 'queue' && isRunning && runId !== null;\n const isPrioritized = prioritizedQueueIds.has(item.id);\n const editorContext: StatewireQueueEditorContext = {\n value: editingText,\n onChange: setEditingText,\n onSave: (next) => saveEdit(item, lane, next),\n onCancel: () => {\n setEditingId(null);\n setEditingText('');\n },\n };\n return (\n <li\n key={item.id}\n className=\"group flex items-start gap-2 rounded-md px-1 py-1.5 hover:bg-background\"\n >\n {isUnsettled ? (\n <Loader2Icon className=\"mt-0.5 size-3.5 shrink-0 animate-spin text-muted-foreground/60\" />\n ) : (\n <CircleIcon className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground/40\" />\n )}\n {isEditing ? (\n renderEditor ? (\n renderEditor(editorContext)\n ) : (\n <DefaultQueueEditor {...editorContext} />\n )\n ) : (\n renderRowContent({ text, index, item })\n )}\n <div\n className={cn('flex shrink-0 items-center gap-1', isEditing && 'hidden')}\n data-testid=\"statewire-queue-row-actions\"\n >\n {isUnsettled ? (\n <span\n className=\"px-1 text-[10px] text-muted-foreground\"\n data-testid=\"statewire-queued-message-sending\"\n >\n Sending…\n </span>\n ) : null}\n {showSendNow ? (\n <button\n type=\"button\"\n aria-label=\"Send now\"\n onClick={() =>\n aui.composer.queueItem({ id: item.id }).move({ lane: 'steer' })\n }\n className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 px-2 py-1 font-medium text-[10px] text-primary hover:bg-primary/5\"\n >\n <SendIcon className=\"size-2.5\" />\n Send now\n </button>\n ) : null}\n {showPrioritize ? (\n <button\n type=\"button\"\n aria-label={isPrioritized ? 'Remove priority' : 'Prioritize'}\n aria-pressed={isPrioritized}\n title={\n isPrioritized\n ? 'Remove priority'\n : 'Prioritize this message. Delivered as soon as the current step ends.'\n }\n onClick={() => {\n if (runId === null) return;\n sendCommand({\n type: 'run/prioritize',\n runId,\n messageId: item.id,\n priority: !isPrioritized,\n });\n }}\n className={cn(\n 'rounded p-1 transition-colors',\n isPrioritized\n ? 'text-amber-500 hover:text-amber-600'\n : 'text-muted-foreground hover:text-amber-500',\n )}\n >\n <ZapIcon className=\"size-3\" />\n </button>\n ) : null}\n {!isUnsettled ? (\n <button\n type=\"button\"\n aria-label=\"Edit queued message\"\n onClick={() => {\n setEditingId(item.id);\n setEditingText(text);\n }}\n className=\"rounded p-1 text-muted-foreground transition-colors hover:text-primary\"\n >\n <PencilIcon className=\"size-3\" />\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label=\"Remove queued message\"\n onClick={() => aui.composer.queueItem({ id: item.id }).remove()}\n className={cn(\n 'rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive',\n !showSendNow &&\n 'opacity-0 focus-visible:opacity-100 group-hover:opacity-100',\n )}\n >\n <Trash2Icon className=\"size-3\" />\n </button>\n </div>\n </li>\n );\n })}\n {failed.map((item, index) => (\n <li\n key={item.id}\n className=\"group flex items-start gap-2 rounded-md px-1 py-1.5 hover:bg-background\"\n >\n <AlertCircleIcon className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" />\n {renderRowContent({\n text: queueEntryText(item.parts),\n index: rows.length + index,\n item,\n })}\n <div\n className=\"flex shrink-0 items-center gap-1\"\n data-testid=\"statewire-queue-row-actions\"\n >\n <span\n className=\"px-1 text-[10px] text-destructive\"\n title={describeQueueSendFailure(item.sendFailure)}\n >\n Failed to send\n </span>\n <button\n type=\"button\"\n aria-label=\"Retry failed message\"\n onClick={() =>\n runFailedSendAction({\n action: 'retry',\n messageId: item.id,\n run: (actions) => actions.retryFailed(item.id),\n })\n }\n className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 px-2 py-1 font-medium text-[10px] text-primary hover:bg-primary/5\"\n >\n <RotateCcwIcon className=\"size-2.5\" />\n Retry\n </button>\n <button\n type=\"button\"\n aria-label=\"Discard failed message\"\n onClick={() =>\n runFailedSendAction({\n action: 'discard',\n messageId: item.id,\n run: (actions) => actions.discardFailed(item.id),\n })\n }\n className=\"rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2Icon className=\"size-3\" />\n </button>\n </div>\n </li>\n ))}\n </ul>\n ) : null}\n </section>\n );\n};\n\n/** Promotes ordinary stranded queue heads once; infrastructure recovery stays server-owned. */\nconst QueueAutoContinue: FC<{\n delayMs: number;\n queueHeadId: string | null;\n retryableRefusal: boolean;\n isContinuable: boolean;\n status: DeepAgentThreadStatus;\n threadId: string | null;\n editing: boolean;\n continuedRef: MutableRefObject<Set<string>>;\n onEventRef: MutableRefObject<((event: StatewireQueueEvent) => void) | undefined>;\n}> = ({\n delayMs,\n queueHeadId,\n retryableRefusal,\n isContinuable,\n status,\n threadId,\n editing,\n continuedRef,\n onEventRef,\n}) => {\n const aui = useAui();\n // The settle that stranded the queue is described by the LAST message's run\n // marker (a marker deeper in history was superseded by a later send). The\n // selectors return primitives so the subscriptions stay shallow-stable.\n const lastMessageMarker = useAuiState((s) => {\n const last = s.thread.messages.at(-1);\n return last ? readRunMarkerFromSourceMessages(last)?.marker : undefined;\n });\n const lastMessageMarkerSource = useAuiState((s) => {\n const last = s.thread.messages.at(-1);\n return last ? readRunMarkerFromSourceMessages(last)?.source : undefined;\n });\n const userStopBlocks = userStopBlocksQueueAutoContinue({\n marker: lastMessageMarker,\n source: lastMessageMarkerSource,\n });\n\n useEffect(() => {\n if (\n !isContinuable ||\n userStopBlocks ||\n retryableRefusal ||\n isServerUpdateStopSource(lastMessageMarkerSource)\n ) return;\n // An open inline edit means the user is mid-revision on some row —\n // dispatching underneath them could send text they are about to change.\n if (editing) return;\n if (queueHeadId === null) return;\n const attemptKey = `${threadId ?? 'unknown'}:${queueHeadId}`;\n if (continuedRef.current.has(attemptKey)) return;\n const timeoutId = setTimeout(() => {\n continuedRef.current.add(attemptKey);\n onEventRef.current?.({ type: 'auto-continued', threadId, status, headEntryId: queueHeadId });\n aui.composer.queueItem({ id: queueHeadId }).move({ lane: 'steer' });\n }, delayMs);\n return () => clearTimeout(timeoutId);\n }, [\n continuedRef,\n aui,\n delayMs,\n editing,\n isContinuable,\n onEventRef,\n queueHeadId,\n retryableRefusal,\n lastMessageMarkerSource,\n status,\n threadId,\n userStopBlocks,\n ]);\n\n return null;\n};\n\n/**\n * The default row body: `[@name](…)` mentions render as chips, not raw\n * markdown links, and the body clamps to two lines. Show more / Show less\n * appears only when the rendered text overflows those two lines, so a short\n * message that wraps in a narrow tray is still caught and a short one gets no\n * dead control.\n */\nconst QueueRowText: FC<{ text: string }> = ({ text }) => {\n const bodyRef = useRef<HTMLParagraphElement>(null);\n const [expanded, setExpanded] = useState(false);\n const [overflows, setOverflows] = useState(false);\n\n useLayoutEffect(() => {\n const body = bodyRef.current;\n // Expanded text has no clamp to overflow; the toggle stays up to collapse it.\n if (!body || expanded) return;\n const measure = (): void => setOverflows(body.scrollHeight > body.clientHeight + 1);\n measure();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(measure);\n observer.observe(body);\n return () => observer.disconnect();\n }, [text, expanded]);\n\n if (!text) {\n return <p className=\"min-w-0 flex-1 text-foreground text-xs\">Queued attachment</p>;\n }\n return (\n <div className=\"min-w-0 flex-1\">\n <p\n ref={bodyRef}\n className={cn(\n 'whitespace-pre-wrap break-words text-foreground text-xs leading-relaxed',\n !expanded && 'line-clamp-2',\n )}\n title={overflows && !expanded ? mentionsToPlainText(text) : undefined}\n >\n {parseMentionsInText(text)}\n </p>\n {overflows || expanded ? (\n <button\n type=\"button\"\n aria-expanded={expanded}\n onClick={() => setExpanded((value) => !value)}\n className=\"mt-0.5 font-medium text-[10px] text-muted-foreground hover:text-foreground\"\n >\n {expanded ? 'Show less' : 'Show more'}\n </button>\n ) : null}\n </div>\n );\n};\n\n/** Plain textarea editor: Enter saves, Shift+Enter breaks a line, Escape cancels. */\nconst DefaultQueueEditor: FC<StatewireQueueEditorContext> = ({\n value,\n onChange,\n onSave,\n onCancel,\n}) => {\n const canSave = value.trim().length > 0;\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>): void => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n if (canSave) onSave(value);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n onCancel();\n }\n };\n return (\n <div className=\"flex min-w-0 flex-1 items-start gap-1\">\n <textarea\n autoFocus\n aria-label=\"Edit message\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={Math.min(6, Math.max(1, value.split('\\n').length))}\n className=\"min-w-0 flex-1 resize-none rounded-md border border-border bg-background px-2 py-1 text-foreground text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring/50\"\n />\n <button\n type=\"button\"\n aria-label=\"Save edit\"\n disabled={!canSave}\n onClick={() => onSave(value)}\n className={cn(\n 'rounded p-1 text-primary transition-colors hover:bg-primary/10',\n !canSave && 'cursor-not-allowed opacity-40',\n )}\n >\n <CheckIcon className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Cancel edit\"\n onClick={onCancel}\n className=\"rounded p-1 text-muted-foreground transition-colors hover:bg-muted\"\n >\n <XIcon className=\"size-3.5\" />\n </button>\n </div>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -256,9 +256,15 @@ function parseMentionsInText(text) {
256
256
  }
257
257
  return parts.length > 0 ? parts : [text];
258
258
  }
259
+ function mentionsToPlainText(text) {
260
+ return text.replace(MENTION_RE, (_match, name, url) => {
261
+ return `@${name || parseMentionUrl(url).name}`;
262
+ });
263
+ }
259
264
  exports.MentionChip = MentionChip;
260
265
  exports.MentionLink = MentionLink;
261
266
  exports.ScreenPointChip = ScreenPointChip;
267
+ exports.mentionsToPlainText = mentionsToPlainText;
262
268
  exports.normalizeAthenaMentionHtml = normalizeAthenaMentionHtml;
263
269
  exports.parseMentionUrl = parseMentionUrl;
264
270
  exports.parseMentionsInText = parseMentionsInText;
@@ -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 { useContext, type FC, type ReactElement, type ReactNode } from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport { CrosshairIcon, Globe2Icon } 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\";\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// ── 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 */\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 = typeof children === \"string\"\n ? children.replace(/^@/, \"\")\n : Array.isArray(children)\n ? String(children[0] ?? \"\").replace(/^@/, \"\")\n : \"\";\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : undefined}\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// ── 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=\"not-prose inline-flex max-w-[260px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600\"\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"font-normal text-slate-400\">\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 thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({ name, type, assetIds, thumbnailUrl, sourceHref, interactive = true }) => {\n const auth = useContext(MentionAuthCtx);\n // A citation's excerpt (the passage the chip points at) is the tooltip; the\n // accessible name stays the action.\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : 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 // `not-prose` matters: assistant messages render inside Tailwind\n // Typography's `prose`, whose zero-specificity `img` margins (~1.7em\n // top/bottom) would otherwise inflate the chip's icon into a tall card\n // that breaks the sentence flow around it.\n const className = cn(\n \"not-prose inline-flex max-w-[220px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600 no-underline\",\n href &&\n \"cursor-pointer transition-colors hover:border-slate-400 hover:bg-slate-50 hover:text-slate-800 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\",\n );\n const contents = (\n <>\n {thumbnailUrl && (\n <img\n src={thumbnailUrl}\n alt=\"\"\n className=\"size-3.5 shrink-0\"\n draggable={false}\n />\n )}\n {!thumbnailUrl && type === \"webpage\" && (\n <Globe2Icon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )}\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {name}\n </span>\n </>\n );\n\n if (href && interactive) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={`Open ${name} in Spaces`}\n title={excerpt ?? `Open ${name} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={excerpt ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\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 assetIds?: 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 return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : citationFallbackLabel(parsed.searchParams),\n id,\n assetIds,\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 return {\n type,\n name,\n id,\n assetIds: type === \"asset\" && id ? [id] : 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\nconst MENTION_RE =\n /\\[@([^\\]]*)\\]\\(((?: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 assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : undefined}\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"],"names":["jsx","isScreenPointHref","parseScreenPointHref","formatScreenPoint","jsxs","CrosshairIcon","useContext","MentionAuthCtx","createOlympusAssetUrl","cn","Fragment","Globe2Icon","id","assetTypeToThumb"],"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 { useContext, type FC, type ReactElement, type ReactNode } from \"react\";\nimport {\n formatScreenPoint,\n isScreenPointHref,\n parseScreenPointHref,\n} from \"@athenaintel/agent-runtime\";\nimport { CrosshairIcon, Globe2Icon } 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\";\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// ── 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 */\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 = typeof children === \"string\"\n ? children.replace(/^@/, \"\")\n : Array.isArray(children)\n ? String(children[0] ?? \"\").replace(/^@/, \"\")\n : \"\";\n\n const parsed = parseMentionUrl(href);\n\n return (\n <MentionChip\n name={name || parsed.name}\n type={parsed.type}\n assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(href) ? href : undefined}\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// ── 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=\"not-prose inline-flex max-w-[260px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600\"\n title={description}\n aria-label={description}\n data-screen-point=\"valid\"\n >\n <CrosshairIcon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {children}\n </span>\n <span className=\"font-normal text-slate-400\">\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 thumbnailUrl?: string;\n sourceHref?: string;\n interactive?: boolean;\n}> = ({ name, type, assetIds, thumbnailUrl, sourceHref, interactive = true }) => {\n const auth = useContext(MentionAuthCtx);\n // A citation's excerpt (the passage the chip points at) is the tooltip; the\n // accessible name stays the action.\n const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : 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 // `not-prose` matters: assistant messages render inside Tailwind\n // Typography's `prose`, whose zero-specificity `img` margins (~1.7em\n // top/bottom) would otherwise inflate the chip's icon into a tall card\n // that breaks the sentence flow around it.\n const className = cn(\n \"not-prose inline-flex max-w-[220px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600 no-underline\",\n href &&\n \"cursor-pointer transition-colors hover:border-slate-400 hover:bg-slate-50 hover:text-slate-800 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1\",\n );\n const contents = (\n <>\n {thumbnailUrl && (\n <img\n src={thumbnailUrl}\n alt=\"\"\n className=\"size-3.5 shrink-0\"\n draggable={false}\n />\n )}\n {!thumbnailUrl && type === \"webpage\" && (\n <Globe2Icon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n )}\n <span className=\"max-w-[180px] overflow-hidden text-ellipsis\">\n {name}\n </span>\n </>\n );\n\n if (href && interactive) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className}\n aria-label={`Open ${name} in Spaces`}\n title={excerpt ?? `Open ${name} in Spaces`}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </a>\n );\n }\n\n return (\n <span\n className={className}\n title={excerpt ?? undefined}\n data-excerpt={excerpt ?? undefined}\n >\n {contents}\n </span>\n );\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 assetIds?: 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 return {\n type: \"asset\",\n name:\n assetIds.length > 1\n ? `${assetIds.length} assets`\n : citationFallbackLabel(parsed.searchParams),\n id,\n assetIds,\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 return {\n type,\n name,\n id,\n assetIds: type === \"asset\" && id ? [id] : 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\nconst MENTION_RE =\n /\\[@([^\\]]*)\\]\\(((?: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 assetIds={parsed.assetIds}\n thumbnailUrl={parsed.thumbnailUrl}\n sourceHref={isSpacesMentionUrl(url) ? url : undefined}\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":["jsx","isScreenPointHref","parseScreenPointHref","formatScreenPoint","jsxs","CrosshairIcon","useContext","MentionAuthCtx","createOlympusAssetUrl","cn","Fragment","Globe2Icon","id","assetTypeToThumb"],"mappings":"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"}
@@ -47,3 +47,5 @@ export declare function normalizeAthenaMentionHtml(text: string): string;
47
47
  * an array of string segments and MentionChip elements.
48
48
  */
49
49
  export declare function parseMentionsInText(text: string): (string | ReactElement)[];
50
+ /** The text as a reader sees it: each `[@name](…)` mention becomes `@name`. */
51
+ export declare function mentionsToPlainText(text: string): string;
@@ -254,10 +254,16 @@ function parseMentionsInText(text) {
254
254
  }
255
255
  return parts.length > 0 ? parts : [text];
256
256
  }
257
+ function mentionsToPlainText(text) {
258
+ return text.replace(MENTION_RE, (_match, name, url) => {
259
+ return `@${name || parseMentionUrl(url).name}`;
260
+ });
261
+ }
257
262
  export {
258
263
  MentionChip,
259
264
  MentionLink,
260
265
  ScreenPointChip,
266
+ mentionsToPlainText,
261
267
  normalizeAthenaMentionHtml,
262
268
  parseMentionUrl,
263
269
  parseMentionsInText