@agents24/chat-react 0.3.1 → 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.
- package/LICENSE +19 -0
- package/README.md +21 -66
- package/compatibility.json +54 -0
- package/dist/active-content.d.ts +10 -0
- package/dist/{chunk-EWZO4QJI.js → chunk-CHAU7FNW.js} +3 -3
- package/dist/chunk-CHAU7FNW.js.map +1 -0
- package/dist/{chunk-FFO6KEB4.js → chunk-LI67M74F.js} +98 -12
- package/dist/chunk-LI67M74F.js.map +1 -0
- package/dist/chunk-NLEOWTZG.js +1327 -0
- package/dist/chunk-NLEOWTZG.js.map +1 -0
- package/dist/index.cjs +66 -1852
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.js +6 -1841
- package/dist/index.js.map +1 -1
- package/dist/latest-thread-scroller.d.ts +7 -7
- package/dist/mcp-oauth.d.ts +3 -1
- package/dist/renderers.d.ts +3 -3
- package/dist/styles.css +407 -0
- package/dist/templates/agent-chat-app.d.ts +18 -0
- package/dist/templates/agent-chat-layout.d.ts +69 -0
- package/dist/templates/agent-chat-shell.d.ts +1 -1
- package/dist/templates/index.cjs +1904 -18
- package/dist/templates/index.cjs.map +1 -1
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.js +529 -11
- package/dist/templates/index.js.map +1 -1
- package/dist/types.d.ts +12 -380
- package/dist/ui/agent-chat-actions.d.ts +1 -2
- package/dist/ui/agent-chat-composer.d.ts +2 -1
- package/dist/ui/agent-chat-message.d.ts +3 -3
- package/dist/ui/agent-response-timeline.d.ts +2 -2
- package/dist/ui/attachment.d.ts +9 -9
- package/dist/ui/bubble.d.ts +4 -4
- package/dist/ui/index.cjs +137 -43
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +62 -1220
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/marker.d.ts +3 -3
- package/dist/ui/mcp-connect-required-card.d.ts +1 -1
- package/dist/ui/message-response.d.ts +1 -1
- package/dist/ui/message.d.ts +6 -6
- package/package.json +27 -8
- package/dist/chunk-EWZO4QJI.js.map +0 -1
- package/dist/chunk-FFO6KEB4.js.map +0 -1
- package/dist/chunk-UU7OXHL3.js +0 -11
- package/dist/chunk-UU7OXHL3.js.map +0 -1
- package/dist/context-window.d.ts +0 -38
- package/dist/controller-actions.d.ts +0 -45
- package/dist/controller-helpers.d.ts +0 -27
- package/dist/controller-hitl.d.ts +0 -22
- package/dist/controller-options.d.ts +0 -15
- package/dist/controller-thread-events.d.ts +0 -7
- package/dist/controller.d.ts +0 -5
- package/dist/message-lifecycle.d.ts +0 -15
- package/dist/model.d.ts +0 -30
- package/dist/sse.d.ts +0 -5
- 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
|
-
#
|
|
1
|
+
# `@agents24/chat-react`
|
|
2
2
|
|
|
3
|
-
Last Updated: 2026-07-
|
|
3
|
+
Last Updated: 2026-07-16
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
7
|
+
```tsx
|
|
8
|
+
import { AgentChatApp } from "@agents24/chat-react/templates";
|
|
9
|
+
import "@agents24/chat-react/styles.css";
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
<AgentChatApp
|
|
12
|
+
agent={{ name: "Support Agent" }}
|
|
13
|
+
client={client}
|
|
14
|
+
mcpRedirectUri={window.location.href}
|
|
15
|
+
/>;
|
|
16
|
+
```
|
|
10
17
|
|
|
11
|
-
|
|
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 package `0.3.0`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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"]}
|