@agents24/chat-react 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +21 -66
  3. package/compatibility.json +54 -0
  4. package/dist/active-content.d.ts +10 -0
  5. package/dist/{chunk-EWZO4QJI.js → chunk-CHAU7FNW.js} +3 -3
  6. package/dist/chunk-CHAU7FNW.js.map +1 -0
  7. package/dist/{chunk-FFO6KEB4.js → chunk-LI67M74F.js} +98 -12
  8. package/dist/chunk-LI67M74F.js.map +1 -0
  9. package/dist/chunk-NLEOWTZG.js +1327 -0
  10. package/dist/chunk-NLEOWTZG.js.map +1 -0
  11. package/dist/index.cjs +66 -1852
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +0 -6
  14. package/dist/index.js +6 -1841
  15. package/dist/index.js.map +1 -1
  16. package/dist/latest-thread-scroller.d.ts +7 -7
  17. package/dist/mcp-oauth.d.ts +3 -1
  18. package/dist/renderers.d.ts +3 -3
  19. package/dist/styles.css +407 -0
  20. package/dist/templates/agent-chat-app.d.ts +18 -0
  21. package/dist/templates/agent-chat-layout.d.ts +69 -0
  22. package/dist/templates/agent-chat-shell.d.ts +1 -1
  23. package/dist/templates/index.cjs +1904 -18
  24. package/dist/templates/index.cjs.map +1 -1
  25. package/dist/templates/index.d.ts +2 -0
  26. package/dist/templates/index.js +529 -11
  27. package/dist/templates/index.js.map +1 -1
  28. package/dist/types.d.ts +12 -380
  29. package/dist/ui/agent-chat-actions.d.ts +1 -2
  30. package/dist/ui/agent-chat-composer.d.ts +2 -1
  31. package/dist/ui/agent-chat-message.d.ts +3 -3
  32. package/dist/ui/agent-response-timeline.d.ts +2 -2
  33. package/dist/ui/attachment.d.ts +9 -9
  34. package/dist/ui/bubble.d.ts +4 -4
  35. package/dist/ui/index.cjs +137 -43
  36. package/dist/ui/index.cjs.map +1 -1
  37. package/dist/ui/index.js +62 -1220
  38. package/dist/ui/index.js.map +1 -1
  39. package/dist/ui/marker.d.ts +3 -3
  40. package/dist/ui/mcp-connect-required-card.d.ts +1 -1
  41. package/dist/ui/message-response.d.ts +1 -1
  42. package/dist/ui/message.d.ts +6 -6
  43. package/package.json +27 -8
  44. package/dist/chunk-EWZO4QJI.js.map +0 -1
  45. package/dist/chunk-FFO6KEB4.js.map +0 -1
  46. package/dist/chunk-UU7OXHL3.js +0 -11
  47. package/dist/chunk-UU7OXHL3.js.map +0 -1
  48. package/dist/context-window.d.ts +0 -38
  49. package/dist/controller-actions.d.ts +0 -45
  50. package/dist/controller-helpers.d.ts +0 -27
  51. package/dist/controller-hitl.d.ts +0 -22
  52. package/dist/controller-options.d.ts +0 -15
  53. package/dist/controller-thread-events.d.ts +0 -7
  54. package/dist/controller.d.ts +0 -5
  55. package/dist/message-lifecycle.d.ts +0 -15
  56. package/dist/model.d.ts +0 -30
  57. package/dist/sse.d.ts +0 -5
  58. package/dist/transport.d.ts +0 -43
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Talmudpedia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE.
package/README.md CHANGED
@@ -1,76 +1,31 @@
1
- # Agents24 Chat React
1
+ # `@agents24/chat-react`
2
2
 
3
- Last Updated: 2026-07-15
3
+ Last Updated: 2026-07-16
4
4
 
5
- Headless React primitives for Agents24 chat surfaces.
5
+ Accessible browser UI and a complete default chat layout for Agents24 client applications. It renders state owned by `@agents24/react`; networking, authentication, refresh, and protocol parsing remain in `@agents24/client`.
6
6
 
7
- The package owns thread hydration, older-page pagination, stream/reattach state, active run cancellation, normalized message parts, and shadcn MessageScroller primitives for latest-thread chat surfaces. Hosts provide transport routes, cache storage, product rendering, source handling, pagination triggers, and styling.
7
+ ```tsx
8
+ import { AgentChatApp } from "@agents24/chat-react/templates";
9
+ import "@agents24/chat-react/styles.css";
8
10
 
9
- ## Public API
11
+ <AgentChatApp
12
+ agent={{ name: "Support Agent" }}
13
+ client={client}
14
+ mcpRedirectUri={window.location.href}
15
+ />;
16
+ ```
10
17
 
11
- - `useAgents24ChatController(options)`
12
- - `MessageScroller`
13
- - `LatestThreadScroller`
14
- - `useMessageScroller()`
15
- - `useMessageScrollerScrollable()`
16
- - `useMessageScrollerVisibility()`
17
- - `useStreamingText(options)`
18
- - `getActiveStreamingTextPartId(message, streamingMessageId)`
19
- - `isActiveStreamingTextPart(message, part, streamingMessageId)`
20
- - `createFetchChatTransport(options)`
21
- - `consumeSseResponse(response, onEvent, { signal })`
22
- - `appendStableStreamingTurn(messages, userMessage, assistantMessage)`
23
- - `upsertStableAssistantMessage(messages, input)`
24
- - normalized chat message, part, transport, and storage types
25
- - typed V2 `pendingHitl`, `isResolvingHitl`, and `resumeHitl(...)` controller state/actions in the current `0.3.x` contract
26
- - strict HITL response-block parsing and `waitForMcpOauthComplete(...)` for validated host-owned OAuth popup completion
27
- - `partsFromResponseBlocks(blocks, fallbackText)`
28
- - `renderChatPart(part, message, renderOptions)`
29
- - `@agents24/chat-react/ui` for shared shadcn-derived `Message`, `Bubble`, `Attachment`, `Marker`, `MessageResponse`, `AgentChatMessage`, `AgentChatComposer`, `McpConnectRequiredCard`, and Agents24 message/attachment adapters
30
- - `@agents24/chat-react/templates` for the slot-based `AgentChatShell` starter shell
18
+ ## Entry points
31
19
 
