@athenaintel/react 0.38.1 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/chat-ui/thread/statewire-history-edge.cjs +13 -0
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.js +13 -0
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/diagnostics/errors.cjs +5 -0
- package/dist/diagnostics/errors.cjs.map +1 -1
- package/dist/diagnostics/errors.d.ts +5 -0
- package/dist/diagnostics/errors.js +5 -0
- package/dist/diagnostics/errors.js.map +1 -1
- package/dist/index.cjs +14 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +5 -2
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.js +5 -2
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/statewire-history-context.cjs +48 -0
- package/dist/provider/statewire-history-context.cjs.map +1 -0
- package/dist/provider/statewire-history-context.d.ts +36 -0
- package/dist/provider/statewire-history-context.js +48 -0
- package/dist/provider/statewire-history-context.js.map +1 -0
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/workbench/ComputerLiveDesktop.cjs +287 -0
- package/dist/workbench/ComputerLiveDesktop.cjs.map +1 -0
- package/dist/workbench/ComputerLiveDesktop.d.ts +27 -0
- package/dist/workbench/ComputerLiveDesktop.js +287 -0
- package/dist/workbench/ComputerLiveDesktop.js.map +1 -0
- package/dist/workbench/SessionWorkbench.cjs +2 -2
- package/dist/workbench/SessionWorkbench.cjs.map +1 -1
- package/dist/workbench/SessionWorkbench.js +2 -2
- package/dist/workbench/SessionWorkbench.js.map +1 -1
- package/dist/workbench/live-desktop-api.cjs +139 -0
- package/dist/workbench/live-desktop-api.cjs.map +1 -0
- package/dist/workbench/live-desktop-api.d.ts +69 -0
- package/dist/workbench/live-desktop-api.js +139 -0
- package/dist/workbench/live-desktop-api.js.map +1 -0
- package/dist/workbench/session-workbench-context.cjs.map +1 -1
- package/dist/workbench/session-workbench-context.d.ts +7 -0
- package/dist/workbench/session-workbench-context.js.map +1 -1
- package/dist/workbench/session-workbench-panes.cjs +38 -2
- package/dist/workbench/session-workbench-panes.cjs.map +1 -1
- package/dist/workbench/session-workbench-panes.js +38 -2
- package/dist/workbench/session-workbench-panes.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -634,8 +634,8 @@ Built-in panes and what each reads:
|
|
|
634
634
|
| Pane | id | Source |
|
|
635
635
|
|---|---|---|
|
|
636
636
|
| Tasks | `tasks` | The thread state's `todos` channel (the deepagents plan), else the newest `write_todos` call in the loaded transcript — "N/M done" and numbered steps with their status |
|
|
637
|
-
| Shell | `shell` | Every `execute` call in the loaded transcript, newest first: first line of the command, the machine it ran on (`sandbox_asset_id` named through the workspace's computers, else "Session sandbox"), the exit status parsed from `[Command succeeded/failed with exit code N]`, the time; searchable; a row expands to its output (first 4,000 characters) |
|
|
638
|
-
| Computer | `computer` | `GET /api/conversations/threads/{id}/workspace`: the session computer, else the most recent computer — its state (`GET /api/sandbox/computer/{id}/status`, a read that never wakes it, re-read every 30 s while shown), "Open in Athena", and "Copy SSH command" (`GET /api/v0/computer/{id}/ssh-access`, offered only when the viewer may edit the computer); the other computers it used below |
|
|
637
|
+
| Shell | `shell` | Every `execute` call in the loaded transcript, newest first: first line of the command, the machine it ran on (`sandbox_asset_id` named through the workspace's computers, else "Session sandbox"), the exit status parsed from `[Command succeeded/failed with exit code N]`, the time; searchable; a row expands to its output (first 4,000 characters). While the thread holds history the chat has not loaded (the chat's history edge reports it, `useStatewireHistory`), the count reads "so far" and **Load earlier commands** loads the next page, the same one scrolling up loads |
|
|
638
|
+
| Computer | `computer` | `GET /api/conversations/threads/{id}/workspace`: the session computer, else the most recent computer — **its screen, live** (`ComputerLiveDesktop`, below), its state (`GET /api/sandbox/computer/{id}/status`, a read that never wakes it, re-read every 30 s while shown), "Open in Athena", and "Copy SSH command" (`GET /api/v0/computer/{id}/ssh-access`, offered only when the viewer may edit the computer); the other computers it used below |
|
|
639
639
|
| Pull requests | `pull-requests` | The thread list's live pull-request state for the session (`enableThreadList`): number, title, state, checks and review decision, each a link to GitHub |
|
|
640
640
|
| Attachments | `attachments` | The workspace's assets — what the session created or edited — with type, last update, "Open in Athena", and a click that opens it in the asset panel when an `AthenaLayout` hosts one |
|
|
641
641
|
|
|
@@ -685,6 +685,18 @@ const MATTER_PANE: SessionWorkbenchPane = {
|
|
|
685
685
|
/>
|
|
686
686
|
```
|
|
687
687
|
|
|
688
|
+
**The computer's screen, live.** `ComputerLiveDesktop` (in the Computer pane,
|
|
689
|
+
or mounted on its own with `assetId`) watches a running computer's display as
|
|
690
|
+
soon as it mounts — `POST /api/sandbox/computer/{id}/live-desktop/session`
|
|
691
|
+
with this page as `embed_origin` — and frames agora's Selkies viewer; **Take
|
|
692
|
+
control** reopens it in control mode, and closing the pane ends the stream (an
|
|
693
|
+
open view keeps the computer awake, up to agora's 30-minute cap). It needs EDIT
|
|
694
|
+
on the computer, in its own workspace, and an app the computer serves on Athena
|
|
695
|
+
(a Marathon host or a verified custom domain in that workspace): agora lets
|
|
696
|
+
only that origin and Olympus frame the viewer, and tells the app only how the
|
|
697
|
+
stream fares. A computer without a display (one not made from the Browser
|
|
698
|
+
template) says so, and a stopped one is never woken.
|
|
699
|
+
|
|
688
700
|
The built-in pane bodies (`SessionTasksPane`, `SessionShellPane`, …), their
|
|
689
701
|
registry entries (`SESSION_WORKBENCH_TASKS_PANE`, …), the data hooks
|
|
690
702
|
(`useAthenaSessionWorkspace`, `useAthenaComputerStatus`,
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const react = require("@assistant-ui/react");
|
|
5
5
|
const react$1 = require("react");
|
|
6
|
+
const statewireHistoryContext = require("../../provider/statewire-history-context.cjs");
|
|
6
7
|
const statewireWindowContext = require("../../provider/statewire-window-context.cjs");
|
|
7
8
|
const threadIdContext = require("../../threads/thread-id-context.cjs");
|
|
8
9
|
const useDeepAgentWindowSync = require("../../agent-runtime/src/use-deep-agent-window-sync.cjs");
|
|
@@ -182,6 +183,7 @@ const StatewireHistoryEdge = ({
|
|
|
182
183
|
hasViewport: hasViewportStore || viewport !== null,
|
|
183
184
|
messages
|
|
184
185
|
});
|
|
186
|
+
const historyStore = react$1.useContext(statewireHistoryContext.StatewireHistoryContext);
|
|
185
187
|
const { hasOlder, canLoadOlder, loading, loadOlder } = useDeepAgentWindowSync.useDeepAgentWindowSync({
|
|
186
188
|
messages,
|
|
187
189
|
toolOutputs,
|
|
@@ -195,6 +197,17 @@ const StatewireHistoryEdge = ({
|
|
|
195
197
|
console.warn(`[StatewireHistoryEdge] ${command} failed:`, error);
|
|
196
198
|
}
|
|
197
199
|
});
|
|
200
|
+
react$1.useEffect(() => {
|
|
201
|
+
if (!historyStore || !threadId) return void 0;
|
|
202
|
+
historyStore.set({ threadId, hasOlder, canLoadOlder, loading, loadOlder });
|
|
203
|
+
return void 0;
|
|
204
|
+
}, [historyStore, threadId, hasOlder, canLoadOlder, loading, loadOlder]);
|
|
205
|
+
react$1.useEffect(() => {
|
|
206
|
+
if (!historyStore || !threadId) return void 0;
|
|
207
|
+
return () => {
|
|
208
|
+
if (historyStore.get().threadId === threadId) historyStore.set(statewireHistoryContext.NO_STATEWIRE_HISTORY);
|
|
209
|
+
};
|
|
210
|
+
}, [historyStore, threadId]);
|
|
198
211
|
react$1.useEffect(() => {
|
|
199
212
|
if (!rowEl || !hasOlder || typeof IntersectionObserver === "undefined") return;
|
|
200
213
|
const observer = new IntersectionObserver(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-history-edge.cjs","sources":["../../../src/chat-ui/thread/statewire-history-edge.tsx"],"sourcesContent":["import {\n getExternalStoreMessages,\n type ThreadMessage,\n type ThreadViewportState,\n useAuiState,\n useThreadViewport,\n useThreadViewportStore,\n} from \"@assistant-ui/react\";\nimport {\n type DeepAgentHistoryLoadEvent,\n type DeepAgentToolOutputStore,\n isStatewireChannelTeardown,\n useDeepAgentWindowSync,\n} from \"@athenaintel/agent-runtime\";\nimport type { FC } from \"react\";\nimport { Component, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { useStatewireToolOutputs } from \"../../provider/statewire-window-context\";\nimport { AthenaStatewireThreadIdContext } from \"../../threads/thread-id-context\";\n\n/** The attribute `ChatThreadShell` stamps on the scroll viewport. */\nexport const THREAD_VIEWPORT_ATTR = \"data-aui-thread-viewport\";\n\n/**\n * How far above the viewport the edge row counts as near: the row sits above\n * the first loaded message, so reaching it means the user has scrolled past\n * everything loaded and the next page should already be on its way. Messages\n * this close to the viewport also count as on screen for cut tool outputs.\n */\nexport const HISTORY_EDGE_NEAR_MARGIN_PX = 1500;\n\n/**\n * Cut tool outputs a transcript reads at once. The reads share the session's\n * command lane with everything the person sends, so a long transcript must\n * not queue dozens of them ahead of a message or a Stop.\n */\nexport const HISTORY_EDGE_TOOL_OUTPUT_CONCURRENCY = 2;\n\n/** What assistant-ui's `MessagePrimitive.Root` stamps on every message. */\nconst MESSAGE_SELECTOR = \"[data-message-id]\";\n\nconst NO_MESSAGE_IDS: ReadonlySet<string> = new Set();\n\nfunction sameIds({ a, b }: { a: ReadonlySet<string>; b: ReadonlySet<string> }): boolean {\n if (a.size !== b.size) return false;\n for (const id of a) if (!b.has(id)) return false;\n return true;\n}\n\n/**\n * Which messages sit within {@link HISTORY_EDGE_NEAR_MARGIN_PX} of the\n * viewport, so cut tool outputs are read for what the person can reach. A\n * thread opens at its newest turn; reading every loaded step's output up\n * front sent dozens of reads, each re-rendering the transcript, for steps\n * nobody had scrolled to.\n *\n * `undefined` (every message counts) where nothing can be observed: no\n * viewport around the row, or no `IntersectionObserver`. A message rendered\n * without a `data-message-id` root (a host's own renderer) cannot be observed\n * either, so it counts as before.\n */\nfunction useMessagesNearViewport({\n viewport,\n hasViewport,\n messages,\n}: {\n viewport: HTMLElement | null;\n /** A viewport is (or will be) registered around the row. */\n hasViewport: boolean;\n /** The converted thread: every change may have mounted or remounted a message. */\n messages: readonly ThreadMessage[];\n}): ((messageId: string) => boolean) | undefined {\n const [nearIds, setNearIds] = useState<ReadonlySet<string>>(NO_MESSAGE_IDS);\n const observable = hasViewport && typeof IntersectionObserver !== \"undefined\";\n const trackRef = useRef<(() => void) | null>(null);\n // Every id a rendered element carries, as of the last scan.\n const renderedIdsRef = useRef<ReadonlySet<string>>(NO_MESSAGE_IDS);\n\n useEffect(() => {\n if (!viewport || !observable) return undefined;\n const near = new Set<Element>();\n const observed = new Set<Element>();\n const publish = () => {\n const ids = new Set<string>();\n for (const element of near) {\n const id = element.getAttribute(\"data-message-id\");\n if (id) ids.add(id);\n }\n setNearIds((previous) => (sameIds({ a: previous, b: ids }) ? previous : ids));\n };\n const intersections = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target.isConnected) near.add(entry.target);\n else near.delete(entry.target);\n }\n publish();\n },\n { root: viewport, rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px` },\n );\n // Messages remount when a page folds into them (a new id), so what is\n // observed follows the rendered elements, not the message list.\n const track = () => {\n let dropped = false;\n for (const element of observed) {\n if (element.isConnected) continue;\n intersections.unobserve(element);\n observed.delete(element);\n dropped = near.delete(element) || dropped;\n }\n const rendered = new Set<string>();\n for (const element of viewport.querySelectorAll(MESSAGE_SELECTOR)) {\n const id = element.getAttribute(\"data-message-id\");\n if (id) rendered.add(id);\n if (observed.has(element)) continue;\n observed.add(element);\n intersections.observe(element);\n }\n renderedIdsRef.current = rendered;\n if (dropped) publish();\n };\n trackRef.current = track;\n track();\n return () => {\n trackRef.current = null;\n renderedIdsRef.current = NO_MESSAGE_IDS;\n intersections.disconnect();\n setNearIds(NO_MESSAGE_IDS);\n };\n }, [viewport, observable]);\n\n // A changed message list is the trigger to re-scan: its messages render in\n // the same commit, so their elements are in the DOM by this effect (a\n // query, no layout read).\n useEffect(() => {\n trackRef.current?.();\n }, [messages]);\n\n return useMemo(() => {\n if (!observable) return undefined;\n return (messageId: string) => {\n // Not registered yet: nothing is on screen to read for.\n if (viewport === null) return false;\n return nearIds.has(messageId) || !renderedIdsRef.current.has(messageId);\n };\n }, [nearIds, observable, viewport]);\n}\n\n/** The wire message a converted message was built from first (its oldest source). */\nfunction oldestSourceId(message: ThreadMessage): string | undefined {\n const source = getExternalStoreMessages<{ id?: unknown }>(message)[0];\n return typeof source?.id === \"string\" ? source.id : undefined;\n}\n\n/**\n * Whether older history landed ahead of what was loaded: the old first\n * message now sits further down (a page ahead of it), or the first message's\n * old oldest wire message now follows earlier ones (a page that folded into\n * the first loaded turn). A first message replaced in place — a branch picked\n * on it, an edit — is neither, and a run streaming at the tail never touches\n * the first message.\n */\nfunction prependedOlderHistory({\n previous,\n next,\n}: {\n previous: readonly ThreadMessage[];\n next: readonly ThreadMessage[];\n}): boolean {\n const previousFirst = previous[0];\n const nextFirst = next[0];\n if (previousFirst === undefined || nextFirst === undefined || previousFirst === nextFirst) return false;\n if (previousFirst.id !== nextFirst.id && next.some((message, index) => index > 0 && message.id === previousFirst.id)) {\n return true;\n }\n const previousOldest = oldestSourceId(previousFirst);\n if (previousOldest === undefined || previousOldest === oldestSourceId(nextFirst)) return false;\n return getExternalStoreMessages<{ id?: unknown }>(nextFirst).some(\n (source, index) => index > 0 && source?.id === previousOldest,\n );\n}\n\ninterface KeepReadingPlaceProps {\n viewport: HTMLElement | null;\n threadId: string | null;\n /** The converted thread. */\n messages: readonly ThreadMessage[];\n}\n\n/**\n * Keeps the reader's place when older history lands. Everything a page adds\n * sits above what the person is reading, so their distance from the bottom of\n * the transcript is what must survive. A class for `getSnapshotBeforeUpdate`:\n * the distance is read right before React applies the commit that renders the\n * page — the one moment it is exact, even for a scroll the browser has not\n * reported yet — and restored after it, instantly (never through the\n * viewport's smooth scrolling). Other commits read no layout.\n *\n * The browser's scroll anchoring and assistant-ui's prepend anchor aim at the\n * same position, so whichever ran first leaves this nothing to do; on its own\n * it covers what they miss: a reader at the very top (browsers never anchor\n * at scroll offset 0) and a page that folds into the first loaded turn (that\n * message remounts under a new id, taking the browser's anchor node with it).\n */\nclass KeepReadingPlace extends Component<KeepReadingPlaceProps> {\n getSnapshotBeforeUpdate(previous: Readonly<KeepReadingPlaceProps>): number | null {\n const { viewport, threadId, messages } = this.props;\n if (\n viewport === null ||\n viewport !== previous.viewport ||\n threadId !== previous.threadId ||\n messages === previous.messages ||\n !prependedOlderHistory({ previous: previous.messages, next: messages })\n ) {\n return null;\n }\n return viewport.scrollHeight - viewport.scrollTop;\n }\n\n componentDidUpdate(\n _previous: Readonly<KeepReadingPlaceProps>,\n _state: unknown,\n fromBottom: number | null,\n ): void {\n const { viewport } = this.props;\n if (viewport === null || fromBottom === null) return;\n const top = viewport.scrollHeight - fromBottom;\n if (Math.abs(viewport.scrollTop - top) >= 1) viewport.scrollTo({ top, behavior: \"instant\" });\n }\n\n render(): null {\n return null;\n }\n}\n\nexport interface HistoryEdgeRowProps {\n /** Whether the server holds messages older than the loaded window. */\n hasOlder: boolean;\n /** Whether the next page can be asked for; `false` with `hasOlder` is the connection's loading depth spent. Defaults to `hasOlder`. */\n canLoadOlder?: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n onLoadOlder: () => void;\n className?: string;\n /** The row element, for the viewport observer. */\n rowRef?: (element: HTMLElement | null) => void;\n}\n\n/**\n * The top edge of the LOADED history: a manual way to load the next page of\n * older messages, and the in-flight state. Renders nothing when the whole\n * transcript is loaded (or the head interest has not resolved).\n */\nexport const HistoryEdgeRow: FC<HistoryEdgeRowProps> = ({\n hasOlder,\n canLoadOlder = hasOlder,\n loading,\n onLoadOlder,\n className,\n rowRef,\n}) => {\n if (!hasOlder) return null;\n return (\n <div\n ref={rowRef}\n data-testid=\"thread-history-edge\"\n className={cn(\n \"flex w-full max-w-(--thread-max-width) items-center justify-center py-1 text-xs text-muted-foreground\",\n className,\n )}\n >\n {loading ? (\n <span data-testid=\"thread-history-edge-loading\">Loading earlier messages…</span>\n ) : canLoadOlder ? (\n <button\n type=\"button\"\n data-testid=\"thread-history-edge-load\"\n onClick={onLoadOlder}\n className=\"rounded-lg px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n Load earlier messages\n </button>\n ) : (\n <span data-testid=\"thread-history-edge-limit\">Earlier messages can't be loaded here</span>\n )}\n </div>\n );\n};\n\nexport interface StatewireHistoryEdgeProps {\n /**\n * The thread's tool-output store (the one its converter substitutes from).\n * Defaults to `AthenaProvider`'s; hosts mounting the runtime themselves pass\n * the store they gave `useDeepAgentRuntime({ toolOutputs })`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n className?: string;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: (event: DeepAgentHistoryLoadEvent) => void;\n}\n\n/**\n * The client half of the interest window for a DOM thread — mounted once per\n * thread, inside the scroll viewport above the messages (`ChatThreadShell`'s\n * `beforeMessages` slot):\n *\n * - renders the \"Load earlier messages\" edge row and loads the next page when\n * the row comes near the viewport (scrolling up) or on its button, keeping\n * the reading position when the page lands above the view;\n * - fetches cut tool outputs whole for the cards near the viewport, a few at\n * a time, so they re-render complete through the converter (`toolOutputs`).\n *\n * Inert until the head interest resolves: no row, no reads.\n */\nexport const StatewireHistoryEdge: FC<StatewireHistoryEdgeProps> = ({\n toolOutputs: toolOutputsProp,\n className,\n onHistoryLoad,\n}) => {\n const providerToolOutputs = useStatewireToolOutputs();\n const toolOutputs = toolOutputsProp ?? providerToolOutputs ?? undefined;\n const messages = useAuiState((s) => s.thread.messages);\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const [nearTop, setNearTop] = useState(false);\n const [nearTopEpoch, setNearTopEpoch] = useState(0);\n const nearTopRef = useRef(false);\n const [rowEl, setRowEl] = useState<HTMLElement | null>(null);\n // The scroll viewport the thread registered (assistant-ui's viewport store),\n // known even while the row renders nothing; the row's ancestor otherwise.\n const hasViewportStore = useThreadViewportStore({ optional: true }) !== null;\n const registeredViewport = useThreadViewport({\n optional: true,\n selector: (state: ThreadViewportState) => state.element.viewport,\n });\n const viewport =\n registeredViewport ?? rowEl?.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`) ?? null;\n const isMessageMounted = useMessagesNearViewport({\n viewport,\n hasViewport: hasViewportStore || viewport !== null,\n messages,\n });\n const { hasOlder, canLoadOlder, loading, loadOlder } = useDeepAgentWindowSync({\n messages,\n toolOutputs,\n nearTop,\n nearTopEpoch,\n isMessageMounted,\n maxConcurrentToolOutputs: HISTORY_EDGE_TOOL_OUTPUT_CONCURRENCY,\n onHistoryLoad,\n onError: (error, command) => {\n // Leaving the thread settles an in-flight read as lost: lifecycle, not a failure.\n if (error instanceof Error && isStatewireChannelTeardown(error)) return;\n console.warn(`[StatewireHistoryEdge] ${command} failed:`, error);\n },\n });\n\n // The row's own visibility is the near-top level: a level, not an edge, so\n // pages chain while the user stays at the top and stop once the prepended\n // content has pushed the row away again.\n useEffect(() => {\n if (!rowEl || !hasOlder || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const near = entry.isIntersecting;\n if (nearTopRef.current === near) continue;\n nearTopRef.current = near;\n setNearTop(near);\n if (near) setNearTopEpoch((epoch) => epoch + 1);\n }\n },\n {\n root: viewport,\n rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px`,\n },\n );\n observer.observe(rowEl);\n return () => {\n observer.disconnect();\n nearTopRef.current = false;\n setNearTop(false);\n };\n }, [rowEl, hasOlder, viewport]);\n\n return (\n <>\n <KeepReadingPlace viewport={viewport} threadId={threadId} messages={messages} />\n <HistoryEdgeRow\n rowRef={setRowEl}\n hasOlder={hasOlder}\n canLoadOlder={canLoadOlder}\n loading={loading}\n onLoadOlder={loadOlder}\n className={className}\n />\n </>\n );\n};\n"],"names":["useState","useRef","useEffect","useMemo","getExternalStoreMessages","Component","jsx","cn","useStatewireToolOutputs","useAuiState","useContext","AthenaStatewireThreadIdContext","useThreadViewportStore","useThreadViewport","useDeepAgentWindowSync","isStatewireChannelTeardown","jsxs","Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"statewire-history-edge.cjs","sources":["../../../src/chat-ui/thread/statewire-history-edge.tsx"],"sourcesContent":["import {\n getExternalStoreMessages,\n type ThreadMessage,\n type ThreadViewportState,\n useAuiState,\n useThreadViewport,\n useThreadViewportStore,\n} from \"@assistant-ui/react\";\nimport {\n type DeepAgentHistoryLoadEvent,\n type DeepAgentToolOutputStore,\n isStatewireChannelTeardown,\n useDeepAgentWindowSync,\n} from \"@athenaintel/agent-runtime\";\nimport type { FC } from \"react\";\nimport { Component, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport {\n NO_STATEWIRE_HISTORY,\n StatewireHistoryContext,\n} from \"../../provider/statewire-history-context\";\nimport { useStatewireToolOutputs } from \"../../provider/statewire-window-context\";\nimport { AthenaStatewireThreadIdContext } from \"../../threads/thread-id-context\";\n\n/** The attribute `ChatThreadShell` stamps on the scroll viewport. */\nexport const THREAD_VIEWPORT_ATTR = \"data-aui-thread-viewport\";\n\n/**\n * How far above the viewport the edge row counts as near: the row sits above\n * the first loaded message, so reaching it means the user has scrolled past\n * everything loaded and the next page should already be on its way. Messages\n * this close to the viewport also count as on screen for cut tool outputs.\n */\nexport const HISTORY_EDGE_NEAR_MARGIN_PX = 1500;\n\n/**\n * Cut tool outputs a transcript reads at once. The reads share the session's\n * command lane with everything the person sends, so a long transcript must\n * not queue dozens of them ahead of a message or a Stop.\n */\nexport const HISTORY_EDGE_TOOL_OUTPUT_CONCURRENCY = 2;\n\n/** What assistant-ui's `MessagePrimitive.Root` stamps on every message. */\nconst MESSAGE_SELECTOR = \"[data-message-id]\";\n\nconst NO_MESSAGE_IDS: ReadonlySet<string> = new Set();\n\nfunction sameIds({ a, b }: { a: ReadonlySet<string>; b: ReadonlySet<string> }): boolean {\n if (a.size !== b.size) return false;\n for (const id of a) if (!b.has(id)) return false;\n return true;\n}\n\n/**\n * Which messages sit within {@link HISTORY_EDGE_NEAR_MARGIN_PX} of the\n * viewport, so cut tool outputs are read for what the person can reach. A\n * thread opens at its newest turn; reading every loaded step's output up\n * front sent dozens of reads, each re-rendering the transcript, for steps\n * nobody had scrolled to.\n *\n * `undefined` (every message counts) where nothing can be observed: no\n * viewport around the row, or no `IntersectionObserver`. A message rendered\n * without a `data-message-id` root (a host's own renderer) cannot be observed\n * either, so it counts as before.\n */\nfunction useMessagesNearViewport({\n viewport,\n hasViewport,\n messages,\n}: {\n viewport: HTMLElement | null;\n /** A viewport is (or will be) registered around the row. */\n hasViewport: boolean;\n /** The converted thread: every change may have mounted or remounted a message. */\n messages: readonly ThreadMessage[];\n}): ((messageId: string) => boolean) | undefined {\n const [nearIds, setNearIds] = useState<ReadonlySet<string>>(NO_MESSAGE_IDS);\n const observable = hasViewport && typeof IntersectionObserver !== \"undefined\";\n const trackRef = useRef<(() => void) | null>(null);\n // Every id a rendered element carries, as of the last scan.\n const renderedIdsRef = useRef<ReadonlySet<string>>(NO_MESSAGE_IDS);\n\n useEffect(() => {\n if (!viewport || !observable) return undefined;\n const near = new Set<Element>();\n const observed = new Set<Element>();\n const publish = () => {\n const ids = new Set<string>();\n for (const element of near) {\n const id = element.getAttribute(\"data-message-id\");\n if (id) ids.add(id);\n }\n setNearIds((previous) => (sameIds({ a: previous, b: ids }) ? previous : ids));\n };\n const intersections = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target.isConnected) near.add(entry.target);\n else near.delete(entry.target);\n }\n publish();\n },\n { root: viewport, rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px` },\n );\n // Messages remount when a page folds into them (a new id), so what is\n // observed follows the rendered elements, not the message list.\n const track = () => {\n let dropped = false;\n for (const element of observed) {\n if (element.isConnected) continue;\n intersections.unobserve(element);\n observed.delete(element);\n dropped = near.delete(element) || dropped;\n }\n const rendered = new Set<string>();\n for (const element of viewport.querySelectorAll(MESSAGE_SELECTOR)) {\n const id = element.getAttribute(\"data-message-id\");\n if (id) rendered.add(id);\n if (observed.has(element)) continue;\n observed.add(element);\n intersections.observe(element);\n }\n renderedIdsRef.current = rendered;\n if (dropped) publish();\n };\n trackRef.current = track;\n track();\n return () => {\n trackRef.current = null;\n renderedIdsRef.current = NO_MESSAGE_IDS;\n intersections.disconnect();\n setNearIds(NO_MESSAGE_IDS);\n };\n }, [viewport, observable]);\n\n // A changed message list is the trigger to re-scan: its messages render in\n // the same commit, so their elements are in the DOM by this effect (a\n // query, no layout read).\n useEffect(() => {\n trackRef.current?.();\n }, [messages]);\n\n return useMemo(() => {\n if (!observable) return undefined;\n return (messageId: string) => {\n // Not registered yet: nothing is on screen to read for.\n if (viewport === null) return false;\n return nearIds.has(messageId) || !renderedIdsRef.current.has(messageId);\n };\n }, [nearIds, observable, viewport]);\n}\n\n/** The wire message a converted message was built from first (its oldest source). */\nfunction oldestSourceId(message: ThreadMessage): string | undefined {\n const source = getExternalStoreMessages<{ id?: unknown }>(message)[0];\n return typeof source?.id === \"string\" ? source.id : undefined;\n}\n\n/**\n * Whether older history landed ahead of what was loaded: the old first\n * message now sits further down (a page ahead of it), or the first message's\n * old oldest wire message now follows earlier ones (a page that folded into\n * the first loaded turn). A first message replaced in place — a branch picked\n * on it, an edit — is neither, and a run streaming at the tail never touches\n * the first message.\n */\nfunction prependedOlderHistory({\n previous,\n next,\n}: {\n previous: readonly ThreadMessage[];\n next: readonly ThreadMessage[];\n}): boolean {\n const previousFirst = previous[0];\n const nextFirst = next[0];\n if (previousFirst === undefined || nextFirst === undefined || previousFirst === nextFirst) return false;\n if (previousFirst.id !== nextFirst.id && next.some((message, index) => index > 0 && message.id === previousFirst.id)) {\n return true;\n }\n const previousOldest = oldestSourceId(previousFirst);\n if (previousOldest === undefined || previousOldest === oldestSourceId(nextFirst)) return false;\n return getExternalStoreMessages<{ id?: unknown }>(nextFirst).some(\n (source, index) => index > 0 && source?.id === previousOldest,\n );\n}\n\ninterface KeepReadingPlaceProps {\n viewport: HTMLElement | null;\n threadId: string | null;\n /** The converted thread. */\n messages: readonly ThreadMessage[];\n}\n\n/**\n * Keeps the reader's place when older history lands. Everything a page adds\n * sits above what the person is reading, so their distance from the bottom of\n * the transcript is what must survive. A class for `getSnapshotBeforeUpdate`:\n * the distance is read right before React applies the commit that renders the\n * page — the one moment it is exact, even for a scroll the browser has not\n * reported yet — and restored after it, instantly (never through the\n * viewport's smooth scrolling). Other commits read no layout.\n *\n * The browser's scroll anchoring and assistant-ui's prepend anchor aim at the\n * same position, so whichever ran first leaves this nothing to do; on its own\n * it covers what they miss: a reader at the very top (browsers never anchor\n * at scroll offset 0) and a page that folds into the first loaded turn (that\n * message remounts under a new id, taking the browser's anchor node with it).\n */\nclass KeepReadingPlace extends Component<KeepReadingPlaceProps> {\n getSnapshotBeforeUpdate(previous: Readonly<KeepReadingPlaceProps>): number | null {\n const { viewport, threadId, messages } = this.props;\n if (\n viewport === null ||\n viewport !== previous.viewport ||\n threadId !== previous.threadId ||\n messages === previous.messages ||\n !prependedOlderHistory({ previous: previous.messages, next: messages })\n ) {\n return null;\n }\n return viewport.scrollHeight - viewport.scrollTop;\n }\n\n componentDidUpdate(\n _previous: Readonly<KeepReadingPlaceProps>,\n _state: unknown,\n fromBottom: number | null,\n ): void {\n const { viewport } = this.props;\n if (viewport === null || fromBottom === null) return;\n const top = viewport.scrollHeight - fromBottom;\n if (Math.abs(viewport.scrollTop - top) >= 1) viewport.scrollTo({ top, behavior: \"instant\" });\n }\n\n render(): null {\n return null;\n }\n}\n\nexport interface HistoryEdgeRowProps {\n /** Whether the server holds messages older than the loaded window. */\n hasOlder: boolean;\n /** Whether the next page can be asked for; `false` with `hasOlder` is the connection's loading depth spent. Defaults to `hasOlder`. */\n canLoadOlder?: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n onLoadOlder: () => void;\n className?: string;\n /** The row element, for the viewport observer. */\n rowRef?: (element: HTMLElement | null) => void;\n}\n\n/**\n * The top edge of the LOADED history: a manual way to load the next page of\n * older messages, and the in-flight state. Renders nothing when the whole\n * transcript is loaded (or the head interest has not resolved).\n */\nexport const HistoryEdgeRow: FC<HistoryEdgeRowProps> = ({\n hasOlder,\n canLoadOlder = hasOlder,\n loading,\n onLoadOlder,\n className,\n rowRef,\n}) => {\n if (!hasOlder) return null;\n return (\n <div\n ref={rowRef}\n data-testid=\"thread-history-edge\"\n className={cn(\n \"flex w-full max-w-(--thread-max-width) items-center justify-center py-1 text-xs text-muted-foreground\",\n className,\n )}\n >\n {loading ? (\n <span data-testid=\"thread-history-edge-loading\">Loading earlier messages…</span>\n ) : canLoadOlder ? (\n <button\n type=\"button\"\n data-testid=\"thread-history-edge-load\"\n onClick={onLoadOlder}\n className=\"rounded-lg px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n Load earlier messages\n </button>\n ) : (\n <span data-testid=\"thread-history-edge-limit\">Earlier messages can't be loaded here</span>\n )}\n </div>\n );\n};\n\nexport interface StatewireHistoryEdgeProps {\n /**\n * The thread's tool-output store (the one its converter substitutes from).\n * Defaults to `AthenaProvider`'s; hosts mounting the runtime themselves pass\n * the store they gave `useDeepAgentRuntime({ toolOutputs })`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n className?: string;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: (event: DeepAgentHistoryLoadEvent) => void;\n}\n\n/**\n * The client half of the interest window for a DOM thread — mounted once per\n * thread, inside the scroll viewport above the messages (`ChatThreadShell`'s\n * `beforeMessages` slot):\n *\n * - renders the \"Load earlier messages\" edge row and loads the next page when\n * the row comes near the viewport (scrolling up) or on its button, keeping\n * the reading position when the page lands above the view;\n * - fetches cut tool outputs whole for the cards near the viewport, a few at\n * a time, so they re-render complete through the converter (`toolOutputs`).\n *\n * Inert until the head interest resolves: no row, no reads.\n */\nexport const StatewireHistoryEdge: FC<StatewireHistoryEdgeProps> = ({\n toolOutputs: toolOutputsProp,\n className,\n onHistoryLoad,\n}) => {\n const providerToolOutputs = useStatewireToolOutputs();\n const toolOutputs = toolOutputsProp ?? providerToolOutputs ?? undefined;\n const messages = useAuiState((s) => s.thread.messages);\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const [nearTop, setNearTop] = useState(false);\n const [nearTopEpoch, setNearTopEpoch] = useState(0);\n const nearTopRef = useRef(false);\n const [rowEl, setRowEl] = useState<HTMLElement | null>(null);\n // The scroll viewport the thread registered (assistant-ui's viewport store),\n // known even while the row renders nothing; the row's ancestor otherwise.\n const hasViewportStore = useThreadViewportStore({ optional: true }) !== null;\n const registeredViewport = useThreadViewport({\n optional: true,\n selector: (state: ThreadViewportState) => state.element.viewport,\n });\n const viewport =\n registeredViewport ?? rowEl?.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`) ?? null;\n const isMessageMounted = useMessagesNearViewport({\n viewport,\n hasViewport: hasViewportStore || viewport !== null,\n messages,\n });\n const historyStore = useContext(StatewireHistoryContext);\n const { hasOlder, canLoadOlder, loading, loadOlder } = useDeepAgentWindowSync({\n messages,\n toolOutputs,\n nearTop,\n nearTopEpoch,\n isMessageMounted,\n maxConcurrentToolOutputs: HISTORY_EDGE_TOOL_OUTPUT_CONCURRENCY,\n onHistoryLoad,\n onError: (error, command) => {\n // Leaving the thread settles an in-flight read as lost: lifecycle, not a failure.\n if (error instanceof Error && isStatewireChannelTeardown(error)) return;\n console.warn(`[StatewireHistoryEdge] ${command} failed:`, error);\n },\n });\n\n // Other surfaces of the provider (the workbench's Shell pane) read where the\n // history stands; leaving the thread or unmounting withdraws the report.\n useEffect(() => {\n if (!historyStore || !threadId) return undefined;\n historyStore.set({ threadId, hasOlder, canLoadOlder, loading, loadOlder });\n return undefined;\n }, [historyStore, threadId, hasOlder, canLoadOlder, loading, loadOlder]);\n useEffect(() => {\n if (!historyStore || !threadId) return undefined;\n return () => {\n if (historyStore.get().threadId === threadId) historyStore.set(NO_STATEWIRE_HISTORY);\n };\n }, [historyStore, threadId]);\n\n // The row's own visibility is the near-top level: a level, not an edge, so\n // pages chain while the user stays at the top and stop once the prepended\n // content has pushed the row away again.\n useEffect(() => {\n if (!rowEl || !hasOlder || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const near = entry.isIntersecting;\n if (nearTopRef.current === near) continue;\n nearTopRef.current = near;\n setNearTop(near);\n if (near) setNearTopEpoch((epoch) => epoch + 1);\n }\n },\n {\n root: viewport,\n rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px`,\n },\n );\n observer.observe(rowEl);\n return () => {\n observer.disconnect();\n nearTopRef.current = false;\n setNearTop(false);\n };\n }, [rowEl, hasOlder, viewport]);\n\n return (\n <>\n <KeepReadingPlace viewport={viewport} threadId={threadId} messages={messages} />\n <HistoryEdgeRow\n rowRef={setRowEl}\n hasOlder={hasOlder}\n canLoadOlder={canLoadOlder}\n loading={loading}\n onLoadOlder={loadOlder}\n className={className}\n />\n </>\n );\n};\n"],"names":["useState","useRef","useEffect","useMemo","getExternalStoreMessages","Component","jsx","cn","useStatewireToolOutputs","useAuiState","useContext","AthenaStatewireThreadIdContext","useThreadViewportStore","useThreadViewport","StatewireHistoryContext","useDeepAgentWindowSync","isStatewireChannelTeardown","NO_STATEWIRE_HISTORY","jsxs","Fragment"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useAuiState, useThreadViewportStore, useThreadViewport, getExternalStoreMessages } from "@assistant-ui/react";
|
|
3
3
|
import { useContext, useState, useRef, useEffect, useMemo, Component } from "react";
|
|
4
|
+
import { StatewireHistoryContext, NO_STATEWIRE_HISTORY } from "../../provider/statewire-history-context.js";
|
|
4
5
|
import { useStatewireToolOutputs } from "../../provider/statewire-window-context.js";
|
|
5
6
|
import { AthenaStatewireThreadIdContext } from "../../threads/thread-id-context.js";
|
|
6
7
|
import { useDeepAgentWindowSync } from "../../agent-runtime/src/use-deep-agent-window-sync.js";
|
|
@@ -180,6 +181,7 @@ const StatewireHistoryEdge = ({
|
|
|
180
181
|
hasViewport: hasViewportStore || viewport !== null,
|
|
181
182
|
messages
|
|
182
183
|
});
|
|
184
|
+
const historyStore = useContext(StatewireHistoryContext);
|
|
183
185
|
const { hasOlder, canLoadOlder, loading, loadOlder } = useDeepAgentWindowSync({
|
|
184
186
|
messages,
|
|
185
187
|
toolOutputs,
|
|
@@ -193,6 +195,17 @@ const StatewireHistoryEdge = ({
|
|
|
193
195
|
console.warn(`[StatewireHistoryEdge] ${command} failed:`, error);
|
|
194
196
|
}
|
|
195
197
|
});
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
if (!historyStore || !threadId) return void 0;
|
|
200
|
+
historyStore.set({ threadId, hasOlder, canLoadOlder, loading, loadOlder });
|
|
201
|
+
return void 0;
|
|
202
|
+
}, [historyStore, threadId, hasOlder, canLoadOlder, loading, loadOlder]);
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (!historyStore || !threadId) return void 0;
|
|
205
|
+
return () => {
|
|
206
|
+
if (historyStore.get().threadId === threadId) historyStore.set(NO_STATEWIRE_HISTORY);
|
|
207
|
+
};
|
|
208
|
+
}, [historyStore, threadId]);
|
|
196
209
|
useEffect(() => {
|
|
197
210
|
if (!rowEl || !hasOlder || typeof IntersectionObserver === "undefined") return;
|
|
198
211
|
const observer = new IntersectionObserver(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-history-edge.js","sources":["../../../src/chat-ui/thread/statewire-history-edge.tsx"],"sourcesContent":["import {\n getExternalStoreMessages,\n type ThreadMessage,\n type ThreadViewportState,\n useAuiState,\n useThreadViewport,\n useThreadViewportStore,\n} from \"@assistant-ui/react\";\nimport {\n type DeepAgentHistoryLoadEvent,\n type DeepAgentToolOutputStore,\n isStatewireChannelTeardown,\n useDeepAgentWindowSync,\n} from \"@athenaintel/agent-runtime\";\nimport type { FC } from \"react\";\nimport { Component, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { useStatewireToolOutputs } from \"../../provider/statewire-window-context\";\nimport { AthenaStatewireThreadIdContext } from \"../../threads/thread-id-context\";\n\n/** The attribute `ChatThreadShell` stamps on the scroll viewport. */\nexport const THREAD_VIEWPORT_ATTR = \"data-aui-thread-viewport\";\n\n/**\n * How far above the viewport the edge row counts as near: the row sits above\n * the first loaded message, so reaching it means the user has scrolled past\n * everything loaded and the next page should already be on its way. Messages\n * this close to the viewport also count as on screen for cut tool outputs.\n */\nexport const HISTORY_EDGE_NEAR_MARGIN_PX = 1500;\n\n/**\n * Cut tool outputs a transcript reads at once. The reads share the session's\n * command lane with everything the person sends, so a long transcript must\n * not queue dozens of them ahead of a message or a Stop.\n */\nexport const HISTORY_EDGE_TOOL_OUTPUT_CONCURRENCY = 2;\n\n/** What assistant-ui's `MessagePrimitive.Root` stamps on every message. */\nconst MESSAGE_SELECTOR = \"[data-message-id]\";\n\nconst NO_MESSAGE_IDS: ReadonlySet<string> = new Set();\n\nfunction sameIds({ a, b }: { a: ReadonlySet<string>; b: ReadonlySet<string> }): boolean {\n if (a.size !== b.size) return false;\n for (const id of a) if (!b.has(id)) return false;\n return true;\n}\n\n/**\n * Which messages sit within {@link HISTORY_EDGE_NEAR_MARGIN_PX} of the\n * viewport, so cut tool outputs are read for what the person can reach. A\n * thread opens at its newest turn; reading every loaded step's output up\n * front sent dozens of reads, each re-rendering the transcript, for steps\n * nobody had scrolled to.\n *\n * `undefined` (every message counts) where nothing can be observed: no\n * viewport around the row, or no `IntersectionObserver`. A message rendered\n * without a `data-message-id` root (a host's own renderer) cannot be observed\n * either, so it counts as before.\n */\nfunction useMessagesNearViewport({\n viewport,\n hasViewport,\n messages,\n}: {\n viewport: HTMLElement | null;\n /** A viewport is (or will be) registered around the row. */\n hasViewport: boolean;\n /** The converted thread: every change may have mounted or remounted a message. */\n messages: readonly ThreadMessage[];\n}): ((messageId: string) => boolean) | undefined {\n const [nearIds, setNearIds] = useState<ReadonlySet<string>>(NO_MESSAGE_IDS);\n const observable = hasViewport && typeof IntersectionObserver !== \"undefined\";\n const trackRef = useRef<(() => void) | null>(null);\n // Every id a rendered element carries, as of the last scan.\n const renderedIdsRef = useRef<ReadonlySet<string>>(NO_MESSAGE_IDS);\n\n useEffect(() => {\n if (!viewport || !observable) return undefined;\n const near = new Set<Element>();\n const observed = new Set<Element>();\n const publish = () => {\n const ids = new Set<string>();\n for (const element of near) {\n const id = element.getAttribute(\"data-message-id\");\n if (id) ids.add(id);\n }\n setNearIds((previous) => (sameIds({ a: previous, b: ids }) ? previous : ids));\n };\n const intersections = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target.isConnected) near.add(entry.target);\n else near.delete(entry.target);\n }\n publish();\n },\n { root: viewport, rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px` },\n );\n // Messages remount when a page folds into them (a new id), so what is\n // observed follows the rendered elements, not the message list.\n const track = () => {\n let dropped = false;\n for (const element of observed) {\n if (element.isConnected) continue;\n intersections.unobserve(element);\n observed.delete(element);\n dropped = near.delete(element) || dropped;\n }\n const rendered = new Set<string>();\n for (const element of viewport.querySelectorAll(MESSAGE_SELECTOR)) {\n const id = element.getAttribute(\"data-message-id\");\n if (id) rendered.add(id);\n if (observed.has(element)) continue;\n observed.add(element);\n intersections.observe(element);\n }\n renderedIdsRef.current = rendered;\n if (dropped) publish();\n };\n trackRef.current = track;\n track();\n return () => {\n trackRef.current = null;\n renderedIdsRef.current = NO_MESSAGE_IDS;\n intersections.disconnect();\n setNearIds(NO_MESSAGE_IDS);\n };\n }, [viewport, observable]);\n\n // A changed message list is the trigger to re-scan: its messages render in\n // the same commit, so their elements are in the DOM by this effect (a\n // query, no layout read).\n useEffect(() => {\n trackRef.current?.();\n }, [messages]);\n\n return useMemo(() => {\n if (!observable) return undefined;\n return (messageId: string) => {\n // Not registered yet: nothing is on screen to read for.\n if (viewport === null) return false;\n return nearIds.has(messageId) || !renderedIdsRef.current.has(messageId);\n };\n }, [nearIds, observable, viewport]);\n}\n\n/** The wire message a converted message was built from first (its oldest source). */\nfunction oldestSourceId(message: ThreadMessage): string | undefined {\n const source = getExternalStoreMessages<{ id?: unknown }>(message)[0];\n return typeof source?.id === \"string\" ? source.id : undefined;\n}\n\n/**\n * Whether older history landed ahead of what was loaded: the old first\n * message now sits further down (a page ahead of it), or the first message's\n * old oldest wire message now follows earlier ones (a page that folded into\n * the first loaded turn). A first message replaced in place — a branch picked\n * on it, an edit — is neither, and a run streaming at the tail never touches\n * the first message.\n */\nfunction prependedOlderHistory({\n previous,\n next,\n}: {\n previous: readonly ThreadMessage[];\n next: readonly ThreadMessage[];\n}): boolean {\n const previousFirst = previous[0];\n const nextFirst = next[0];\n if (previousFirst === undefined || nextFirst === undefined || previousFirst === nextFirst) return false;\n if (previousFirst.id !== nextFirst.id && next.some((message, index) => index > 0 && message.id === previousFirst.id)) {\n return true;\n }\n const previousOldest = oldestSourceId(previousFirst);\n if (previousOldest === undefined || previousOldest === oldestSourceId(nextFirst)) return false;\n return getExternalStoreMessages<{ id?: unknown }>(nextFirst).some(\n (source, index) => index > 0 && source?.id === previousOldest,\n );\n}\n\ninterface KeepReadingPlaceProps {\n viewport: HTMLElement | null;\n threadId: string | null;\n /** The converted thread. */\n messages: readonly ThreadMessage[];\n}\n\n/**\n * Keeps the reader's place when older history lands. Everything a page adds\n * sits above what the person is reading, so their distance from the bottom of\n * the transcript is what must survive. A class for `getSnapshotBeforeUpdate`:\n * the distance is read right before React applies the commit that renders the\n * page — the one moment it is exact, even for a scroll the browser has not\n * reported yet — and restored after it, instantly (never through the\n * viewport's smooth scrolling). Other commits read no layout.\n *\n * The browser's scroll anchoring and assistant-ui's prepend anchor aim at the\n * same position, so whichever ran first leaves this nothing to do; on its own\n * it covers what they miss: a reader at the very top (browsers never anchor\n * at scroll offset 0) and a page that folds into the first loaded turn (that\n * message remounts under a new id, taking the browser's anchor node with it).\n */\nclass KeepReadingPlace extends Component<KeepReadingPlaceProps> {\n getSnapshotBeforeUpdate(previous: Readonly<KeepReadingPlaceProps>): number | null {\n const { viewport, threadId, messages } = this.props;\n if (\n viewport === null ||\n viewport !== previous.viewport ||\n threadId !== previous.threadId ||\n messages === previous.messages ||\n !prependedOlderHistory({ previous: previous.messages, next: messages })\n ) {\n return null;\n }\n return viewport.scrollHeight - viewport.scrollTop;\n }\n\n componentDidUpdate(\n _previous: Readonly<KeepReadingPlaceProps>,\n _state: unknown,\n fromBottom: number | null,\n ): void {\n const { viewport } = this.props;\n if (viewport === null || fromBottom === null) return;\n const top = viewport.scrollHeight - fromBottom;\n if (Math.abs(viewport.scrollTop - top) >= 1) viewport.scrollTo({ top, behavior: \"instant\" });\n }\n\n render(): null {\n return null;\n }\n}\n\nexport interface HistoryEdgeRowProps {\n /** Whether the server holds messages older than the loaded window. */\n hasOlder: boolean;\n /** Whether the next page can be asked for; `false` with `hasOlder` is the connection's loading depth spent. Defaults to `hasOlder`. */\n canLoadOlder?: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n onLoadOlder: () => void;\n className?: string;\n /** The row element, for the viewport observer. */\n rowRef?: (element: HTMLElement | null) => void;\n}\n\n/**\n * The top edge of the LOADED history: a manual way to load the next page of\n * older messages, and the in-flight state. Renders nothing when the whole\n * transcript is loaded (or the head interest has not resolved).\n */\nexport const HistoryEdgeRow: FC<HistoryEdgeRowProps> = ({\n hasOlder,\n canLoadOlder = hasOlder,\n loading,\n onLoadOlder,\n className,\n rowRef,\n}) => {\n if (!hasOlder) return null;\n return (\n <div\n ref={rowRef}\n data-testid=\"thread-history-edge\"\n className={cn(\n \"flex w-full max-w-(--thread-max-width) items-center justify-center py-1 text-xs text-muted-foreground\",\n className,\n )}\n >\n {loading ? (\n <span data-testid=\"thread-history-edge-loading\">Loading earlier messages…</span>\n ) : canLoadOlder ? (\n <button\n type=\"button\"\n data-testid=\"thread-history-edge-load\"\n onClick={onLoadOlder}\n className=\"rounded-lg px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n Load earlier messages\n </button>\n ) : (\n <span data-testid=\"thread-history-edge-limit\">Earlier messages can't be loaded here</span>\n )}\n </div>\n );\n};\n\nexport interface StatewireHistoryEdgeProps {\n /**\n * The thread's tool-output store (the one its converter substitutes from).\n * Defaults to `AthenaProvider`'s; hosts mounting the runtime themselves pass\n * the store they gave `useDeepAgentRuntime({ toolOutputs })`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n className?: string;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: (event: DeepAgentHistoryLoadEvent) => void;\n}\n\n/**\n * The client half of the interest window for a DOM thread — mounted once per\n * thread, inside the scroll viewport above the messages (`ChatThreadShell`'s\n * `beforeMessages` slot):\n *\n * - renders the \"Load earlier messages\" edge row and loads the next page when\n * the row comes near the viewport (scrolling up) or on its button, keeping\n * the reading position when the page lands above the view;\n * - fetches cut tool outputs whole for the cards near the viewport, a few at\n * a time, so they re-render complete through the converter (`toolOutputs`).\n *\n * Inert until the head interest resolves: no row, no reads.\n */\nexport const StatewireHistoryEdge: FC<StatewireHistoryEdgeProps> = ({\n toolOutputs: toolOutputsProp,\n className,\n onHistoryLoad,\n}) => {\n const providerToolOutputs = useStatewireToolOutputs();\n const toolOutputs = toolOutputsProp ?? providerToolOutputs ?? undefined;\n const messages = useAuiState((s) => s.thread.messages);\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const [nearTop, setNearTop] = useState(false);\n const [nearTopEpoch, setNearTopEpoch] = useState(0);\n const nearTopRef = useRef(false);\n const [rowEl, setRowEl] = useState<HTMLElement | null>(null);\n // The scroll viewport the thread registered (assistant-ui's viewport store),\n // known even while the row renders nothing; the row's ancestor otherwise.\n const hasViewportStore = useThreadViewportStore({ optional: true }) !== null;\n const registeredViewport = useThreadViewport({\n optional: true,\n selector: (state: ThreadViewportState) => state.element.viewport,\n });\n const viewport =\n registeredViewport ?? rowEl?.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`) ?? null;\n const isMessageMounted = useMessagesNearViewport({\n viewport,\n hasViewport: hasViewportStore || viewport !== null,\n messages,\n });\n const { hasOlder, canLoadOlder, loading, loadOlder } = useDeepAgentWindowSync({\n messages,\n toolOutputs,\n nearTop,\n nearTopEpoch,\n isMessageMounted,\n maxConcurrentToolOutputs: HISTORY_EDGE_TOOL_OUTPUT_CONCURRENCY,\n onHistoryLoad,\n onError: (error, command) => {\n // Leaving the thread settles an in-flight read as lost: lifecycle, not a failure.\n if (error instanceof Error && isStatewireChannelTeardown(error)) return;\n console.warn(`[StatewireHistoryEdge] ${command} failed:`, error);\n },\n });\n\n // The row's own visibility is the near-top level: a level, not an edge, so\n // pages chain while the user stays at the top and stop once the prepended\n // content has pushed the row away again.\n useEffect(() => {\n if (!rowEl || !hasOlder || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const near = entry.isIntersecting;\n if (nearTopRef.current === near) continue;\n nearTopRef.current = near;\n setNearTop(near);\n if (near) setNearTopEpoch((epoch) => epoch + 1);\n }\n },\n {\n root: viewport,\n rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px`,\n },\n );\n observer.observe(rowEl);\n return () => {\n observer.disconnect();\n nearTopRef.current = false;\n setNearTop(false);\n };\n }, [rowEl, hasOlder, viewport]);\n\n return (\n <>\n <KeepReadingPlace viewport={viewport} threadId={threadId} messages={messages} />\n <HistoryEdgeRow\n rowRef={setRowEl}\n hasOlder={hasOlder}\n canLoadOlder={canLoadOlder}\n loading={loading}\n onLoadOlder={loadOlder}\n className={className}\n />\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"statewire-history-edge.js","sources":["../../../src/chat-ui/thread/statewire-history-edge.tsx"],"sourcesContent":["import {\n getExternalStoreMessages,\n type ThreadMessage,\n type ThreadViewportState,\n useAuiState,\n useThreadViewport,\n useThreadViewportStore,\n} from \"@assistant-ui/react\";\nimport {\n type DeepAgentHistoryLoadEvent,\n type DeepAgentToolOutputStore,\n isStatewireChannelTeardown,\n useDeepAgentWindowSync,\n} from \"@athenaintel/agent-runtime\";\nimport type { FC } from \"react\";\nimport { Component, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport {\n NO_STATEWIRE_HISTORY,\n StatewireHistoryContext,\n} from \"../../provider/statewire-history-context\";\nimport { useStatewireToolOutputs } from \"../../provider/statewire-window-context\";\nimport { AthenaStatewireThreadIdContext } from \"../../threads/thread-id-context\";\n\n/** The attribute `ChatThreadShell` stamps on the scroll viewport. */\nexport const THREAD_VIEWPORT_ATTR = \"data-aui-thread-viewport\";\n\n/**\n * How far above the viewport the edge row counts as near: the row sits above\n * the first loaded message, so reaching it means the user has scrolled past\n * everything loaded and the next page should already be on its way. Messages\n * this close to the viewport also count as on screen for cut tool outputs.\n */\nexport const HISTORY_EDGE_NEAR_MARGIN_PX = 1500;\n\n/**\n * Cut tool outputs a transcript reads at once. The reads share the session's\n * command lane with everything the person sends, so a long transcript must\n * not queue dozens of them ahead of a message or a Stop.\n */\nexport const HISTORY_EDGE_TOOL_OUTPUT_CONCURRENCY = 2;\n\n/** What assistant-ui's `MessagePrimitive.Root` stamps on every message. */\nconst MESSAGE_SELECTOR = \"[data-message-id]\";\n\nconst NO_MESSAGE_IDS: ReadonlySet<string> = new Set();\n\nfunction sameIds({ a, b }: { a: ReadonlySet<string>; b: ReadonlySet<string> }): boolean {\n if (a.size !== b.size) return false;\n for (const id of a) if (!b.has(id)) return false;\n return true;\n}\n\n/**\n * Which messages sit within {@link HISTORY_EDGE_NEAR_MARGIN_PX} of the\n * viewport, so cut tool outputs are read for what the person can reach. A\n * thread opens at its newest turn; reading every loaded step's output up\n * front sent dozens of reads, each re-rendering the transcript, for steps\n * nobody had scrolled to.\n *\n * `undefined` (every message counts) where nothing can be observed: no\n * viewport around the row, or no `IntersectionObserver`. A message rendered\n * without a `data-message-id` root (a host's own renderer) cannot be observed\n * either, so it counts as before.\n */\nfunction useMessagesNearViewport({\n viewport,\n hasViewport,\n messages,\n}: {\n viewport: HTMLElement | null;\n /** A viewport is (or will be) registered around the row. */\n hasViewport: boolean;\n /** The converted thread: every change may have mounted or remounted a message. */\n messages: readonly ThreadMessage[];\n}): ((messageId: string) => boolean) | undefined {\n const [nearIds, setNearIds] = useState<ReadonlySet<string>>(NO_MESSAGE_IDS);\n const observable = hasViewport && typeof IntersectionObserver !== \"undefined\";\n const trackRef = useRef<(() => void) | null>(null);\n // Every id a rendered element carries, as of the last scan.\n const renderedIdsRef = useRef<ReadonlySet<string>>(NO_MESSAGE_IDS);\n\n useEffect(() => {\n if (!viewport || !observable) return undefined;\n const near = new Set<Element>();\n const observed = new Set<Element>();\n const publish = () => {\n const ids = new Set<string>();\n for (const element of near) {\n const id = element.getAttribute(\"data-message-id\");\n if (id) ids.add(id);\n }\n setNearIds((previous) => (sameIds({ a: previous, b: ids }) ? previous : ids));\n };\n const intersections = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target.isConnected) near.add(entry.target);\n else near.delete(entry.target);\n }\n publish();\n },\n { root: viewport, rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px` },\n );\n // Messages remount when a page folds into them (a new id), so what is\n // observed follows the rendered elements, not the message list.\n const track = () => {\n let dropped = false;\n for (const element of observed) {\n if (element.isConnected) continue;\n intersections.unobserve(element);\n observed.delete(element);\n dropped = near.delete(element) || dropped;\n }\n const rendered = new Set<string>();\n for (const element of viewport.querySelectorAll(MESSAGE_SELECTOR)) {\n const id = element.getAttribute(\"data-message-id\");\n if (id) rendered.add(id);\n if (observed.has(element)) continue;\n observed.add(element);\n intersections.observe(element);\n }\n renderedIdsRef.current = rendered;\n if (dropped) publish();\n };\n trackRef.current = track;\n track();\n return () => {\n trackRef.current = null;\n renderedIdsRef.current = NO_MESSAGE_IDS;\n intersections.disconnect();\n setNearIds(NO_MESSAGE_IDS);\n };\n }, [viewport, observable]);\n\n // A changed message list is the trigger to re-scan: its messages render in\n // the same commit, so their elements are in the DOM by this effect (a\n // query, no layout read).\n useEffect(() => {\n trackRef.current?.();\n }, [messages]);\n\n return useMemo(() => {\n if (!observable) return undefined;\n return (messageId: string) => {\n // Not registered yet: nothing is on screen to read for.\n if (viewport === null) return false;\n return nearIds.has(messageId) || !renderedIdsRef.current.has(messageId);\n };\n }, [nearIds, observable, viewport]);\n}\n\n/** The wire message a converted message was built from first (its oldest source). */\nfunction oldestSourceId(message: ThreadMessage): string | undefined {\n const source = getExternalStoreMessages<{ id?: unknown }>(message)[0];\n return typeof source?.id === \"string\" ? source.id : undefined;\n}\n\n/**\n * Whether older history landed ahead of what was loaded: the old first\n * message now sits further down (a page ahead of it), or the first message's\n * old oldest wire message now follows earlier ones (a page that folded into\n * the first loaded turn). A first message replaced in place — a branch picked\n * on it, an edit — is neither, and a run streaming at the tail never touches\n * the first message.\n */\nfunction prependedOlderHistory({\n previous,\n next,\n}: {\n previous: readonly ThreadMessage[];\n next: readonly ThreadMessage[];\n}): boolean {\n const previousFirst = previous[0];\n const nextFirst = next[0];\n if (previousFirst === undefined || nextFirst === undefined || previousFirst === nextFirst) return false;\n if (previousFirst.id !== nextFirst.id && next.some((message, index) => index > 0 && message.id === previousFirst.id)) {\n return true;\n }\n const previousOldest = oldestSourceId(previousFirst);\n if (previousOldest === undefined || previousOldest === oldestSourceId(nextFirst)) return false;\n return getExternalStoreMessages<{ id?: unknown }>(nextFirst).some(\n (source, index) => index > 0 && source?.id === previousOldest,\n );\n}\n\ninterface KeepReadingPlaceProps {\n viewport: HTMLElement | null;\n threadId: string | null;\n /** The converted thread. */\n messages: readonly ThreadMessage[];\n}\n\n/**\n * Keeps the reader's place when older history lands. Everything a page adds\n * sits above what the person is reading, so their distance from the bottom of\n * the transcript is what must survive. A class for `getSnapshotBeforeUpdate`:\n * the distance is read right before React applies the commit that renders the\n * page — the one moment it is exact, even for a scroll the browser has not\n * reported yet — and restored after it, instantly (never through the\n * viewport's smooth scrolling). Other commits read no layout.\n *\n * The browser's scroll anchoring and assistant-ui's prepend anchor aim at the\n * same position, so whichever ran first leaves this nothing to do; on its own\n * it covers what they miss: a reader at the very top (browsers never anchor\n * at scroll offset 0) and a page that folds into the first loaded turn (that\n * message remounts under a new id, taking the browser's anchor node with it).\n */\nclass KeepReadingPlace extends Component<KeepReadingPlaceProps> {\n getSnapshotBeforeUpdate(previous: Readonly<KeepReadingPlaceProps>): number | null {\n const { viewport, threadId, messages } = this.props;\n if (\n viewport === null ||\n viewport !== previous.viewport ||\n threadId !== previous.threadId ||\n messages === previous.messages ||\n !prependedOlderHistory({ previous: previous.messages, next: messages })\n ) {\n return null;\n }\n return viewport.scrollHeight - viewport.scrollTop;\n }\n\n componentDidUpdate(\n _previous: Readonly<KeepReadingPlaceProps>,\n _state: unknown,\n fromBottom: number | null,\n ): void {\n const { viewport } = this.props;\n if (viewport === null || fromBottom === null) return;\n const top = viewport.scrollHeight - fromBottom;\n if (Math.abs(viewport.scrollTop - top) >= 1) viewport.scrollTo({ top, behavior: \"instant\" });\n }\n\n render(): null {\n return null;\n }\n}\n\nexport interface HistoryEdgeRowProps {\n /** Whether the server holds messages older than the loaded window. */\n hasOlder: boolean;\n /** Whether the next page can be asked for; `false` with `hasOlder` is the connection's loading depth spent. Defaults to `hasOlder`. */\n canLoadOlder?: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n onLoadOlder: () => void;\n className?: string;\n /** The row element, for the viewport observer. */\n rowRef?: (element: HTMLElement | null) => void;\n}\n\n/**\n * The top edge of the LOADED history: a manual way to load the next page of\n * older messages, and the in-flight state. Renders nothing when the whole\n * transcript is loaded (or the head interest has not resolved).\n */\nexport const HistoryEdgeRow: FC<HistoryEdgeRowProps> = ({\n hasOlder,\n canLoadOlder = hasOlder,\n loading,\n onLoadOlder,\n className,\n rowRef,\n}) => {\n if (!hasOlder) return null;\n return (\n <div\n ref={rowRef}\n data-testid=\"thread-history-edge\"\n className={cn(\n \"flex w-full max-w-(--thread-max-width) items-center justify-center py-1 text-xs text-muted-foreground\",\n className,\n )}\n >\n {loading ? (\n <span data-testid=\"thread-history-edge-loading\">Loading earlier messages…</span>\n ) : canLoadOlder ? (\n <button\n type=\"button\"\n data-testid=\"thread-history-edge-load\"\n onClick={onLoadOlder}\n className=\"rounded-lg px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n Load earlier messages\n </button>\n ) : (\n <span data-testid=\"thread-history-edge-limit\">Earlier messages can't be loaded here</span>\n )}\n </div>\n );\n};\n\nexport interface StatewireHistoryEdgeProps {\n /**\n * The thread's tool-output store (the one its converter substitutes from).\n * Defaults to `AthenaProvider`'s; hosts mounting the runtime themselves pass\n * the store they gave `useDeepAgentRuntime({ toolOutputs })`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n className?: string;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: (event: DeepAgentHistoryLoadEvent) => void;\n}\n\n/**\n * The client half of the interest window for a DOM thread — mounted once per\n * thread, inside the scroll viewport above the messages (`ChatThreadShell`'s\n * `beforeMessages` slot):\n *\n * - renders the \"Load earlier messages\" edge row and loads the next page when\n * the row comes near the viewport (scrolling up) or on its button, keeping\n * the reading position when the page lands above the view;\n * - fetches cut tool outputs whole for the cards near the viewport, a few at\n * a time, so they re-render complete through the converter (`toolOutputs`).\n *\n * Inert until the head interest resolves: no row, no reads.\n */\nexport const StatewireHistoryEdge: FC<StatewireHistoryEdgeProps> = ({\n toolOutputs: toolOutputsProp,\n className,\n onHistoryLoad,\n}) => {\n const providerToolOutputs = useStatewireToolOutputs();\n const toolOutputs = toolOutputsProp ?? providerToolOutputs ?? undefined;\n const messages = useAuiState((s) => s.thread.messages);\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const [nearTop, setNearTop] = useState(false);\n const [nearTopEpoch, setNearTopEpoch] = useState(0);\n const nearTopRef = useRef(false);\n const [rowEl, setRowEl] = useState<HTMLElement | null>(null);\n // The scroll viewport the thread registered (assistant-ui's viewport store),\n // known even while the row renders nothing; the row's ancestor otherwise.\n const hasViewportStore = useThreadViewportStore({ optional: true }) !== null;\n const registeredViewport = useThreadViewport({\n optional: true,\n selector: (state: ThreadViewportState) => state.element.viewport,\n });\n const viewport =\n registeredViewport ?? rowEl?.closest<HTMLElement>(`[${THREAD_VIEWPORT_ATTR}]`) ?? null;\n const isMessageMounted = useMessagesNearViewport({\n viewport,\n hasViewport: hasViewportStore || viewport !== null,\n messages,\n });\n const historyStore = useContext(StatewireHistoryContext);\n const { hasOlder, canLoadOlder, loading, loadOlder } = useDeepAgentWindowSync({\n messages,\n toolOutputs,\n nearTop,\n nearTopEpoch,\n isMessageMounted,\n maxConcurrentToolOutputs: HISTORY_EDGE_TOOL_OUTPUT_CONCURRENCY,\n onHistoryLoad,\n onError: (error, command) => {\n // Leaving the thread settles an in-flight read as lost: lifecycle, not a failure.\n if (error instanceof Error && isStatewireChannelTeardown(error)) return;\n console.warn(`[StatewireHistoryEdge] ${command} failed:`, error);\n },\n });\n\n // Other surfaces of the provider (the workbench's Shell pane) read where the\n // history stands; leaving the thread or unmounting withdraws the report.\n useEffect(() => {\n if (!historyStore || !threadId) return undefined;\n historyStore.set({ threadId, hasOlder, canLoadOlder, loading, loadOlder });\n return undefined;\n }, [historyStore, threadId, hasOlder, canLoadOlder, loading, loadOlder]);\n useEffect(() => {\n if (!historyStore || !threadId) return undefined;\n return () => {\n if (historyStore.get().threadId === threadId) historyStore.set(NO_STATEWIRE_HISTORY);\n };\n }, [historyStore, threadId]);\n\n // The row's own visibility is the near-top level: a level, not an edge, so\n // pages chain while the user stays at the top and stop once the prepended\n // content has pushed the row away again.\n useEffect(() => {\n if (!rowEl || !hasOlder || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const near = entry.isIntersecting;\n if (nearTopRef.current === near) continue;\n nearTopRef.current = near;\n setNearTop(near);\n if (near) setNearTopEpoch((epoch) => epoch + 1);\n }\n },\n {\n root: viewport,\n rootMargin: `${HISTORY_EDGE_NEAR_MARGIN_PX}px 0px`,\n },\n );\n observer.observe(rowEl);\n return () => {\n observer.disconnect();\n nearTopRef.current = false;\n setNearTop(false);\n };\n }, [rowEl, hasOlder, viewport]);\n\n return (\n <>\n <KeepReadingPlace viewport={viewport} threadId={threadId} messages={messages} />\n <HistoryEdgeRow\n rowRef={setRowEl}\n hasOlder={hasOlder}\n canLoadOlder={canLoadOlder}\n loading={loading}\n onLoadOlder={loadOlder}\n className={className}\n />\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -74,6 +74,11 @@ const ATHENA_SDK_ERROR_CODES = {
|
|
|
74
74
|
* SSH target.
|
|
75
75
|
*/
|
|
76
76
|
session_workspace_failed: "session_workspace_failed",
|
|
77
|
+
/**
|
|
78
|
+
* A computer's live desktop session
|
|
79
|
+
* (`/api/sandbox/computer/{id}/live-desktop/session`) was refused or failed.
|
|
80
|
+
*/
|
|
81
|
+
live_desktop_failed: "live_desktop_failed",
|
|
77
82
|
/** Anything else. */
|
|
78
83
|
unknown: "unknown"
|
|
79
84
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","sources":["../../src/diagnostics/errors.ts"],"sourcesContent":["/**\n * Typed SDK errors. Every failure the SDK surfaces to a consumer carries a\n * stable `code` (matchable in code), a human `message`, and a `hint` — the\n * one-line \"what to do about it\" that turns a silent failure into a debuggable\n * one. Backend rejections keep their structured `{ status, detail }` payload.\n */\n\nexport const ATHENA_SDK_ERROR_CODES = {\n /** No token and no API key resolved before a request had to be sent. */\n auth_missing: 'auth_missing',\n /** The server refused the credential (401 / statewire `unauthorized` fin). */\n auth_rejected: 'auth_rejected',\n /**\n * `/_athena/auth` did not answer within the timeout on the last attempt of\n * its bounded retry cycle. The bridge stopped retrying (see\n * `ParentBridgeState.error`).\n */\n auth_bridge_timeout: 'auth_bridge_timeout',\n /**\n * `/_athena/auth` refused the mint (a 4xx other than 404), answered a body\n * it cannot read, or kept failing (5xx, 408, 429, network) through its\n * bounded retry cycle. The bridge stopped retrying and hands out no token\n * (see `ParentBridgeState.error`).\n */\n auth_bridge_failed: 'auth_bridge_failed',\n /**\n * `AthenaSSOProvider`'s session read (`/api/sso/userinfo`) kept failing\n * (5xx, 408, 429, network, timeout) through its bounded retries, or\n * answered another status it does not retry, or a 2xx without a JSON body.\n * The provider shows `displayOnError` with reason `request_failed`.\n */\n auth_sso_failed: 'auth_sso_failed',\n /** The statewire sync URL could not be derived from the configuration. */\n config_invalid: 'config_invalid',\n /** The thread list request failed. */\n threads_list_failed: 'threads_list_failed',\n /** Loading a thread's history failed. */\n thread_load_failed: 'thread_load_failed',\n /**\n * Pinning / unpinning a thread failed. `status` 404 / 405 means the backend\n * predates `POST /api/conversations/threads/favorite` (see\n * `isThreadFavoriteUnavailableError`); the pin then stays read-only.\n */\n thread_favorite_failed: 'thread_favorite_failed',\n /**\n * Renaming a thread failed. `status` 404 / 405 means the backend predates\n * `POST /api/conversations/threads/rename` (see\n * `isThreadRenameUnavailableError`); the list then offers no rename.\n */\n thread_rename_failed: 'thread_rename_failed',\n /**\n * Archiving a thread failed. `status` 404 / 405 means the backend has no\n * archive route (see `isThreadArchiveUnavailableError`); the list then\n * offers no archive action.\n */\n thread_archive_failed: 'thread_archive_failed',\n /** The statewire host rejected a command (structured `{status, detail}`). */\n statewire_rejected: 'statewire_rejected',\n /** The statewire connection is gone and will not reconnect on its own. */\n statewire_gone: 'statewire_gone',\n /** The selected collab agent / channel was refused by the backend. */\n collab_agent_rejected: 'collab_agent_rejected',\n /** A React render error crashed the chat surface (caught by the SDK's error boundary). */\n chat_render_crash: 'chat_render_crash',\n /** A pasted or dropped file could not be uploaded as a composer attachment. */\n attachment_failed: 'attachment_failed',\n /** Mention suggestions for a scope failed after every retry. */\n mentions_fetch_failed: 'mentions_fetch_failed',\n /** A composer write queued before the chat was ready can no longer land (thread switched, identity changed, provider unmounted). */\n composer_write_dropped: 'composer_write_dropped',\n /** The repository code search request (`/api/search/repositories`) failed. */\n repo_search_failed: 'repo_search_failed',\n /**\n * A session workbench read failed: the session's workspace\n * (`/api/conversations/threads/{id}/workspace`) or a computer's status or\n * SSH target.\n */\n session_workspace_failed: 'session_workspace_failed',\n /** Anything else. */\n unknown: 'unknown',\n} as const;\n\nexport type AthenaSdkErrorCode =\n (typeof ATHENA_SDK_ERROR_CODES)[keyof typeof ATHENA_SDK_ERROR_CODES];\n\nexport interface AthenaSdkErrorOptions {\n code: AthenaSdkErrorCode;\n message: string;\n /** One line telling the integrator what to check or change. */\n hint?: string;\n /** HTTP-ish status when the failure came from a server. */\n status?: number;\n /** Structured server detail (e.g. `{ code, message }` from agora). */\n detail?: unknown;\n cause?: unknown;\n /** Extra structured context safe to log (never credentials). */\n context?: Record<string, unknown>;\n}\n\nexport class AthenaSdkError extends Error {\n readonly code: AthenaSdkErrorCode;\n readonly hint: string | undefined;\n readonly status: number | undefined;\n readonly detail: unknown;\n readonly context: Record<string, unknown>;\n\n constructor(options: AthenaSdkErrorOptions) {\n super(options.message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'AthenaSdkError';\n this.code = options.code;\n this.hint = options.hint;\n this.status = options.status;\n this.detail = options.detail;\n this.context = options.context ?? {};\n }\n\n /** A log/console-friendly one-liner: `[code] message — hint`. */\n describe(): string {\n return `[${this.code}] ${this.message}${this.hint ? ` — ${this.hint}` : ''}`;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n hint: this.hint,\n status: this.status,\n detail: this.detail,\n context: this.context,\n };\n }\n}\n\nexport function isAthenaSdkError(value: unknown): value is AthenaSdkError {\n return value instanceof AthenaSdkError;\n}\n\n/** Wrap any thrown value as an `AthenaSdkError` (idempotent for SDK errors). */\nexport function toAthenaSdkError(\n value: unknown,\n fallback: Omit<AthenaSdkErrorOptions, 'cause'>,\n): AthenaSdkError {\n if (isAthenaSdkError(value)) return value;\n const message =\n value instanceof Error && value.message ? value.message : fallback.message;\n return new AthenaSdkError({ ...fallback, message, cause: value });\n}\n\n/** Structured `{ code, message }` detail from an agora rejection, if present. */\nexport function readRejectionDetail(\n detail: unknown,\n): { code: string | null; message: string | null } {\n if (typeof detail === 'string') return { code: null, message: detail || null };\n if (!detail || typeof detail !== 'object') return { code: null, message: null };\n const record = detail as { code?: unknown; message?: unknown };\n return {\n code: typeof record.code === 'string' ? record.code : null,\n message: typeof record.message === 'string' ? record.message : null,\n };\n}\n"],"names":[],"mappings":";;;;;AAOO,MAAM,yBAAyB;AAAA;AAAA,EAEpC,cAAc;AAAA;AAAA,EAEd,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,iBAAiB;AAAA;AAAA,EAEjB,gBAAgB;AAAA;AAAA,EAEhB,qBAAqB;AAAA;AAAA,EAErB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMxB,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtB,uBAAuB;AAAA;AAAA,EAEvB,oBAAoB;AAAA;AAAA,EAEpB,gBAAgB;AAAA;AAAA,EAEhB,uBAAuB;AAAA;AAAA,EAEvB,mBAAmB;AAAA;AAAA,EAEnB,mBAAmB;AAAA;AAAA,EAEnB,uBAAuB;AAAA;AAAA,EAEvB,wBAAwB;AAAA;AAAA,EAExB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,0BAA0B;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"errors.cjs","sources":["../../src/diagnostics/errors.ts"],"sourcesContent":["/**\n * Typed SDK errors. Every failure the SDK surfaces to a consumer carries a\n * stable `code` (matchable in code), a human `message`, and a `hint` — the\n * one-line \"what to do about it\" that turns a silent failure into a debuggable\n * one. Backend rejections keep their structured `{ status, detail }` payload.\n */\n\nexport const ATHENA_SDK_ERROR_CODES = {\n /** No token and no API key resolved before a request had to be sent. */\n auth_missing: 'auth_missing',\n /** The server refused the credential (401 / statewire `unauthorized` fin). */\n auth_rejected: 'auth_rejected',\n /**\n * `/_athena/auth` did not answer within the timeout on the last attempt of\n * its bounded retry cycle. The bridge stopped retrying (see\n * `ParentBridgeState.error`).\n */\n auth_bridge_timeout: 'auth_bridge_timeout',\n /**\n * `/_athena/auth` refused the mint (a 4xx other than 404), answered a body\n * it cannot read, or kept failing (5xx, 408, 429, network) through its\n * bounded retry cycle. The bridge stopped retrying and hands out no token\n * (see `ParentBridgeState.error`).\n */\n auth_bridge_failed: 'auth_bridge_failed',\n /**\n * `AthenaSSOProvider`'s session read (`/api/sso/userinfo`) kept failing\n * (5xx, 408, 429, network, timeout) through its bounded retries, or\n * answered another status it does not retry, or a 2xx without a JSON body.\n * The provider shows `displayOnError` with reason `request_failed`.\n */\n auth_sso_failed: 'auth_sso_failed',\n /** The statewire sync URL could not be derived from the configuration. */\n config_invalid: 'config_invalid',\n /** The thread list request failed. */\n threads_list_failed: 'threads_list_failed',\n /** Loading a thread's history failed. */\n thread_load_failed: 'thread_load_failed',\n /**\n * Pinning / unpinning a thread failed. `status` 404 / 405 means the backend\n * predates `POST /api/conversations/threads/favorite` (see\n * `isThreadFavoriteUnavailableError`); the pin then stays read-only.\n */\n thread_favorite_failed: 'thread_favorite_failed',\n /**\n * Renaming a thread failed. `status` 404 / 405 means the backend predates\n * `POST /api/conversations/threads/rename` (see\n * `isThreadRenameUnavailableError`); the list then offers no rename.\n */\n thread_rename_failed: 'thread_rename_failed',\n /**\n * Archiving a thread failed. `status` 404 / 405 means the backend has no\n * archive route (see `isThreadArchiveUnavailableError`); the list then\n * offers no archive action.\n */\n thread_archive_failed: 'thread_archive_failed',\n /** The statewire host rejected a command (structured `{status, detail}`). */\n statewire_rejected: 'statewire_rejected',\n /** The statewire connection is gone and will not reconnect on its own. */\n statewire_gone: 'statewire_gone',\n /** The selected collab agent / channel was refused by the backend. */\n collab_agent_rejected: 'collab_agent_rejected',\n /** A React render error crashed the chat surface (caught by the SDK's error boundary). */\n chat_render_crash: 'chat_render_crash',\n /** A pasted or dropped file could not be uploaded as a composer attachment. */\n attachment_failed: 'attachment_failed',\n /** Mention suggestions for a scope failed after every retry. */\n mentions_fetch_failed: 'mentions_fetch_failed',\n /** A composer write queued before the chat was ready can no longer land (thread switched, identity changed, provider unmounted). */\n composer_write_dropped: 'composer_write_dropped',\n /** The repository code search request (`/api/search/repositories`) failed. */\n repo_search_failed: 'repo_search_failed',\n /**\n * A session workbench read failed: the session's workspace\n * (`/api/conversations/threads/{id}/workspace`) or a computer's status or\n * SSH target.\n */\n session_workspace_failed: 'session_workspace_failed',\n /**\n * A computer's live desktop session\n * (`/api/sandbox/computer/{id}/live-desktop/session`) was refused or failed.\n */\n live_desktop_failed: 'live_desktop_failed',\n /** Anything else. */\n unknown: 'unknown',\n} as const;\n\nexport type AthenaSdkErrorCode =\n (typeof ATHENA_SDK_ERROR_CODES)[keyof typeof ATHENA_SDK_ERROR_CODES];\n\nexport interface AthenaSdkErrorOptions {\n code: AthenaSdkErrorCode;\n message: string;\n /** One line telling the integrator what to check or change. */\n hint?: string;\n /** HTTP-ish status when the failure came from a server. */\n status?: number;\n /** Structured server detail (e.g. `{ code, message }` from agora). */\n detail?: unknown;\n cause?: unknown;\n /** Extra structured context safe to log (never credentials). */\n context?: Record<string, unknown>;\n}\n\nexport class AthenaSdkError extends Error {\n readonly code: AthenaSdkErrorCode;\n readonly hint: string | undefined;\n readonly status: number | undefined;\n readonly detail: unknown;\n readonly context: Record<string, unknown>;\n\n constructor(options: AthenaSdkErrorOptions) {\n super(options.message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'AthenaSdkError';\n this.code = options.code;\n this.hint = options.hint;\n this.status = options.status;\n this.detail = options.detail;\n this.context = options.context ?? {};\n }\n\n /** A log/console-friendly one-liner: `[code] message — hint`. */\n describe(): string {\n return `[${this.code}] ${this.message}${this.hint ? ` — ${this.hint}` : ''}`;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n hint: this.hint,\n status: this.status,\n detail: this.detail,\n context: this.context,\n };\n }\n}\n\nexport function isAthenaSdkError(value: unknown): value is AthenaSdkError {\n return value instanceof AthenaSdkError;\n}\n\n/** Wrap any thrown value as an `AthenaSdkError` (idempotent for SDK errors). */\nexport function toAthenaSdkError(\n value: unknown,\n fallback: Omit<AthenaSdkErrorOptions, 'cause'>,\n): AthenaSdkError {\n if (isAthenaSdkError(value)) return value;\n const message =\n value instanceof Error && value.message ? value.message : fallback.message;\n return new AthenaSdkError({ ...fallback, message, cause: value });\n}\n\n/** Structured `{ code, message }` detail from an agora rejection, if present. */\nexport function readRejectionDetail(\n detail: unknown,\n): { code: string | null; message: string | null } {\n if (typeof detail === 'string') return { code: null, message: detail || null };\n if (!detail || typeof detail !== 'object') return { code: null, message: null };\n const record = detail as { code?: unknown; message?: unknown };\n return {\n code: typeof record.code === 'string' ? record.code : null,\n message: typeof record.message === 'string' ? record.message : null,\n };\n}\n"],"names":[],"mappings":";;;;;AAOO,MAAM,yBAAyB;AAAA;AAAA,EAEpC,cAAc;AAAA;AAAA,EAEd,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,iBAAiB;AAAA;AAAA,EAEjB,gBAAgB;AAAA;AAAA,EAEhB,qBAAqB;AAAA;AAAA,EAErB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMxB,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtB,uBAAuB;AAAA;AAAA,EAEvB,oBAAoB;AAAA;AAAA,EAEpB,gBAAgB;AAAA;AAAA,EAEhB,uBAAuB;AAAA;AAAA,EAEvB,mBAAmB;AAAA;AAAA,EAEnB,mBAAmB;AAAA;AAAA,EAEnB,uBAAuB;AAAA;AAAA,EAEvB,wBAAwB;AAAA;AAAA,EAExB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA,EAK1B,qBAAqB;AAAA;AAAA,EAErB,SAAS;AACX;AAmBO,MAAM,uBAAuB,MAAM;AAAA,EAOxC,YAAY,SAAgC;AAC1C,UAAM,QAAQ,SAAS,QAAQ,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAA,CAAO;AAPlF;AACA;AACA;AACA;AACA;AAIP,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ;AACpB,SAAK,OAAO,QAAQ;AACpB,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU,QAAQ,WAAW,CAAA;AAAA,EACpC;AAAA;AAAA,EAGA,WAAmB;AACjB,WAAO,IAAI,KAAK,IAAI,KAAK,KAAK,OAAO,GAAG,KAAK,OAAO,MAAM,KAAK,IAAI,KAAK,EAAE;AAAA,EAC5E;AAAA,EAEA,SAAkC;AAChC,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,IAAA;AAAA,EAElB;AACF;AAEO,SAAS,iBAAiB,OAAyC;AACxE,SAAO,iBAAiB;AAC1B;AAGO,SAAS,iBACd,OACA,UACgB;AAChB,MAAI,iBAAiB,KAAK,EAAG,QAAO;AACpC,QAAM,UACJ,iBAAiB,SAAS,MAAM,UAAU,MAAM,UAAU,SAAS;AACrE,SAAO,IAAI,eAAe,EAAE,GAAG,UAAU,SAAS,OAAO,OAAO;AAClE;AAGO,SAAS,oBACd,QACiD;AACjD,MAAI,OAAO,WAAW,SAAU,QAAO,EAAE,MAAM,MAAM,SAAS,UAAU,KAAA;AACxE,MAAI,CAAC,UAAU,OAAO,WAAW,iBAAiB,EAAE,MAAM,MAAM,SAAS,KAAA;AACzE,QAAM,SAAS;AACf,SAAO;AAAA,IACL,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,IACtD,SAAS,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;AAAA,EAAA;AAEnE;;;;;;"}
|
|
@@ -75,6 +75,11 @@ export declare const ATHENA_SDK_ERROR_CODES: {
|
|
|
75
75
|
* SSH target.
|
|
76
76
|
*/
|
|
77
77
|
readonly session_workspace_failed: 'session_workspace_failed';
|
|
78
|
+
/**
|
|
79
|
+
* A computer's live desktop session
|
|
80
|
+
* (`/api/sandbox/computer/{id}/live-desktop/session`) was refused or failed.
|
|
81
|
+
*/
|
|
82
|
+
readonly live_desktop_failed: 'live_desktop_failed';
|
|
78
83
|
/** Anything else. */
|
|
79
84
|
readonly unknown: 'unknown';
|
|
80
85
|
};
|
|
@@ -72,6 +72,11 @@ const ATHENA_SDK_ERROR_CODES = {
|
|
|
72
72
|
* SSH target.
|
|
73
73
|
*/
|
|
74
74
|
session_workspace_failed: "session_workspace_failed",
|
|
75
|
+
/**
|
|
76
|
+
* A computer's live desktop session
|
|
77
|
+
* (`/api/sandbox/computer/{id}/live-desktop/session`) was refused or failed.
|
|
78
|
+
*/
|
|
79
|
+
live_desktop_failed: "live_desktop_failed",
|
|
75
80
|
/** Anything else. */
|
|
76
81
|
unknown: "unknown"
|
|
77
82
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sources":["../../src/diagnostics/errors.ts"],"sourcesContent":["/**\n * Typed SDK errors. Every failure the SDK surfaces to a consumer carries a\n * stable `code` (matchable in code), a human `message`, and a `hint` — the\n * one-line \"what to do about it\" that turns a silent failure into a debuggable\n * one. Backend rejections keep their structured `{ status, detail }` payload.\n */\n\nexport const ATHENA_SDK_ERROR_CODES = {\n /** No token and no API key resolved before a request had to be sent. */\n auth_missing: 'auth_missing',\n /** The server refused the credential (401 / statewire `unauthorized` fin). */\n auth_rejected: 'auth_rejected',\n /**\n * `/_athena/auth` did not answer within the timeout on the last attempt of\n * its bounded retry cycle. The bridge stopped retrying (see\n * `ParentBridgeState.error`).\n */\n auth_bridge_timeout: 'auth_bridge_timeout',\n /**\n * `/_athena/auth` refused the mint (a 4xx other than 404), answered a body\n * it cannot read, or kept failing (5xx, 408, 429, network) through its\n * bounded retry cycle. The bridge stopped retrying and hands out no token\n * (see `ParentBridgeState.error`).\n */\n auth_bridge_failed: 'auth_bridge_failed',\n /**\n * `AthenaSSOProvider`'s session read (`/api/sso/userinfo`) kept failing\n * (5xx, 408, 429, network, timeout) through its bounded retries, or\n * answered another status it does not retry, or a 2xx without a JSON body.\n * The provider shows `displayOnError` with reason `request_failed`.\n */\n auth_sso_failed: 'auth_sso_failed',\n /** The statewire sync URL could not be derived from the configuration. */\n config_invalid: 'config_invalid',\n /** The thread list request failed. */\n threads_list_failed: 'threads_list_failed',\n /** Loading a thread's history failed. */\n thread_load_failed: 'thread_load_failed',\n /**\n * Pinning / unpinning a thread failed. `status` 404 / 405 means the backend\n * predates `POST /api/conversations/threads/favorite` (see\n * `isThreadFavoriteUnavailableError`); the pin then stays read-only.\n */\n thread_favorite_failed: 'thread_favorite_failed',\n /**\n * Renaming a thread failed. `status` 404 / 405 means the backend predates\n * `POST /api/conversations/threads/rename` (see\n * `isThreadRenameUnavailableError`); the list then offers no rename.\n */\n thread_rename_failed: 'thread_rename_failed',\n /**\n * Archiving a thread failed. `status` 404 / 405 means the backend has no\n * archive route (see `isThreadArchiveUnavailableError`); the list then\n * offers no archive action.\n */\n thread_archive_failed: 'thread_archive_failed',\n /** The statewire host rejected a command (structured `{status, detail}`). */\n statewire_rejected: 'statewire_rejected',\n /** The statewire connection is gone and will not reconnect on its own. */\n statewire_gone: 'statewire_gone',\n /** The selected collab agent / channel was refused by the backend. */\n collab_agent_rejected: 'collab_agent_rejected',\n /** A React render error crashed the chat surface (caught by the SDK's error boundary). */\n chat_render_crash: 'chat_render_crash',\n /** A pasted or dropped file could not be uploaded as a composer attachment. */\n attachment_failed: 'attachment_failed',\n /** Mention suggestions for a scope failed after every retry. */\n mentions_fetch_failed: 'mentions_fetch_failed',\n /** A composer write queued before the chat was ready can no longer land (thread switched, identity changed, provider unmounted). */\n composer_write_dropped: 'composer_write_dropped',\n /** The repository code search request (`/api/search/repositories`) failed. */\n repo_search_failed: 'repo_search_failed',\n /**\n * A session workbench read failed: the session's workspace\n * (`/api/conversations/threads/{id}/workspace`) or a computer's status or\n * SSH target.\n */\n session_workspace_failed: 'session_workspace_failed',\n /** Anything else. */\n unknown: 'unknown',\n} as const;\n\nexport type AthenaSdkErrorCode =\n (typeof ATHENA_SDK_ERROR_CODES)[keyof typeof ATHENA_SDK_ERROR_CODES];\n\nexport interface AthenaSdkErrorOptions {\n code: AthenaSdkErrorCode;\n message: string;\n /** One line telling the integrator what to check or change. */\n hint?: string;\n /** HTTP-ish status when the failure came from a server. */\n status?: number;\n /** Structured server detail (e.g. `{ code, message }` from agora). */\n detail?: unknown;\n cause?: unknown;\n /** Extra structured context safe to log (never credentials). */\n context?: Record<string, unknown>;\n}\n\nexport class AthenaSdkError extends Error {\n readonly code: AthenaSdkErrorCode;\n readonly hint: string | undefined;\n readonly status: number | undefined;\n readonly detail: unknown;\n readonly context: Record<string, unknown>;\n\n constructor(options: AthenaSdkErrorOptions) {\n super(options.message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'AthenaSdkError';\n this.code = options.code;\n this.hint = options.hint;\n this.status = options.status;\n this.detail = options.detail;\n this.context = options.context ?? {};\n }\n\n /** A log/console-friendly one-liner: `[code] message — hint`. */\n describe(): string {\n return `[${this.code}] ${this.message}${this.hint ? ` — ${this.hint}` : ''}`;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n hint: this.hint,\n status: this.status,\n detail: this.detail,\n context: this.context,\n };\n }\n}\n\nexport function isAthenaSdkError(value: unknown): value is AthenaSdkError {\n return value instanceof AthenaSdkError;\n}\n\n/** Wrap any thrown value as an `AthenaSdkError` (idempotent for SDK errors). */\nexport function toAthenaSdkError(\n value: unknown,\n fallback: Omit<AthenaSdkErrorOptions, 'cause'>,\n): AthenaSdkError {\n if (isAthenaSdkError(value)) return value;\n const message =\n value instanceof Error && value.message ? value.message : fallback.message;\n return new AthenaSdkError({ ...fallback, message, cause: value });\n}\n\n/** Structured `{ code, message }` detail from an agora rejection, if present. */\nexport function readRejectionDetail(\n detail: unknown,\n): { code: string | null; message: string | null } {\n if (typeof detail === 'string') return { code: null, message: detail || null };\n if (!detail || typeof detail !== 'object') return { code: null, message: null };\n const record = detail as { code?: unknown; message?: unknown };\n return {\n code: typeof record.code === 'string' ? record.code : null,\n message: typeof record.message === 'string' ? record.message : null,\n };\n}\n"],"names":[],"mappings":";;;AAOO,MAAM,yBAAyB;AAAA;AAAA,EAEpC,cAAc;AAAA;AAAA,EAEd,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,iBAAiB;AAAA;AAAA,EAEjB,gBAAgB;AAAA;AAAA,EAEhB,qBAAqB;AAAA;AAAA,EAErB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMxB,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtB,uBAAuB;AAAA;AAAA,EAEvB,oBAAoB;AAAA;AAAA,EAEpB,gBAAgB;AAAA;AAAA,EAEhB,uBAAuB;AAAA;AAAA,EAEvB,mBAAmB;AAAA;AAAA,EAEnB,mBAAmB;AAAA;AAAA,EAEnB,uBAAuB;AAAA;AAAA,EAEvB,wBAAwB;AAAA;AAAA,EAExB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,0BAA0B;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"errors.js","sources":["../../src/diagnostics/errors.ts"],"sourcesContent":["/**\n * Typed SDK errors. Every failure the SDK surfaces to a consumer carries a\n * stable `code` (matchable in code), a human `message`, and a `hint` — the\n * one-line \"what to do about it\" that turns a silent failure into a debuggable\n * one. Backend rejections keep their structured `{ status, detail }` payload.\n */\n\nexport const ATHENA_SDK_ERROR_CODES = {\n /** No token and no API key resolved before a request had to be sent. */\n auth_missing: 'auth_missing',\n /** The server refused the credential (401 / statewire `unauthorized` fin). */\n auth_rejected: 'auth_rejected',\n /**\n * `/_athena/auth` did not answer within the timeout on the last attempt of\n * its bounded retry cycle. The bridge stopped retrying (see\n * `ParentBridgeState.error`).\n */\n auth_bridge_timeout: 'auth_bridge_timeout',\n /**\n * `/_athena/auth` refused the mint (a 4xx other than 404), answered a body\n * it cannot read, or kept failing (5xx, 408, 429, network) through its\n * bounded retry cycle. The bridge stopped retrying and hands out no token\n * (see `ParentBridgeState.error`).\n */\n auth_bridge_failed: 'auth_bridge_failed',\n /**\n * `AthenaSSOProvider`'s session read (`/api/sso/userinfo`) kept failing\n * (5xx, 408, 429, network, timeout) through its bounded retries, or\n * answered another status it does not retry, or a 2xx without a JSON body.\n * The provider shows `displayOnError` with reason `request_failed`.\n */\n auth_sso_failed: 'auth_sso_failed',\n /** The statewire sync URL could not be derived from the configuration. */\n config_invalid: 'config_invalid',\n /** The thread list request failed. */\n threads_list_failed: 'threads_list_failed',\n /** Loading a thread's history failed. */\n thread_load_failed: 'thread_load_failed',\n /**\n * Pinning / unpinning a thread failed. `status` 404 / 405 means the backend\n * predates `POST /api/conversations/threads/favorite` (see\n * `isThreadFavoriteUnavailableError`); the pin then stays read-only.\n */\n thread_favorite_failed: 'thread_favorite_failed',\n /**\n * Renaming a thread failed. `status` 404 / 405 means the backend predates\n * `POST /api/conversations/threads/rename` (see\n * `isThreadRenameUnavailableError`); the list then offers no rename.\n */\n thread_rename_failed: 'thread_rename_failed',\n /**\n * Archiving a thread failed. `status` 404 / 405 means the backend has no\n * archive route (see `isThreadArchiveUnavailableError`); the list then\n * offers no archive action.\n */\n thread_archive_failed: 'thread_archive_failed',\n /** The statewire host rejected a command (structured `{status, detail}`). */\n statewire_rejected: 'statewire_rejected',\n /** The statewire connection is gone and will not reconnect on its own. */\n statewire_gone: 'statewire_gone',\n /** The selected collab agent / channel was refused by the backend. */\n collab_agent_rejected: 'collab_agent_rejected',\n /** A React render error crashed the chat surface (caught by the SDK's error boundary). */\n chat_render_crash: 'chat_render_crash',\n /** A pasted or dropped file could not be uploaded as a composer attachment. */\n attachment_failed: 'attachment_failed',\n /** Mention suggestions for a scope failed after every retry. */\n mentions_fetch_failed: 'mentions_fetch_failed',\n /** A composer write queued before the chat was ready can no longer land (thread switched, identity changed, provider unmounted). */\n composer_write_dropped: 'composer_write_dropped',\n /** The repository code search request (`/api/search/repositories`) failed. */\n repo_search_failed: 'repo_search_failed',\n /**\n * A session workbench read failed: the session's workspace\n * (`/api/conversations/threads/{id}/workspace`) or a computer's status or\n * SSH target.\n */\n session_workspace_failed: 'session_workspace_failed',\n /**\n * A computer's live desktop session\n * (`/api/sandbox/computer/{id}/live-desktop/session`) was refused or failed.\n */\n live_desktop_failed: 'live_desktop_failed',\n /** Anything else. */\n unknown: 'unknown',\n} as const;\n\nexport type AthenaSdkErrorCode =\n (typeof ATHENA_SDK_ERROR_CODES)[keyof typeof ATHENA_SDK_ERROR_CODES];\n\nexport interface AthenaSdkErrorOptions {\n code: AthenaSdkErrorCode;\n message: string;\n /** One line telling the integrator what to check or change. */\n hint?: string;\n /** HTTP-ish status when the failure came from a server. */\n status?: number;\n /** Structured server detail (e.g. `{ code, message }` from agora). */\n detail?: unknown;\n cause?: unknown;\n /** Extra structured context safe to log (never credentials). */\n context?: Record<string, unknown>;\n}\n\nexport class AthenaSdkError extends Error {\n readonly code: AthenaSdkErrorCode;\n readonly hint: string | undefined;\n readonly status: number | undefined;\n readonly detail: unknown;\n readonly context: Record<string, unknown>;\n\n constructor(options: AthenaSdkErrorOptions) {\n super(options.message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'AthenaSdkError';\n this.code = options.code;\n this.hint = options.hint;\n this.status = options.status;\n this.detail = options.detail;\n this.context = options.context ?? {};\n }\n\n /** A log/console-friendly one-liner: `[code] message — hint`. */\n describe(): string {\n return `[${this.code}] ${this.message}${this.hint ? ` — ${this.hint}` : ''}`;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n hint: this.hint,\n status: this.status,\n detail: this.detail,\n context: this.context,\n };\n }\n}\n\nexport function isAthenaSdkError(value: unknown): value is AthenaSdkError {\n return value instanceof AthenaSdkError;\n}\n\n/** Wrap any thrown value as an `AthenaSdkError` (idempotent for SDK errors). */\nexport function toAthenaSdkError(\n value: unknown,\n fallback: Omit<AthenaSdkErrorOptions, 'cause'>,\n): AthenaSdkError {\n if (isAthenaSdkError(value)) return value;\n const message =\n value instanceof Error && value.message ? value.message : fallback.message;\n return new AthenaSdkError({ ...fallback, message, cause: value });\n}\n\n/** Structured `{ code, message }` detail from an agora rejection, if present. */\nexport function readRejectionDetail(\n detail: unknown,\n): { code: string | null; message: string | null } {\n if (typeof detail === 'string') return { code: null, message: detail || null };\n if (!detail || typeof detail !== 'object') return { code: null, message: null };\n const record = detail as { code?: unknown; message?: unknown };\n return {\n code: typeof record.code === 'string' ? record.code : null,\n message: typeof record.message === 'string' ? record.message : null,\n };\n}\n"],"names":[],"mappings":";;;AAOO,MAAM,yBAAyB;AAAA;AAAA,EAEpC,cAAc;AAAA;AAAA,EAEd,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,iBAAiB;AAAA;AAAA,EAEjB,gBAAgB;AAAA;AAAA,EAEhB,qBAAqB;AAAA;AAAA,EAErB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMxB,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtB,uBAAuB;AAAA;AAAA,EAEvB,oBAAoB;AAAA;AAAA,EAEpB,gBAAgB;AAAA;AAAA,EAEhB,uBAAuB;AAAA;AAAA,EAEvB,mBAAmB;AAAA;AAAA,EAEnB,mBAAmB;AAAA;AAAA,EAEnB,uBAAuB;AAAA;AAAA,EAEvB,wBAAwB;AAAA;AAAA,EAExB,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA,EAK1B,qBAAqB;AAAA;AAAA,EAErB,SAAS;AACX;AAmBO,MAAM,uBAAuB,MAAM;AAAA,EAOxC,YAAY,SAAgC;AAC1C,UAAM,QAAQ,SAAS,QAAQ,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAA,CAAO;AAPlF;AACA;AACA;AACA;AACA;AAIP,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ;AACpB,SAAK,OAAO,QAAQ;AACpB,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU,QAAQ,WAAW,CAAA;AAAA,EACpC;AAAA;AAAA,EAGA,WAAmB;AACjB,WAAO,IAAI,KAAK,IAAI,KAAK,KAAK,OAAO,GAAG,KAAK,OAAO,MAAM,KAAK,IAAI,KAAK,EAAE;AAAA,EAC5E;AAAA,EAEA,SAAkC;AAChC,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,IAAA;AAAA,EAElB;AACF;AAEO,SAAS,iBAAiB,OAAyC;AACxE,SAAO,iBAAiB;AAC1B;AAGO,SAAS,iBACd,OACA,UACgB;AAChB,MAAI,iBAAiB,KAAK,EAAG,QAAO;AACpC,QAAM,UACJ,iBAAiB,SAAS,MAAM,UAAU,MAAM,UAAU,SAAS;AACrE,SAAO,IAAI,eAAe,EAAE,GAAG,UAAU,SAAS,OAAO,OAAO;AAClE;AAGO,SAAS,oBACd,QACiD;AACjD,MAAI,OAAO,WAAW,SAAU,QAAO,EAAE,MAAM,MAAM,SAAS,UAAU,KAAA;AACxE,MAAI,CAAC,UAAU,OAAO,WAAW,iBAAiB,EAAE,MAAM,MAAM,SAAS,KAAA;AACzE,QAAM,SAAS;AACf,SAAO;AAAA,IACL,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,IACtD,SAAS,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;AAAA,EAAA;AAEnE;"}
|
package/dist/index.cjs
CHANGED
|
@@ -66,6 +66,9 @@ const sessionWorkbenchPanes = require("./workbench/session-workbench-panes.cjs")
|
|
|
66
66
|
const sessionWorkbenchModel = require("./workbench/session-workbench-model.cjs");
|
|
67
67
|
const sessionWorkspaceApi = require("./workbench/session-workspace-api.cjs");
|
|
68
68
|
const useSessionWorkspace = require("./workbench/use-session-workspace.cjs");
|
|
69
|
+
const ComputerLiveDesktop = require("./workbench/ComputerLiveDesktop.cjs");
|
|
70
|
+
const liveDesktopApi = require("./workbench/live-desktop-api.cjs");
|
|
71
|
+
const statewireHistoryContext = require("./provider/statewire-history-context.cjs");
|
|
69
72
|
const useAssetEmbed = require("./assets/use-asset-embed.cjs");
|
|
70
73
|
const AthenaContext = require("./provider/AthenaContext.cjs");
|
|
71
74
|
const ThreadList = require("./threads/ThreadList.cjs");
|
|
@@ -327,6 +330,17 @@ exports.sessionWorkspacePath = sessionWorkspaceApi.sessionWorkspacePath;
|
|
|
327
330
|
exports.useAthenaComputerSshAccess = useSessionWorkspace.useAthenaComputerSshAccess;
|
|
328
331
|
exports.useAthenaComputerStatus = useSessionWorkspace.useAthenaComputerStatus;
|
|
329
332
|
exports.useAthenaSessionWorkspace = useSessionWorkspace.useAthenaSessionWorkspace;
|
|
333
|
+
exports.ComputerLiveDesktop = ComputerLiveDesktop.ComputerLiveDesktop;
|
|
334
|
+
exports.LIVE_DESKTOP_READY_TIMEOUT_MS = ComputerLiveDesktop.LIVE_DESKTOP_READY_TIMEOUT_MS;
|
|
335
|
+
exports.LIVE_DESKTOP_VIEWER_MESSAGE_SOURCE = liveDesktopApi.LIVE_DESKTOP_VIEWER_MESSAGE_SOURCE;
|
|
336
|
+
exports.LiveDesktopMode = liveDesktopApi.LiveDesktopMode;
|
|
337
|
+
exports.computerLiveDesktopSessionPath = liveDesktopApi.computerLiveDesktopSessionPath;
|
|
338
|
+
exports.createComputerLiveDesktopSession = liveDesktopApi.createComputerLiveDesktopSession;
|
|
339
|
+
exports.finalLiveDesktopCloseNotice = liveDesktopApi.finalLiveDesktopCloseNotice;
|
|
340
|
+
exports.liveDesktopViewerUrl = liveDesktopApi.liveDesktopViewerUrl;
|
|
341
|
+
exports.readLiveDesktopSession = liveDesktopApi.readLiveDesktopSession;
|
|
342
|
+
exports.readLiveDesktopViewerMessage = liveDesktopApi.readLiveDesktopViewerMessage;
|
|
343
|
+
exports.useStatewireHistory = statewireHistoryContext.useStatewireHistory;
|
|
330
344
|
exports.appendCitationReferenceParams = useAssetEmbed.appendCitationReferenceParams;
|
|
331
345
|
exports.useAssetEmbed = useAssetEmbed.useAssetEmbed;
|
|
332
346
|
exports.useAthenaConfig = AthenaContext.useAthenaConfig;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -104,6 +104,12 @@ export { computerSshAccessPath, computerStatusPath, fetchComputerRuntimeStatus,
|
|
|
104
104
|
export type { ComputerRuntimeStatus, ComputerSshAccess, SessionWorkspace, SessionWorkspaceAsset, SessionWorkspaceRole, } from './workbench/session-workspace-api';
|
|
105
105
|
export { useAthenaComputerSshAccess, useAthenaComputerStatus, useAthenaSessionWorkspace, } from './workbench/use-session-workspace';
|
|
106
106
|
export type { UseAthenaSessionWorkspaceOptions } from './workbench/use-session-workspace';
|
|
107
|
+
export { ComputerLiveDesktop, LIVE_DESKTOP_READY_TIMEOUT_MS, } from './workbench/ComputerLiveDesktop';
|
|
108
|
+
export type { ComputerLiveDesktopProps } from './workbench/ComputerLiveDesktop';
|
|
109
|
+
export { computerLiveDesktopSessionPath, createComputerLiveDesktopSession, finalLiveDesktopCloseNotice, LIVE_DESKTOP_VIEWER_MESSAGE_SOURCE, LiveDesktopMode, liveDesktopViewerUrl, readLiveDesktopSession, readLiveDesktopViewerMessage, } from './workbench/live-desktop-api';
|
|
110
|
+
export type { LiveDesktopSession, LiveDesktopViewerMessage, } from './workbench/live-desktop-api';
|
|
111
|
+
export { useStatewireHistory } from './provider/statewire-history-context';
|
|
112
|
+
export type { StatewireHistoryState } from './provider/statewire-history-context';
|
|
107
113
|
export { appendCitationReferenceParams, useAssetEmbed } from './assets/use-asset-embed';
|
|
108
114
|
export type { UseAssetEmbedOptions, UseAssetEmbedResult } from './assets/use-asset-embed';
|
|
109
115
|
export { useAthenaConfig } from './provider/AthenaContext';
|
package/dist/index.js
CHANGED
|
@@ -64,6 +64,9 @@ import { DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS, DEFAULT_SESSION_WORKBENCH_PANE
|
|
|
64
64
|
import { SESSION_SANDBOX_LABEL, SHELL_OUTPUT_PREVIEW_CHARS, assetPanelTypeOf, assetTypeLabel, athenaLinkAppUrl, computerStateBadge, deriveShellRows, deriveTaskSteps, filterShellRows, filterWorkbenchPanes, reviewDecisionLabel, shellMachine, taskProgress, toolResultText, workspaceComputers } from "./workbench/session-workbench-model.js";
|
|
65
65
|
import { computerSshAccessPath, computerStatusPath, fetchComputerRuntimeStatus, fetchComputerSshAccess, fetchSessionWorkspace, readComputerRuntimeStatus, readComputerSshAccess, readSessionWorkspace, sessionWorkspacePath } from "./workbench/session-workspace-api.js";
|
|
66
66
|
import { useAthenaComputerSshAccess, useAthenaComputerStatus, useAthenaSessionWorkspace } from "./workbench/use-session-workspace.js";
|
|
67
|
+
import { ComputerLiveDesktop, LIVE_DESKTOP_READY_TIMEOUT_MS } from "./workbench/ComputerLiveDesktop.js";
|
|
68
|
+
import { LIVE_DESKTOP_VIEWER_MESSAGE_SOURCE, LiveDesktopMode, computerLiveDesktopSessionPath, createComputerLiveDesktopSession, finalLiveDesktopCloseNotice, liveDesktopViewerUrl, readLiveDesktopSession, readLiveDesktopViewerMessage } from "./workbench/live-desktop-api.js";
|
|
69
|
+
import { useStatewireHistory } from "./provider/statewire-history-context.js";
|
|
67
70
|
import { appendCitationReferenceParams, useAssetEmbed } from "./assets/use-asset-embed.js";
|
|
68
71
|
import { useAthenaConfig } from "./provider/AthenaContext.js";
|
|
69
72
|
import { ThreadList } from "./threads/ThreadList.js";
|
|
@@ -156,6 +159,7 @@ export {
|
|
|
156
159
|
CollapsibleTrigger,
|
|
157
160
|
ComposerDropZone,
|
|
158
161
|
ComposerQuotePreview,
|
|
162
|
+
ComputerLiveDesktop,
|
|
159
163
|
CopyToolSpecButton,
|
|
160
164
|
CreateDocumentToolUI,
|
|
161
165
|
CreateEmailDraftToolUI,
|
|
@@ -188,8 +192,11 @@ export {
|
|
|
188
192
|
Inbox,
|
|
189
193
|
InboxButton,
|
|
190
194
|
InstallPackageToolUI,
|
|
195
|
+
LIVE_DESKTOP_READY_TIMEOUT_MS,
|
|
196
|
+
LIVE_DESKTOP_VIEWER_MESSAGE_SOURCE,
|
|
191
197
|
ListDatabaseTablesToolUI,
|
|
192
198
|
ListFilesToolUI,
|
|
199
|
+
LiveDesktopMode,
|
|
193
200
|
MODEL_PROVIDER_LABELS,
|
|
194
201
|
NestedPtcComponentsProvider,
|
|
195
202
|
NestedPtcMessages,
|
|
@@ -304,6 +311,7 @@ export {
|
|
|
304
311
|
columnLettersToNumber,
|
|
305
312
|
columnNumberToLetters,
|
|
306
313
|
composeDictationDraft,
|
|
314
|
+
computerLiveDesktopSessionPath,
|
|
307
315
|
computerSshAccessPath,
|
|
308
316
|
computerStateBadge,
|
|
309
317
|
computerStatusPath,
|
|
@@ -313,6 +321,7 @@ export {
|
|
|
313
321
|
createAssetToolUI,
|
|
314
322
|
createAthenaSpacesUrl,
|
|
315
323
|
createAttachmentStore,
|
|
324
|
+
createComputerLiveDesktopSession,
|
|
316
325
|
createQuoteStore,
|
|
317
326
|
createRecordedDictationEngine,
|
|
318
327
|
createThread,
|
|
@@ -337,6 +346,7 @@ export {
|
|
|
337
346
|
fetchSessionWorkspace,
|
|
338
347
|
filterShellRows,
|
|
339
348
|
filterWorkbenchPanes,
|
|
349
|
+
finalLiveDesktopCloseNotice,
|
|
340
350
|
formatA1Cell,
|
|
341
351
|
formatA1Range,
|
|
342
352
|
formatToolName,
|
|
@@ -372,6 +382,7 @@ export {
|
|
|
372
382
|
isThreadRenameUnavailableError,
|
|
373
383
|
isToolResultSuccess,
|
|
374
384
|
listThreads,
|
|
385
|
+
liveDesktopViewerUrl,
|
|
375
386
|
matchesSessionsQuery,
|
|
376
387
|
mintThreadId,
|
|
377
388
|
normalizeAgentCatalog,
|
|
@@ -393,6 +404,8 @@ export {
|
|
|
393
404
|
readComputerSshAccess,
|
|
394
405
|
readGithubWakeEvent,
|
|
395
406
|
readInterruptMessage,
|
|
407
|
+
readLiveDesktopSession,
|
|
408
|
+
readLiveDesktopViewerMessage,
|
|
396
409
|
readRejectionDetail,
|
|
397
410
|
readRunStartRefusal,
|
|
398
411
|
readSessionWorkspace,
|
|
@@ -487,6 +500,7 @@ export {
|
|
|
487
500
|
useSessionWorkbenchWorkspace,
|
|
488
501
|
useStatewireApprovalRequest,
|
|
489
502
|
useStatewireConversationHistory,
|
|
503
|
+
useStatewireHistory,
|
|
490
504
|
useStatewireToolOutputs,
|
|
491
505
|
useToolAppearance,
|
|
492
506
|
withCaptureGateBypass,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|