import 'rollup-plugin-inject-process-env'; import { Attribution } from "@xapp/chat-widget-core"; import { ActionType } from "../store/ChatAction"; import { ChatServer, ChatServerOptions, OfflineMessage, VisitorInfoMessage } from "./ChatServer"; import { ChatMessageRequest, ChatServerMessage } from "./ChatServerMessage"; /** * Page context to provide information about the user's current page. * Enables more relevant AI responses based on context. */ interface PageContext { url?: string; path?: string; title?: string; referrer?: string; localTime?: string; /** * Marketing attribution parameters, as an allowlisted subset of the query * string. url and referrer are stripped of their query strings for privacy, * which also strips the click that paid for the visit — this carries the * attribution across without carrying the rest of the query string. */ attribution?: Attribution; } /** * Storage keys for MCP session persistence. * Uses localStorage directly (not Redux) for MCP-specific data. */ export declare const MCP_STORAGE_KEYS: { readonly USER_ID: "xapp_mcp_userId"; readonly SESSION_ID: "xapp_mcp_sessionId"; }; /** * Returns the current local time as an ISO 8601 string with the local timezone offset. * Unlike toISOString(), this preserves the user's local time zone (e.g. "2024-01-15T14:30:00-04:00") * so the agent server can correctly determine time-of-day context (morning greetings, etc.). */ export declare function getLocalISOString(date?: Date): string; export declare function getPageContext(override?: PageContext): PageContext; export interface MCPChatConfig { /** * Server URL for MCP connections. * * For homeowner mode: Include the appId in the URL * e.g., "https://agent.xapp.ai/abc123" * * For business_owner mode: Just the base URL * e.g., "https://agent.xapp.ai" * * The full stream endpoint is also accepted * (e.g. "https://agent.xapp.ai/abc123/chat/stream") -- see getBaseUrl, which normalises * it back to the base so the stream and close paths are both derived correctly. */ readonly serverUrl: string; /** * Mode determines endpoint paths and authentication requirements. * - homeowner: /chat/stream and /chat/close on serverUrl (unauthenticated) * - business_owner: /api/chat/stream and /api/chat/close (requires authToken) * * @default "homeowner" */ readonly mode?: "homeowner" | "business_owner"; /** * Optional session ID for conversation continuity */ readonly sessionId?: string; /** * Auth token required for business_owner mode. * Will throw an error if mode is "business_owner" and authToken is not provided. */ readonly authToken?: string; /** * Enable debug mode to show tool calls in the chat */ readonly debugMode?: boolean; /** * Override the automatically detected page context. * Useful for testing in Storybook or simulating different pages. */ readonly pageContextOverride?: PageContext; /** * Show an "AI" badge on the avatar for AI-generated messages. */ readonly showAiBadge?: boolean; /** * Disable device fingerprinting for privacy-conscious deployments. * When true, no fingerprint data is sent to the server. * Note: This may reduce the server's ability to identify returning users. */ readonly disableFingerprinting?: boolean; /** * Unique per-instance channel id (e.g. "chat-widget-eaf9a4"), sent to the server * so events and leads can be attributed to this widget instance. */ readonly channelId?: string; } export declare class MCPChat implements ChatServer { private visitorInfo; private _userId; private _sessionId; private _accessToken; private _debugMode; private _attributes; private dispatch; private isNewSession; private readonly configurableMessages; private readonly config; private readonly options; private readonly hooks?; private abortController?; private currentMessageId?; private streamBuffer; private streamThrottleTimer?; private streamingMessageIdForThrottle?; private visibilityHandler?; private isInitializingSession; private sessionInitPromise?; private resolveSessionInit?; private isValidatingSession; private conversationOnScreen; private currentToolCalls; private lastSeq; private isReplaying; private isSleeping; private isRequestingReplay; private needsReplayOnWakeup; private userIdIsServerAssigned; private sessionStartTime; private sentMessageCount; constructor(config: MCPChatConfig, options?: ChatServerOptions); init(dispatch: (action: ActionType) => void): void; /** * Validates an existing session with the server ("Silent Handshake"). * Called when widget reopens with a stored sessionId. * * The server will respond with: * - "connected" event if session is valid * - "session_expired" event if session is gone (triggers initializeNewSession) * * This is lightweight - no LLM call, just a quick DB check to: * - Validate session before user tries to send a message * - Update lastActivityAt in persistent session * - Confirm userId is still valid */ private validateSession; /** * Builds the common request payload with identity and context. */ private buildRequestPayload; /** * Initializes a new session by calling the endpoint with { newSession: true }. * This returns a greeting message and session ID without making an LLM call. */ private initializeNewSession; private setConnectionStatus; private setAccountStatus; /** * The paths this mode hangs off its base URL: [stream, close]. */ private getEndpointPaths; /** * The base the endpoint paths are built from. * * A configured `serverUrl` arrives in either of two shapes: the base * (`https://agentic.xapp.ai/`), which is what this class was written for, or the * full stream endpoint (`https://agentic.xapp.ai//chat/stream`), which is the form * issue #1403 documents as the Dev and Prod defaults. Appending unconditionally turns the * second into `.../chat/stream/chat/stream`. * * Taking `serverUrl` verbatim instead is not the fix: `/chat/close` has to be derived * from something, and a widget configured with the full stream URL would then send every * close request to the stream endpoint. So normalise back to the base and build both * paths from it, which leaves either configured form working. * * Only the paths belonging to the CONFIGURED mode come off. A homeowner URL handed to a * business_owner widget is a misconfiguration, not something to quietly reinterpret, and * an appId that merely ends in `/stream` is not an endpoint path. */ private getBaseUrl; private getEndpoint; private getCloseEndpoint; private sendNewMessage; private userJoined; private typing; private stopTyping; private sendFailureMessage; /** * Gets the retry configuration, using custom config if provided or defaults. */ private getRetryConfig; /** * Delays execution for the specified number of seconds. */ private delay; /** * Determines a user-friendly error message based on the error type. */ private getErrorMessage; private dispatchStreamStart; private dispatchStreamChunk; private dispatchStreamEnd; private dispatchStreamAbort; private dispatchStreamResume; private dispatchToolStart; private dispatchToolEnd; /** * Starts the throttled streaming for a message. * Text will be dispatched at a readable pace. */ private startStreamThrottle; /** * Adds text to the stream buffer for throttled dispatch. */ private bufferStreamText; /** * Dispatches whatever is buffered right now, without waiting on the throttle timer. * * Used when a bubble has to be closed mid-turn (`message_start`): the split has to be * synchronous, because frames arrive in order on one `onmessage` callback and deferring it * to a microtask would let the next `text` frame land in the bubble we are closing. */ private flushStreamBufferSync; /** * Flushes any remaining buffered text and stops the throttle timer. * Returns a promise that resolves when all buffered text has been dispatched. */ private flushStreamBuffer; /** * Immediately stops throttling and clears the buffer (used on abort/error). */ private stopStreamThrottle; sendOfflineMsg(_: OfflineMessage, cb: (error?: Error) => void): void; sendChatMsg(message: ChatServerMessage, cb: (err?: Error) => void): Promise; bargeOut(_: (err?: Error) => void): Promise; bargeIn(_agentName: string, __: (err?: Error) => void): Promise; /** * Ensures a nick has the "agent:" prefix required for agent message detection. */ private ensureAgentNick; private getBot; sendChatMsgRequest(serviceRequest: ChatMessageRequest, cb: (err?: Error) => void): Promise; private postMessage; /** * Attempts a single SSE request. Throws on failure. */ private attemptSSERequest; /** * Performs an SSE request with the given body. * Used for both new session initialization and regular messages. */ private performSSERequest; sendTyping(): void; setVisitorInfo(visitorInfo: VisitorInfoMessage, sessionId: string, cb: (error?: Error) => void): void; sendChatRating(): void; sendFile(_: File, cb: (err?: Error) => void): void; markAsRead(): void; flush(): void; dispose(): void; sleep(): void; wakeup(): void; private requestReplay; /** * Sends a best-effort close notification to the agent server. * Uses navigator.sendBeacon() so the request survives page unload. * Called from the beforeunload event handler and the widget close button. */ notifyDisconnecting(reason?: "close_tab" | "widget_close"): void; private startSession; get userId(): string; get sessionId(): string; } export {};