32
- `useAgents24ChatController` exposes reactive `threads`, `activeThreadId`, `activeThread`, `activeRunId`, `pendingHitl`, `isResolvingHitl`, `isRefreshingThreads`, `resumeHitl(...)`, `loadThreadById(threadId)`, `attachRun(runId, threadId)`, and `startNewThread()` in addition to message and stream state. It hydrates selected threads, detects active runs from `active_run`/`last_run_status`, attaches through the host transport, detaches local SSE streams on navigation, and refreshes history after terminal events. V2 resume always reattaches after successful or idempotent resolution so the backend snapshot remains authoritative.
20
+ - `@agents24/chat-react`: renderers, secure MCP popup validation, streaming-text helpers, and `LatestThreadScroller`.
21
+ - `@agents24/chat-react/ui`: messages, response timeline, composer, actions, attachments, HITL, and other reusable UI primitives.
22
+ - `@agents24/chat-react/templates`: `AgentChatShell`, controlled `AgentChatLayout`, and client-bound `AgentChatApp`.
23
+ - `@agents24/chat-react/styles.css`: packaged layout, responsive, focus, reduced-motion, and theme styles.
33
24
 
34
- Hosts can pass `onRuntimeEvent(event, context)` and `onThreadDetailLoaded(detail)` to layer product-specific runtime UI, trace panels, analytics, or metadata over the shared chat lifecycle without forking stream handling. That surface is enough for running-history badges and read-only live thread inspectors without a separate host-owned reattach state machine.
25
+ `AgentChatApp` bootstraps the deployment, binds `@agents24/react` state, uploads attachments, supports model selection when the deployment permits it, performs exact MCP popup callback validation, and maps typed client errors to loading, reconnecting, offline, denied, quota, failed, and expired-session states.
35
26
 
36
- `ChatMessage.parts` is the only render model. Backend `response_blocks` are normalized into ordered parts such as `text`, `tool-get_meteo`, `ui-blocks`, `reasoning`, `hitl`, `error`, and `data`. The shared HITL action strip renders a neutral, borderless pending state and a compact terminal outcome for Tool review, MCP authentication, User Approval, and app-data permission. Tool review exposes only **Allow once** and **Don't allow**; verbose risk metadata, argument details, Edit, and Respond are not part of the chat presentation. Tool UI is client-owned through `renderPart`, `toolRenderers`, and `fallbackToolRenderer`; the package does not infer English tool labels or group tool rows.
27
+ `AgentChatLayout` is the controlled option. Hosts provide messages, threads, state, submit/stop handlers, and optional slots while retaining the package's responsive thread navigation, accessible live state, focus handling, safe active-content policy, chronological scrolling, transcript outline, composer, and default message/HITL presentation.
37
28
 
38
- MCP OAuth remains a host side effect. `startMcpAuth(...)` returns both `authorization_url` and the authoritative `callback_origin`; hosts open the popup and pass both values to `waitForMcpOauthComplete(...)` before calling `resumeHitl(...)`. The utility verifies popup identity, callback origin, server identity, and connection UUID and owns timeout/close cleanup.
29
+ The package does not construct platform routes, accept organization API keys, fetch or parse SSE, refresh credentials, or duplicate controller/protocol types. Product-owned citations, source viewers, TTS, artifacts, analytics, and debug controls belong in layout slots or custom renderers.
39
30
 
40
- `useStreamingText` owns reusable visual pacing for active assistant text. It returns `displayedText`, `isAnimating`, `mode`, and `parseIncompleteMarkdown` so host renderers can pass those values into their markdown component without the package depending on any markdown/UI library. The package only chooses text timing, cache behavior, reduced-motion handling, and active text-part helpers.
41
-
42
- Network access is always injected by the host. This browser package never requires platform API keys.
43
-
44
- `streamMessage`, `attachRun`, and `subscribeThreadEvents` require owner-provided abort signals. `createFetchChatTransport` forwards each signal through connection setup and SSE consumption, and `useAgents24ChatController` aborts owned requests when it unmounts. Local abort detaches the client stream; use `cancelRun` when the durable backend run must also stop.
45
-
46
- ## Message Lifecycle
47
-
48
- Chat surfaces should keep active assistant turns as stable rows in `messages`: append the user row and assistant placeholder together, stream updates into that assistant `messageId`, finalize that same row in place, then clear the streaming marker. `appendStableStreamingTurn` and `upsertStableAssistantMessage` centralize that invariant for custom hosts. Avoid host-side synthetic assistant rows because removing and re-adding the same response can flicker and can make MessageScroller treat the turn as new content.
49
-
50
- The server owns assistant projection through `response_blocks` and `assistant_output_text`. Package version `0.2.0` removes the former public `textFromFinalOutput` helper; clients must not infer chat text from workflow `final_output`.
51
-
52
- ## MessageScroller Behavior
53
-
54
- The package re-exports shadcn `@shadcn/react/message-scroller` and provides `LatestThreadScroller`, a thin native preset around the official primitives. Host chat shells should prefer:
55
-
56
- - `LatestThreadScroller.Provider`
57
- - `LatestThreadScroller.Root`
58
- - `LatestThreadScroller.Viewport`
59
- - `LatestThreadScroller.Content`
60
- - `LatestThreadScroller.Item`
61
- - `LatestThreadScroller.Button`
62
- - `LatestThreadScroller.Outline`
63
-
64
- The preset defaults to `autoScroll`, `defaultScrollPosition="last-anchor"`, `scrollPreviousItemPeek={64}`, and `preserveScrollOnPrepend`. Each item requires a stable `messageId`; callers set `scrollAnchor` on turn-start rows. MessageScroller owns anchoring, stream follow, detach/reattach, resize handling, visibility, outline jumping, and the latest button state. Hosts keep only product pagination triggers for loading older data and product message rendering.
65
-
66
- ## Shared UI Subpaths
67
-
68
- `@agents24/chat-react/ui` contains package-owned source adapted from the shadcn CLI `message`, `bubble`, `attachment`, and `marker` components. Base primitive names stay close to shadcn, while Agents24-specific compatibility adapters sit beside them as `Message`, `MessageContent`, `MessageActions`, `MessageAction`, `MessageAttachments`, `MessageAttachment`, and `MessageResponse`.
69
-
70
- `AgentChatMessage` and `AgentChatComposer` provide the default customer starter UI used by `AgentChatShell` consumers that want a platform-playground-like baseline without importing platform internals. Hosts can still replace assistant/user message bodies, actions, HITL side effects, and toolbar controls through composition slots.
71
-
72
- `AgentChatMessage` collapses long user messages by default to keep bubbles compact. The default user bubble shows a four-line preview with `Show more` / `Show less` controls, and hosts can tune the threshold, visible lines, or labels through the `userMessage*` props.
73
-
74
- `@agents24/chat-react/templates` exposes `AgentChatShell`, a convenience shell that composes `LatestThreadScroller`, the outline rail, latest button, composer overlay spacing, older-history trigger, scroll-state reporting, and message slots. Product apps still own transport, citations, TTS, source panes, artifact actions, and row-specific rendering.
75
-
76
- The starter shell is suitable for public/shared chat pages and customer apps. Internal playground/admin controls such as traces, debug actions, source viewers, citations, TTS, artifacts, and approval side effects remain host-owned extensions.
31
+ React and ReactDOM `>=19 <20` are required. See `compatibility.json` for the exact protocol contract hash.
@@ -0,0 +1,54 @@
1
+ {
2
+ "contractHash": "de2b507f5d56de2bf7c4d6739c03617c4b4feef6ae827aaaac5f1d6150ec678e",
3
+ "contractVersion": "0.2.0",
4
+ "corpus": "conformance/v1/corpus.json",
5
+ "generatedBy": "@agents24/sdk-contract",
6
+ "packages": {
7
+ "@agents24/ai-sdk": {
8
+ "ai": ">=7 <8",
9
+ "aiSdkReact": ">=4 <5",
10
+ "peer": "@agents24/client"
11
+ },
12
+ "@agents24/chat-react": {
13
+ "peer": "@agents24/react",
14
+ "react": ">=19 <20",
15
+ "reactDom": ">=19 <20"
16
+ },
17
+ "@agents24/client": {
18
+ "contract": "de2b507f5d56de2bf7c4d6739c03617c4b4feef6ae827aaaac5f1d6150ec678e",
19
+ "runtime": "run-stream.v2"
20
+ },
21
+ "@agents24/node": {
22
+ "contract": "de2b507f5d56de2bf7c4d6739c03617c4b4feef6ae827aaaac5f1d6150ec678e",
23
+ "node": "^22.0.0 || ^24.0.0"
24
+ },
25
+ "@agents24/react": {
26
+ "peer": "@agents24/client",
27
+ "react": ">=19 <20"
28
+ },
29
+ "@talmudpedia/runtime-sdk": {
30
+ "contract": "de2b507f5d56de2bf7c4d6739c03617c4b4feef6ae827aaaac5f1d6150ec678e",
31
+ "protocolOwner": "@agents24/client"
32
+ },
33
+ "agents24": {
34
+ "contract": "de2b507f5d56de2bf7c4d6739c03617c4b4feef6ae827aaaac5f1d6150ec678e",
35
+ "python": ">=3.10"
36
+ }
37
+ },
38
+ "protocols": {
39
+ "runtime": {
40
+ "minimum": "2.0.0",
41
+ "wire": "run-stream.v2"
42
+ },
43
+ "threadSummary": {
44
+ "minimum": "1.0.0",
45
+ "wire": "agents24.thread_summary_event.v1"
46
+ }
47
+ },
48
+ "schemaVersion": "agents24.compatibility.v1",
49
+ "targetOperationCounts": {
50
+ "client": 19,
51
+ "node": 55,
52
+ "python": 55
53
+ }
54
+ }
@@ -0,0 +1,10 @@
1
+ export declare const CHAT_MARKDOWN_HARDEN_OPTIONS: Readonly<{
2
+ allowDataImages: false;
3
+ allowedImagePrefixes: string[];
4
+ allowedLinkPrefixes: string[];
5
+ allowedProtocols: string[];
6
+ defaultOrigin: undefined;
7
+ imageBlockPolicy: "text-only";
8
+ linkBlockPolicy: "text-only";
9
+ }>;
10
+ export declare function safeChatMarkdownHref(value?: string): string | undefined;
@@ -29,7 +29,7 @@ function useStreamingText({
29
29
  const cacheAdapter = cache === false ? null : cache;
30
30
  const [displayedText, setDisplayedText] = useState(() => {
31
31
  const cachedText = cacheAdapter?.get(id);
32
- return isStreaming && cachedText !== text ? "" : text;
32
+ return cachedText && text.startsWith(cachedText) ? cachedText : text;
33
33
  });
34
34
  const targetRef = useRef(text);
35
35
  const displayedRef = useRef(displayedText);
@@ -48,7 +48,7 @@ function useStreamingText({
48
48
  if (idRef.current === id) return;
49
49
  idRef.current = id;
50
50
  const cachedText = cacheAdapter?.get(id);
51
- const initial = isStreaming && cachedText !== text ? "" : text;
51
+ const initial = cachedText && text.startsWith(cachedText) ? cachedText : text;
52
52
  shouldAnimateRef.current = isStreaming && initial !== text;
53
53
  displayedRef.current = initial;
54
54
  setDisplayedText(initial);
@@ -137,4 +137,4 @@ export {
137
137
  isActiveStreamingTextPart,
138
138
  useStreamingText
139
139
  };
140
- //# sourceMappingURL=chunk-EWZO4QJI.js.map
140
+ //# sourceMappingURL=chunk-CHAU7FNW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/streaming-text.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\nimport type { ChatMessage, ChatPart } from \"./types\";\n\nexport type StreamingTextMode = \"streaming\" | \"static\";\n\nexport type StreamingTextCache = {\n get: (id: string) => string | undefined;\n set: (id: string, text: string) => void;\n};\n\nexport type UseStreamingTextOptions = {\n id: string;\n isStreaming: boolean;\n text: string;\n cache?: StreamingTextCache | false;\n charsPerSecond?: number;\n catchupThreshold?: number;\n maxCatchupChars?: number;\n};\n\nexport type UseStreamingTextResult = {\n displayedText: string;\n isAnimating: boolean;\n mode: StreamingTextMode;\n parseIncompleteMarkdown: boolean;\n};\n\nconst DEFAULT_COMPLETED_TEXT_CACHE_SIZE = 200;\nconst defaultCompletedTextCache = new Map<string, string>();\n\nconst defaultStreamingTextCache: StreamingTextCache = {\n get: (id) => defaultCompletedTextCache.get(id),\n set: (id, text) => {\n defaultCompletedTextCache.set(id, text);\n if (defaultCompletedTextCache.size > DEFAULT_COMPLETED_TEXT_CACHE_SIZE) {\n const firstKey = defaultCompletedTextCache.keys().next().value;\n if (firstKey) defaultCompletedTextCache.delete(firstKey);\n }\n },\n};\n\nexport const getActiveStreamingTextPartId = (\n message: ChatMessage,\n streamingMessageId: string | null\n) => {\n if (message.role !== \"assistant\" || streamingMessageId !== message.id) return null;\n return (\n message.parts\n .slice()\n .reverse()\n .find((part) => part.kind === \"text\")?.id || null\n );\n};\n\nexport const isActiveStreamingTextPart = (\n message: ChatMessage,\n part: ChatPart,\n streamingMessageId: string | null\n) => part.kind === \"text\" && part.id === getActiveStreamingTextPartId(message, streamingMessageId);\n\nexport function useStreamingText({\n id,\n isStreaming,\n text,\n cache = defaultStreamingTextCache,\n charsPerSecond = 72,\n catchupThreshold = 40,\n maxCatchupChars = 20,\n}: UseStreamingTextOptions): UseStreamingTextResult {\n const cacheAdapter = cache === false ? null : cache;\n const [displayedText, setDisplayedText] = useState(() => {\n const cachedText = cacheAdapter?.get(id);\n return cachedText && text.startsWith(cachedText) ? cachedText : text;\n });\n const targetRef = useRef(text);\n const displayedRef = useRef(displayedText);\n const rafRef = useRef<number | null>(null);\n const lastFrameAtRef = useRef<number | null>(null);\n const idRef = useRef(id);\n const shouldAnimateRef = useRef(isStreaming);\n\n useEffect(() => {\n if (isStreaming || !text) return;\n cacheAdapter?.set(id, text);\n }, [cacheAdapter, id, isStreaming, text]);\n\n useEffect(() => {\n targetRef.current = text;\n }, [text]);\n\n useEffect(() => {\n if (idRef.current === id) return;\n\n idRef.current = id;\n const cachedText = cacheAdapter?.get(id);\n const initial = cachedText && text.startsWith(cachedText) ? cachedText : text;\n shouldAnimateRef.current = isStreaming && initial !== text;\n displayedRef.current = initial;\n setDisplayedText(initial);\n\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n lastFrameAtRef.current = null;\n }, [cacheAdapter, id, isStreaming, text]);\n\n useEffect(() => {\n if (typeof window === \"undefined\") {\n displayedRef.current = text;\n setDisplayedText(text);\n return;\n }\n\n const reduceMotion =\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n if (reduceMotion) {\n displayedRef.current = text;\n setDisplayedText(text);\n shouldAnimateRef.current = false;\n return;\n }\n\n const cachedText = cacheAdapter?.get(id);\n if (cachedText === text) {\n displayedRef.current = text;\n setDisplayedText(text);\n shouldAnimateRef.current = false;\n return;\n }\n\n if (isStreaming) {\n shouldAnimateRef.current = true;\n }\n\n if (!shouldAnimateRef.current) {\n displayedRef.current = text;\n setDisplayedText(text);\n return;\n }\n\n if (!text.startsWith(displayedRef.current)) {\n displayedRef.current = \"\";\n setDisplayedText(\"\");\n }\n\n const tick = (timestamp: number) => {\n const previousTimestamp = lastFrameAtRef.current ?? timestamp;\n lastFrameAtRef.current = timestamp;\n\n const target = targetRef.current;\n const current = displayedRef.current;\n\n if (current.length >= target.length) {\n shouldAnimateRef.current = false;\n rafRef.current = null;\n return;\n }\n\n const elapsedMs = Math.max(8, timestamp - previousTimestamp);\n const charsFromTime = Math.max(1, Math.floor((elapsedMs / 1000) * charsPerSecond));\n const gap = target.length - current.length;\n const catchupStep =\n gap > catchupThreshold ? Math.min(maxCatchupChars, Math.ceil(gap / 10)) : charsFromTime;\n const nextLength = Math.min(\n target.length,\n current.length + Math.max(charsFromTime, catchupStep)\n );\n\n const next = target.slice(0, nextLength);\n displayedRef.current = next;\n setDisplayedText(next);\n rafRef.current = window.requestAnimationFrame(tick);\n };\n\n if (rafRef.current === null && displayedRef.current.length < text.length) {\n rafRef.current = window.requestAnimationFrame(tick);\n }\n\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n lastFrameAtRef.current = null;\n };\n }, [cacheAdapter, catchupThreshold, charsPerSecond, id, isStreaming, maxCatchupChars, text]);\n\n return {\n displayedText,\n isAnimating: isStreaming,\n mode: isStreaming ? \"streaming\" : \"static\",\n parseIncompleteMarkdown: isStreaming,\n };\n}\n"],"mappings":"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","names":[]}
@@ -57,7 +57,7 @@ function LatestThreadScrollerRoot({
57
57
  {
58
58
  ref: setRef,
59
59
  className: cx(
60
- "relative flex size-full min-h-0 flex-1 flex-col overflow-hidden",
60
+ "a24-scroller relative flex size-full min-h-0 flex-1 flex-col overflow-hidden",
61
61
  className
62
62
  ),
63
63
  ...props
@@ -87,7 +87,7 @@ function LatestThreadScrollerViewport({
87
87
  {
88
88
  ref: setRef,
89
89
  className: cx(
90
- "size-full min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain",
90
+ "a24-scroller__viewport size-full min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain",
91
91
  className
92
92
  ),
93
93
  preserveScrollOnPrepend,
@@ -117,7 +117,7 @@ function LatestThreadScrollerContent({
117
117
  {
118
118
  ref: setRef,
119
119
  "data-agents24-latest-thread-content": "",
120
- className: cx("flex h-max min-h-full flex-col gap-8 p-4", className),
120
+ className: cx("a24-scroller__content flex h-max min-h-full flex-col gap-8 p-4", className),
121
121
  ...props
122
122
  }
123
123
  );
@@ -131,7 +131,7 @@ function LatestThreadScrollerItem({
131
131
  MessageScroller.Item,
132
132
  {
133
133
  className: cx(
134
- "min-w-0 shrink-0 overflow-visible",
134
+ "a24-scroller__item min-w-0 shrink-0 overflow-visible",
135
135
  className
136
136
  ),
137
137
  messageId,
@@ -166,7 +166,7 @@ function LatestThreadScrollerButton({
166
166
  ...buttonProps,
167
167
  "aria-label": direction === "end" ? "Scroll to latest message" : "Scroll to first message",
168
168
  className: cx(
169
- "absolute bottom-4 left-1/2 z-50 flex size-9 -translate-x-1/2 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-md transition-all duration-200 data-[active=false]:pointer-events-none data-[active=false]:translate-y-2 data-[active=false]:opacity-0 data-[active=true]:translate-y-0 data-[active=true]:opacity-100",
169
+ "a24-scroller__button absolute bottom-4 left-1/2 z-50 flex size-9 -translate-x-1/2 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-md transition-[background-color,border-color,color,opacity,transform] duration-200 data-[active=false]:pointer-events-none data-[active=false]:translate-y-2 data-[active=false]:opacity-0 data-[active=true]:translate-y-0 data-[active=true]:opacity-100",
170
170
  className
171
171
  ),
172
172
  type: "button",
@@ -240,14 +240,16 @@ function LatestThreadScrollerOutline({
240
240
  {
241
241
  ref: navRef,
242
242
  "aria-label": "Transcript outline",
243
+ "data-side": railSide,
244
+ "data-visible": hasSideRoom ? "" : void 0,
243
245
  className: cx(
244
- "pointer-events-none absolute top-[46%] z-10 max-h-[min(18rem,calc(100%-10rem))] w-16 -translate-y-1/2 overflow-visible",
246
+ "a24-outline pointer-events-none absolute top-[46%] z-10 max-h-[min(18rem,calc(100%-10rem))] w-16 -translate-y-1/2 overflow-visible",
245
247
  hasSideRoom ? "hidden md:block" : "hidden",
246
248
  railSide === "left" ? "left-1" : "right-1",
247
249
  className
248
250
  ),
249
251
  dir,
250
- children: /* @__PURE__ */ jsx("div", { className: "pointer-events-auto flex flex-col gap-0 py-1", children: items.map((item, index) => {
252
+ children: /* @__PURE__ */ jsx("div", { className: "a24-outline__rail pointer-events-auto flex flex-col gap-0 py-1", children: items.map((item, index) => {
251
253
  const itemMessageIds = item.messageIds && item.messageIds.length > 0 ? item.messageIds : [item.id];
252
254
  const active = itemMessageIds.some(
253
255
  (messageId) => visibleMessageIdSet.has(messageId) || messageId === currentAnchorId
@@ -266,9 +268,10 @@ function LatestThreadScrollerOutline({
266
268
  {
267
269
  type: "button",
268
270
  "data-active": active,
271
+ "data-side": railSide,
269
272
  dir: "ltr",
270
273
  className: cx(
271
- "group relative flex h-2 w-16 items-center rounded-sm outline-none",
274
+ "a24-outline__button group relative flex h-2 w-16 items-center rounded-sm outline-none",
272
275
  railSide === "left" ? "justify-start" : "justify-end"
273
276
  ),
274
277
  "aria-label": `Jump to ${item.label || fallbackLabel}`,
@@ -284,7 +287,7 @@ function LatestThreadScrollerOutline({
284
287
  /* @__PURE__ */ jsx(
285
288
  "span",
286
289
  {
287
- className: "h-0.5 rounded-full bg-muted-foreground/55 transition-[width,background-color,opacity] duration-150 ease-out group-hover:bg-foreground group-focus-visible:bg-foreground group-data-[active=true]:bg-foreground",
290
+ className: "a24-outline__bar h-0.5 rounded-full bg-muted-foreground/55 transition-[width,background-color,opacity] duration-150 ease-out group-hover:bg-foreground group-focus-visible:bg-foreground group-data-[active=true]:bg-foreground",
288
291
  style: {
289
292
  opacity: barOpacity,
290
293
  width: barWidth
@@ -296,7 +299,7 @@ function LatestThreadScrollerOutline({
296
299
  {
297
300
  dir,
298
301
  className: cx(
299
- "pointer-events-none absolute top-1/2 z-20 max-h-32 w-64 -translate-y-1/2 overflow-hidden rounded-lg border border-border/80 bg-background p-3 text-start opacity-0 shadow-lg transition-all duration-200 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-visible:pointer-events-auto group-focus-visible:opacity-100",
302
+ "a24-outline__preview pointer-events-none absolute top-1/2 z-20 max-h-32 w-64 -translate-y-1/2 overflow-hidden rounded-lg border border-border/80 bg-background p-3 text-start opacity-0 shadow-lg transition-[opacity,transform] duration-200 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-visible:pointer-events-auto group-focus-visible:opacity-100",
300
303
  railSide === "left" ? "left-full ml-2 -translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0" : "right-full mr-2 translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0",
301
304
  cardClassName
302
305
  ),
@@ -346,6 +349,87 @@ var LatestThreadScroller = {
346
349
  Outline: LatestThreadScrollerOutline
347
350
  };
348
351
 
352
+ // src/mcp-oauth.ts
353
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
354
+ function isLoopback(hostname) {
355
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
356
+ }
357
+ function normalizedOrigin(value) {
358
+ try {
359
+ const url = new URL(value);
360
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback(url.hostname))) {
361
+ throw new Error("unsafe callback origin");
362
+ }
363
+ if (url.origin === "null") throw new Error("opaque callback origin");
364
+ return url.origin;
365
+ } catch {
366
+ throw new Error("MCP authorization returned an invalid callback origin.");
367
+ }
368
+ }
369
+ function validateMcpCallbackResult(result, expectedServerId) {
370
+ const serverId = String(result.server_id || "").trim();
371
+ const connectionId = String(result.connection_id || "").trim();
372
+ if (!serverId || serverId !== expectedServerId) {
373
+ throw new Error("MCP authorization completed for an unexpected server.");
374
+ }
375
+ if (!UUID_PATTERN.test(connectionId)) {
376
+ throw new Error("MCP authorization returned an invalid connection identity.");
377
+ }
378
+ return result;
379
+ }
380
+ function waitForMcpOauthComplete({
381
+ popup,
382
+ callbackOrigin,
383
+ popupNonce,
384
+ requireConnectionId = false,
385
+ serverId,
386
+ timeoutMs = 12e4
387
+ }) {
388
+ if (!popup) return Promise.reject(new Error("Could not open the MCP authorization popup."));
389
+ const expectedOrigin = normalizedOrigin(callbackOrigin);
390
+ const expectedServerId = String(serverId || "").trim();
391
+ const expectedPopupNonce = String(popupNonce || "").trim();
392
+ if (!expectedServerId) return Promise.reject(new Error("MCP authorization is missing its server identity."));
393
+ if (!expectedPopupNonce) return Promise.reject(new Error("MCP authorization is missing its popup binding."));
394
+ const returnFocusTo = typeof document !== "undefined" ? document.activeElement : null;
395
+ return new Promise((resolve, reject) => {
396
+ let settled = false;
397
+ const finish = (result) => {
398
+ if (settled) return;
399
+ settled = true;
400
+ window.clearTimeout(timeout);
401
+ window.clearInterval(closeTimer);
402
+ window.removeEventListener("message", onMessage);
403
+ returnFocusTo?.focus?.();
404
+ if (result instanceof Error) reject(result);
405
+ else resolve(result);
406
+ };
407
+ const onMessage = (event) => {
408
+ if (event.source !== popup || event.origin !== expectedOrigin) return;
409
+ const data = event.data;
410
+ if (data?.type !== "agents24.mcp.callback" || data.server_id !== expectedServerId || data.popup_nonce !== expectedPopupNonce) return;
411
+ const code = String(data.code || "").trim();
412
+ if (!code || code.length > 2048) return;
413
+ const connectionId = String(data.connection_id || "").trim();
414
+ if ((requireConnectionId || connectionId) && !UUID_PATTERN.test(connectionId)) return;
415
+ finish({
416
+ code,
417
+ ...connectionId ? { connectionId } : {},
418
+ popupNonce: expectedPopupNonce,
419
+ serverId: expectedServerId
420
+ });
421
+ };
422
+ const timeout = window.setTimeout(
423
+ () => finish(new Error("MCP authorization timed out.")),
424
+ Math.max(1, timeoutMs)
425
+ );
426
+ const closeTimer = window.setInterval(() => {
427
+ if (popup.closed) finish(new Error("MCP authorization window was closed before connecting."));
428
+ }, 500);
429
+ window.addEventListener("message", onMessage);
430
+ });
431
+ }
432
+
349
433
  export {
350
434
  LatestThreadScrollerProvider,
351
435
  LatestThreadScrollerRoot,
@@ -354,6 +438,8 @@ export {
354
438
  LatestThreadScrollerItem,
355
439
  LatestThreadScrollerButton,
356
440
  LatestThreadScrollerOutline,
357
- LatestThreadScroller
441
+ LatestThreadScroller,
442
+ validateMcpCallbackResult,
443
+ waitForMcpOauthComplete
358
444
  };
359
- //# sourceMappingURL=chunk-FFO6KEB4.js.map
445
+ //# sourceMappingURL=chunk-LI67M74F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/latest-thread-scroller.tsx","../src/mcp-oauth.ts"],"sourcesContent":["import * as React from \"react\";\nimport {\n MessageScroller,\n useMessageScroller,\n useMessageScrollerVisibility,\n} from \"@shadcn/react/message-scroller\";\n\nconst cx = (...values: Array<string | false | null | undefined>) =>\n values.filter(Boolean).join(\" \");\n\ntype MessageScrollerProviderProps = React.ComponentProps<\n typeof MessageScroller.Provider\n>;\ntype MessageScrollerRootProps = React.ComponentProps<typeof MessageScroller.Root>;\ntype MessageScrollerViewportProps = React.ComponentProps<\n typeof MessageScroller.Viewport\n>;\ntype MessageScrollerContentProps = React.ComponentProps<\n typeof MessageScroller.Content\n>;\ntype MessageScrollerItemProps = React.ComponentProps<typeof MessageScroller.Item>;\ntype MessageScrollerButtonProps = React.ComponentProps<typeof MessageScroller.Button>;\n\ntype LatestThreadScrollerLayoutContextValue = {\n contentElement: HTMLElement | null;\n rootElement: HTMLElement | null;\n setContentElement: (node: HTMLElement | null) => void;\n setRootElement: (node: HTMLElement | null) => void;\n};\n\nconst LatestThreadScrollerLayoutContext =\n React.createContext<LatestThreadScrollerLayoutContextValue | null>(null);\n\nconst useLatestThreadScrollerLayout = () =>\n React.useContext(LatestThreadScrollerLayoutContext);\n\nexport type LatestThreadScrollerProviderProps = MessageScrollerProviderProps;\n\nexport function LatestThreadScrollerProvider({\n autoScroll = true,\n defaultScrollPosition = \"last-anchor\",\n scrollPreviousItemPeek = 64,\n ...props\n}: LatestThreadScrollerProviderProps) {\n return (\n <MessageScroller.Provider\n autoScroll={autoScroll}\n defaultScrollPosition={defaultScrollPosition}\n scrollPreviousItemPeek={scrollPreviousItemPeek}\n {...props}\n />\n );\n}\n\nexport type LatestThreadScrollerRootProps = MessageScrollerRootProps;\n\nexport function LatestThreadScrollerRoot({\n className,\n ref,\n ...props\n}: LatestThreadScrollerRootProps) {\n const [rootElement, setRootElement] = React.useState<HTMLElement | null>(null);\n const [contentElement, setContentElement] =\n React.useState<HTMLElement | null>(null);\n const layoutContext = React.useMemo<LatestThreadScrollerLayoutContextValue>(\n () => ({\n contentElement,\n rootElement,\n setContentElement,\n setRootElement,\n }),\n [contentElement, rootElement]\n );\n const setRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n setRootElement(node);\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n },\n [ref]\n );\n\n return (\n <LatestThreadScrollerLayoutContext.Provider value={layoutContext}>\n <MessageScroller.Root\n ref={setRef}\n className={cx(\n \"a24-scroller relative flex size-full min-h-0 flex-1 flex-col overflow-hidden\",\n className\n )}\n {...props}\n />\n </LatestThreadScrollerLayoutContext.Provider>\n );\n}\n\nexport type LatestThreadScrollerViewportProps =\n MessageScrollerViewportProps & {\n onViewportChange?: (node: HTMLElement | null) => void;\n };\n\nexport function LatestThreadScrollerViewport({\n className,\n onViewportChange,\n preserveScrollOnPrepend = true,\n ref,\n ...props\n}: LatestThreadScrollerViewportProps) {\n const setRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n onViewportChange?.(node);\n },\n [onViewportChange, ref]\n );\n\n return (\n <MessageScroller.Viewport\n ref={setRef}\n className={cx(\n \"a24-scroller__viewport size-full min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain\",\n className\n )}\n preserveScrollOnPrepend={preserveScrollOnPrepend}\n {...props}\n />\n );\n}\n\nexport type LatestThreadScrollerContentProps = MessageScrollerContentProps;\n\nexport function LatestThreadScrollerContent({\n className,\n ref,\n ...props\n}: LatestThreadScrollerContentProps) {\n const layout = useLatestThreadScrollerLayout();\n const setRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n layout?.setContentElement(node);\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n },\n [layout, ref]\n );\n\n return (\n <MessageScroller.Content\n ref={setRef}\n data-agents24-latest-thread-content=\"\"\n className={cx(\"a24-scroller__content flex h-max min-h-full flex-col gap-8 p-4\", className)}\n {...props}\n />\n );\n}\n\nexport type LatestThreadScrollerItemProps = Omit<\n MessageScrollerItemProps,\n \"messageId\"\n> & {\n messageId: string;\n};\n\nexport function LatestThreadScrollerItem({\n className,\n messageId,\n ...props\n}: LatestThreadScrollerItemProps) {\n return (\n <MessageScroller.Item\n className={cx(\n \"a24-scroller__item min-w-0 shrink-0 overflow-visible\",\n className\n )}\n messageId={messageId}\n {...props}\n />\n );\n}\n\nexport type LatestThreadScrollerButtonProps = Omit<\n MessageScrollerButtonProps,\n \"render\"\n> & {\n className?: string;\n render?: MessageScrollerButtonProps[\"render\"];\n};\n\nexport function LatestThreadScrollerButton({\n children,\n className,\n direction = \"end\",\n render,\n ...props\n}: LatestThreadScrollerButtonProps) {\n if (render) {\n return (\n <MessageScroller.Button\n direction={direction}\n render={render}\n {...props}\n />\n );\n }\n\n return (\n <MessageScroller.Button\n direction={direction}\n render={(buttonProps) => (\n <button\n {...(buttonProps as Record<string, unknown>)}\n aria-label={\n direction === \"end\"\n ? \"Scroll to latest message\"\n : \"Scroll to first message\"\n }\n className={cx(\n \"a24-scroller__button absolute bottom-4 left-1/2 z-50 flex size-9 -translate-x-1/2 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-md transition-[background-color,border-color,color,opacity,transform] duration-200 data-[active=false]:pointer-events-none data-[active=false]:translate-y-2 data-[active=false]:opacity-0 data-[active=true]:translate-y-0 data-[active=true]:opacity-100\",\n className\n )}\n type=\"button\"\n >\n {children ?? (direction === \"end\" ? \"↓\" : \"↑\")}\n </button>\n )}\n {...props}\n />\n );\n}\n\nexport type LatestThreadScrollerOutlineItem = {\n id: string;\n label: string;\n messageIds?: string[];\n responsePreview?: string;\n};\n\nexport type LatestThreadScrollerOutlineProps = {\n cardClassName?: string;\n className?: string;\n contentMaxWidth?: number;\n dir?: \"ltr\" | \"rtl\";\n items: LatestThreadScrollerOutlineItem[];\n minContainerWidthForRail?: number;\n minSideGutterForRail?: number;\n side?: \"left\" | \"right\";\n};\n\nexport function LatestThreadScrollerOutline({\n cardClassName,\n className,\n contentMaxWidth = 768,\n dir = \"ltr\",\n items,\n minContainerWidthForRail = 0,\n minSideGutterForRail = 48,\n side,\n}: LatestThreadScrollerOutlineProps) {\n const navRef = React.useRef<HTMLElement | null>(null);\n const layout = useLatestThreadScrollerLayout();\n const [hasSideRoom, setHasSideRoom] = React.useState(false);\n const [hoveredIndex, setHoveredIndex] = React.useState<number | null>(null);\n const { scrollToMessage } = useMessageScroller();\n const { currentAnchorId, visibleMessageIds } = useMessageScrollerVisibility();\n const visibleMessageIdSet = React.useMemo(\n () => new Set(visibleMessageIds),\n [visibleMessageIds]\n );\n const focusIndex = hoveredIndex ?? -1;\n const railSide = side ?? (dir === \"rtl\" ? \"right\" : \"left\");\n\n React.useEffect(() => {\n const root = layout?.rootElement ?? navRef.current?.parentElement;\n if (!root) return;\n\n const updateSideRoom = () => {\n const rootRect = root.getBoundingClientRect();\n const rootWidth = rootRect.width;\n const content = layout?.contentElement;\n const sideGutter = content\n ? Math.max(\n 0,\n railSide === \"left\"\n ? content.getBoundingClientRect().left - rootRect.left\n : rootRect.right - content.getBoundingClientRect().right\n )\n : Math.max(0, (rootWidth - contentMaxWidth) / 2);\n const nextHasSideRoom =\n rootWidth >= minContainerWidthForRail &&\n sideGutter >= minSideGutterForRail;\n setHasSideRoom((current) =>\n current === nextHasSideRoom ? current : nextHasSideRoom\n );\n };\n\n updateSideRoom();\n if (typeof ResizeObserver === \"undefined\") return;\n const resizeObserver = new ResizeObserver(updateSideRoom);\n resizeObserver.observe(root);\n const content = layout?.contentElement;\n if (content) {\n resizeObserver.observe(content);\n }\n return () => resizeObserver.disconnect();\n }, [\n contentMaxWidth,\n layout?.contentElement,\n layout?.rootElement,\n minContainerWidthForRail,\n minSideGutterForRail,\n railSide,\n ]);\n\n if (items.length < 2) {\n return null;\n }\n\n return (\n <nav\n ref={navRef}\n aria-label=\"Transcript outline\"\n data-side={railSide}\n data-visible={hasSideRoom ? \"\" : undefined}\n className={cx(\n \"a24-outline pointer-events-none absolute top-[46%] z-10 max-h-[min(18rem,calc(100%-10rem))] w-16 -translate-y-1/2 overflow-visible\",\n hasSideRoom ? \"hidden md:block\" : \"hidden\",\n railSide === \"left\" ? \"left-1\" : \"right-1\",\n className\n )}\n dir={dir}\n >\n <div className=\"a24-outline__rail pointer-events-auto flex flex-col gap-0 py-1\">\n {items.map((item, index) => {\n const itemMessageIds =\n item.messageIds && item.messageIds.length > 0\n ? item.messageIds\n : [item.id];\n const active = itemMessageIds.some(\n (messageId) =>\n visibleMessageIdSet.has(messageId) || messageId === currentAnchorId\n );\n const distance =\n focusIndex >= 0 ? Math.abs(index - focusIndex) : Number.POSITIVE_INFINITY;\n const isFocused = distance === 0;\n const isNear = distance === 1;\n const isNextNear = distance === 2;\n const barWidth = isFocused\n ? 50\n : isNear\n ? 36\n : isNextNear\n ? 23\n : 10;\n const barOpacity = isFocused ? 1 : isNear ? 0.78 : isNextNear ? 0.58 : 0.36;\n const fallbackLabel = `Turn ${index + 1}`;\n const previewLabel =\n item.label && item.label !== fallbackLabel ? item.label : \"\";\n const responsePreview = (item.responsePreview || \"\").trim();\n\n return (\n <button\n key={item.id}\n type=\"button\"\n data-active={active}\n data-side={railSide}\n dir=\"ltr\"\n className={cx(\n \"a24-outline__button group relative flex h-2 w-16 items-center rounded-sm outline-none\",\n railSide === \"left\" ? \"justify-start\" : \"justify-end\"\n )}\n aria-label={`Jump to ${item.label || fallbackLabel}`}\n onBlur={() => setHoveredIndex(null)}\n onFocus={() => setHoveredIndex(index)}\n onMouseEnter={() => setHoveredIndex(index)}\n onMouseLeave={() => setHoveredIndex(null)}\n onClick={() =>\n scrollToMessage(item.id, {\n align: \"start\",\n behavior: \"smooth\",\n })\n }\n >\n <span\n className=\"a24-outline__bar h-0.5 rounded-full bg-muted-foreground/55 transition-[width,background-color,opacity] duration-150 ease-out group-hover:bg-foreground group-focus-visible:bg-foreground group-data-[active=true]:bg-foreground\"\n style={{\n opacity: barOpacity,\n width: barWidth,\n }}\n />\n {previewLabel ? (\n <span\n dir={dir}\n className={cx(\n \"a24-outline__preview pointer-events-none absolute top-1/2 z-20 max-h-32 w-64 -translate-y-1/2 overflow-hidden rounded-lg border border-border/80 bg-background p-3 text-start opacity-0 shadow-lg transition-[opacity,transform] duration-200 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-visible:pointer-events-auto group-focus-visible:opacity-100\",\n railSide === \"left\"\n ? \"left-full ml-2 -translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0\"\n : \"right-full mr-2 translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0\",\n cardClassName\n )}\n >\n <span\n className=\"block overflow-hidden text-sm leading-5 text-foreground\"\n style={{\n display: \"-webkit-box\",\n WebkitBoxOrient: \"vertical\",\n WebkitLineClamp: responsePreview ? 2 : 4,\n }}\n >\n {previewLabel}\n </span>\n {responsePreview ? (\n <span\n className=\"mt-1 block overflow-hidden text-sm leading-5 text-muted-foreground\"\n style={{\n display: \"-webkit-box\",\n WebkitBoxOrient: \"vertical\",\n WebkitLineClamp: 2,\n }}\n >\n {responsePreview}\n </span>\n ) : null}\n </span>\n ) : null}\n </button>\n );\n })}\n </div>\n </nav>\n );\n}\n\nexport const LatestThreadScroller = {\n Provider: LatestThreadScrollerProvider,\n Root: LatestThreadScrollerRoot,\n Viewport: LatestThreadScrollerViewport,\n Content: LatestThreadScrollerContent,\n Item: LatestThreadScrollerItem,\n Button: LatestThreadScrollerButton,\n Outline: LatestThreadScrollerOutline,\n};\n","import type { McpCallbackResult } from \"@agents24/client\";\nimport type { ChatMcpOauthCompletion, WaitForMcpOauthCompleteInput } from \"./types\";\n\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\nfunction isLoopback(hostname: string): boolean {\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"[::1]\";\n}\n\nfunction normalizedOrigin(value: string): string {\n try {\n const url = new URL(value);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && isLoopback(url.hostname))) {\n throw new Error(\"unsafe callback origin\");\n }\n if (url.origin === \"null\") throw new Error(\"opaque callback origin\");\n return url.origin;\n } catch {\n throw new Error(\"MCP authorization returned an invalid callback origin.\");\n }\n}\n\nexport function validateMcpCallbackResult(\n result: McpCallbackResult,\n expectedServerId: string,\n): McpCallbackResult {\n const serverId = String(result.server_id || \"\").trim();\n const connectionId = String(result.connection_id || \"\").trim();\n if (!serverId || serverId !== expectedServerId) {\n throw new Error(\"MCP authorization completed for an unexpected server.\");\n }\n if (!UUID_PATTERN.test(connectionId)) {\n throw new Error(\"MCP authorization returned an invalid connection identity.\");\n }\n return result;\n}\n\nexport function waitForMcpOauthComplete({\n popup,\n callbackOrigin,\n popupNonce,\n requireConnectionId = false,\n serverId,\n timeoutMs = 120_000,\n}: WaitForMcpOauthCompleteInput): Promise<ChatMcpOauthCompletion> {\n if (!popup) return Promise.reject(new Error(\"Could not open the MCP authorization popup.\"));\n const expectedOrigin = normalizedOrigin(callbackOrigin);\n const expectedServerId = String(serverId || \"\").trim();\n const expectedPopupNonce = String(popupNonce || \"\").trim();\n if (!expectedServerId) return Promise.reject(new Error(\"MCP authorization is missing its server identity.\"));\n if (!expectedPopupNonce) return Promise.reject(new Error(\"MCP authorization is missing its popup binding.\"));\n const returnFocusTo = typeof document !== \"undefined\"\n ? document.activeElement as { focus?: () => void } | null\n : null;\n\n return new Promise((resolve, reject) => {\n let settled = false;\n const finish = (result: ChatMcpOauthCompletion | Error) => {\n if (settled) return;\n settled = true;\n window.clearTimeout(timeout);\n window.clearInterval(closeTimer);\n window.removeEventListener(\"message\", onMessage);\n returnFocusTo?.focus?.();\n if (result instanceof Error) reject(result);\n else resolve(result);\n };\n const onMessage = (event: MessageEvent) => {\n if (event.source !== popup || event.origin !== expectedOrigin) return;\n const data = event.data as {\n type?: string;\n code?: string;\n connection_id?: string;\n popup_nonce?: string;\n server_id?: string;\n } | null;\n if (\n data?.type !== \"agents24.mcp.callback\" ||\n data.server_id !== expectedServerId ||\n data.popup_nonce !== expectedPopupNonce\n ) return;\n const code = String(data.code || \"\").trim();\n if (!code || code.length > 2048) return;\n const connectionId = String(data.connection_id || \"\").trim();\n if ((requireConnectionId || connectionId) && !UUID_PATTERN.test(connectionId)) return;\n finish({\n code,\n ...(connectionId ? { connectionId } : {}),\n popupNonce: expectedPopupNonce,\n serverId: expectedServerId,\n });\n };\n const timeout = window.setTimeout(\n () => finish(new Error(\"MCP authorization timed out.\")),\n Math.max(1, timeoutMs),\n );\n const closeTimer = window.setInterval(() => {\n if (popup.closed) finish(new Error(\"MCP authorization window was closed before connecting.\"));\n }, 500);\n window.addEventListener(\"message\", onMessage);\n });\n}\n"],"mappings":"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","names":["content"]}