@raisindb/client 0.5.9 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/{chunk-IFV4ECHC.mjs → chunk-ASOWGNQZ.mjs} +3 -3
- package/dist/{chunk-ZUUSBLVP.mjs → chunk-FYWYSNQG.mjs} +3 -3
- package/dist/{chunk-765DLJMZ.mjs → chunk-NOPZAMIV.mjs} +31 -2
- package/dist/chunk-NOPZAMIV.mjs.map +1 -0
- package/dist/{chunk-AULHRX5C.mjs → chunk-TPZNJNZR.mjs} +2 -2
- package/dist/{chunk-VMUT6PIO.mjs → chunk-ZCRYQCXS.mjs} +2 -2
- package/dist/{conversation-list-store-CHaqGtSi.d.mts → conversation-list-store-CqgIk8Rn.d.mts} +179 -3
- package/dist/{conversation-list-store-CHaqGtSi.d.ts → conversation-list-store-CqgIk8Rn.d.ts} +179 -3
- package/dist/{index-AobOnS4A.d.mts → index-BD3rutlF.d.mts} +1 -1
- package/dist/{index-DiL2gplR.d.ts → index-CDUAMIub.d.ts} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +383 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +356 -10
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +30 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +3 -3
- package/dist/{svelte-sql-FQqis53r.d.ts → svelte-sql-D9YU1wYS.d.ts} +1 -1
- package/dist/{svelte-sql-BUrNcoHR.d.mts → svelte-sql-l1wmTWM4.d.mts} +1 -1
- package/dist/svelte.d.mts +2 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +30 -1
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +3 -3
- package/dist/vue.d.mts +2 -2
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +30 -1
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +2 -2
- package/package.json +1 -1
- package/dist/chunk-765DLJMZ.mjs.map +0 -1
- /package/dist/{chunk-IFV4ECHC.mjs.map → chunk-ASOWGNQZ.mjs.map} +0 -0
- /package/dist/{chunk-ZUUSBLVP.mjs.map → chunk-FYWYSNQG.mjs.map} +0 -0
- /package/dist/{chunk-AULHRX5C.mjs.map → chunk-TPZNJNZR.mjs.map} +0 -0
- /package/dist/{chunk-VMUT6PIO.mjs.map → chunk-ZCRYQCXS.mjs.map} +0 -0
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/integrations/react/index.ts","../src/integrations/react/use-auth.ts","../src/connection.ts","../src/utils/reconnect.ts","../src/logger.ts","../src/integrations/react/use-connection.ts","../src/integrations/react/use-sql.ts","../src/integrations/react/use-subscription.ts","../src/utils/plan-projection.ts","../src/utils/chat-events.ts","../src/stores/conversation-store.ts","../src/utils/home-path.ts","../src/stores/conversation-list-store.ts","../src/integrations/react-conversation.ts","../src/errors.ts","../src/streaming/sse-client.ts","../src/flow-client.ts","../src/integrations/react-flow.ts"],"sourcesContent":["/**\n * React integration layer for RaisinDB.\n *\n * Provides a `createRaisinReact(React)` factory that returns a Provider\n * component and hooks for auth, connection, queries, and subscriptions.\n *\n * Uses the \"bring your own React\" pattern to avoid a peer dependency.\n *\n * @example\n * ```ts\n * import React from 'react';\n * import { RaisinClient, LocalStorageTokenStorage, createRaisinReact } from '@raisindb/client';\n *\n * const client = new RaisinClient('wss://localhost:8443/sys/default/myrepo', {\n * tokenStorage: new LocalStorageTokenStorage('myrepo'),\n * });\n *\n * export const {\n * RaisinProvider, useAuth, useConnection, useDatabase,\n * useSql, useSubscription, useConversation, useFlow,\n * } = createRaisinReact(React);\n * ```\n */\n\nimport type {\n ReactLikeWithContext,\n RaisinProviderProps,\n RaisinContextValue,\n UseAuthReturn,\n UseConnectionReturn,\n UseSqlOptions,\n UseSqlReturn,\n UseSubscriptionOptions,\n} from './types';\n\nimport type { RaisinClient } from '../../client';\nimport type { Database } from '../../database';\nimport type { EventMessage } from '../../protocol';\nimport type {\n ConversationStoreOptions,\n} from '../../stores/conversation-store';\nimport type { ConversationListStoreOptions } from '../../stores/conversation-list-store';\n\nimport { createUseAuth } from './use-auth';\nimport { createUseConnection } from './use-connection';\nimport { createUseSql } from './use-sql';\nimport { createUseSubscription } from './use-subscription';\n\nimport {\n useConversation as rawUseConversation,\n useConversationList as rawUseConversationList,\n type UseConversationReturn,\n type UseConversationListReturn,\n} from '../react-conversation';\nimport {\n useFlow as rawUseFlow,\n type UseFlowOptions,\n type UseFlowReturn,\n} from '../react-flow';\n\nexport interface RaisinReact {\n RaisinProvider: (props: RaisinProviderProps) => any;\n useRaisinClient: () => RaisinClient;\n useDatabase: (repository?: string) => Database;\n useAuth: () => UseAuthReturn;\n useConnection: () => UseConnectionReturn;\n useSql: <T = Record<string, unknown>>(sql: string, params?: unknown[], options?: UseSqlOptions) => UseSqlReturn<T>;\n useSubscription: (options: UseSubscriptionOptions, callback: (event: EventMessage) => void) => void;\n useConversation: (options: ConversationStoreOptions) => UseConversationReturn;\n useConversationList: (options: ConversationListStoreOptions) => UseConversationListReturn;\n useFlow: (options: UseFlowOptions) => UseFlowReturn;\n}\n\n/**\n * Create the full React integration for RaisinDB.\n *\n * Accepts a React instance and returns a Provider component plus hooks.\n * The React instance must include `createContext`, `useContext`, `useMemo`,\n * and `createElement` in addition to the base `ReactLike` interface.\n *\n * @param react - The React instance\n * @returns Provider component and hooks\n */\nexport function createRaisinReact(react: ReactLikeWithContext): RaisinReact {\n const RaisinContext = react.createContext<RaisinContextValue | null>(null);\n\n // --- Provider ---\n function RaisinProvider(props: RaisinProviderProps): any {\n const { client, repository, children } = props;\n\n const value = react.useMemo(\n () => ({ client, repository }),\n [client, repository],\n );\n\n return react.createElement(\n RaisinContext.Provider as unknown,\n { value },\n children,\n );\n }\n\n // --- Context accessors ---\n function useRaisinClient(): RaisinClient {\n const ctx = react.useContext(RaisinContext);\n if (!ctx) {\n throw new Error('useRaisinClient must be used within a <RaisinProvider>');\n }\n return ctx.client;\n }\n\n function useDatabase(repository?: string): Database {\n const ctx = react.useContext(RaisinContext);\n if (!ctx) {\n throw new Error('useDatabase must be used within a <RaisinProvider>');\n }\n const repo = repository ?? ctx.repository;\n if (!repo) {\n throw new Error('useDatabase requires a repository — pass it as an argument or set it on <RaisinProvider>');\n }\n return react.useMemo(() => ctx.client.database(repo), [ctx.client, repo]);\n }\n\n // --- Core hooks ---\n const useAuth = createUseAuth(react, RaisinContext);\n const useConnection = createUseConnection(react, RaisinContext);\n const useSql = createUseSql(react, RaisinContext);\n const useSubscription = createUseSubscription(react, RaisinContext);\n\n // --- Pre-bound existing hooks ---\n function useConversation(options: ConversationStoreOptions): UseConversationReturn {\n return rawUseConversation(react, options);\n }\n\n function useConversationList(options: ConversationListStoreOptions): UseConversationListReturn {\n return rawUseConversationList(react, options);\n }\n\n function useFlow(options: UseFlowOptions): UseFlowReturn {\n return rawUseFlow(react, options);\n }\n\n return {\n RaisinProvider,\n useRaisinClient,\n useDatabase,\n useAuth,\n useConnection,\n useSql,\n useSubscription,\n useConversation,\n useConversationList,\n useFlow,\n };\n}\n\n// Re-export types\nexport type {\n ReactLikeWithContext,\n RaisinProviderProps,\n RaisinContextValue,\n UseAuthReturn,\n UseConnectionReturn,\n UseSqlOptions,\n UseSqlReturn,\n UseSubscriptionOptions,\n} from './types';\n\n// Hook return/option types (parity with the @raisindb/client/vue subpath):\n// apps need these to type props that carry a hook's return value around.\nexport type {\n ReactLike,\n UseConversationReturn,\n UseConversationListReturn,\n} from '../react-conversation';\nexport type { UseFlowOptions, UseFlowReturn, FlowStatus } from '../react-flow';\nexport type {\n ConversationStoreOptions,\n ConversationStoreSnapshot,\n ToolCallInfo,\n} from '../../stores/conversation-store';\nexport type {\n ConversationListStoreOptions,\n ConversationListSnapshot,\n} from '../../stores/conversation-list-store';\nexport type { PlanProjection, PlanProjectionTask } from '../../utils/plan-projection';\nexport type { ChatMessage, ConversationListItem } from '../../types/chat';\n","import type { ReactLikeWithContext, ReactContext, RaisinContextValue, UseAuthReturn } from './types';\nimport type { IdentityUser } from '../../auth';\n\nexport function createUseAuth(\n react: ReactLikeWithContext,\n context: ReactContext<RaisinContextValue | null>,\n): () => UseAuthReturn {\n return function useAuth(): UseAuthReturn {\n const { useState, useEffect, useRef, useCallback, useContext } = react;\n\n const ctx = useContext(context);\n if (!ctx) {\n throw new Error('useAuth must be used within a <RaisinProvider>');\n }\n const { client } = ctx;\n\n const [user, setUser] = useState<IdentityUser | null>(client.getStoredUser());\n const [isLoading, setIsLoading] = useState(false);\n const mountedRef = useRef(true);\n\n useEffect(() => {\n mountedRef.current = true;\n\n const unsubAuth = client.onAuthStateChange(({ event, session }) => {\n if (!mountedRef.current) return;\n if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED' || event === 'USER_UPDATED') {\n setUser(session.user);\n } else if (event === 'SIGNED_OUT' || event === 'SESSION_EXPIRED') {\n setUser(null);\n }\n });\n\n const unsubUser = client.onUserChange(({ node }) => {\n if (!mountedRef.current) return;\n // Update user with the latest node properties\n setUser((prev) => {\n if (!prev) return prev;\n return {\n ...prev,\n home: node.path,\n displayName: (node.properties.display_name as string) ?? prev.displayName,\n avatarUrl: (node.properties.avatar_url as string) ?? prev.avatarUrl,\n };\n });\n });\n\n return () => {\n mountedRef.current = false;\n unsubAuth();\n unsubUser();\n };\n }, [client]);\n\n const login = useCallback(async (email: string, password: string, repository: string) => {\n setIsLoading(true);\n try {\n const result = await client.loginWithEmail(email, password, repository);\n return result;\n } finally {\n if (mountedRef.current) setIsLoading(false);\n }\n }, [client]);\n\n const register = useCallback(async (email: string, password: string, repository: string, displayName?: string) => {\n setIsLoading(true);\n try {\n const result = await client.registerWithEmail(email, password, repository, displayName);\n return result;\n } finally {\n if (mountedRef.current) setIsLoading(false);\n }\n }, [client]);\n\n const logout = useCallback(async (options?: { disconnect?: boolean; reconnect?: boolean }) => {\n setIsLoading(true);\n try {\n await client.logout(options);\n } finally {\n if (mountedRef.current) setIsLoading(false);\n }\n }, [client]);\n\n const initSession = useCallback(async (repository: string) => {\n setIsLoading(true);\n try {\n const result = await client.initSession(repository);\n return result;\n } finally {\n if (mountedRef.current) setIsLoading(false);\n }\n }, [client]);\n\n return {\n user,\n isAuthenticated: user !== null,\n isLoading,\n login,\n register,\n logout,\n initSession,\n };\n };\n}\n","/**\n * Universal WebSocket connection manager\n *\n * Detects environment (browser vs Node.js) and uses appropriate WebSocket implementation.\n * Handles auto-reconnect, heartbeat, and connection state management.\n */\n\nimport { EventEmitter } from 'events';\nimport { ReconnectManager, ReconnectOptions } from './utils/reconnect';\nimport { logger } from './logger';\n\n/**\n * Connection state\n */\nexport enum ConnectionState {\n Disconnected = 'disconnected',\n Connecting = 'connecting',\n Connected = 'connected',\n Reconnecting = 'reconnecting',\n Closed = 'closed',\n}\n\n/**\n * Connection options\n */\nexport interface ConnectionOptions {\n /** Auto-reconnect on disconnect (default: true) */\n autoReconnect?: boolean;\n /** Reconnection options */\n reconnectOptions?: ReconnectOptions;\n /** Heartbeat interval in milliseconds (default: 30000, 0 to disable) */\n heartbeatInterval?: number;\n /** Heartbeat timeout in milliseconds (default: 5000) */\n heartbeatTimeout?: number;\n /** WebSocket protocols */\n protocols?: string | string[];\n /**\n * Extra headers to send on the WebSocket upgrade request\n * (e.g. `x-tenant-id` for tenant-less `/ws` URLs).\n *\n * Only honored by WebSocket implementations that support custom headers\n * (Node.js `ws` package). Browser WebSockets silently ignore them — the\n * server then resolves the tenant from the request (default: \"default\").\n */\n headers?: Record<string, string>;\n}\n\n/**\n * Connection events\n */\nexport interface ConnectionEvents {\n stateChange: (state: ConnectionState) => void;\n message: (data: ArrayBuffer) => void;\n error: (error: Error) => void;\n close: (event: CloseEvent) => void;\n}\n\n/**\n * Type for WebSocket implementation (browser or Node.js)\n */\ntype WebSocketImpl = typeof WebSocket | typeof import('ws').WebSocket;\n\n/**\n * Get the appropriate WebSocket implementation for the current environment\n */\nfunction getWebSocketImpl(): WebSocketImpl {\n // Browser environment\n if (typeof WebSocket !== 'undefined') {\n return WebSocket as WebSocketImpl;\n }\n\n // Node.js environment\n try {\n // Use dynamic import to avoid bundler issues\n const ws = require('ws');\n return ws.WebSocket || ws.default || ws;\n } catch (error) {\n throw new Error(\n 'WebSocket implementation not found. In Node.js, please install the \"ws\" package: npm install ws'\n );\n }\n}\n\n/**\n * Universal WebSocket connection manager\n */\nexport class Connection extends EventEmitter {\n private ws?: WebSocket;\n private url: string;\n private state: ConnectionState = ConnectionState.Disconnected;\n private reconnectManager: ReconnectManager;\n private heartbeatTimer?: NodeJS.Timeout;\n private heartbeatTimeoutTimer?: NodeJS.Timeout;\n private options: Required<Omit<ConnectionOptions, 'reconnectOptions' | 'protocols' | 'headers'>> & {\n protocols?: string | string[];\n headers?: Record<string, string>;\n };\n private WebSocketImpl: WebSocketImpl;\n private manualClose = false;\n\n constructor(url: string, options: ConnectionOptions = {}) {\n super();\n // Convert raisin:// / raisins:// to ws:// / wss:// for the WebSocket layer\n this.url = url\n .replace(/^raisin:\\/\\//, 'ws://')\n .replace(/^raisins:\\/\\//, 'wss://');\n this.options = {\n autoReconnect: options.autoReconnect ?? true,\n heartbeatInterval: options.heartbeatInterval ?? 30000,\n heartbeatTimeout: options.heartbeatTimeout ?? 5000,\n protocols: options.protocols,\n headers: options.headers,\n };\n this.reconnectManager = new ReconnectManager(options.reconnectOptions);\n this.WebSocketImpl = getWebSocketImpl();\n }\n\n /**\n * Connect to the WebSocket server\n */\n async connect(): Promise<void> {\n if (this.state === ConnectionState.Connected || this.state === ConnectionState.Connecting) {\n return;\n }\n\n this.manualClose = false;\n this.setState(ConnectionState.Connecting);\n\n return new Promise((resolve, reject) => {\n try {\n // Create WebSocket instance.\n // The third argument (upgrade headers) is only understood by the\n // Node.js `ws` package. Browsers do NOT simply ignore it - Chromium\n // fails the handshake when a third argument is passed - so only pass\n // it to non-browser implementations. Browser connections rely on the\n // server-side tenant resolution instead of upgrade headers.\n const isBrowserWebSocket =\n typeof WebSocket !== 'undefined' && this.WebSocketImpl === WebSocket;\n this.ws = (\n isBrowserWebSocket || !this.options.headers\n ? new (this.WebSocketImpl as any)(this.url, this.options.protocols)\n : new (this.WebSocketImpl as any)(this.url, this.options.protocols, {\n headers: this.options.headers,\n })\n ) as WebSocket;\n\n // Set binary type for MessagePack\n this.ws.binaryType = 'arraybuffer';\n\n // Handle open event\n this.ws.onopen = () => {\n this.setState(ConnectionState.Connected);\n this.reconnectManager.reset();\n this.startHeartbeat();\n resolve();\n };\n\n // Handle message event\n this.ws.onmessage = (event: MessageEvent) => {\n logger.debug(`WebSocket onmessage fired - data type: ${typeof event.data}, is ArrayBuffer: ${event.data instanceof ArrayBuffer}`);\n this.resetHeartbeat();\n const data = event.data as ArrayBuffer;\n logger.debug(`Received binary message - size: ${data.byteLength} bytes`);\n logger.debug(`Emitting 'message' event to client handlers`);\n this.emit('message', data);\n };\n\n // Handle error event\n this.ws.onerror = () => {\n const error = new Error('WebSocket error occurred');\n this.emit('error', error);\n if (this.state === ConnectionState.Connecting) {\n reject(error);\n }\n };\n\n // Handle close event\n this.ws.onclose = (event: CloseEvent) => {\n this.stopHeartbeat();\n this.emit('close', event);\n\n if (!this.manualClose && this.options.autoReconnect) {\n this.setState(ConnectionState.Reconnecting);\n this.reconnect();\n } else {\n this.setState(ConnectionState.Disconnected);\n }\n\n if (this.state === ConnectionState.Connecting) {\n reject(new Error(`Connection closed: ${event.reason || 'Unknown reason'}`));\n }\n };\n } catch (error) {\n this.setState(ConnectionState.Disconnected);\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from the WebSocket server\n */\n disconnect(): void {\n this.manualClose = true;\n this.reconnectManager.cancelReconnect();\n this.stopHeartbeat();\n\n if (this.ws) {\n this.ws.close(1000, 'Client disconnect');\n this.ws = undefined;\n }\n\n this.setState(ConnectionState.Closed);\n }\n\n /**\n * Send data through the WebSocket\n *\n * @param data - Data to send (Uint8Array for MessagePack)\n */\n send(data: Uint8Array): void {\n if (this.state !== ConnectionState.Connected || !this.ws) {\n throw new Error('Cannot send data: not connected');\n }\n\n // Check actual WebSocket readyState\n if (this.ws.readyState !== 1) { // WebSocket.OPEN = 1\n logger.error(`WebSocket not open - readyState: ${this.ws.readyState} (0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED)`);\n throw new Error(`Cannot send data: WebSocket not open (readyState: ${this.ws.readyState})`);\n }\n\n logger.debug(`Sending message - size: ${data.length} bytes, readyState: ${this.ws.readyState}`);\n\n try {\n this.ws.send(data);\n logger.debug(`Message sent successfully`);\n } catch (error) {\n logger.error(`Failed to send message:`, error);\n throw new Error(`Failed to send data: ${error}`);\n }\n }\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return this.state;\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.state === ConnectionState.Connected;\n }\n\n /**\n * Whether auto-reconnect is enabled for this connection.\n *\n * Used by the client to decide whether requests issued while the\n * connection is down can be queued (the connection will come back) or\n * should fail immediately.\n */\n isAutoReconnectEnabled(): boolean {\n return this.options.autoReconnect;\n }\n\n /**\n * Reconnect to the server\n */\n private reconnect(): void {\n const success = this.reconnectManager.scheduleReconnect(async () => {\n try {\n await this.connect();\n } catch (error) {\n // Error is already handled by connect()\n // Reconnection will be scheduled again if autoReconnect is enabled\n }\n });\n\n if (!success) {\n this.setState(ConnectionState.Disconnected);\n this.emit('error', new Error('Max reconnection attempts reached'));\n }\n }\n\n /**\n * Set connection state and emit event\n */\n private setState(state: ConnectionState): void {\n if (this.state !== state) {\n this.state = state;\n this.emit('stateChange', state);\n }\n }\n\n /**\n * Start heartbeat mechanism\n */\n private startHeartbeat(): void {\n if (this.options.heartbeatInterval <= 0) {\n return;\n }\n\n this.stopHeartbeat();\n\n // Note: WebSocket implementations (browser and ws library) automatically handle\n // ping/pong frames at the protocol level, so we don't need application-level heartbeat\n // that sends empty binary messages. The underlying WebSocket will keep the connection alive.\n }\n\n /**\n * Stop heartbeat mechanism\n */\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer);\n this.heartbeatTimeoutTimer = undefined;\n }\n }\n\n /**\n * Reset heartbeat timeout (called when message received)\n */\n private resetHeartbeat(): void {\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer);\n this.heartbeatTimeoutTimer = undefined;\n }\n }\n}\n","/**\n * Reconnection manager with exponential backoff\n */\n\n/**\n * Reconnection strategy options\n */\nexport interface ReconnectOptions {\n /** Initial delay in milliseconds (default: 1000) */\n initialDelay?: number;\n /** Maximum delay in milliseconds (default: 30000) */\n maxDelay?: number;\n /** Backoff multiplier (default: 2) */\n backoffMultiplier?: number;\n /** Maximum number of reconnection attempts (default: Infinity) */\n maxAttempts?: number;\n /** Add random jitter to delay (default: true). Jitter is +/- 25% of the computed delay. */\n jitter?: boolean;\n}\n\n/**\n * Reconnection manager with exponential backoff\n */\nexport class ReconnectManager {\n private currentDelay: number;\n private attempts = 0;\n private reconnectTimeout?: NodeJS.Timeout;\n private isReconnecting = false;\n\n private readonly initialDelay: number;\n private readonly maxDelay: number;\n private readonly backoffMultiplier: number;\n private readonly maxAttempts: number;\n private readonly jitter: boolean;\n\n constructor(options: ReconnectOptions = {}) {\n this.initialDelay = options.initialDelay ?? 1000;\n this.maxDelay = options.maxDelay ?? 30000;\n this.backoffMultiplier = options.backoffMultiplier ?? 2;\n this.maxAttempts = options.maxAttempts ?? Infinity;\n this.jitter = options.jitter ?? true;\n this.currentDelay = this.initialDelay;\n }\n\n /**\n * Schedule a reconnection attempt\n *\n * @param callback - Function to call when it's time to reconnect\n * @returns true if reconnection was scheduled, false if max attempts reached\n */\n scheduleReconnect(callback: () => void | Promise<void>): boolean {\n // Check if we've exceeded max attempts\n if (this.attempts >= this.maxAttempts) {\n return false;\n }\n\n // Clear any existing timeout\n this.cancelReconnect();\n\n this.isReconnecting = true;\n this.attempts++;\n\n // Apply jitter: +/- 25% of the computed delay\n let delay = this.currentDelay;\n if (this.jitter) {\n const jitterRange = delay * 0.25;\n delay += (Math.random() * 2 - 1) * jitterRange;\n delay = Math.max(0, Math.round(delay));\n }\n\n this.reconnectTimeout = setTimeout(async () => {\n try {\n await callback();\n // If successful, reset the delay\n this.reset();\n } catch (error) {\n // Increase delay for next attempt\n this.currentDelay = Math.min(\n this.currentDelay * this.backoffMultiplier,\n this.maxDelay\n );\n }\n }, delay);\n\n return true;\n }\n\n /**\n * Cancel any pending reconnection attempt\n */\n cancelReconnect(): void {\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = undefined;\n }\n this.isReconnecting = false;\n }\n\n /**\n * Reset the reconnection state (call after successful connection)\n */\n reset(): void {\n this.cancelReconnect();\n this.currentDelay = this.initialDelay;\n this.attempts = 0;\n }\n\n /**\n * Check if currently in reconnection mode\n */\n isActive(): boolean {\n return this.isReconnecting;\n }\n\n /**\n * Get the current number of reconnection attempts\n */\n getAttempts(): number {\n return this.attempts;\n }\n\n /**\n * Get the current delay in milliseconds\n */\n getCurrentDelay(): number {\n return this.currentDelay;\n }\n\n /**\n * Check if max attempts have been reached\n */\n hasReachedMaxAttempts(): boolean {\n return this.attempts >= this.maxAttempts;\n }\n}\n","/**\n * Configurable logger for RaisinDB client\n */\n\n/**\n * Log levels in order of verbosity (lower = less verbose)\n */\nexport enum LogLevel {\n Silent = 0,\n Error = 1,\n Warn = 2,\n Info = 3,\n Debug = 4,\n}\n\n/**\n * Logger configuration\n */\nexport interface LoggerConfig {\n /** Current log level (default: Error) */\n level: LogLevel;\n /** Prefix for all log messages (default: \"[RaisinDB]\") */\n prefix: string;\n}\n\n/**\n * Global logger configuration\n */\nlet config: LoggerConfig = {\n level: LogLevel.Error,\n prefix: '[RaisinDB]',\n};\n\n/**\n * Configure the logger\n *\n * @param options - Logger configuration options\n *\n * @example\n * ```typescript\n * import { configureLogger, LogLevel } from '@raisindb/client';\n *\n * // Enable debug logging\n * configureLogger({ level: LogLevel.Debug });\n *\n * // Disable all logging\n * configureLogger({ level: LogLevel.Silent });\n * ```\n */\nexport function configureLogger(options: Partial<LoggerConfig>): void {\n config = { ...config, ...options };\n}\n\n/**\n * Get current logger configuration\n */\nexport function getLoggerConfig(): LoggerConfig {\n return { ...config };\n}\n\n/**\n * Set log level\n *\n * @param level - Log level to set\n */\nexport function setLogLevel(level: LogLevel): void {\n config.level = level;\n}\n\n/**\n * Get current log level\n */\nexport function getLogLevel(): LogLevel {\n return config.level;\n}\n\n/**\n * Logger instance with level-aware logging methods\n */\nexport const logger = {\n /**\n * Log debug message (only shown when level >= Debug)\n */\n debug(...args: unknown[]): void {\n if (config.level >= LogLevel.Debug) {\n console.log(config.prefix, ...args);\n }\n },\n\n /**\n * Log info message (only shown when level >= Info)\n */\n info(...args: unknown[]): void {\n if (config.level >= LogLevel.Info) {\n console.log(config.prefix, ...args);\n }\n },\n\n /**\n * Log warning message (only shown when level >= Warn)\n */\n warn(...args: unknown[]): void {\n if (config.level >= LogLevel.Warn) {\n console.warn(config.prefix, ...args);\n }\n },\n\n /**\n * Log error message (only shown when level >= Error)\n */\n error(...args: unknown[]): void {\n if (config.level >= LogLevel.Error) {\n console.error(config.prefix, ...args);\n }\n },\n};\n\nif (typeof globalThis !== 'undefined') {\n (globalThis as any).__raisinDebug = (enable = true) => {\n setLogLevel(enable ? LogLevel.Debug : LogLevel.Error);\n console.log(`[RaisinDB] Debug logging ${enable ? 'ENABLED' : 'DISABLED'} (includes server handler logs)`);\n };\n}\n","import type { ReactLikeWithContext, ReactContext, RaisinContextValue, UseConnectionReturn } from './types';\nimport { ConnectionState } from '../../connection';\n\nexport function createUseConnection(\n react: ReactLikeWithContext,\n context: ReactContext<RaisinContextValue | null>,\n): () => UseConnectionReturn {\n return function useConnection(): UseConnectionReturn {\n const { useState, useEffect, useCallback, useContext } = react;\n\n const ctx = useContext(context);\n if (!ctx) {\n throw new Error('useConnection must be used within a <RaisinProvider>');\n }\n const { client } = ctx;\n\n const [state, setState] = useState<ConnectionState>(client.getConnectionState());\n const [isReady, setIsReady] = useState(client.isReady());\n\n useEffect(() => {\n const unsubConn = client.onConnectionStateChange((newState) => {\n setState(newState);\n });\n\n const unsubReady = client.onReadyStateChange((ready) => {\n setIsReady(ready);\n });\n\n return () => {\n unsubConn();\n unsubReady();\n };\n }, [client]);\n\n const connect = useCallback(async () => {\n await client.connect();\n }, [client]);\n\n const disconnect = useCallback(() => {\n client.disconnect();\n }, [client]);\n\n return {\n state,\n isConnected: state === ConnectionState.Connected,\n isReady,\n connect,\n disconnect,\n };\n };\n}\n","import type { ReactLikeWithContext, ReactContext, RaisinContextValue, UseSqlOptions, UseSqlReturn } from './types';\nimport type { SqlResult } from '../../protocol';\n\nfunction rowsToObjects<T>(result: SqlResult): T[] {\n return result.rows as T[];\n}\n\nexport function createUseSql(\n react: ReactLikeWithContext,\n context: ReactContext<RaisinContextValue | null>,\n): <T = Record<string, unknown>>(sql: string, params?: unknown[], options?: UseSqlOptions) => UseSqlReturn<T> {\n return function useSql<T = Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n options?: UseSqlOptions,\n ): UseSqlReturn<T> {\n const { useState, useEffect, useRef, useCallback, useContext } = react;\n\n const ctx = useContext(context);\n if (!ctx) {\n throw new Error('useSql must be used within a <RaisinProvider>');\n }\n const { client, repository: defaultRepo } = ctx;\n\n const [data, setData] = useState<T[] | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false;\n const repo = options?.repository ?? defaultRepo;\n const refetchOnReconnect = options?.refetchOnReconnect !== false;\n const realtime = options?.realtime;\n\n // Serialize params for dependency comparison\n const paramsKey = params ? JSON.stringify(params) : '';\n\n const fetchData = useCallback(async () => {\n if (!repo) return;\n setIsLoading(true);\n setError(null);\n try {\n const db = client.database(repo);\n const result = await db.executeSql(sql, params);\n if (mountedRef.current) {\n setData(rowsToObjects<T>(result));\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n if (mountedRef.current) {\n setIsLoading(false);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, repo, sql, paramsKey]);\n\n // Initial fetch and re-fetch on query/params change\n useEffect(() => {\n mountedRef.current = true;\n if (enabled) {\n fetchData();\n }\n return () => {\n mountedRef.current = false;\n };\n }, [enabled, fetchData]);\n\n // Refetch on reconnect\n useEffect(() => {\n if (!enabled || !refetchOnReconnect) return;\n\n const unsub = client.onReconnected(() => {\n fetchData();\n });\n return unsub;\n }, [client, enabled, refetchOnReconnect, fetchData]);\n\n // Realtime subscription: refetch on matching events\n useEffect(() => {\n if (!enabled || !realtime || !repo) return;\n\n const db = client.database(repo);\n let unsubscribe: (() => Promise<void>) | undefined;\n\n db.events().subscribe(\n {\n workspace: realtime.workspace,\n event_types: realtime.eventTypes,\n path: realtime.path,\n node_type: realtime.nodeType,\n },\n () => {\n fetchData();\n },\n ).then((sub) => {\n unsubscribe = () => sub.unsubscribe();\n });\n\n return () => {\n unsubscribe?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, enabled, repo, realtime?.workspace, realtime?.path, realtime?.nodeType, fetchData]);\n\n return {\n data,\n isLoading,\n error,\n refetch: fetchData,\n };\n };\n}\n","import type { ReactLikeWithContext, ReactContext, RaisinContextValue, UseSubscriptionOptions } from './types';\nimport type { EventMessage } from '../../protocol';\n\nexport function createUseSubscription(\n react: ReactLikeWithContext,\n context: ReactContext<RaisinContextValue | null>,\n): (options: UseSubscriptionOptions, callback: (event: EventMessage) => void) => void {\n return function useSubscription(\n options: UseSubscriptionOptions,\n callback: (event: EventMessage) => void,\n ): void {\n const { useEffect, useRef, useContext } = react;\n\n const ctx = useContext(context);\n if (!ctx) {\n throw new Error('useSubscription must be used within a <RaisinProvider>');\n }\n const { client, repository } = ctx;\n\n // callbackRef pattern: avoid re-subscribing when callback identity changes\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n const enabled = options.enabled !== false;\n\n useEffect(() => {\n if (!enabled || !repository || !options.workspace) return;\n\n const db = client.database(repository);\n let unsubscribe: (() => Promise<void>) | undefined;\n let cancelled = false;\n\n db.events().subscribe(\n {\n workspace: options.workspace,\n event_types: options.event_types,\n path: options.path,\n node_type: options.node_type,\n include_node: options.include_node,\n },\n (event) => {\n if (!cancelled) {\n callbackRef.current(event);\n }\n },\n ).then((sub) => {\n if (cancelled) {\n sub.unsubscribe();\n } else {\n unsubscribe = () => sub.unsubscribe();\n }\n });\n\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, repository, enabled, options.workspace, options.path, options.node_type, options.include_node]);\n };\n}\n","/**\n * Plan projection utility.\n *\n * Builds deterministic plan/task state from persisted chat messages.\n */\n\nimport type { ChatMessage } from '../types/chat';\n\nexport interface PlanProjectionTask {\n taskId?: string;\n title: string;\n status: string;\n description?: string;\n priority?: string;\n delegation?: {\n id?: string;\n status: string;\n agentRef?: string;\n flowInstanceId?: string;\n branch?: string;\n baseBranch?: string;\n result?: unknown;\n error?: string;\n };\n}\n\nexport interface PlanProjection {\n key: string;\n planPath?: string;\n planId?: string;\n title: string;\n status: string;\n requiresApproval: boolean;\n tasks: PlanProjectionTask[];\n sourceMessagePath?: string;\n updatedAt?: string;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n return value as Record<string, unknown>;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nexport function parsePlanTasks(rawTasks: unknown): PlanProjectionTask[] {\n if (!Array.isArray(rawTasks)) return [];\n return rawTasks\n // The return type is ANNOTATED, not inferred. `satisfies` on each branch\n // checks that branch but does not widen it, so without this the array is a\n // union of two object literals plus null — and `PlanProjectionTask` is not\n // assignable to that union (`taskId` is optional in the interface and\n // required in the inferred literal), which makes the type predicate below\n // illegal and fails the DTS build for the whole package.\n .map((rawTask, index): PlanProjectionTask | null => {\n if (typeof rawTask === 'string') {\n return {\n title: rawTask,\n status: 'pending',\n } satisfies PlanProjectionTask;\n }\n const task = asRecord(rawTask);\n if (!task) return null;\n const delegation = asRecord(task.delegation);\n return {\n taskId: asString(task.task_id) ?? asString(task.id),\n title: asString(task.title) ?? `Task ${index + 1}`,\n status: asString(task.status) ?? 'pending',\n description: asString(task.description),\n priority: asString(task.priority),\n delegation: delegation ? {\n id: asString(delegation.id),\n status: asString(delegation.status) ?? 'unknown',\n agentRef: asString(delegation.agent_ref) ?? asString(delegation.agentRef),\n flowInstanceId: asString(delegation.flow_instance_id) ?? asString(delegation.flowInstanceId),\n branch: asString(delegation.branch),\n baseBranch: asString(delegation.base_branch) ?? asString(delegation.baseBranch),\n result: delegation.result,\n error: asString(delegation.error),\n } : undefined,\n } satisfies PlanProjectionTask;\n })\n .filter((task): task is PlanProjectionTask => task !== null);\n}\n\nconst TERMINAL_TASK_STATUSES = new Set(['completed', 'cancelled']);\nconst TERMINAL_PLAN_STATUSES = new Set(['completed', 'failed', 'cancelled']);\n\n/**\n * Same race as upsertTask: an ai_task_update message carrying a stale\n * plan_status (e.g. \"in_progress\", computed before the final task's\n * completion count caught up) can be persisted and thus replayed here after\n * the message that actually reflects the terminal plan status. Guard it the\n * same way - never regress out of a terminal plan status.\n */\nfunction setPlanStatus(plan: PlanProjection, nextStatus: string | undefined): void {\n if (!nextStatus) return;\n if (TERMINAL_PLAN_STATUSES.has(plan.status) && !TERMINAL_PLAN_STATUSES.has(nextStatus)) {\n return;\n }\n plan.status = nextStatus;\n}\n\nexport function upsertTask(plan: PlanProjection, nextTask: PlanProjectionTask): void {\n const existingIndex = plan.tasks.findIndex((task) =>\n (nextTask.taskId && task.taskId === nextTask.taskId) ||\n (!nextTask.taskId && task.title === nextTask.title)\n );\n if (existingIndex === -1) {\n plan.tasks.push(nextTask);\n return;\n }\n const existing = plan.tasks[existingIndex];\n // Mirrors the server-side monotonic guard in update-task/index.js: tool\n // calls from one model turn can execute in parallel, so an ai_task_update\n // message for an earlier \"in_progress\" transition can be persisted (and\n // thus replayed here) after the \"completed\" message for the same task.\n // Never let that regress an already-terminal status.\n if (TERMINAL_TASK_STATUSES.has(existing.status) && !TERMINAL_TASK_STATUSES.has(nextTask.status)) {\n return;\n }\n plan.tasks[existingIndex] = {\n ...existing,\n ...nextTask,\n };\n}\n\nexport function ensurePlan(\n plans: Map<string, PlanProjection>,\n key: string,\n fallbackTitle: string,\n timestamp: string | undefined,\n): PlanProjection {\n const existing = plans.get(key);\n if (existing) {\n if (timestamp) existing.updatedAt = timestamp;\n return existing;\n }\n const created: PlanProjection = {\n key,\n title: fallbackTitle,\n status: 'pending_approval',\n requiresApproval: true,\n tasks: [],\n updatedAt: timestamp,\n };\n plans.set(key, created);\n return created;\n}\n\n/**\n * Build deterministic plan/task state from persisted chat messages.\n * Uses ai_plan as canonical plan source and ai_task_update for incremental task status changes.\n */\nexport function projectPlansFromMessages(messages: ChatMessage[]): PlanProjection[] {\n const ordered = [...messages].sort((a, b) => {\n const aTime = Date.parse(a.timestamp || '');\n const bTime = Date.parse(b.timestamp || '');\n return (Number.isNaN(aTime) ? 0 : aTime) - (Number.isNaN(bTime) ? 0 : bTime);\n });\n\n const plans = new Map<string, PlanProjection>();\n\n for (const message of ordered) {\n const timestamp = message.timestamp;\n const data = asRecord(message.data);\n\n if (message.messageType === 'ai_plan' && data) {\n const planPath = asString(data.plan_path);\n const planId = asString(data.plan_id);\n const key = planPath || planId || message.path || message.id || `plan:${timestamp}`;\n const plan = ensurePlan(\n plans,\n key,\n asString(data.title) || message.content || 'Plan',\n timestamp\n );\n\n plan.planPath = planPath ?? plan.planPath;\n plan.planId = planId ?? plan.planId;\n plan.title = asString(data.title) || plan.title;\n setPlanStatus(plan, asString(data.status));\n plan.requiresApproval = Boolean(data.requires_approval) || plan.status === 'pending_approval';\n plan.sourceMessagePath = message.path ?? plan.sourceMessagePath;\n\n const parsedTasks = parsePlanTasks(data.tasks);\n if (parsedTasks.length > 0) {\n plan.tasks = parsedTasks;\n }\n }\n\n if (message.messageType === 'ai_task_update' && data) {\n const planPath = asString(data.plan_path);\n const planId = asString(data.plan_id);\n const key = planPath || planId;\n if (!key) continue;\n\n const plan = ensurePlan(plans, key, 'Plan', timestamp);\n plan.planPath = planPath ?? plan.planPath;\n plan.planId = planId ?? plan.planId;\n setPlanStatus(plan, asString(data.plan_status));\n plan.requiresApproval = plan.status === 'pending_approval';\n\n const taskUpdate: PlanProjectionTask = {\n taskId: asString(data.task_id),\n title: asString(data.task_title) || asString(data.title) || 'Task',\n status: asString(data.status) || 'pending',\n };\n upsertTask(plan, taskUpdate);\n }\n\n if (Array.isArray(message.children)) {\n for (const child of message.children) {\n if (child.type !== 'plan') continue;\n const key = child.path || message.path || message.id || `plan-child:${child.id}:${timestamp}`;\n const plan = ensurePlan(plans, key, child.planTitle || message.content || 'Plan', timestamp);\n plan.title = child.planTitle || plan.title;\n plan.status = child.status || plan.status;\n plan.requiresApproval = plan.status === 'pending_approval';\n if (Array.isArray(child.tasks) && child.tasks.length > 0) {\n plan.tasks = child.tasks.map((task) => ({\n taskId: task.id,\n title: task.title,\n status: task.status,\n delegation: task.delegation ? {\n id: task.delegation.id,\n status: task.delegation.status,\n agentRef: task.delegation.agentRef,\n flowInstanceId: task.delegation.flowInstanceId,\n branch: task.delegation.branch,\n baseBranch: task.delegation.baseBranch,\n result: task.delegation.result,\n error: task.delegation.error,\n } : undefined,\n }));\n }\n }\n }\n }\n\n // Belt-and-suspenders: if every task landed in a terminal state but the\n // plan's own status field is stuck non-terminal (the same message-race\n // that setPlanStatus guards against can also leave it simply never\n // updated), derive completion from the tasks - mirrors the server's\n // auto-complete check in update-task/index.js's updatePlanProgress.\n for (const plan of plans.values()) {\n if (\n !TERMINAL_PLAN_STATUSES.has(plan.status) &&\n plan.tasks.length > 0 &&\n plan.tasks.every((task) => TERMINAL_TASK_STATUSES.has(task.status))\n ) {\n plan.status = 'completed';\n plan.requiresApproval = false;\n }\n }\n\n return [...plans.values()].sort((a, b) => {\n const aTime = Date.parse(a.updatedAt || '');\n const bTime = Date.parse(b.updatedAt || '');\n return (Number.isNaN(bTime) ? 0 : bTime) - (Number.isNaN(aTime) ? 0 : aTime);\n });\n}\n","import type { ChatDoneEvent, ChatEvent } from '../types/chat';\n\n/**\n * Detect safety-net done events emitted by backend recovery paths.\n *\n * These events can appear while async tool calls are still running.\n * They carry `recovered: true` and an empty content payload.\n */\nexport function isRecoveredDoneEvent(event: ChatEvent): event is ChatDoneEvent & { recovered: true } {\n if (event.type !== 'done') return false;\n const done = event as ChatDoneEvent & { recovered?: unknown };\n if (done.recovered !== true) return false;\n return typeof done.content !== 'string' || done.content.trim().length === 0;\n}\n\n","/**\n * Framework-agnostic conversation store for RaisinDB.\n *\n * Manages the full conversation lifecycle: creating conversations, sending\n * messages, streaming responses, persistent SSE subscriptions, message\n * history, tool call tracking, and plan projection. Notifies subscribers\n * on every state change so UI frameworks (React, Svelte, Vue) can bind.\n *\n * @example Svelte 5:\n * ```typescript\n * const store = new ConversationStore({ database: db, conversationPath: '/...' });\n * let snapshot = $state(store.getSnapshot());\n * store.subscribe(s => { snapshot = s; });\n * ```\n *\n * @example React (via useConversation hook):\n * ```tsx\n * const chat = useConversation(React, { database: db, conversationPath: '/...' });\n * ```\n */\n\nimport type { ConversationManager, ConversationSubscription } from '../conversations';\nimport type { Database } from '../database';\nimport type { ChatMessage, ChatEvent, ChatLogEvent } from '../types/chat';\nimport { logger, getLogLevel, LogLevel } from '../logger';\nimport { projectPlansFromMessages, type PlanProjection } from '../utils/plan-projection';\nimport { isRecoveredDoneEvent } from '../utils/chat-events';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Info about an in-flight tool call */\nexport interface ToolCallInfo {\n id: string;\n functionName: string;\n arguments: unknown;\n status: 'running' | 'completed' | 'failed';\n result?: unknown;\n durationMs?: number;\n}\n\n/** Snapshot of the conversation store state */\nexport interface ConversationStoreSnapshot {\n /** The conversation object, if loaded */\n conversation: { conversationPath: string; type: string } | null;\n /** All messages in the conversation */\n messages: ChatMessage[];\n /** Whether the AI is currently generating a response */\n isStreaming: boolean;\n /** Whether the chat is waiting for user input */\n isWaiting: boolean;\n /** Accumulated text from the current streaming response */\n streamingText: string;\n /** Current error, if any */\n error: string | null;\n /** Tracks in-flight tool executions */\n activeToolCalls: ToolCallInfo[];\n /** Deterministic plan/task projection from persisted messages */\n plans: PlanProjection[];\n /** Whether messages are being loaded */\n isLoading: boolean;\n /** Path to the conversation node */\n conversationPath: string | null;\n}\n\n/** Options for creating a ConversationStore */\nexport interface ConversationStoreOptions {\n /** Pre-configured Database instance */\n database: Database;\n /** Resume an existing conversation */\n conversationPath?: string;\n /** Create a new conversation on first message */\n createOptions?: {\n participant: string;\n input?: Record<string, unknown>;\n };\n /** Callback for individual chat events */\n onEvent?: (event: ChatEvent) => void;\n /** Streaming inactivity timeout in ms (default: 120000). Auto-recovers when no SSE event arrives within this window. */\n streamingTimeoutMs?: number;\n /** Activity watchdog polling interval in ms (default: 30000). Periodically checks backend turn health while streaming. */\n watchdogIntervalMs?: number;\n}\n\ntype Subscriber = (snapshot: ConversationStoreSnapshot) => void;\n\n// ---------------------------------------------------------------------------\n// ConversationStore\n// ---------------------------------------------------------------------------\n\n/**\n * Framework-agnostic store managing a single conversation's state.\n *\n * Handles the full lifecycle: creating conversations, sending messages,\n * streaming responses, tool call tracking, plan projection, and deferred\n * message reload on turn completion.\n */\nexport class ConversationStore {\n private manager: ConversationManager;\n private subscribers = new Set<Subscriber>();\n private onEvent?: (event: ChatEvent) => void;\n private createOptions?: { participant: string; input?: Record<string, unknown> };\n\n // Persistent SSE subscription\n private subscription: ConversationSubscription | null = null;\n\n // Internal mutable state\n private _conversationPath: string | null;\n private _messages: ChatMessage[] = [];\n private _isStreaming = false;\n private _isWaiting = false;\n private _streamingText = '';\n private _error: string | null = null;\n private _activeToolCalls: ToolCallInfo[] = [];\n private _isLoading = false;\n private _conversationType: string | null = null;\n\n // Streaming timeout (Fix A)\n private _streamingTimeoutMs: number;\n private _streamingTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Activity watchdog (Fix B)\n private _watchdogIntervalMs: number;\n private _watchdogTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: ConversationStoreOptions) {\n this.manager = options.database.conversations;\n this._conversationPath = options.conversationPath ?? null;\n this.createOptions = options.createOptions;\n this.onEvent = options.onEvent;\n this._streamingTimeoutMs = options.streamingTimeoutMs ?? 120_000;\n this._watchdogIntervalMs = options.watchdogIntervalMs ?? 30_000;\n\n // Auto-subscribe to existing conversation\n if (this._conversationPath) {\n this.ensureSubscription();\n }\n }\n\n // ==========================================================================\n // Subscription\n // ==========================================================================\n\n /**\n * Subscribe to state changes.\n * Returns an unsubscribe function.\n */\n subscribe(callback: Subscriber): () => void {\n this.subscribers.add(callback);\n callback(this.getSnapshot());\n return () => { this.subscribers.delete(callback); };\n }\n\n /**\n * Get the current state snapshot.\n */\n getSnapshot(): ConversationStoreSnapshot {\n return {\n conversation: this._conversationPath\n ? { conversationPath: this._conversationPath, type: this._conversationType || 'ai_chat' }\n : null,\n messages: [...this._messages],\n isStreaming: this._isStreaming,\n isWaiting: this._isWaiting,\n streamingText: this._streamingText,\n error: this._error,\n activeToolCalls: [...this._activeToolCalls],\n plans: projectPlansFromMessages(this._messages),\n isLoading: this._isLoading,\n conversationPath: this._conversationPath,\n };\n }\n\n // ==========================================================================\n // Actions\n // ==========================================================================\n\n /**\n * Send a user message and stream the AI response.\n *\n * If no conversation exists yet and `createOptions` was provided,\n * the conversation is created automatically on the first call.\n */\n async sendMessage(content: string): Promise<void> {\n if (this._isStreaming) {\n logger.warn('[ConversationStore] Cannot send message while streaming');\n return;\n }\n\n logger.info('[ConversationStore] Sending message', { path: this._conversationPath });\n this._error = null;\n\n // Create conversation if needed\n if (!this._conversationPath) {\n if (!this.createOptions) {\n throw new Error('No conversationPath and no createOptions provided');\n }\n try {\n const convo = await this.manager.create({\n participant: this.createOptions.participant,\n input: this.createOptions.input,\n });\n this._conversationPath = convo.conversationPath;\n this._conversationType = convo.type;\n\n for (const event of convo.initialEvents ?? []) {\n this.onEvent?.(event);\n this.handleEvent(event);\n }\n\n this.notify();\n } catch (err) {\n this._error = err instanceof Error ? err.message : String(err);\n this.notify();\n return;\n }\n }\n\n // Add user message optimistically\n const userMessage: ChatMessage = {\n role: 'user',\n content,\n timestamp: new Date().toISOString(),\n };\n this._messages.push(userMessage);\n\n // Start streaming\n this._isStreaming = true;\n this._isWaiting = false;\n this._streamingText = '';\n this._activeToolCalls = [];\n this.resetStreamingTimer();\n this.startWatchdog();\n this.notify();\n\n // Use persistent SSE subscription + create message node\n try {\n this.ensureSubscription();\n await this.subscription!.waitUntilConnected();\n await this.manager.createUserMessage(this._conversationPath!, content);\n // Events arrive via the persistent subscription → handleEvent\n } catch (err) {\n logger.error('[ConversationStore] Send failed', err);\n this._error = err instanceof Error ? err.message : String(err);\n this._isStreaming = false;\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n }\n }\n\n /**\n * Load messages from the persisted node tree.\n * Useful for loading history on page reload.\n */\n async loadMessages(): Promise<ChatMessage[]> {\n if (!this._conversationPath) return [];\n this._isLoading = true;\n this.notify();\n try {\n const messages = await this.manager.getMessages(this._conversationPath);\n logger.debug(`[ConversationStore] Loaded ${messages.length} messages`);\n if (messages.length > 0) {\n this._messages = messages;\n // Rebuild tool call state from messages\n this._activeToolCalls = [];\n }\n return messages;\n } finally {\n this._isLoading = false;\n this.notify();\n }\n }\n\n /**\n * Approve a pending plan.\n */\n async approvePlan(planPath: string): Promise<void> {\n await this.manager.approvePlan(planPath);\n }\n\n /**\n * Reject a pending plan.\n */\n async rejectPlan(planPath: string, feedback?: string): Promise<void> {\n await this.manager.rejectPlan(planPath, feedback);\n }\n\n /**\n * Mark a single message as read.\n */\n async markMessageAsRead(messagePath: string): Promise<void> {\n await this.manager.markMessageAsRead(messagePath);\n }\n\n /**\n * Stop the current streaming response.\n */\n stop(): void {\n this._isStreaming = false;\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n }\n\n /**\n * Clean up the store and release resources.\n */\n destroy(): void {\n this.stop();\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.subscription?.unsubscribe();\n this.subscription = null;\n this.subscribers.clear();\n }\n\n /**\n * Get the conversation path.\n */\n getConversationPath(): string | null {\n return this._conversationPath;\n }\n\n // ==========================================================================\n // Internal\n // ==========================================================================\n\n private ensureSubscription(): void {\n if (this.subscription) return;\n if (!this._conversationPath) return;\n\n this.subscription = this.manager.subscribe(\n this._conversationPath,\n (event) => {\n this.onEvent?.(event);\n this.handleEvent(event);\n },\n );\n }\n\n private handleEvent(event: ChatEvent): void {\n logger.debug('[ConversationStore] SSE:', event.type);\n // Every SSE event resets the streaming inactivity timer\n if (this._isStreaming) {\n this.resetStreamingTimer();\n }\n switch (event.type) {\n case 'text_chunk':\n this.markStreamingActivity();\n this._streamingText += event.text;\n this.notify();\n break;\n\n case 'thought_chunk':\n this.markStreamingActivity();\n this.notify();\n break;\n\n case 'assistant_message':\n this._messages.push(event.message);\n this._streamingText = '';\n this.notify();\n break;\n\n case 'tool_call_started': {\n this.markStreamingActivity();\n logger.debug('[ConversationStore] Tool started:', event.functionName);\n const info: ToolCallInfo = {\n id: event.toolCallId,\n functionName: event.functionName,\n arguments: event.arguments,\n status: 'running',\n };\n this._activeToolCalls.push(info);\n this.notify();\n break;\n }\n\n case 'tool_call_completed': {\n this.markStreamingActivity();\n logger.debug('[ConversationStore] Tool completed:', event.toolCallId);\n const idx = this._activeToolCalls.findIndex(tc => tc.id === event.toolCallId);\n if (idx >= 0) {\n this._activeToolCalls[idx] = {\n ...this._activeToolCalls[idx],\n status: event.error ? 'failed' : 'completed',\n result: event.result,\n durationMs: event.durationMs,\n };\n }\n this.notify();\n break;\n }\n\n case 'waiting': {\n logger.info('[ConversationStore] Waiting for input');\n this.flushStreamingText(event.timestamp);\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n // Deferred reload: pick up canonical messages from node tree\n this.deferredReload();\n break;\n }\n\n case 'done': {\n if (isRecoveredDoneEvent(event)) {\n logger.debug('[ConversationStore] Ignoring recovered done event');\n this.reconcileRecoveredDone();\n break;\n }\n const doneEvent = event as import('../types/chat').ChatDoneEvent;\n if (doneEvent.dispatchPhase && doneEvent.dispatchPhase !== 'terminal') {\n logger.debug('[ConversationStore] Ignoring non-terminal done event');\n break;\n }\n logger.info('[ConversationStore] Turn done');\n this.flushStreamingText(event.timestamp);\n // Safety net: if streaming missed chunks, use the complete response from the event\n if (doneEvent.content && !this.lastMessageIsAssistant()) {\n this._messages.push({\n role: (doneEvent.role as ChatMessage['role']) || 'assistant',\n content: doneEvent.content,\n timestamp: event.timestamp,\n senderDisplayName: doneEvent.senderDisplayName,\n finishReason: doneEvent.finishReason,\n });\n }\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n break;\n }\n\n case 'completed': {\n this.flushStreamingText(event.timestamp);\n if (event.messages && event.messages.length > 0) {\n this._messages = event.messages;\n }\n this._isStreaming = false;\n this._isWaiting = false;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n break;\n }\n\n case 'failed':\n logger.error('[ConversationStore] Turn failed', event.error);\n this._error = event.error;\n this._isStreaming = false;\n // A failed turn ends any waiting state too - otherwise UIs keyed on\n // isWaiting show a thinking indicator forever after a failure.\n this._isWaiting = false;\n this._streamingText = '';\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n break;\n\n case 'conversation_created':\n this._conversationPath = event.conversationPath;\n this.notify();\n break;\n\n case 'message_saved':\n // A message was persisted — reload to pick it up, then notify\n this.silentReload().catch(() => {});\n this.notify();\n break;\n\n case 'message_delivered':\n this._messages.push(event.message);\n this.notify();\n break;\n\n case 'log': {\n const logEvent = event as ChatLogEvent;\n const currentLevel = getLogLevel();\n const levelMap: Record<string, LogLevel> = {\n debug: LogLevel.Debug,\n info: LogLevel.Info,\n warn: LogLevel.Warn,\n error: LogLevel.Error,\n };\n const eventLevel = levelMap[logEvent.level] ?? LogLevel.Debug;\n if (currentLevel >= eventLevel) {\n const fn = logEvent.level === 'error' ? console.error\n : logEvent.level === 'warn' ? console.warn\n : logEvent.level === 'debug' ? console.debug\n : console.info;\n fn('[server]', logEvent.message);\n }\n break;\n }\n }\n }\n\n /**\n * Agent activity observed while the store thought the turn was over.\n *\n * This happens when execution continues WITHOUT a user message — plan\n * approval in approve_then_auto / step_by_step (approvePlan() returns an\n * enqueue receipt and the agent starts executing), or agent-initiated\n * follow-ups. The events arrive over the persistent live subscription\n * (no history replay), so activity is always \"now\": re-enter streaming\n * state so UIs show progress and the end-of-turn handling (done/waiting\n * → deferred reload) runs again for this continuation.\n */\n private markStreamingActivity(): void {\n if (this._isStreaming) return;\n logger.debug('[ConversationStore] Agent activity while idle — re-entering streaming state');\n this._isStreaming = true;\n this._isWaiting = false;\n this._streamingText = '';\n this.resetStreamingTimer();\n this.startWatchdog();\n }\n\n /**\n * If accumulated streaming text hasn't been captured in an assistant_message,\n * synthesize one.\n */\n private flushStreamingText(timestamp: string): void {\n if (this._streamingText && !this.lastMessageIsAssistant()) {\n this._messages.push({\n role: 'assistant',\n content: this._streamingText,\n timestamp,\n });\n }\n this._streamingText = '';\n }\n\n private lastMessageIsAssistant(): boolean {\n const last = this._messages[this._messages.length - 1];\n return last?.role === 'assistant';\n }\n\n /**\n * After a turn completes, reload canonical messages from the node tree\n * to catch any events missed during SSE gaps.\n */\n private deferredReload(): void {\n if (!this._conversationPath) return;\n logger.debug('[ConversationStore] Scheduling deferred reload');\n const attemptReload = (delay: number, retriesLeft: number) => {\n setTimeout(async () => {\n try {\n const prevCount = this._messages.length;\n await this.silentReload();\n // If no new messages appeared and retries remain, try again with exponential backoff\n if (this._messages.length === prevCount && retriesLeft > 0) {\n attemptReload(delay * 2, retriesLeft - 1);\n }\n } catch {}\n }, delay);\n };\n attemptReload(500, 2); // 500ms, 1000ms, 2000ms\n }\n\n private async silentReload(): Promise<void> {\n if (!this._conversationPath) return;\n try {\n const fresh = await this.manager.getMessages(this._conversationPath);\n if (fresh.length === 0) return;\n\n // Try incremental merge: match existing messages by role+content\n // to preserve object references (prevents Svelte DOM recreation)\n let matchCount = 0;\n const limit = Math.min(this._messages.length, fresh.length);\n for (let i = 0; i < limit; i++) {\n if (this._messages[i].role !== fresh[i].role ||\n this._messages[i].content !== fresh[i].content) break;\n matchCount++;\n }\n\n if (matchCount === this._messages.length && fresh.length === matchCount) {\n // Same content, possibly different metadata — update silently\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n if (f.finishReason) e.finishReason = f.finishReason;\n }\n await this.rebuildToolCallState();\n return; // No visible change — don't notify\n }\n\n if (matchCount === this._messages.length && fresh.length > matchCount) {\n // Existing messages match prefix — append new ones\n for (let i = matchCount; i < fresh.length; i++) {\n this._messages.push(fresh[i]);\n }\n // Also update metadata on matched messages\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n }\n await this.rebuildToolCallState();\n this.notify(); // New messages to show\n return;\n }\n\n // Fresh is a prefix subset — outbox delivery pending, keep extras\n if (matchCount === fresh.length && fresh.length < this._messages.length) {\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n }\n await this.rebuildToolCallState();\n return; // Keep streaming-flushed extras — message_saved will trigger next reload\n }\n\n // Content mismatch — full replace\n this._messages = fresh;\n await this.rebuildToolCallState();\n this.notify();\n } catch (err) {\n logger.debug('[ConversationStore] Silent reload failed:', err);\n }\n }\n\n // ==========================================================================\n // Streaming timeout (Fix A) — auto-recover on SSE inactivity\n // ==========================================================================\n\n private resetStreamingTimer(): void {\n this.clearStreamingTimer();\n if (!this._isStreaming) return;\n this._streamingTimer = setTimeout(() => {\n if (this._isStreaming) {\n logger.warn('[ConversationStore] Streaming timeout — no SSE activity for', this._streamingTimeoutMs, 'ms');\n this.recoverFromHang();\n }\n }, this._streamingTimeoutMs);\n }\n\n private clearStreamingTimer(): void {\n if (this._streamingTimer) {\n clearTimeout(this._streamingTimer);\n this._streamingTimer = null;\n }\n }\n\n // ==========================================================================\n // Activity watchdog (Fix B) — periodic backend health check\n // ==========================================================================\n\n private startWatchdog(): void {\n this.stopWatchdog();\n if (!this._conversationPath) return;\n this._watchdogTimer = setInterval(async () => {\n if (!this._isStreaming || !this._conversationPath) {\n this.stopWatchdog();\n return;\n }\n try {\n const health = await this.manager.checkTurnHealth(this._conversationPath);\n if (health === 'done' && this._isStreaming) {\n logger.warn('[ConversationStore] Watchdog detected completed turn while still streaming — recovering');\n this.recoverFromHang();\n }\n } catch {\n // Ignore watchdog errors — it's a best-effort check\n }\n }, this._watchdogIntervalMs);\n }\n\n private stopWatchdog(): void {\n if (this._watchdogTimer) {\n clearInterval(this._watchdogTimer);\n this._watchdogTimer = null;\n }\n }\n\n // ==========================================================================\n // Hang recovery (shared by timeout + watchdog)\n // ==========================================================================\n\n private recoverFromHang(): void {\n logger.info('[ConversationStore] Recovering from hang');\n this.clearStreamingTimer();\n this.stopWatchdog();\n this._isStreaming = false;\n this._isWaiting = true;\n this._streamingText = '';\n this.notify();\n // Reload to pick up any persisted state we missed\n this.silentReload().catch(() => {});\n }\n\n /**\n * Reconcile a recovered done event against persisted state.\n * If the backend turn is actually complete, finalize the local state.\n * Otherwise keep streaming active and let normal events continue.\n */\n private reconcileRecoveredDone(): void {\n if (!this._conversationPath) return;\n void this.manager.checkTurnHealth(this._conversationPath).then((health) => {\n if (health !== 'done') return;\n if (!this._isStreaming) return;\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n }).catch(() => {\n // Best effort only; streaming watchdog still protects against hangs.\n });\n }\n\n // ==========================================================================\n // Tool call state rebuild (Fix C) — reconstruct from node tree on reload\n // ==========================================================================\n\n private async rebuildToolCallState(): Promise<void> {\n if (!this._conversationPath) {\n this._activeToolCalls = [];\n return;\n }\n try {\n const pendingCalls = await this.manager.getActiveToolCalls(this._conversationPath);\n if (pendingCalls.length > 0) {\n this._activeToolCalls = pendingCalls.map(tc => ({\n id: tc.id,\n functionName: tc.name,\n arguments: undefined,\n status: 'running' as const,\n }));\n // Keep streaming active if there are pending tool calls\n if (!this._isStreaming) {\n this._isStreaming = true;\n this.resetStreamingTimer();\n this.startWatchdog();\n }\n } else {\n this._activeToolCalls = [];\n // No pending tools — check if we should clear streaming\n if (this._isStreaming) {\n const lastMsg = this._messages[this._messages.length - 1];\n const isLastAssistantDone = lastMsg?.role === 'assistant' &&\n (lastMsg.dispatchPhase\n ? lastMsg.dispatchPhase === 'terminal'\n : (!!lastMsg.finishReason && lastMsg.finishReason !== 'tool_calls'));\n if (isLastAssistantDone) {\n this._isStreaming = false;\n this._isWaiting = true;\n this.clearStreamingTimer();\n this.stopWatchdog();\n }\n }\n }\n } catch {\n this._activeToolCalls = [];\n }\n }\n\n private notify(): void {\n const snapshot = this.getSnapshot();\n for (const sub of this.subscribers) {\n try { sub(snapshot); }\n catch (err) { logger.error('Error in conversation store subscriber:', err); }\n }\n }\n}\n","/**\n * User home path normalization.\n *\n * Some server responses (JWT claims, RAISIN_CURRENT_USER(), identity\n * endpoints) return the user home as a workspace-prefixed path like\n * `/raisin:access_control/users/internal/alice`, while others return the\n * workspace-relative form `/users/internal/alice`. SDK code that builds\n * child paths (inbox, chats, outbox) expects the workspace-relative form.\n */\n\n/** Workspace prefix that may leak into user home paths */\nconst ACCESS_CONTROL_PREFIX = '/raisin:access_control';\n\n/**\n * Normalize a user home path to its workspace-relative form.\n *\n * Strips a leading `/raisin:access_control` workspace prefix and trailing\n * slashes, so `/raisin:access_control/users/internal/alice/` becomes\n * `/users/internal/alice`. Paths without the prefix are returned unchanged\n * (modulo trailing-slash trimming). Returns `null` for empty input.\n *\n * @example\n * ```typescript\n * normalizeHomePath('/raisin:access_control/users/internal/alice');\n * // => '/users/internal/alice'\n * normalizeHomePath('/users/internal/alice');\n * // => '/users/internal/alice'\n * ```\n */\nexport function normalizeHomePath(home: string | null | undefined): string | null {\n if (home === null || home === undefined) return null;\n\n let path = home.trim();\n if (path.length === 0) return null;\n\n if (path === ACCESS_CONTROL_PREFIX) return '/';\n if (path.startsWith(`${ACCESS_CONTROL_PREFIX}/`)) {\n path = path.slice(ACCESS_CONTROL_PREFIX.length);\n }\n\n // Trim trailing slashes (but keep root \"/\")\n while (path.length > 1 && path.endsWith('/')) {\n path = path.slice(0, -1);\n }\n\n return path;\n}\n","/**\n * Multi-conversation list store for RaisinDB.\n *\n * Manages the list of conversations: loading, creating, deleting, marking\n * as read, and optional real-time updates via WebSocket events.\n *\n * Provides a factory (`getConversationStore`) that returns cached\n * per-conversation `ConversationStore` instances.\n *\n * @example Svelte 5:\n * ```typescript\n * const listStore = new ConversationListStore({ database: db, realtime: true });\n * let snapshot = $state(listStore.getSnapshot());\n * listStore.subscribe(s => { snapshot = s; });\n * await listStore.load();\n * ```\n */\n\nimport type { Database } from '../database';\nimport type { ConversationType, ConversationListItem } from '../types/chat';\nimport { ConversationStore, type ConversationStoreOptions } from './conversation-store';\nimport { logger } from '../logger';\nimport { normalizeHomePath } from '../utils/home-path';\n\n/**\n * Build the realtime subscription filters for a user's chats folder.\n *\n * The server matches subscription paths literally with glob semantics:\n * `*` matches exactly one path segment, `**` matches recursively, and there\n * is NO implicit prefix matching — a plain path only matches that exact\n * node. To observe conversations created/updated under the chats folder we\n * therefore need the recursive `/**` suffix.\n *\n * Exported for testing.\n * @internal\n */\nexport function buildChatsSubscriptionFilters(userHome: string): {\n workspace: string;\n path: string;\n event_types: string[];\n} {\n const home = normalizeHomePath(userHome) ?? userHome;\n return {\n workspace: 'raisin:access_control',\n path: `${home}/inbox/chats/**`,\n event_types: ['node:created', 'node:updated'],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Snapshot of the conversation list state */\nexport interface ConversationListSnapshot {\n conversations: ConversationListItem[];\n totalUnreadCount: number;\n isLoading: boolean;\n error: string | null;\n}\n\n/** Options for creating a ConversationListStore */\nexport interface ConversationListStoreOptions {\n database: Database;\n /** Filter by conversation type */\n type?: ConversationType;\n /** Subscribe to WebSocket events for new conversations */\n realtime?: boolean;\n}\n\ntype Subscriber = (snapshot: ConversationListSnapshot) => void;\n\n// ---------------------------------------------------------------------------\n// ConversationListStore\n// ---------------------------------------------------------------------------\n\nexport class ConversationListStore {\n private database: Database;\n private type?: ConversationType;\n private realtime: boolean;\n private subscribers = new Set<Subscriber>();\n\n // Internal state\n private _conversations: ConversationListItem[] = [];\n private _isLoading = false;\n private _error: string | null = null;\n\n // Cached ConversationStore instances\n private _stores = new Map<string, ConversationStore>();\n\n // Real-time subscription\n private _realtimeSubscription: { unsubscribe: () => void } | null = null;\n\n constructor(options: ConversationListStoreOptions) {\n this.database = options.database;\n this.type = options.type;\n this.realtime = options.realtime ?? false;\n }\n\n // ==========================================================================\n // Subscription\n // ==========================================================================\n\n subscribe(callback: Subscriber): () => void {\n this.subscribers.add(callback);\n callback(this.getSnapshot());\n return () => { this.subscribers.delete(callback); };\n }\n\n getSnapshot(): ConversationListSnapshot {\n return {\n conversations: [...this._conversations],\n totalUnreadCount: this._conversations.reduce((sum, c) => sum + (c.unreadCount ?? 0), 0),\n isLoading: this._isLoading,\n error: this._error,\n };\n }\n\n // ==========================================================================\n // Actions\n // ==========================================================================\n\n /**\n * Load conversations from the server.\n */\n async load(): Promise<void> {\n this._isLoading = true;\n this._error = null;\n this.notify();\n\n try {\n const conversations = await this.database.conversations.list({\n type: this.type,\n });\n logger.debug(`[ConversationListStore] Loaded ${conversations.length} conversations`);\n this._conversations = conversations;\n\n if (this.realtime) {\n this.startRealtimeSubscription();\n }\n } catch (err) {\n logger.error('[ConversationListStore] Failed to load conversations', err);\n this._error = err instanceof Error ? err.message : String(err);\n } finally {\n this._isLoading = false;\n this.notify();\n }\n }\n\n /**\n * Create a new conversation.\n */\n async createConversation(options: {\n participant: string;\n subject?: string;\n input?: Record<string, unknown>;\n }): Promise<ConversationListItem> {\n const convo = await this.database.conversations.create(options);\n\n const listItem: ConversationListItem = {\n id: convo.id,\n type: convo.type,\n conversationPath: convo.conversationPath,\n conversationWorkspace: convo.conversationWorkspace,\n agentRef: convo.agentRef,\n participants: convo.participants,\n unreadCount: 0,\n updatedAt: new Date().toISOString(),\n };\n\n this._conversations = [listItem, ...this._conversations];\n this.notify();\n return listItem;\n }\n\n /**\n * Delete a conversation.\n */\n async deleteConversation(conversationPath: string): Promise<void> {\n // Destroy cached store\n const store = this._stores.get(conversationPath);\n if (store) {\n store.destroy();\n this._stores.delete(conversationPath);\n }\n\n await this.database.conversations.delete(conversationPath);\n this._conversations = this._conversations.filter(c => c.conversationPath !== conversationPath);\n this.notify();\n }\n\n /**\n * Mark a conversation as read.\n */\n async markAsRead(conversationPath: string): Promise<void> {\n await this.database.conversations.markAsRead(conversationPath);\n this._conversations = this._conversations.map(c =>\n c.conversationPath === conversationPath ? { ...c, unreadCount: 0 } : c\n );\n this.notify();\n }\n\n /**\n * Get or create a ConversationStore for a specific conversation.\n * Caches instances so repeated calls return the same store.\n */\n getConversationStore(\n conversationPath: string,\n overrides?: Partial<ConversationStoreOptions>,\n ): ConversationStore {\n let store = this._stores.get(conversationPath);\n if (!store) {\n store = new ConversationStore({\n database: this.database,\n conversationPath,\n ...overrides,\n });\n this._stores.set(conversationPath, store);\n }\n return store;\n }\n\n /**\n * Clean up all resources.\n */\n destroy(): void {\n this._realtimeSubscription?.unsubscribe();\n this._realtimeSubscription = null;\n\n for (const store of this._stores.values()) {\n store.destroy();\n }\n this._stores.clear();\n this.subscribers.clear();\n }\n\n // ==========================================================================\n // Internal\n // ==========================================================================\n\n private async startRealtimeSubscription(): Promise<void> {\n if (this._realtimeSubscription) return;\n logger.debug('[ConversationListStore] Starting realtime subscription');\n\n try {\n const workspace = this.database.workspace('raisin:access_control');\n const events = workspace.events();\n\n // We need the user home path to subscribe to the right folder\n const result = await this.database.executeSql(\n `SELECT RAISIN_CURRENT_USER()->>'path'::String as home`,\n );\n const rows = result.rows as unknown as Record<string, unknown>[];\n const userHome = rows?.[0]?.home as string;\n if (!userHome) return;\n\n this._realtimeSubscription = await events.subscribe(\n buildChatsSubscriptionFilters(userHome),\n async (event) => {\n const eventData = event as any;\n const nodeId = eventData.payload?.node_id;\n if (!nodeId) return;\n\n try {\n const nodeResult = await this.database.executeSql(\n `SELECT id, path, node_type, properties, created_at, updated_at FROM 'raisin:access_control' WHERE id = $1 LIMIT 1`,\n [nodeId],\n );\n const row = (nodeResult.rows as unknown as Record<string, unknown>[])?.[0];\n if (!row || row.node_type !== 'raisin:Conversation') return;\n\n const convPath = row.path as string;\n const props = (row.properties ?? {}) as Record<string, unknown>;\n const item: ConversationListItem = {\n id: row.id as string,\n type: (props.conversation_type as ConversationType) ?? 'ai_chat',\n conversationPath: convPath,\n conversationWorkspace: 'raisin:access_control',\n agentRef: props.agent_ref as string | undefined,\n participants: props.participants as string[] | undefined,\n unreadCount: (props.unread_count as number) ?? 0,\n lastMessage: props.last_message as { content: string; sender_id: string; created_at: string } | undefined,\n updatedAt: (row.updated_at ?? row.created_at) as string,\n };\n\n const existingIdx = this._conversations.findIndex(c => c.conversationPath === convPath);\n if (existingIdx >= 0) {\n // Update existing conversation and move to top\n this._conversations.splice(existingIdx, 1);\n }\n this._conversations = [item, ...this._conversations];\n this.notify();\n } catch (err) {\n logger.debug('Failed to process conversation event:', err);\n }\n },\n );\n } catch (err) {\n logger.debug('Failed to start realtime subscription:', err);\n }\n }\n\n private notify(): void {\n const snapshot = this.getSnapshot();\n for (const sub of this.subscribers) {\n try { sub(snapshot); }\n catch (err) { logger.error('Error in conversation list store subscriber:', err); }\n }\n }\n}\n","/**\n * React adapters for the RaisinDB conversation stores.\n *\n * Since the SDK does not depend on React, the hooks accept React as a\n * parameter to avoid a hard dependency while providing full type safety.\n *\n * @example\n * ```tsx\n * import React from 'react';\n * import { useConversation, useConversationList } from '@raisindb/client';\n *\n * function Chat() {\n * const chat = useConversation(React, {\n * database: db,\n * conversationPath: '/...',\n * });\n * return (\n * <div>\n * {chat.messages.map((m, i) => <div key={i}>{m.content}</div>)}\n * {chat.isStreaming && <p>{chat.streamingText}</p>}\n * </div>\n * );\n * }\n * ```\n */\n\nimport {\n ConversationStore,\n type ConversationStoreOptions,\n type ConversationStoreSnapshot,\n} from '../stores/conversation-store';\nimport {\n ConversationListStore,\n type ConversationListStoreOptions,\n type ConversationListSnapshot,\n} from '../stores/conversation-list-store';\nimport type { ChatMessage, ConversationListItem } from '../types/chat';\n\n/** Minimal subset of the React API needed by the hooks */\nexport interface ReactLike {\n useState<T>(initial: T | (() => T)): [T, (value: T | ((prev: T) => T)) => void];\n useEffect(effect: () => void | (() => void), deps?: unknown[]): void;\n useRef<T>(initial: T): { current: T };\n useCallback<T extends (...args: never[]) => unknown>(fn: T, deps: unknown[]): T;\n}\n\n// ---------------------------------------------------------------------------\n// useConversation\n// ---------------------------------------------------------------------------\n\nconst INITIAL_SNAPSHOT: ConversationStoreSnapshot = {\n conversation: null,\n messages: [],\n isStreaming: false,\n isWaiting: false,\n streamingText: '',\n error: null,\n activeToolCalls: [],\n plans: [],\n isLoading: false,\n conversationPath: null,\n};\n\nexport interface UseConversationReturn extends ConversationStoreSnapshot {\n sendMessage: (content: string) => Promise<void>;\n approvePlan: (planPath: string) => Promise<void>;\n rejectPlan: (planPath: string, feedback?: string) => Promise<void>;\n stop: () => void;\n loadMessages: () => Promise<ChatMessage[]>;\n}\n\nexport function useConversation(\n react: ReactLike,\n options: ConversationStoreOptions,\n): UseConversationReturn {\n const { useState, useEffect, useRef, useCallback } = react;\n\n const [snapshot, setSnapshot] = useState<ConversationStoreSnapshot>(INITIAL_SNAPSHOT);\n const storeRef = useRef<ConversationStore | null>(null);\n\n useEffect(() => {\n const store = new ConversationStore(options);\n storeRef.current = store;\n\n const unsubscribe = store.subscribe((next) => setSnapshot(next));\n\n return () => {\n unsubscribe();\n store.destroy();\n storeRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options.conversationPath]);\n\n const sendMessage = useCallback(async (content: string) => {\n await storeRef.current?.sendMessage(content);\n }, []);\n\n const approvePlan = useCallback(async (planPath: string) => {\n await storeRef.current?.approvePlan(planPath);\n }, []);\n\n const rejectPlan = useCallback(async (planPath: string, feedback?: string) => {\n await storeRef.current?.rejectPlan(planPath, feedback);\n }, []);\n\n const stop = useCallback(() => { storeRef.current?.stop(); }, []);\n\n const loadMessages = useCallback(async () => {\n return (await storeRef.current?.loadMessages()) ?? [];\n }, []);\n\n return {\n ...snapshot,\n sendMessage,\n approvePlan,\n rejectPlan,\n stop,\n loadMessages,\n };\n}\n\n// ---------------------------------------------------------------------------\n// useConversationList\n// ---------------------------------------------------------------------------\n\n// isLoading starts TRUE: the hook always calls store.load() in its mount\n// effect, but the first render happens before that effect runs — reporting\n// \"not loading\" there makes consumers mis-resolve an empty list as \"loaded,\n// no conversations\" (e.g. failing to resume the latest conversation).\nconst INITIAL_LIST_SNAPSHOT: ConversationListSnapshot = {\n conversations: [],\n totalUnreadCount: 0,\n isLoading: true,\n error: null,\n};\n\nexport interface UseConversationListReturn extends ConversationListSnapshot {\n createConversation: (options: { participant: string; subject?: string }) => Promise<ConversationListItem>;\n deleteConversation: (conversationPath: string) => Promise<void>;\n markAsRead: (conversationPath: string) => Promise<void>;\n}\n\nexport function useConversationList(\n react: ReactLike,\n options: ConversationListStoreOptions,\n): UseConversationListReturn {\n const { useState, useEffect, useRef, useCallback } = react;\n\n const [snapshot, setSnapshot] = useState<ConversationListSnapshot>(INITIAL_LIST_SNAPSHOT);\n const storeRef = useRef<ConversationListStore | null>(null);\n\n useEffect(() => {\n const store = new ConversationListStore(options);\n storeRef.current = store;\n\n const unsubscribe = store.subscribe((next) => setSnapshot(next));\n store.load();\n\n return () => {\n unsubscribe();\n store.destroy();\n storeRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options.type, options.realtime]);\n\n const createConversation = useCallback(async (opts: { participant: string; subject?: string }) => {\n return storeRef.current!.createConversation(opts);\n }, []);\n\n const deleteConversation = useCallback(async (conversationPath: string) => {\n await storeRef.current?.deleteConversation(conversationPath);\n }, []);\n\n const markAsRead = useCallback(async (conversationPath: string) => {\n await storeRef.current?.markAsRead(conversationPath);\n }, []);\n\n return {\n ...snapshot,\n createConversation,\n deleteConversation,\n markAsRead,\n };\n}\n","/**\n * Error types for the RaisinDB JS SDK.\n *\n * Provides a structured error hierarchy for programmatic error handling.\n * All errors extend RaisinError and include a string `code` field for\n * easy matching in switch statements or error handlers.\n *\n * @example\n * ```typescript\n * import { RaisinError, RaisinConnectionError } from '@raisindb/client';\n *\n * try {\n * await flows.run('/flows/my-flow', {});\n * } catch (err) {\n * if (err instanceof RaisinConnectionError) {\n * console.log('Network issue:', err.code);\n * }\n * }\n * ```\n */\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\n/** Error codes for connection errors */\nexport type ConnectionErrorCode =\n | 'CONNECTION_FAILED'\n | 'CONNECTION_LOST'\n | 'SSE_STREAM_ERROR';\n\n/**\n * Error codes for authentication errors.\n *\n * Includes the well-known codes plus any server-provided code string\n * (e.g. identity endpoints return codes like `LOGIN_FAILED`).\n */\nexport type AuthErrorCode =\n | 'AUTH_UNAUTHORIZED'\n | 'AUTH_FORBIDDEN'\n | 'AUTH_TOKEN_EXPIRED'\n | 'LOGIN_FAILED'\n | 'REGISTRATION_FAILED'\n // eslint-disable-next-line @typescript-eslint/ban-types\n | (string & {});\n\n/** Error codes for flow execution errors */\nexport type FlowErrorCode =\n | 'FLOW_NOT_FOUND'\n | 'FLOW_EXECUTION_FAILED'\n | 'FLOW_INSTANCE_NOT_FOUND'\n | 'FLOW_RESUME_FAILED';\n\n/** Error codes for timeout errors */\nexport type TimeoutErrorCode =\n | 'REQUEST_TIMEOUT'\n | 'CONNECTION_TIMEOUT'\n | 'POLL_TIMEOUT'\n | 'SSE_INACTIVITY_TIMEOUT';\n\n/** Error codes for abort/cancellation errors */\nexport type AbortErrorCode = 'ABORTED';\n\n/** All error codes */\nexport type RaisinErrorCode =\n | ConnectionErrorCode\n | AuthErrorCode\n | FlowErrorCode\n | TimeoutErrorCode\n | AbortErrorCode;\n\n// ============================================================================\n// Error Classes\n// ============================================================================\n\n/**\n * Base error class for all RaisinDB SDK errors.\n *\n * All SDK errors extend this class, making it easy to catch any\n * RaisinDB-specific error with a single `instanceof` check.\n */\nexport class RaisinError extends Error {\n /** String error code for programmatic handling */\n readonly code: RaisinErrorCode;\n /** Optional additional details */\n readonly details?: unknown;\n\n constructor(message: string, code: RaisinErrorCode, details?: unknown) {\n super(message);\n this.name = 'RaisinError';\n this.code = code;\n this.details = details;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Thrown when a network or connection failure occurs.\n *\n * This includes SSE stream errors, fetch failures, and lost connections.\n */\nexport class RaisinConnectionError extends RaisinError {\n /** HTTP status code, if available */\n readonly status?: number;\n\n constructor(message: string, code: ConnectionErrorCode, options?: { status?: number; details?: unknown }) {\n super(message, code, options?.details);\n this.name = 'RaisinConnectionError';\n this.status = options?.status;\n }\n}\n\n/**\n * Thrown when an authentication or authorization failure occurs.\n *\n * This includes 401 Unauthorized, 403 Forbidden, and expired tokens.\n */\nexport class RaisinAuthError extends RaisinError {\n /** HTTP status code (401 or 403) */\n readonly status: number;\n\n constructor(message: string, code: AuthErrorCode, status: number, details?: unknown) {\n super(message, code, details);\n this.name = 'RaisinAuthError';\n this.status = status;\n }\n}\n\n/**\n * Thrown when a flow execution error occurs.\n *\n * This includes flow not found, execution failures, and resume errors.\n */\nexport class RaisinFlowError extends RaisinError {\n /** Flow instance ID, if available */\n readonly instanceId?: string;\n\n constructor(message: string, code: FlowErrorCode, instanceId?: string, details?: unknown) {\n super(message, code, details);\n this.name = 'RaisinFlowError';\n this.instanceId = instanceId;\n }\n}\n\n/**\n * Thrown when a request or operation times out.\n *\n * Includes the timeout duration for debugging.\n */\nexport class RaisinTimeoutError extends RaisinError {\n /** Timeout duration in milliseconds */\n readonly timeoutMs: number;\n\n constructor(message: string, code: TimeoutErrorCode, timeoutMs: number, details?: unknown) {\n super(message, code, details);\n this.name = 'RaisinTimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Thrown when an operation is aborted via AbortSignal.\n */\nexport class RaisinAbortError extends RaisinError {\n constructor(message = 'Operation was aborted') {\n super(message, 'ABORTED');\n this.name = 'RaisinAbortError';\n }\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Classify an HTTP response error into the appropriate RaisinError subclass.\n *\n * @internal\n */\nexport function classifyHttpError(\n status: number,\n message: string,\n details?: unknown,\n): RaisinError {\n if (status === 401) {\n return new RaisinAuthError(message, 'AUTH_UNAUTHORIZED', status, details);\n }\n if (status === 403) {\n return new RaisinAuthError(message, 'AUTH_FORBIDDEN', status, details);\n }\n if (status === 404) {\n return new RaisinFlowError(message, 'FLOW_NOT_FOUND', undefined, details);\n }\n return new RaisinConnectionError(message, 'CONNECTION_FAILED', { status, details });\n}\n","/**\n * SSE (Server-Sent Events) client for streaming flow execution events.\n *\n * Uses fetch-based approach for better control over connection lifecycle,\n * headers, and reconnection compared to the native EventSource API.\n */\n\nimport { logger } from '../logger';\nimport { ReconnectManager, type ReconnectOptions } from '../utils/reconnect';\nimport { RaisinConnectionError, RaisinAuthError, RaisinAbortError, RaisinTimeoutError } from '../errors';\n\n/**\n * Parsed SSE event from the wire format\n */\nexport interface SSEEvent<T = unknown> {\n /** SSE event type (from \"event:\" field) */\n type: string;\n /** Parsed event data */\n data: T;\n /** SSE event ID (from \"id:\" field, if present) */\n id?: string;\n}\n\n/**\n * SSE connection state\n */\nexport type SSEConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';\n\n/**\n * Callback for SSE connection state changes\n */\nexport type SSEStateCallback = (state: SSEConnectionState) => void;\n\n/**\n * Options for creating an SSE client\n */\nexport interface SSEClientOptions {\n /** HTTP headers to include (e.g., Authorization) */\n headers?: Record<string, string>;\n /** Event types to filter for (if empty, all events are passed through) */\n eventTypes?: string[];\n /** Reconnection options (uses ReconnectManager) */\n reconnect?: ReconnectOptions & {\n /** Whether to automatically reconnect on disconnect (default: true) */\n enabled?: boolean;\n };\n /** Custom fetch implementation */\n fetch?: typeof fetch;\n /** Connection state change callback */\n onStateChange?: SSEStateCallback;\n /** Last-Event-ID for resuming streams */\n lastEventId?: string;\n /** External AbortSignal for cancellation */\n signal?: AbortSignal;\n /** HTTP method (default: 'GET'). Use 'POST' to avoid path in URL params. */\n method?: 'GET' | 'POST';\n /** Request body for POST requests (JSON-serialized automatically) */\n body?: unknown;\n /**\n * Inactivity timeout in milliseconds (default: disabled).\n *\n * The timer is reset whenever any bytes arrive on the stream (events,\n * comments/keep-alives, partial frames). If the stream produces nothing\n * for this long — including a fetch that never responds — the connection\n * is torn down. With reconnection enabled the client reconnects; with\n * reconnection disabled the stream ends with a `RaisinTimeoutError`\n * (code `SSE_INACTIVITY_TIMEOUT`).\n */\n inactivityTimeoutMs?: number;\n}\n\n/**\n * Parse a single SSE message block into event type, data, and id.\n *\n * SSE wire format:\n * event: <type>\\n\n * data: <json>\\n\n * id: <id>\\n\n * \\n\n */\nfunction parseSSEBlock(block: string): { event: string; data: string; id?: string } | null {\n let event = 'message';\n let data = '';\n let id: string | undefined;\n\n const lines = block.split('\\n');\n for (const line of lines) {\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n // Accumulate data lines (SSE spec allows multi-line data)\n if (data) data += '\\n';\n data += line.slice(5).trim();\n } else if (line.startsWith('id:')) {\n id = line.slice(3).trim();\n }\n // Lines starting with \":\" are comments (keep-alive), ignore them\n // Empty lines are block separators, handled by the caller\n }\n\n if (!data) return null;\n\n return { event, data, id };\n}\n\n/**\n * Fetch-based SSE client with reconnection, filtering, and AsyncIterable support.\n *\n * @example\n * ```typescript\n * const sse = new SSEClient('https://api.example.com/events/flow/123', {\n * headers: { Authorization: `Bearer ${token}` },\n * eventTypes: ['step_started', 'step_completed', 'flow_completed'],\n * });\n *\n * // AsyncIterable usage\n * for await (const event of sse) {\n * console.log(event.type, event.data);\n * }\n *\n * // Callback usage\n * sse.connect((event) => {\n * console.log(event.type, event.data);\n * });\n *\n * // Cleanup\n * sse.close();\n * ```\n */\nexport class SSEClient<T = unknown> implements AsyncIterable<SSEEvent<T>> {\n private url: string;\n private options: SSEClientOptions;\n private abortController: AbortController | null = null;\n private reconnectManager: ReconnectManager;\n private fetchImpl: typeof fetch;\n private _state: SSEConnectionState = 'disconnected';\n private _lastEventId?: string;\n private eventTypeSet: Set<string> | null;\n private autoReconnect: boolean;\n private externalSignal?: AbortSignal;\n private inactivityTimeoutMs?: number;\n\n constructor(url: string, options: SSEClientOptions = {}) {\n this.url = url;\n this.options = options;\n this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);\n this._lastEventId = options.lastEventId;\n this.autoReconnect = options.reconnect?.enabled ?? true;\n this.externalSignal = options.signal;\n this.inactivityTimeoutMs = options.inactivityTimeoutMs;\n this.eventTypeSet = options.eventTypes?.length\n ? new Set(options.eventTypes)\n : null;\n\n this.reconnectManager = new ReconnectManager({\n initialDelay: options.reconnect?.initialDelay ?? 1000,\n maxDelay: options.reconnect?.maxDelay ?? 30000,\n backoffMultiplier: options.reconnect?.backoffMultiplier ?? 2,\n maxAttempts: options.reconnect?.maxAttempts ?? Infinity,\n });\n }\n\n /** Current connection state */\n get state(): SSEConnectionState {\n return this._state;\n }\n\n /**\n * Returns a promise that resolves once the SSE connection is established.\n *\n * Useful for ensuring the connection is ready before triggering events\n * on the server side (e.g., calling resume before consuming the stream).\n * If the client is already connected, resolves immediately.\n */\n waitUntilConnected(): Promise<void> {\n if (this._state === 'connected') return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const originalOnState = this.options.onStateChange;\n this.options.onStateChange = (state) => {\n originalOnState?.(state);\n if (state === 'connected') {\n // Restore original callback and resolve\n this.options.onStateChange = originalOnState;\n resolve();\n } else if (state === 'disconnected') {\n this.options.onStateChange = originalOnState;\n reject(new Error('SSE connection failed'));\n }\n };\n });\n }\n\n /** Last received event ID */\n get lastEventId(): string | undefined {\n return this._lastEventId;\n }\n\n private setState(state: SSEConnectionState): void {\n if (this._state !== state) {\n this._state = state;\n this.options.onStateChange?.(state);\n }\n }\n\n /**\n * Connect and consume events via callback.\n *\n * @param onEvent - Called for each matching SSE event\n * @param onError - Called when an error occurs (optional)\n */\n async connect(\n onEvent: (event: SSEEvent<T>) => void,\n onError?: (error: Error) => void,\n ): Promise<void> {\n this.close();\n await this.startStream(onEvent, onError);\n }\n\n /**\n * Close the SSE connection and stop reconnection attempts.\n */\n close(): void {\n this.reconnectManager.cancelReconnect();\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n this.setState('disconnected');\n }\n\n /**\n * AsyncIterable interface: allows `for await (const event of sseClient)`.\n *\n * The iterator ends when close() is called or the server closes the stream\n * (and auto-reconnect is disabled or max attempts are exhausted).\n */\n async *[Symbol.asyncIterator](): AsyncIterator<SSEEvent<T>> {\n // Use a queue and resolve-based approach for backpressure\n type QueueItem =\n | { done: false; value: SSEEvent<T> }\n | { done: true; error?: Error };\n\n const queue: QueueItem[] = [];\n let resolve: ((item: QueueItem) => void) | null = null;\n let finished = false;\n\n const push = (item: QueueItem) => {\n if (finished) return;\n if (resolve) {\n const r = resolve;\n resolve = null;\n r(item);\n } else {\n queue.push(item);\n }\n };\n\n const pull = (): Promise<QueueItem> => {\n const queued = queue.shift();\n if (queued) return Promise.resolve(queued);\n return new Promise<QueueItem>((r) => {\n resolve = r;\n });\n };\n\n // Start streaming in the background\n this.startStream(\n (event) => push({ done: false, value: event }),\n (error) => push({ done: true, error }),\n ).then(() => {\n push({ done: true });\n }).catch((error) => {\n push({ done: true, error });\n });\n\n try {\n while (!finished) {\n const item = await pull();\n if (item.done) {\n finished = true;\n if (item.error) {\n throw item.error;\n }\n return;\n }\n yield item.value;\n }\n } finally {\n finished = true;\n this.close();\n }\n }\n\n /**\n * Core streaming loop: connects, reads the SSE stream, and handles reconnection.\n */\n private async startStream(\n onEvent: (event: SSEEvent<T>) => void,\n onError?: (error: Error) => void,\n ): Promise<void> {\n while (true) {\n try {\n await this.readStream(onEvent);\n\n // Stream ended cleanly (server closed connection)\n if (!this.autoReconnect || this._state === 'disconnected') {\n return;\n }\n\n // Schedule reconnection\n this.setState('reconnecting');\n const scheduled = await this.waitForReconnect();\n if (!scheduled) {\n // Max attempts reached\n logger.warn('SSE max reconnection attempts reached');\n this.setState('disconnected');\n return;\n }\n } catch (error) {\n if (this._state === 'disconnected') {\n // close() was called, exit cleanly\n return;\n }\n\n const err = error instanceof Error ? error : new Error(String(error));\n\n // AbortError means close() was called or external AbortSignal fired\n if (err.name === 'AbortError' || err instanceof RaisinAbortError) {\n return;\n }\n\n // Inactivity timeout: reconnect silently if enabled, otherwise end\n // the stream with the typed timeout error.\n if (err instanceof RaisinTimeoutError && err.code === 'SSE_INACTIVITY_TIMEOUT') {\n logger.warn('SSE inactivity timeout:', err.message);\n if (!this.autoReconnect) {\n this.setState('disconnected');\n onError?.(err);\n throw err;\n }\n this.setState('reconnecting');\n const scheduled = await this.waitForReconnect();\n if (!scheduled) {\n logger.warn('SSE max reconnection attempts reached');\n this.setState('disconnected');\n onError?.(err);\n throw err;\n }\n continue;\n }\n\n logger.error('SSE connection error:', err.message);\n onError?.(err);\n\n if (!this.autoReconnect) {\n this.setState('disconnected');\n throw err;\n }\n\n // Schedule reconnection\n this.setState('reconnecting');\n const scheduled = await this.waitForReconnect();\n if (!scheduled) {\n logger.warn('SSE max reconnection attempts reached');\n this.setState('disconnected');\n throw err;\n }\n }\n }\n }\n\n /**\n * Perform a single fetch and read the response body as SSE stream.\n */\n private async readStream(onEvent: (event: SSEEvent<T>) => void): Promise<void> {\n this.abortController = new AbortController();\n this.setState('connecting');\n\n // If an external signal is provided, forward its abort to our internal controller\n if (this.externalSignal) {\n if (this.externalSignal.aborted) {\n this.abortController.abort();\n } else {\n const ctrl = this.abortController;\n this.externalSignal.addEventListener('abort', () => ctrl.abort(), { once: true });\n }\n }\n\n const headers: Record<string, string> = {\n Accept: 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...this.options.headers,\n };\n\n // Resume from last event ID if available\n if (this._lastEventId) {\n headers['Last-Event-ID'] = this._lastEventId;\n }\n\n const method = this.options.method ?? 'GET';\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: this.abortController.signal,\n };\n if (method === 'POST' && this.options.body !== undefined) {\n (fetchOptions.headers as Record<string, string>)['Content-Type'] = 'application/json';\n fetchOptions.body = JSON.stringify(this.options.body);\n }\n\n const response = await this.withInactivityTimeout(\n this.fetchImpl(this.url, fetchOptions),\n );\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n const msg = `SSE connection failed: ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`;\n if (response.status === 401 || response.status === 403) {\n throw new RaisinAuthError(\n msg,\n response.status === 401 ? 'AUTH_UNAUTHORIZED' : 'AUTH_FORBIDDEN',\n response.status,\n );\n }\n throw new RaisinConnectionError(msg, 'SSE_STREAM_ERROR', { status: response.status });\n }\n\n if (!response.body) {\n throw new RaisinConnectionError('SSE response has no body', 'SSE_STREAM_ERROR');\n }\n\n this.setState('connected');\n this.reconnectManager.reset();\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await this.withInactivityTimeout(reader.read());\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // SSE events are separated by double newlines\n const blocks = buffer.split('\\n\\n');\n // Last element is incomplete, keep it in the buffer\n buffer = blocks.pop() ?? '';\n\n for (const block of blocks) {\n const trimmed = block.trim();\n if (!trimmed) continue;\n\n const parsed = parseSSEBlock(trimmed);\n if (!parsed) continue;\n\n // Track event ID for reconnection\n if (parsed.id) {\n this._lastEventId = parsed.id;\n }\n\n // Apply event type filter\n if (this.eventTypeSet && !this.eventTypeSet.has(parsed.event)) {\n continue;\n }\n\n // Parse JSON data\n let data: T;\n try {\n data = JSON.parse(parsed.data) as T;\n } catch {\n // If data isn't valid JSON, pass it as-is\n data = parsed.data as unknown as T;\n }\n\n const sseEvent = {\n type: parsed.event,\n data,\n id: parsed.id,\n };\n onEvent(sseEvent);\n }\n }\n } finally {\n try {\n reader.releaseLock();\n } catch {\n // Releasing while a read is pending can throw in some implementations\n }\n }\n }\n\n /**\n * Race a stream operation against the inactivity timeout.\n *\n * If no bytes (or response) arrive within `inactivityTimeoutMs`, the\n * internal connection is aborted and the operation rejects with a\n * `RaisinTimeoutError` (code `SSE_INACTIVITY_TIMEOUT`). The timer is\n * effectively reset on every chunk because each `reader.read()` is\n * wrapped individually.\n */\n private withInactivityTimeout<T>(promise: Promise<T>): Promise<T> {\n const timeoutMs = this.inactivityTimeoutMs;\n if (!timeoutMs || timeoutMs <= 0) return promise;\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n // Swallow the eventual rejection of the underlying promise\n // (e.g. AbortError after we abort) to avoid unhandled rejections.\n promise.catch(() => undefined);\n this.abortController?.abort();\n reject(\n new RaisinTimeoutError(\n `SSE stream inactive for ${timeoutMs}ms`,\n 'SSE_INACTIVITY_TIMEOUT',\n timeoutMs,\n ),\n );\n }, timeoutMs);\n\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n }\n\n /**\n * Wait for the reconnect manager to schedule a reconnection attempt.\n * Returns true if reconnection was scheduled, false if max attempts reached.\n */\n private waitForReconnect(): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const scheduled = this.reconnectManager.scheduleReconnect(() => {\n resolve(true);\n });\n if (!scheduled) {\n resolve(false);\n }\n });\n }\n}\n","/**\n * FlowClient for executing and monitoring RaisinDB flows.\n *\n * Provides methods to:\n * - Run a flow by path and receive an instance ID\n * - Stream real-time execution events via SSE\n * - Resume a waiting flow with external data\n * - Respond to human tasks within a flow\n *\n * @example\n * ```typescript\n * import { RaisinHttpClient } from '@raisindb/client';\n * import { FlowClient } from '@raisindb/client/flow-client';\n *\n * const httpClient = new RaisinHttpClient('http://localhost:8081');\n * await httpClient.authenticate({ username: 'admin', password: 'admin' });\n *\n * const flows = new FlowClient(httpClient, 'my-repo');\n *\n * // Start a flow\n * const { instance_id } = await flows.run('/flows/my-flow', { key: 'value' });\n *\n * // Stream events\n * for await (const event of flows.streamEvents(instance_id)) {\n * console.log(event.type, event);\n * if (event.type === 'flow_completed') break;\n * }\n * ```\n */\n\nimport type { AuthManager } from './auth';\nimport type { FlowsApi } from './flows';\nimport type {\n FlowRunResponse,\n FlowExecutionEvent,\n FlowInstanceStatusResponse,\n FlowCompletedEvent,\n FlowFailedEvent,\n} from './types/flow';\nimport { SSEClient, type SSEEvent } from './streaming/sse-client';\nimport {\n classifyHttpError,\n RaisinAbortError,\n RaisinTimeoutError,\n} from './errors';\n\n/** Options for creating a FlowClient */\nexport interface FlowClientOptions {\n /** Request timeout in milliseconds (default: 60000 for long-running flows) */\n requestTimeout?: number;\n /** Custom fetch implementation */\n fetch?: typeof fetch;\n}\n\n/** Result from `runAndWait()` */\nexport interface FlowRunResult {\n /** Flow instance ID */\n instanceId: string;\n /** Terminal status */\n status: 'completed' | 'failed';\n /** Flow output (when completed) */\n output?: unknown;\n /** Error message (when failed) */\n error?: string;\n}\n\n/** Result from `runAndCollect()` */\nexport interface FlowCollectResult {\n /** Flow instance ID */\n instanceId: string;\n /** All collected events */\n events: FlowExecutionEvent[];\n}\n\n/**\n * Minimal interface the FlowClient needs from the HTTP client.\n * This avoids a hard dependency on the full RaisinHttpClient class.\n */\ninterface HttpClientLike {\n /** Get the auth manager for token access */\n getAuthManager(): AuthManager;\n}\n\n/**\n * Client for executing and monitoring RaisinDB flows.\n *\n * Uses the HTTP REST API for flow operations and SSE for real-time event streaming.\n */\nexport class FlowClient {\n private baseUrl: string;\n private repository: string;\n private authManager: AuthManager;\n private fetchImpl: typeof fetch;\n private requestTimeout: number;\n private flowsApi?: FlowsApi;\n\n /**\n * Create a new FlowClient.\n *\n * @param baseUrl - Base HTTP URL of the RaisinDB server (e.g., \"http://localhost:8081\")\n * @param repository - Repository name\n * @param authManager - Auth manager for token access\n * @param options - Additional options\n * @param flowsApi - Optional FlowsApi for WebSocket-based flow operations\n */\n constructor(\n baseUrl: string,\n repository: string,\n authManager: AuthManager,\n options: FlowClientOptions = {},\n flowsApi?: FlowsApi,\n ) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n this.repository = repository;\n this.authManager = authManager;\n this.fetchImpl = options.fetch ?? fetch;\n this.requestTimeout = options.requestTimeout ?? 60000;\n this.flowsApi = flowsApi;\n }\n\n /**\n * Create a FlowClient from an existing RaisinHttpClient.\n *\n * @param httpClient - An authenticated RaisinHttpClient instance\n * @param baseUrl - Base HTTP URL (same as the one used for the HTTP client)\n * @param repository - Repository name\n * @param options - Additional options\n */\n static fromHttpClient(\n httpClient: HttpClientLike,\n baseUrl: string,\n repository: string,\n options: FlowClientOptions = {},\n flowsApi?: FlowsApi,\n ): FlowClient {\n return new FlowClient(\n baseUrl,\n repository,\n httpClient.getAuthManager(),\n options,\n flowsApi,\n );\n }\n\n // ==========================================================================\n // Flow Execution\n // ==========================================================================\n\n /**\n * Execute a flow by path.\n *\n * Creates a FlowInstance and queues it for execution. Returns immediately\n * with the instance ID - use `streamEvents()` to monitor progress.\n *\n * @param flowPath - Path to the raisin:Flow node (e.g., \"/flows/my-flow\")\n * @param input - Input data passed to the flow\n * @returns Flow run response with instance_id and job_id\n *\n * @example\n * ```typescript\n * const result = await flows.run('/flows/process-order', {\n * orderId: 'ord-123',\n * items: [{ sku: 'ABC', qty: 2 }],\n * });\n * console.log('Instance:', result.instance_id);\n * ```\n */\n async run(\n flowPath: string,\n input: Record<string, unknown> = {},\n options?: { signal?: AbortSignal },\n ): Promise<FlowRunResponse> {\n if (this.flowsApi) {\n return this.flowsApi.run(flowPath, input);\n }\n const response = await this.request<FlowRunResponse>({\n method: 'POST',\n path: `/api/flows/${this.repository}/run`,\n body: { flow_path: flowPath, input },\n signal: options?.signal,\n });\n return response;\n }\n\n // ==========================================================================\n // Instance Status\n // ==========================================================================\n\n /**\n * Get the current status of a flow instance.\n *\n * Returns the instance status, variables, and metadata. Useful for\n * polling-based approaches where SSE is not reliable (e.g., initial\n * flow execution where events may be missed).\n *\n * @param instanceId - Flow instance ID (from `run()`)\n * @returns Instance status including variables and flow path\n *\n * @example\n * ```typescript\n * const status = await flows.getInstanceStatus('instance-123');\n * console.log('Status:', status.status); // 'waiting', 'completed', etc.\n * ```\n */\n async getInstanceStatus(\n instanceId: string,\n options?: { signal?: AbortSignal },\n ): Promise<FlowInstanceStatusResponse> {\n if (this.flowsApi) {\n return this.flowsApi.getInstanceStatus(instanceId);\n }\n return this.request<FlowInstanceStatusResponse>({\n method: 'GET',\n path: `/api/flows/${this.repository}/instances/${instanceId}`,\n signal: options?.signal,\n });\n }\n\n // ==========================================================================\n // Event Streaming (SSE)\n // ==========================================================================\n\n /**\n * Stream real-time execution events for a flow instance via SSE.\n *\n * Returns an async iterable that yields FlowExecutionEvent objects.\n * The stream automatically closes when a terminal event is received\n * (flow_completed or flow_failed).\n *\n * @param instanceId - Flow instance ID (from `run()`)\n * @returns Async iterable of flow execution events\n *\n * @example\n * ```typescript\n * const { instance_id } = await flows.run('/flows/my-flow', { key: 'value' });\n *\n * for await (const event of flows.streamEvents(instance_id)) {\n * switch (event.type) {\n * case 'step_started':\n * console.log(`Step ${event.node_id} started`);\n * break;\n * case 'step_completed':\n * console.log(`Step ${event.node_id} completed in ${event.duration_ms}ms`);\n * break;\n * case 'text_chunk':\n * process.stdout.write(event.text);\n * break;\n * case 'flow_completed':\n * console.log('Flow done:', event.output);\n * break;\n * case 'flow_failed':\n * console.error('Flow failed:', event.error);\n * break;\n * }\n * }\n * ```\n */\n async *streamEvents(\n instanceId: string,\n options?: { signal?: AbortSignal },\n ): AsyncIterable<FlowExecutionEvent> {\n if (this.flowsApi) {\n const sub = await this.flowsApi.subscribeEvents(instanceId);\n try {\n for await (const event of sub.events) {\n if (options?.signal?.aborted) throw new RaisinAbortError();\n yield event;\n if (event.type === 'flow_completed' || event.type === 'flow_failed') {\n return;\n }\n }\n } finally {\n await sub.unsubscribe();\n }\n return;\n }\n const url = `${this.baseUrl}/api/flows/${this.repository}/instances/${instanceId}/events`;\n const headers: Record<string, string> = {};\n\n const token = this.authManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n const sse = new SSEClient<FlowExecutionEvent>(url, {\n headers,\n eventTypes: ['flow-event', 'message'],\n reconnect: { enabled: false },\n fetch: this.fetchImpl,\n signal: options?.signal,\n });\n\n try {\n for await (const sseEvent of sse) {\n const event = sseEvent.data;\n yield event;\n\n // Stop on terminal events\n if (\n event.type === 'flow_completed' ||\n event.type === 'flow_failed'\n ) {\n return;\n }\n }\n } finally {\n sse.close();\n }\n }\n\n /**\n * Create an eagerly-connected event stream for a flow instance.\n *\n * Unlike `streamEvents()` (a lazy async generator), this method opens the\n * SSE connection immediately and waits until it is established before\n * returning. This is critical for resume flows: you must subscribe to\n * events BEFORE calling `resume()`, otherwise events emitted between\n * the resume and the SSE connection are lost.\n *\n * @param instanceId - Flow instance ID\n * @returns Object with an async iterable `events` and a `close()` method\n */\n async createEventStream(\n instanceId: string,\n options?: { signal?: AbortSignal },\n ): Promise<{\n events: AsyncIterable<FlowExecutionEvent>;\n close: () => void;\n }> {\n if (this.flowsApi) {\n const sub = await this.flowsApi.subscribeEvents(instanceId);\n return {\n events: sub.events,\n close: () => { sub.unsubscribe(); },\n };\n }\n const url = `${this.baseUrl}/api/flows/${this.repository}/instances/${instanceId}/events`;\n const headers: Record<string, string> = {};\n\n const token = this.authManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n const sse = new SSEClient<FlowExecutionEvent>(url, {\n headers,\n eventTypes: ['flow-event', 'message'],\n reconnect: { enabled: false },\n fetch: this.fetchImpl,\n signal: options?.signal,\n });\n\n // Kick-start the async generator to initiate the SSE connection.\n // Async generators are lazy — the body (which calls startStream)\n // only executes when .next() is called.\n const iterator = sse[Symbol.asyncIterator]();\n let pendingFirst: Promise<IteratorResult<SSEEvent<FlowExecutionEvent>>> | null = iterator.next();\n\n // Now that startStream is running, wait for the HTTP response\n await sse.waitUntilConnected();\n\n // Wrap in a terminal-event-aware iterable\n const events: AsyncIterable<FlowExecutionEvent> = {\n [Symbol.asyncIterator]() {\n return {\n async next() {\n let result;\n if (pendingFirst) {\n result = await pendingFirst;\n pendingFirst = null;\n } else {\n result = await iterator.next();\n }\n if (result.done) return { done: true as const, value: undefined };\n const event = result.value.data;\n return { done: false, value: event };\n },\n async return() {\n await iterator.return?.();\n sse.close();\n return { done: true as const, value: undefined };\n },\n };\n },\n };\n\n return { events, close: () => sse.close() };\n }\n\n // ==========================================================================\n // Flow Resume & Human Task\n // ==========================================================================\n\n /**\n * Resume a flow instance that is in a waiting state.\n *\n * Use this when a flow is waiting for external input (tool results,\n * external events, etc.). The resume data is passed to the waiting step.\n *\n * @param instanceId - Flow instance ID\n * @param data - Resume data to pass to the waiting step\n *\n * @example\n * ```typescript\n * await flows.resume('instance-123', {\n * tool_name: 'get_weather',\n * result: { temperature: 72, conditions: 'sunny' },\n * });\n * ```\n */\n async resume(\n instanceId: string,\n data: unknown,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n if (this.flowsApi) {\n await this.flowsApi.resume(instanceId, data);\n return;\n }\n await this.request<void>({\n method: 'POST',\n path: `/api/flows/${this.repository}/instances/${instanceId}/resume`,\n body: { resume_data: data },\n signal: options?.signal,\n });\n }\n\n /**\n * Respond to a human task within a flow.\n *\n * When a flow is waiting on a human_task step (approval, input, review),\n * use this method to submit the response. Completion is validated\n * (caller must be the task's assignee or an admin) and resumes the flow.\n *\n * Note: prefer `InboxApi` for full inbox functionality (listing tasks,\n * task details); this is a convenience passthrough to the same endpoint.\n *\n * @param instanceId - Flow instance ID (unused; kept for API stability)\n * @param taskId - The inbox task node ID (or path)\n * @param response - The response data\n *\n * @example\n * ```typescript\n * // Approve a task\n * await flows.respondToHumanTask('instance-123', 'task-abc', {\n * action: 'approve',\n * comment: 'Looks good!',\n * });\n * ```\n */\n async respondToHumanTask(\n _instanceId: string,\n taskId: string,\n response: unknown,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n await this.request<void>({\n method: 'POST',\n path: `/api/inbox/${this.repository}/tasks/${encodeURIComponent(taskId)}/complete`,\n body: { response },\n signal: options?.signal,\n });\n }\n\n // ==========================================================================\n // Convenience Methods\n // ==========================================================================\n\n /**\n * Run a flow and wait for the final result.\n *\n * Starts the flow, streams all events, and returns the terminal outcome.\n * This is a one-shot helper for fire-and-forget flows where you only\n * care about the final output.\n *\n * @param flowPath - Path to the raisin:Flow node\n * @param input - Input data passed to the flow\n * @param options - Optional abort signal\n * @returns The flow outcome including output or error\n *\n * @example\n * ```typescript\n * const result = await flows.runAndWait('/flows/process-order', { orderId: '123' });\n * if (result.status === 'completed') {\n * console.log('Output:', result.output);\n * }\n * ```\n */\n async runAndWait(\n flowPath: string,\n input: Record<string, unknown> = {},\n options?: { signal?: AbortSignal },\n ): Promise<FlowRunResult> {\n const { instance_id } = await this.run(flowPath, input, options);\n\n for await (const event of this.streamEvents(instance_id, options)) {\n if (event.type === 'flow_completed') {\n return {\n instanceId: instance_id,\n status: 'completed',\n output: (event as FlowCompletedEvent).output,\n };\n }\n if (event.type === 'flow_failed') {\n return {\n instanceId: instance_id,\n status: 'failed',\n error: (event as FlowFailedEvent).error,\n };\n }\n }\n\n return { instanceId: instance_id, status: 'failed', error: 'Stream ended without terminal event' };\n }\n\n /**\n * Run a flow and collect all events into an array.\n *\n * Starts the flow, streams all events, and returns them as a collected array.\n * Useful for testing, logging, or post-processing all flow events.\n *\n * @param flowPath - Path to the raisin:Flow node\n * @param input - Input data passed to the flow\n * @param options - Optional abort signal\n * @returns The instance ID and all collected events\n *\n * @example\n * ```typescript\n * const { events } = await flows.runAndCollect('/flows/etl-pipeline', { source: 'db' });\n * const steps = events.filter(e => e.type === 'step_completed');\n * console.log(`Completed ${steps.length} steps`);\n * ```\n */\n async runAndCollect(\n flowPath: string,\n input: Record<string, unknown> = {},\n options?: { signal?: AbortSignal },\n ): Promise<FlowCollectResult> {\n const { instance_id } = await this.run(flowPath, input, options);\n const events: FlowExecutionEvent[] = [];\n\n for await (const event of this.streamEvents(instance_id, options)) {\n events.push(event);\n }\n\n return { instanceId: instance_id, events };\n }\n\n // ==========================================================================\n // Internal HTTP request helper\n // ==========================================================================\n\n private async request<T>(options: {\n method: string;\n path: string;\n body?: unknown;\n signal?: AbortSignal;\n }): Promise<T> {\n const url = `${this.baseUrl}${options.path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n const token = this.authManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.requestTimeout);\n\n // Forward external signal to our internal controller\n if (options.signal) {\n if (options.signal.aborted) {\n clearTimeout(timeoutId);\n throw new RaisinAbortError();\n }\n options.signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const response = await this.fetchImpl(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage: string;\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.message || errorJson.error || errorText;\n } catch {\n errorMessage =\n errorText || `HTTP ${response.status}: ${response.statusText}`;\n }\n\n throw classifyHttpError(response.status, errorMessage);\n }\n\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n return (await response.json()) as T;\n }\n\n return undefined as unknown as T;\n } catch (error) {\n clearTimeout(timeoutId);\n // Re-throw our own errors as-is\n if (error instanceof RaisinAbortError || error instanceof RaisinTimeoutError) {\n throw error;\n }\n if (error instanceof Error && error.name === 'AbortError') {\n // Distinguish external abort from internal timeout\n if (options.signal?.aborted) {\n throw new RaisinAbortError();\n }\n throw new RaisinTimeoutError(\n `Request timeout after ${this.requestTimeout}ms`,\n 'REQUEST_TIMEOUT',\n this.requestTimeout,\n );\n }\n throw error;\n }\n }\n}\n","/**\n * React flow adapter for RaisinDB.\n *\n * Provides a `useFlow` hook that wraps the FlowClient for executing\n * and monitoring RaisinDB flows in React applications. Returns reactive\n * state tracking flow events, status, and providing control actions.\n *\n * Since this SDK package does not depend on React, the hook accepts\n * React as a parameter via the `ReactLike` interface.\n *\n * @example\n * ```tsx\n * import React from 'react';\n * import { useFlow } from '@raisindb/client';\n *\n * function OrderProcessor() {\n * const flow = useFlow(React, {\n * baseUrl: 'http://localhost:8081',\n * repository: 'my-repo',\n * authManager,\n * });\n *\n * const handleRun = async () => {\n * await flow.run('/flows/process-order', { orderId: 'ord-123' });\n * };\n *\n * return (\n * <div>\n * <button onClick={handleRun} disabled={flow.isRunning}>Run</button>\n * <p>Status: {flow.status}</p>\n * {flow.events.map((e, i) => <div key={i}>{e.type}</div>)}\n * {flow.error && <p className=\"error\">{flow.error}</p>}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { FlowClient } from '../flow-client';\nimport type { FlowClientOptions } from '../flow-client';\nimport type { AuthManager } from '../auth';\nimport type { FlowsApi } from '../flows';\nimport type { Database } from '../database';\nimport type { FlowExecutionEvent } from '../types/flow';\nimport type { ReactLike } from './react-conversation';\n\n/** Options for creating the FlowClient within useFlow */\nexport interface UseFlowOptions {\n /**\n * Pre-configured Database instance (from `client.database()`).\n * When provided, `baseUrl`, `repository`, `authManager`, and `flowsApi` are derived\n * automatically — no manual wiring needed.\n */\n database?: Database;\n /** Base HTTP URL of the RaisinDB server */\n baseUrl?: string;\n /** Repository name */\n repository?: string;\n /** Auth manager for token access */\n authManager?: AuthManager;\n /** Additional FlowClient options */\n clientOptions?: FlowClientOptions;\n /** Optional FlowsApi for WebSocket-based flow operations */\n flowsApi?: FlowsApi;\n}\n\n/** Flow execution status */\nexport type FlowStatus = 'idle' | 'running' | 'waiting' | 'completed' | 'failed';\n\n/** Return value from the useFlow hook */\nexport interface UseFlowReturn {\n /** Collected flow execution events */\n events: FlowExecutionEvent[];\n /** Current flow status */\n status: FlowStatus;\n /** Whether a flow is currently running */\n isRunning: boolean;\n /** Current error, if any */\n error: string | null;\n /** Output from the completed flow */\n output: unknown | null;\n /** Current flow instance ID, if running */\n instanceId: string | null;\n /** Run a flow by path with optional input data */\n run: (flowPath: string, input?: Record<string, unknown>) => Promise<void>;\n /** Resume a waiting flow with data */\n resume: (data: unknown) => Promise<void>;\n /** Reset the hook state (clear events, status, error) */\n reset: () => void;\n}\n\n/**\n * React hook for executing and monitoring RaisinDB flows.\n *\n * Manages the full lifecycle: starting a flow, streaming events,\n * resuming waiting flows, and tracking completion status.\n * Auto-cleans up on component unmount.\n *\n * @param react - The React instance (pass `React` or `{ useState, useEffect, useRef, useCallback }`)\n * @param options - Flow client options (baseUrl, repository, authManager, etc.)\n * @returns Reactive flow state and action functions\n *\n * @example\n * ```tsx\n * import React from 'react';\n * import { useFlow } from '@raisindb/client';\n *\n * function DataPipeline() {\n * const flow = useFlow(React, {\n * baseUrl: 'http://localhost:8081',\n * repository: 'my-repo',\n * authManager,\n * });\n *\n * return (\n * <div>\n * <button onClick={() => flow.run('/flows/etl', { source: 'api' })}>\n * Start Pipeline\n * </button>\n * <p>Status: {flow.status}</p>\n * <p>Events: {flow.events.length}</p>\n * {flow.status === 'waiting' && (\n * <button onClick={() => flow.resume({ approved: true })}>\n * Approve\n * </button>\n * )}\n * {flow.output && <pre>{JSON.stringify(flow.output, null, 2)}</pre>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useFlow(react: ReactLike, options: UseFlowOptions): UseFlowReturn {\n const { useState, useEffect, useRef, useCallback } = react;\n\n const [events, setEvents] = useState<FlowExecutionEvent[]>([]);\n const [status, setStatus] = useState<FlowStatus>('idle');\n const [error, setError] = useState<string | null>(null);\n const [output, setOutput] = useState<unknown | null>(null);\n const [instanceId, setInstanceId] = useState<string | null>(null);\n\n const clientRef = useRef<FlowClient | null>(null);\n const abortRef = useRef(false);\n\n // Create the FlowClient once\n useEffect(() => {\n if (options.database) {\n clientRef.current = options.database.flow;\n } else if (options.baseUrl && options.repository && options.authManager) {\n clientRef.current = new FlowClient(\n options.baseUrl,\n options.repository,\n options.authManager,\n options.clientOptions,\n options.flowsApi,\n );\n }\n\n return () => {\n abortRef.current = true;\n clientRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options.database, options.baseUrl, options.repository]);\n\n const run = useCallback(async (flowPath: string, input: Record<string, unknown> = {}) => {\n const client = clientRef.current;\n if (!client) return;\n\n // Reset state\n abortRef.current = false;\n setEvents([]);\n setError(null);\n setOutput(null);\n setStatus('running');\n\n try {\n const result = await client.run(flowPath, input);\n setInstanceId(result.instance_id);\n\n for await (const event of client.streamEvents(result.instance_id)) {\n if (abortRef.current) break;\n\n setEvents((prev) => [...prev, event]);\n\n if (event.type === 'flow_waiting') {\n setStatus('waiting');\n } else if (event.type === 'flow_completed') {\n setOutput(event.output);\n setStatus('completed');\n } else if (event.type === 'flow_failed') {\n setError(event.error);\n setStatus('failed');\n }\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n setStatus('failed');\n }\n }, []);\n\n const resume = useCallback(async (data: unknown) => {\n const client = clientRef.current;\n if (!client || !instanceId) return;\n\n setStatus('running');\n setError(null);\n\n try {\n // Open event stream before resuming to avoid missing events\n const stream = await client.createEventStream(instanceId);\n await client.resume(instanceId, data);\n\n for await (const event of stream.events) {\n if (abortRef.current) break;\n\n setEvents((prev) => [...prev, event]);\n\n if (event.type === 'flow_waiting') {\n setStatus('waiting');\n stream.close();\n return;\n } else if (event.type === 'flow_completed') {\n setOutput(event.output);\n setStatus('completed');\n stream.close();\n return;\n } else if (event.type === 'flow_failed') {\n setError(event.error);\n setStatus('failed');\n stream.close();\n return;\n }\n }\n\n stream.close();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n setStatus('failed');\n }\n }, [instanceId]);\n\n const reset = useCallback(() => {\n abortRef.current = true;\n setEvents([]);\n setStatus('idle');\n setError(null);\n setOutput(null);\n setInstanceId(null);\n }, []);\n\n return {\n events,\n status,\n isRunning: status === 'running',\n error,\n output,\n instanceId,\n run,\n resume,\n reset,\n };\n}\n"],"mappings":"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","names":["scheduled","useConversation","useConversationList","useFlow"]}
|
|
1
|
+
{"version":3,"sources":["../src/integrations/react/index.ts","../src/integrations/react/use-auth.ts","../src/connection.ts","../src/utils/reconnect.ts","../src/logger.ts","../src/integrations/react/use-connection.ts","../src/integrations/react/use-sql.ts","../src/integrations/react/use-subscription.ts","../src/utils/plan-projection.ts","../src/utils/chat-events.ts","../src/stores/conversation-store.ts","../src/utils/home-path.ts","../src/stores/conversation-list-store.ts","../src/integrations/react-conversation.ts","../src/errors.ts","../src/streaming/sse-client.ts","../src/flow-client.ts","../src/integrations/react-flow.ts"],"sourcesContent":["/**\n * React integration layer for RaisinDB.\n *\n * Provides a `createRaisinReact(React)` factory that returns a Provider\n * component and hooks for auth, connection, queries, and subscriptions.\n *\n * Uses the \"bring your own React\" pattern to avoid a peer dependency.\n *\n * @example\n * ```ts\n * import React from 'react';\n * import { RaisinClient, LocalStorageTokenStorage, createRaisinReact } from '@raisindb/client';\n *\n * const client = new RaisinClient('wss://localhost:8443/sys/default/myrepo', {\n * tokenStorage: new LocalStorageTokenStorage('myrepo'),\n * });\n *\n * export const {\n * RaisinProvider, useAuth, useConnection, useDatabase,\n * useSql, useSubscription, useConversation, useFlow,\n * } = createRaisinReact(React);\n * ```\n */\n\nimport type {\n ReactLikeWithContext,\n RaisinProviderProps,\n RaisinContextValue,\n UseAuthReturn,\n UseConnectionReturn,\n UseSqlOptions,\n UseSqlReturn,\n UseSubscriptionOptions,\n} from './types';\n\nimport type { RaisinClient } from '../../client';\nimport type { Database } from '../../database';\nimport type { EventMessage } from '../../protocol';\nimport type {\n ConversationStoreOptions,\n} from '../../stores/conversation-store';\nimport type { ConversationListStoreOptions } from '../../stores/conversation-list-store';\n\nimport { createUseAuth } from './use-auth';\nimport { createUseConnection } from './use-connection';\nimport { createUseSql } from './use-sql';\nimport { createUseSubscription } from './use-subscription';\n\nimport {\n useConversation as rawUseConversation,\n useConversationList as rawUseConversationList,\n type UseConversationReturn,\n type UseConversationListReturn,\n} from '../react-conversation';\nimport {\n useFlow as rawUseFlow,\n type UseFlowOptions,\n type UseFlowReturn,\n} from '../react-flow';\n\nexport interface RaisinReact {\n RaisinProvider: (props: RaisinProviderProps) => any;\n useRaisinClient: () => RaisinClient;\n useDatabase: (repository?: string) => Database;\n useAuth: () => UseAuthReturn;\n useConnection: () => UseConnectionReturn;\n useSql: <T = Record<string, unknown>>(sql: string, params?: unknown[], options?: UseSqlOptions) => UseSqlReturn<T>;\n useSubscription: (options: UseSubscriptionOptions, callback: (event: EventMessage) => void) => void;\n useConversation: (options: ConversationStoreOptions) => UseConversationReturn;\n useConversationList: (options: ConversationListStoreOptions) => UseConversationListReturn;\n useFlow: (options: UseFlowOptions) => UseFlowReturn;\n}\n\n/**\n * Create the full React integration for RaisinDB.\n *\n * Accepts a React instance and returns a Provider component plus hooks.\n * The React instance must include `createContext`, `useContext`, `useMemo`,\n * and `createElement` in addition to the base `ReactLike` interface.\n *\n * @param react - The React instance\n * @returns Provider component and hooks\n */\nexport function createRaisinReact(react: ReactLikeWithContext): RaisinReact {\n const RaisinContext = react.createContext<RaisinContextValue | null>(null);\n\n // --- Provider ---\n function RaisinProvider(props: RaisinProviderProps): any {\n const { client, repository, children } = props;\n\n const value = react.useMemo(\n () => ({ client, repository }),\n [client, repository],\n );\n\n return react.createElement(\n RaisinContext.Provider as unknown,\n { value },\n children,\n );\n }\n\n // --- Context accessors ---\n function useRaisinClient(): RaisinClient {\n const ctx = react.useContext(RaisinContext);\n if (!ctx) {\n throw new Error('useRaisinClient must be used within a <RaisinProvider>');\n }\n return ctx.client;\n }\n\n function useDatabase(repository?: string): Database {\n const ctx = react.useContext(RaisinContext);\n if (!ctx) {\n throw new Error('useDatabase must be used within a <RaisinProvider>');\n }\n const repo = repository ?? ctx.repository;\n if (!repo) {\n throw new Error('useDatabase requires a repository — pass it as an argument or set it on <RaisinProvider>');\n }\n return react.useMemo(() => ctx.client.database(repo), [ctx.client, repo]);\n }\n\n // --- Core hooks ---\n const useAuth = createUseAuth(react, RaisinContext);\n const useConnection = createUseConnection(react, RaisinContext);\n const useSql = createUseSql(react, RaisinContext);\n const useSubscription = createUseSubscription(react, RaisinContext);\n\n // --- Pre-bound existing hooks ---\n function useConversation(options: ConversationStoreOptions): UseConversationReturn {\n return rawUseConversation(react, options);\n }\n\n function useConversationList(options: ConversationListStoreOptions): UseConversationListReturn {\n return rawUseConversationList(react, options);\n }\n\n function useFlow(options: UseFlowOptions): UseFlowReturn {\n return rawUseFlow(react, options);\n }\n\n return {\n RaisinProvider,\n useRaisinClient,\n useDatabase,\n useAuth,\n useConnection,\n useSql,\n useSubscription,\n useConversation,\n useConversationList,\n useFlow,\n };\n}\n\n// Re-export types\nexport type {\n ReactLikeWithContext,\n RaisinProviderProps,\n RaisinContextValue,\n UseAuthReturn,\n UseConnectionReturn,\n UseSqlOptions,\n UseSqlReturn,\n UseSubscriptionOptions,\n} from './types';\n\n// Hook return/option types (parity with the @raisindb/client/vue subpath):\n// apps need these to type props that carry a hook's return value around.\nexport type {\n ReactLike,\n UseConversationReturn,\n UseConversationListReturn,\n} from '../react-conversation';\nexport type { UseFlowOptions, UseFlowReturn, FlowStatus } from '../react-flow';\nexport type {\n ConversationStoreOptions,\n ConversationStoreSnapshot,\n ToolCallInfo,\n} from '../../stores/conversation-store';\nexport type {\n ConversationListStoreOptions,\n ConversationListSnapshot,\n} from '../../stores/conversation-list-store';\nexport type { PlanProjection, PlanProjectionTask } from '../../utils/plan-projection';\nexport type { ChatMessage, ConversationListItem } from '../../types/chat';\n","import type { ReactLikeWithContext, ReactContext, RaisinContextValue, UseAuthReturn } from './types';\nimport type { IdentityUser } from '../../auth';\n\nexport function createUseAuth(\n react: ReactLikeWithContext,\n context: ReactContext<RaisinContextValue | null>,\n): () => UseAuthReturn {\n return function useAuth(): UseAuthReturn {\n const { useState, useEffect, useRef, useCallback, useContext } = react;\n\n const ctx = useContext(context);\n if (!ctx) {\n throw new Error('useAuth must be used within a <RaisinProvider>');\n }\n const { client } = ctx;\n\n const [user, setUser] = useState<IdentityUser | null>(client.getStoredUser());\n const [isLoading, setIsLoading] = useState(false);\n const mountedRef = useRef(true);\n\n useEffect(() => {\n mountedRef.current = true;\n\n const unsubAuth = client.onAuthStateChange(({ event, session }) => {\n if (!mountedRef.current) return;\n if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED' || event === 'USER_UPDATED') {\n setUser(session.user);\n } else if (event === 'SIGNED_OUT' || event === 'SESSION_EXPIRED') {\n setUser(null);\n }\n });\n\n const unsubUser = client.onUserChange(({ node }) => {\n if (!mountedRef.current) return;\n // Update user with the latest node properties\n setUser((prev) => {\n if (!prev) return prev;\n return {\n ...prev,\n home: node.path,\n displayName: (node.properties.display_name as string) ?? prev.displayName,\n avatarUrl: (node.properties.avatar_url as string) ?? prev.avatarUrl,\n };\n });\n });\n\n return () => {\n mountedRef.current = false;\n unsubAuth();\n unsubUser();\n };\n }, [client]);\n\n const login = useCallback(async (email: string, password: string, repository: string) => {\n setIsLoading(true);\n try {\n const result = await client.loginWithEmail(email, password, repository);\n return result;\n } finally {\n if (mountedRef.current) setIsLoading(false);\n }\n }, [client]);\n\n const register = useCallback(async (email: string, password: string, repository: string, displayName?: string) => {\n setIsLoading(true);\n try {\n const result = await client.registerWithEmail(email, password, repository, displayName);\n return result;\n } finally {\n if (mountedRef.current) setIsLoading(false);\n }\n }, [client]);\n\n const logout = useCallback(async (options?: { disconnect?: boolean; reconnect?: boolean }) => {\n setIsLoading(true);\n try {\n await client.logout(options);\n } finally {\n if (mountedRef.current) setIsLoading(false);\n }\n }, [client]);\n\n const initSession = useCallback(async (repository: string) => {\n setIsLoading(true);\n try {\n const result = await client.initSession(repository);\n return result;\n } finally {\n if (mountedRef.current) setIsLoading(false);\n }\n }, [client]);\n\n return {\n user,\n isAuthenticated: user !== null,\n isLoading,\n login,\n register,\n logout,\n initSession,\n };\n };\n}\n","/**\n * Universal WebSocket connection manager\n *\n * Detects environment (browser vs Node.js) and uses appropriate WebSocket implementation.\n * Handles auto-reconnect, heartbeat, and connection state management.\n */\n\nimport { EventEmitter } from 'events';\nimport { ReconnectManager, ReconnectOptions } from './utils/reconnect';\nimport { logger } from './logger';\n\n/**\n * Connection state\n */\nexport enum ConnectionState {\n Disconnected = 'disconnected',\n Connecting = 'connecting',\n Connected = 'connected',\n Reconnecting = 'reconnecting',\n Closed = 'closed',\n}\n\n/**\n * Connection options\n */\nexport interface ConnectionOptions {\n /** Auto-reconnect on disconnect (default: true) */\n autoReconnect?: boolean;\n /** Reconnection options */\n reconnectOptions?: ReconnectOptions;\n /** Heartbeat interval in milliseconds (default: 30000, 0 to disable) */\n heartbeatInterval?: number;\n /** Heartbeat timeout in milliseconds (default: 5000) */\n heartbeatTimeout?: number;\n /** WebSocket protocols */\n protocols?: string | string[];\n /**\n * Extra headers to send on the WebSocket upgrade request\n * (e.g. `x-tenant-id` for tenant-less `/ws` URLs).\n *\n * Only honored by WebSocket implementations that support custom headers\n * (Node.js `ws` package). Browser WebSockets silently ignore them — the\n * server then resolves the tenant from the request (default: \"default\").\n */\n headers?: Record<string, string>;\n}\n\n/**\n * Connection events\n */\nexport interface ConnectionEvents {\n stateChange: (state: ConnectionState) => void;\n message: (data: ArrayBuffer) => void;\n error: (error: Error) => void;\n close: (event: CloseEvent) => void;\n}\n\n/**\n * Type for WebSocket implementation (browser or Node.js)\n */\ntype WebSocketImpl = typeof WebSocket | typeof import('ws').WebSocket;\n\n/**\n * Get the appropriate WebSocket implementation for the current environment\n */\nfunction getWebSocketImpl(): WebSocketImpl {\n // Browser environment\n if (typeof WebSocket !== 'undefined') {\n return WebSocket as WebSocketImpl;\n }\n\n // Node.js environment\n try {\n // Use dynamic import to avoid bundler issues\n const ws = require('ws');\n return ws.WebSocket || ws.default || ws;\n } catch (error) {\n throw new Error(\n 'WebSocket implementation not found. In Node.js, please install the \"ws\" package: npm install ws'\n );\n }\n}\n\n/**\n * Universal WebSocket connection manager\n */\nexport class Connection extends EventEmitter {\n private ws?: WebSocket;\n private url: string;\n private state: ConnectionState = ConnectionState.Disconnected;\n private reconnectManager: ReconnectManager;\n private heartbeatTimer?: NodeJS.Timeout;\n private heartbeatTimeoutTimer?: NodeJS.Timeout;\n private options: Required<Omit<ConnectionOptions, 'reconnectOptions' | 'protocols' | 'headers'>> & {\n protocols?: string | string[];\n headers?: Record<string, string>;\n };\n private WebSocketImpl: WebSocketImpl;\n private manualClose = false;\n\n constructor(url: string, options: ConnectionOptions = {}) {\n super();\n // Convert raisin:// / raisins:// to ws:// / wss:// for the WebSocket layer\n this.url = url\n .replace(/^raisin:\\/\\//, 'ws://')\n .replace(/^raisins:\\/\\//, 'wss://');\n this.options = {\n autoReconnect: options.autoReconnect ?? true,\n heartbeatInterval: options.heartbeatInterval ?? 30000,\n heartbeatTimeout: options.heartbeatTimeout ?? 5000,\n protocols: options.protocols,\n headers: options.headers,\n };\n this.reconnectManager = new ReconnectManager(options.reconnectOptions);\n this.WebSocketImpl = getWebSocketImpl();\n }\n\n /**\n * Connect to the WebSocket server\n */\n async connect(): Promise<void> {\n if (this.state === ConnectionState.Connected || this.state === ConnectionState.Connecting) {\n return;\n }\n\n this.manualClose = false;\n this.setState(ConnectionState.Connecting);\n\n return new Promise((resolve, reject) => {\n try {\n // Create WebSocket instance.\n // The third argument (upgrade headers) is only understood by the\n // Node.js `ws` package. Browsers do NOT simply ignore it - Chromium\n // fails the handshake when a third argument is passed - so only pass\n // it to non-browser implementations. Browser connections rely on the\n // server-side tenant resolution instead of upgrade headers.\n const isBrowserWebSocket =\n typeof WebSocket !== 'undefined' && this.WebSocketImpl === WebSocket;\n this.ws = (\n isBrowserWebSocket || !this.options.headers\n ? new (this.WebSocketImpl as any)(this.url, this.options.protocols)\n : new (this.WebSocketImpl as any)(this.url, this.options.protocols, {\n headers: this.options.headers,\n })\n ) as WebSocket;\n\n // Set binary type for MessagePack\n this.ws.binaryType = 'arraybuffer';\n\n // Handle open event\n this.ws.onopen = () => {\n this.setState(ConnectionState.Connected);\n this.reconnectManager.reset();\n this.startHeartbeat();\n resolve();\n };\n\n // Handle message event\n this.ws.onmessage = (event: MessageEvent) => {\n logger.debug(`WebSocket onmessage fired - data type: ${typeof event.data}, is ArrayBuffer: ${event.data instanceof ArrayBuffer}`);\n this.resetHeartbeat();\n const data = event.data as ArrayBuffer;\n logger.debug(`Received binary message - size: ${data.byteLength} bytes`);\n logger.debug(`Emitting 'message' event to client handlers`);\n this.emit('message', data);\n };\n\n // Handle error event\n this.ws.onerror = () => {\n const error = new Error('WebSocket error occurred');\n this.emit('error', error);\n if (this.state === ConnectionState.Connecting) {\n reject(error);\n }\n };\n\n // Handle close event\n this.ws.onclose = (event: CloseEvent) => {\n this.stopHeartbeat();\n this.emit('close', event);\n\n if (!this.manualClose && this.options.autoReconnect) {\n this.setState(ConnectionState.Reconnecting);\n this.reconnect();\n } else {\n this.setState(ConnectionState.Disconnected);\n }\n\n if (this.state === ConnectionState.Connecting) {\n reject(new Error(`Connection closed: ${event.reason || 'Unknown reason'}`));\n }\n };\n } catch (error) {\n this.setState(ConnectionState.Disconnected);\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from the WebSocket server\n */\n disconnect(): void {\n this.manualClose = true;\n this.reconnectManager.cancelReconnect();\n this.stopHeartbeat();\n\n if (this.ws) {\n this.ws.close(1000, 'Client disconnect');\n this.ws = undefined;\n }\n\n this.setState(ConnectionState.Closed);\n }\n\n /**\n * Send data through the WebSocket\n *\n * @param data - Data to send (Uint8Array for MessagePack)\n */\n send(data: Uint8Array): void {\n if (this.state !== ConnectionState.Connected || !this.ws) {\n throw new Error('Cannot send data: not connected');\n }\n\n // Check actual WebSocket readyState\n if (this.ws.readyState !== 1) { // WebSocket.OPEN = 1\n logger.error(`WebSocket not open - readyState: ${this.ws.readyState} (0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED)`);\n throw new Error(`Cannot send data: WebSocket not open (readyState: ${this.ws.readyState})`);\n }\n\n logger.debug(`Sending message - size: ${data.length} bytes, readyState: ${this.ws.readyState}`);\n\n try {\n this.ws.send(data);\n logger.debug(`Message sent successfully`);\n } catch (error) {\n logger.error(`Failed to send message:`, error);\n throw new Error(`Failed to send data: ${error}`);\n }\n }\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return this.state;\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.state === ConnectionState.Connected;\n }\n\n /**\n * Whether auto-reconnect is enabled for this connection.\n *\n * Used by the client to decide whether requests issued while the\n * connection is down can be queued (the connection will come back) or\n * should fail immediately.\n */\n isAutoReconnectEnabled(): boolean {\n return this.options.autoReconnect;\n }\n\n /**\n * Reconnect to the server\n */\n private reconnect(): void {\n const success = this.reconnectManager.scheduleReconnect(async () => {\n try {\n await this.connect();\n } catch (error) {\n // Error is already handled by connect()\n // Reconnection will be scheduled again if autoReconnect is enabled\n }\n });\n\n if (!success) {\n this.setState(ConnectionState.Disconnected);\n this.emit('error', new Error('Max reconnection attempts reached'));\n }\n }\n\n /**\n * Set connection state and emit event\n */\n private setState(state: ConnectionState): void {\n if (this.state !== state) {\n this.state = state;\n this.emit('stateChange', state);\n }\n }\n\n /**\n * Start heartbeat mechanism\n */\n private startHeartbeat(): void {\n if (this.options.heartbeatInterval <= 0) {\n return;\n }\n\n this.stopHeartbeat();\n\n // Note: WebSocket implementations (browser and ws library) automatically handle\n // ping/pong frames at the protocol level, so we don't need application-level heartbeat\n // that sends empty binary messages. The underlying WebSocket will keep the connection alive.\n }\n\n /**\n * Stop heartbeat mechanism\n */\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer);\n this.heartbeatTimeoutTimer = undefined;\n }\n }\n\n /**\n * Reset heartbeat timeout (called when message received)\n */\n private resetHeartbeat(): void {\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer);\n this.heartbeatTimeoutTimer = undefined;\n }\n }\n}\n","/**\n * Reconnection manager with exponential backoff\n */\n\n/**\n * Reconnection strategy options\n */\nexport interface ReconnectOptions {\n /** Initial delay in milliseconds (default: 1000) */\n initialDelay?: number;\n /** Maximum delay in milliseconds (default: 30000) */\n maxDelay?: number;\n /** Backoff multiplier (default: 2) */\n backoffMultiplier?: number;\n /** Maximum number of reconnection attempts (default: Infinity) */\n maxAttempts?: number;\n /** Add random jitter to delay (default: true). Jitter is +/- 25% of the computed delay. */\n jitter?: boolean;\n}\n\n/**\n * Reconnection manager with exponential backoff\n */\nexport class ReconnectManager {\n private currentDelay: number;\n private attempts = 0;\n private reconnectTimeout?: NodeJS.Timeout;\n private isReconnecting = false;\n\n private readonly initialDelay: number;\n private readonly maxDelay: number;\n private readonly backoffMultiplier: number;\n private readonly maxAttempts: number;\n private readonly jitter: boolean;\n\n constructor(options: ReconnectOptions = {}) {\n this.initialDelay = options.initialDelay ?? 1000;\n this.maxDelay = options.maxDelay ?? 30000;\n this.backoffMultiplier = options.backoffMultiplier ?? 2;\n this.maxAttempts = options.maxAttempts ?? Infinity;\n this.jitter = options.jitter ?? true;\n this.currentDelay = this.initialDelay;\n }\n\n /**\n * Schedule a reconnection attempt\n *\n * @param callback - Function to call when it's time to reconnect\n * @returns true if reconnection was scheduled, false if max attempts reached\n */\n scheduleReconnect(callback: () => void | Promise<void>): boolean {\n // Check if we've exceeded max attempts\n if (this.attempts >= this.maxAttempts) {\n return false;\n }\n\n // Clear any existing timeout\n this.cancelReconnect();\n\n this.isReconnecting = true;\n this.attempts++;\n\n // Apply jitter: +/- 25% of the computed delay\n let delay = this.currentDelay;\n if (this.jitter) {\n const jitterRange = delay * 0.25;\n delay += (Math.random() * 2 - 1) * jitterRange;\n delay = Math.max(0, Math.round(delay));\n }\n\n this.reconnectTimeout = setTimeout(async () => {\n try {\n await callback();\n // If successful, reset the delay\n this.reset();\n } catch (error) {\n // Increase delay for next attempt\n this.currentDelay = Math.min(\n this.currentDelay * this.backoffMultiplier,\n this.maxDelay\n );\n }\n }, delay);\n\n return true;\n }\n\n /**\n * Cancel any pending reconnection attempt\n */\n cancelReconnect(): void {\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = undefined;\n }\n this.isReconnecting = false;\n }\n\n /**\n * Reset the reconnection state (call after successful connection)\n */\n reset(): void {\n this.cancelReconnect();\n this.currentDelay = this.initialDelay;\n this.attempts = 0;\n }\n\n /**\n * Check if currently in reconnection mode\n */\n isActive(): boolean {\n return this.isReconnecting;\n }\n\n /**\n * Get the current number of reconnection attempts\n */\n getAttempts(): number {\n return this.attempts;\n }\n\n /**\n * Get the current delay in milliseconds\n */\n getCurrentDelay(): number {\n return this.currentDelay;\n }\n\n /**\n * Check if max attempts have been reached\n */\n hasReachedMaxAttempts(): boolean {\n return this.attempts >= this.maxAttempts;\n }\n}\n","/**\n * Configurable logger for RaisinDB client\n */\n\n/**\n * Log levels in order of verbosity (lower = less verbose)\n */\nexport enum LogLevel {\n Silent = 0,\n Error = 1,\n Warn = 2,\n Info = 3,\n Debug = 4,\n}\n\n/**\n * Logger configuration\n */\nexport interface LoggerConfig {\n /** Current log level (default: Error) */\n level: LogLevel;\n /** Prefix for all log messages (default: \"[RaisinDB]\") */\n prefix: string;\n}\n\n/**\n * Global logger configuration\n */\nlet config: LoggerConfig = {\n level: LogLevel.Error,\n prefix: '[RaisinDB]',\n};\n\n/**\n * Configure the logger\n *\n * @param options - Logger configuration options\n *\n * @example\n * ```typescript\n * import { configureLogger, LogLevel } from '@raisindb/client';\n *\n * // Enable debug logging\n * configureLogger({ level: LogLevel.Debug });\n *\n * // Disable all logging\n * configureLogger({ level: LogLevel.Silent });\n * ```\n */\nexport function configureLogger(options: Partial<LoggerConfig>): void {\n config = { ...config, ...options };\n}\n\n/**\n * Get current logger configuration\n */\nexport function getLoggerConfig(): LoggerConfig {\n return { ...config };\n}\n\n/**\n * Set log level\n *\n * @param level - Log level to set\n */\nexport function setLogLevel(level: LogLevel): void {\n config.level = level;\n}\n\n/**\n * Get current log level\n */\nexport function getLogLevel(): LogLevel {\n return config.level;\n}\n\n/**\n * Logger instance with level-aware logging methods\n */\nexport const logger = {\n /**\n * Log debug message (only shown when level >= Debug)\n */\n debug(...args: unknown[]): void {\n if (config.level >= LogLevel.Debug) {\n console.log(config.prefix, ...args);\n }\n },\n\n /**\n * Log info message (only shown when level >= Info)\n */\n info(...args: unknown[]): void {\n if (config.level >= LogLevel.Info) {\n console.log(config.prefix, ...args);\n }\n },\n\n /**\n * Log warning message (only shown when level >= Warn)\n */\n warn(...args: unknown[]): void {\n if (config.level >= LogLevel.Warn) {\n console.warn(config.prefix, ...args);\n }\n },\n\n /**\n * Log error message (only shown when level >= Error)\n */\n error(...args: unknown[]): void {\n if (config.level >= LogLevel.Error) {\n console.error(config.prefix, ...args);\n }\n },\n};\n\nif (typeof globalThis !== 'undefined') {\n (globalThis as any).__raisinDebug = (enable = true) => {\n setLogLevel(enable ? LogLevel.Debug : LogLevel.Error);\n console.log(`[RaisinDB] Debug logging ${enable ? 'ENABLED' : 'DISABLED'} (includes server handler logs)`);\n };\n}\n","import type { ReactLikeWithContext, ReactContext, RaisinContextValue, UseConnectionReturn } from './types';\nimport { ConnectionState } from '../../connection';\n\nexport function createUseConnection(\n react: ReactLikeWithContext,\n context: ReactContext<RaisinContextValue | null>,\n): () => UseConnectionReturn {\n return function useConnection(): UseConnectionReturn {\n const { useState, useEffect, useCallback, useContext } = react;\n\n const ctx = useContext(context);\n if (!ctx) {\n throw new Error('useConnection must be used within a <RaisinProvider>');\n }\n const { client } = ctx;\n\n const [state, setState] = useState<ConnectionState>(client.getConnectionState());\n const [isReady, setIsReady] = useState(client.isReady());\n\n useEffect(() => {\n const unsubConn = client.onConnectionStateChange((newState) => {\n setState(newState);\n });\n\n const unsubReady = client.onReadyStateChange((ready) => {\n setIsReady(ready);\n });\n\n return () => {\n unsubConn();\n unsubReady();\n };\n }, [client]);\n\n const connect = useCallback(async () => {\n await client.connect();\n }, [client]);\n\n const disconnect = useCallback(() => {\n client.disconnect();\n }, [client]);\n\n return {\n state,\n isConnected: state === ConnectionState.Connected,\n isReady,\n connect,\n disconnect,\n };\n };\n}\n","import type { ReactLikeWithContext, ReactContext, RaisinContextValue, UseSqlOptions, UseSqlReturn } from './types';\nimport type { SqlResult } from '../../protocol';\n\nfunction rowsToObjects<T>(result: SqlResult): T[] {\n return result.rows as T[];\n}\n\nexport function createUseSql(\n react: ReactLikeWithContext,\n context: ReactContext<RaisinContextValue | null>,\n): <T = Record<string, unknown>>(sql: string, params?: unknown[], options?: UseSqlOptions) => UseSqlReturn<T> {\n return function useSql<T = Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n options?: UseSqlOptions,\n ): UseSqlReturn<T> {\n const { useState, useEffect, useRef, useCallback, useContext } = react;\n\n const ctx = useContext(context);\n if (!ctx) {\n throw new Error('useSql must be used within a <RaisinProvider>');\n }\n const { client, repository: defaultRepo } = ctx;\n\n const [data, setData] = useState<T[] | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const mountedRef = useRef(true);\n\n const enabled = options?.enabled !== false;\n const repo = options?.repository ?? defaultRepo;\n const refetchOnReconnect = options?.refetchOnReconnect !== false;\n const realtime = options?.realtime;\n\n // Serialize params for dependency comparison\n const paramsKey = params ? JSON.stringify(params) : '';\n\n const fetchData = useCallback(async () => {\n if (!repo) return;\n setIsLoading(true);\n setError(null);\n try {\n const db = client.database(repo);\n const result = await db.executeSql(sql, params);\n if (mountedRef.current) {\n setData(rowsToObjects<T>(result));\n }\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n if (mountedRef.current) {\n setIsLoading(false);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, repo, sql, paramsKey]);\n\n // Initial fetch and re-fetch on query/params change\n useEffect(() => {\n mountedRef.current = true;\n if (enabled) {\n fetchData();\n }\n return () => {\n mountedRef.current = false;\n };\n }, [enabled, fetchData]);\n\n // Refetch on reconnect\n useEffect(() => {\n if (!enabled || !refetchOnReconnect) return;\n\n const unsub = client.onReconnected(() => {\n fetchData();\n });\n return unsub;\n }, [client, enabled, refetchOnReconnect, fetchData]);\n\n // Realtime subscription: refetch on matching events\n useEffect(() => {\n if (!enabled || !realtime || !repo) return;\n\n const db = client.database(repo);\n let unsubscribe: (() => Promise<void>) | undefined;\n\n db.events().subscribe(\n {\n workspace: realtime.workspace,\n event_types: realtime.eventTypes,\n path: realtime.path,\n node_type: realtime.nodeType,\n },\n () => {\n fetchData();\n },\n ).then((sub) => {\n unsubscribe = () => sub.unsubscribe();\n });\n\n return () => {\n unsubscribe?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, enabled, repo, realtime?.workspace, realtime?.path, realtime?.nodeType, fetchData]);\n\n return {\n data,\n isLoading,\n error,\n refetch: fetchData,\n };\n };\n}\n","import type { ReactLikeWithContext, ReactContext, RaisinContextValue, UseSubscriptionOptions } from './types';\nimport type { EventMessage } from '../../protocol';\n\nexport function createUseSubscription(\n react: ReactLikeWithContext,\n context: ReactContext<RaisinContextValue | null>,\n): (options: UseSubscriptionOptions, callback: (event: EventMessage) => void) => void {\n return function useSubscription(\n options: UseSubscriptionOptions,\n callback: (event: EventMessage) => void,\n ): void {\n const { useEffect, useRef, useContext } = react;\n\n const ctx = useContext(context);\n if (!ctx) {\n throw new Error('useSubscription must be used within a <RaisinProvider>');\n }\n const { client, repository } = ctx;\n\n // callbackRef pattern: avoid re-subscribing when callback identity changes\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n const enabled = options.enabled !== false;\n\n useEffect(() => {\n if (!enabled || !repository || !options.workspace) return;\n\n const db = client.database(repository);\n let unsubscribe: (() => Promise<void>) | undefined;\n let cancelled = false;\n\n db.events().subscribe(\n {\n workspace: options.workspace,\n event_types: options.event_types,\n path: options.path,\n node_type: options.node_type,\n include_node: options.include_node,\n },\n (event) => {\n if (!cancelled) {\n callbackRef.current(event);\n }\n },\n ).then((sub) => {\n if (cancelled) {\n sub.unsubscribe();\n } else {\n unsubscribe = () => sub.unsubscribe();\n }\n });\n\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, repository, enabled, options.workspace, options.path, options.node_type, options.include_node]);\n };\n}\n","/**\n * Plan projection utility.\n *\n * Builds deterministic plan/task state from persisted chat messages.\n */\n\nimport type { ChatMessage } from '../types/chat';\n\nexport interface PlanProjectionTask {\n taskId?: string;\n title: string;\n status: string;\n description?: string;\n priority?: string;\n delegation?: {\n id?: string;\n status: string;\n agentRef?: string;\n flowInstanceId?: string;\n branch?: string;\n baseBranch?: string;\n result?: unknown;\n error?: string;\n };\n}\n\nexport interface PlanProjection {\n key: string;\n planPath?: string;\n planId?: string;\n title: string;\n status: string;\n requiresApproval: boolean;\n tasks: PlanProjectionTask[];\n sourceMessagePath?: string;\n updatedAt?: string;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n return value as Record<string, unknown>;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nexport function parsePlanTasks(rawTasks: unknown): PlanProjectionTask[] {\n if (!Array.isArray(rawTasks)) return [];\n return rawTasks\n // The return type is ANNOTATED, not inferred. `satisfies` on each branch\n // checks that branch but does not widen it, so without this the array is a\n // union of two object literals plus null — and `PlanProjectionTask` is not\n // assignable to that union (`taskId` is optional in the interface and\n // required in the inferred literal), which makes the type predicate below\n // illegal and fails the DTS build for the whole package.\n .map((rawTask, index): PlanProjectionTask | null => {\n if (typeof rawTask === 'string') {\n return {\n title: rawTask,\n status: 'pending',\n } satisfies PlanProjectionTask;\n }\n const task = asRecord(rawTask);\n if (!task) return null;\n const delegation = asRecord(task.delegation);\n return {\n taskId: asString(task.task_id) ?? asString(task.id),\n title: asString(task.title) ?? `Task ${index + 1}`,\n status: asString(task.status) ?? 'pending',\n description: asString(task.description),\n priority: asString(task.priority),\n delegation: delegation ? {\n id: asString(delegation.id),\n status: asString(delegation.status) ?? 'unknown',\n agentRef: asString(delegation.agent_ref) ?? asString(delegation.agentRef),\n flowInstanceId: asString(delegation.flow_instance_id) ?? asString(delegation.flowInstanceId),\n branch: asString(delegation.branch),\n baseBranch: asString(delegation.base_branch) ?? asString(delegation.baseBranch),\n result: delegation.result,\n error: asString(delegation.error),\n } : undefined,\n } satisfies PlanProjectionTask;\n })\n .filter((task): task is PlanProjectionTask => task !== null);\n}\n\nconst TERMINAL_TASK_STATUSES = new Set(['completed', 'cancelled']);\nconst TERMINAL_PLAN_STATUSES = new Set(['completed', 'failed', 'cancelled']);\n\n/**\n * Same race as upsertTask: an ai_task_update message carrying a stale\n * plan_status (e.g. \"in_progress\", computed before the final task's\n * completion count caught up) can be persisted and thus replayed here after\n * the message that actually reflects the terminal plan status. Guard it the\n * same way - never regress out of a terminal plan status.\n */\nfunction setPlanStatus(plan: PlanProjection, nextStatus: string | undefined): void {\n if (!nextStatus) return;\n if (TERMINAL_PLAN_STATUSES.has(plan.status) && !TERMINAL_PLAN_STATUSES.has(nextStatus)) {\n return;\n }\n plan.status = nextStatus;\n}\n\nexport function upsertTask(plan: PlanProjection, nextTask: PlanProjectionTask): void {\n const existingIndex = plan.tasks.findIndex((task) =>\n (nextTask.taskId && task.taskId === nextTask.taskId) ||\n (!nextTask.taskId && task.title === nextTask.title)\n );\n if (existingIndex === -1) {\n plan.tasks.push(nextTask);\n return;\n }\n const existing = plan.tasks[existingIndex];\n // Mirrors the server-side monotonic guard in update-task/index.js: tool\n // calls from one model turn can execute in parallel, so an ai_task_update\n // message for an earlier \"in_progress\" transition can be persisted (and\n // thus replayed here) after the \"completed\" message for the same task.\n // Never let that regress an already-terminal status.\n if (TERMINAL_TASK_STATUSES.has(existing.status) && !TERMINAL_TASK_STATUSES.has(nextTask.status)) {\n return;\n }\n plan.tasks[existingIndex] = {\n ...existing,\n ...nextTask,\n };\n}\n\nexport function ensurePlan(\n plans: Map<string, PlanProjection>,\n key: string,\n fallbackTitle: string,\n timestamp: string | undefined,\n): PlanProjection {\n const existing = plans.get(key);\n if (existing) {\n if (timestamp) existing.updatedAt = timestamp;\n return existing;\n }\n const created: PlanProjection = {\n key,\n title: fallbackTitle,\n status: 'pending_approval',\n requiresApproval: true,\n tasks: [],\n updatedAt: timestamp,\n };\n plans.set(key, created);\n return created;\n}\n\n/**\n * Build deterministic plan/task state from persisted chat messages.\n * Uses ai_plan as canonical plan source and ai_task_update for incremental task status changes.\n */\nexport function projectPlansFromMessages(messages: ChatMessage[]): PlanProjection[] {\n const ordered = [...messages].sort((a, b) => {\n const aTime = Date.parse(a.timestamp || '');\n const bTime = Date.parse(b.timestamp || '');\n return (Number.isNaN(aTime) ? 0 : aTime) - (Number.isNaN(bTime) ? 0 : bTime);\n });\n\n const plans = new Map<string, PlanProjection>();\n\n for (const message of ordered) {\n const timestamp = message.timestamp;\n const data = asRecord(message.data);\n\n if (message.messageType === 'ai_plan' && data) {\n const planPath = asString(data.plan_path);\n const planId = asString(data.plan_id);\n const key = planPath || planId || message.path || message.id || `plan:${timestamp}`;\n const plan = ensurePlan(\n plans,\n key,\n asString(data.title) || message.content || 'Plan',\n timestamp\n );\n\n plan.planPath = planPath ?? plan.planPath;\n plan.planId = planId ?? plan.planId;\n plan.title = asString(data.title) || plan.title;\n setPlanStatus(plan, asString(data.status));\n plan.requiresApproval = Boolean(data.requires_approval) || plan.status === 'pending_approval';\n plan.sourceMessagePath = message.path ?? plan.sourceMessagePath;\n\n const parsedTasks = parsePlanTasks(data.tasks);\n if (parsedTasks.length > 0) {\n plan.tasks = parsedTasks;\n }\n }\n\n if (message.messageType === 'ai_task_update' && data) {\n const planPath = asString(data.plan_path);\n const planId = asString(data.plan_id);\n const key = planPath || planId;\n if (!key) continue;\n\n const plan = ensurePlan(plans, key, 'Plan', timestamp);\n plan.planPath = planPath ?? plan.planPath;\n plan.planId = planId ?? plan.planId;\n setPlanStatus(plan, asString(data.plan_status));\n plan.requiresApproval = plan.status === 'pending_approval';\n\n const taskUpdate: PlanProjectionTask = {\n taskId: asString(data.task_id),\n title: asString(data.task_title) || asString(data.title) || 'Task',\n status: asString(data.status) || 'pending',\n };\n upsertTask(plan, taskUpdate);\n }\n\n if (Array.isArray(message.children)) {\n for (const child of message.children) {\n if (child.type !== 'plan') continue;\n const key = child.path || message.path || message.id || `plan-child:${child.id}:${timestamp}`;\n const plan = ensurePlan(plans, key, child.planTitle || message.content || 'Plan', timestamp);\n plan.title = child.planTitle || plan.title;\n plan.status = child.status || plan.status;\n plan.requiresApproval = plan.status === 'pending_approval';\n if (Array.isArray(child.tasks) && child.tasks.length > 0) {\n plan.tasks = child.tasks.map((task) => ({\n taskId: task.id,\n title: task.title,\n status: task.status,\n delegation: task.delegation ? {\n id: task.delegation.id,\n status: task.delegation.status,\n agentRef: task.delegation.agentRef,\n flowInstanceId: task.delegation.flowInstanceId,\n branch: task.delegation.branch,\n baseBranch: task.delegation.baseBranch,\n result: task.delegation.result,\n error: task.delegation.error,\n } : undefined,\n }));\n }\n }\n }\n }\n\n // Belt-and-suspenders: if every task landed in a terminal state but the\n // plan's own status field is stuck non-terminal (the same message-race\n // that setPlanStatus guards against can also leave it simply never\n // updated), derive completion from the tasks - mirrors the server's\n // auto-complete check in update-task/index.js's updatePlanProgress.\n for (const plan of plans.values()) {\n if (\n !TERMINAL_PLAN_STATUSES.has(plan.status) &&\n plan.tasks.length > 0 &&\n plan.tasks.every((task) => TERMINAL_TASK_STATUSES.has(task.status))\n ) {\n plan.status = 'completed';\n plan.requiresApproval = false;\n }\n }\n\n return [...plans.values()].sort((a, b) => {\n const aTime = Date.parse(a.updatedAt || '');\n const bTime = Date.parse(b.updatedAt || '');\n return (Number.isNaN(bTime) ? 0 : bTime) - (Number.isNaN(aTime) ? 0 : aTime);\n });\n}\n","import type { ChatDoneEvent, ChatEvent } from '../types/chat';\n\n/**\n * Detect safety-net done events emitted by backend recovery paths.\n *\n * These events can appear while async tool calls are still running.\n * They carry `recovered: true` and an empty content payload.\n */\nexport function isRecoveredDoneEvent(event: ChatEvent): event is ChatDoneEvent & { recovered: true } {\n if (event.type !== 'done') return false;\n const done = event as ChatDoneEvent & { recovered?: unknown };\n if (done.recovered !== true) return false;\n return typeof done.content !== 'string' || done.content.trim().length === 0;\n}\n\n","/**\n * Framework-agnostic conversation store for RaisinDB.\n *\n * Manages the full conversation lifecycle: creating conversations, sending\n * messages, streaming responses, persistent SSE subscriptions, message\n * history, tool call tracking, and plan projection. Notifies subscribers\n * on every state change so UI frameworks (React, Svelte, Vue) can bind.\n *\n * @example Svelte 5:\n * ```typescript\n * const store = new ConversationStore({ database: db, conversationPath: '/...' });\n * let snapshot = $state(store.getSnapshot());\n * store.subscribe(s => { snapshot = s; });\n * ```\n *\n * @example React (via useConversation hook):\n * ```tsx\n * const chat = useConversation(React, { database: db, conversationPath: '/...' });\n * ```\n */\n\nimport type { ConversationManager, ConversationSubscription } from '../conversations';\nimport type { StartAnonymousOptions } from '../visitor-chat';\nimport type { Database } from '../database';\nimport type { ChatMessage, ChatEvent, ChatLogEvent } from '../types/chat';\nimport { logger, getLogLevel, LogLevel } from '../logger';\nimport { projectPlansFromMessages, type PlanProjection } from '../utils/plan-projection';\nimport { isRecoveredDoneEvent } from '../utils/chat-events';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Info about an in-flight tool call */\nexport interface ToolCallInfo {\n id: string;\n functionName: string;\n arguments: unknown;\n status: 'running' | 'completed' | 'failed';\n result?: unknown;\n durationMs?: number;\n}\n\n/** Snapshot of the conversation store state */\nexport interface ConversationStoreSnapshot {\n /** The conversation object, if loaded */\n conversation: { conversationPath: string; type: string } | null;\n /** All messages in the conversation */\n messages: ChatMessage[];\n /** Whether the AI is currently generating a response */\n isStreaming: boolean;\n /** Whether the chat is waiting for user input */\n isWaiting: boolean;\n /** Accumulated text from the current streaming response */\n streamingText: string;\n /** Current error, if any */\n error: string | null;\n /** Tracks in-flight tool executions */\n activeToolCalls: ToolCallInfo[];\n /** Deterministic plan/task projection from persisted messages */\n plans: PlanProjection[];\n /** Whether messages are being loaded */\n isLoading: boolean;\n /** Path to the conversation node */\n conversationPath: string | null;\n}\n\n/** Options for creating a ConversationStore */\nexport interface ConversationStoreOptions {\n /** Pre-configured Database instance */\n database: Database;\n /** Resume an existing conversation */\n conversationPath?: string;\n /** Create a new conversation on first message */\n createOptions?: {\n participant: string;\n input?: Record<string, unknown>;\n /**\n * Chat WITHOUT login (a public website): starts or resumes an anonymous\n * visitor conversation (`db.conversations.startAnonymous`). `true`, or the\n * start options. The agent must allow anonymous visitors.\n */\n anonymous?: boolean | StartAnonymousOptions;\n };\n /** Callback for individual chat events */\n onEvent?: (event: ChatEvent) => void;\n /** Streaming inactivity timeout in ms (default: 120000). Auto-recovers when no SSE event arrives within this window. */\n streamingTimeoutMs?: number;\n /** Activity watchdog polling interval in ms (default: 30000). Periodically checks backend turn health while streaming. */\n watchdogIntervalMs?: number;\n}\n\ntype Subscriber = (snapshot: ConversationStoreSnapshot) => void;\n\n// ---------------------------------------------------------------------------\n// ConversationStore\n// ---------------------------------------------------------------------------\n\n/**\n * Framework-agnostic store managing a single conversation's state.\n *\n * Handles the full lifecycle: creating conversations, sending messages,\n * streaming responses, tool call tracking, plan projection, and deferred\n * message reload on turn completion.\n */\nexport class ConversationStore {\n private manager: ConversationManager;\n private subscribers = new Set<Subscriber>();\n private onEvent?: (event: ChatEvent) => void;\n private createOptions?: ConversationStoreOptions['createOptions'];\n\n // Persistent SSE subscription\n private subscription: ConversationSubscription | null = null;\n\n // Internal mutable state\n private _conversationPath: string | null;\n private _messages: ChatMessage[] = [];\n private _isStreaming = false;\n private _isWaiting = false;\n private _streamingText = '';\n private _error: string | null = null;\n private _activeToolCalls: ToolCallInfo[] = [];\n private _isLoading = false;\n private _conversationType: string | null = null;\n\n // Streaming timeout (Fix A)\n private _streamingTimeoutMs: number;\n private _streamingTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Activity watchdog (Fix B)\n private _watchdogIntervalMs: number;\n private _watchdogTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: ConversationStoreOptions) {\n this.manager = options.database.conversations;\n this._conversationPath = options.conversationPath ?? null;\n this.createOptions = options.createOptions;\n this.onEvent = options.onEvent;\n this._streamingTimeoutMs = options.streamingTimeoutMs ?? 120_000;\n this._watchdogIntervalMs = options.watchdogIntervalMs ?? 30_000;\n\n // Auto-subscribe to existing conversation\n if (this._conversationPath) {\n this.ensureSubscription();\n } else if (this.createOptions?.anonymous) {\n // An anonymous store resumes the tab's visitor conversation, if any.\n void this.resumeAnonymous();\n }\n }\n\n private anonymousOptions(): StartAnonymousOptions | null {\n const a = this.createOptions?.anonymous;\n if (!a) return null;\n return a === true ? {} : a;\n }\n\n /**\n * Resume the visitor conversation this tab already has (the session secret\n * and conversation id live in sessionStorage), and load its history. A tab\n * without one starts nothing until the first message.\n */\n private async resumeAnonymous(): Promise<void> {\n const opts = this.anonymousOptions();\n if (!opts || !this.createOptions || this._conversationPath) return;\n const agent = this.createOptions.participant;\n try {\n if (!this.manager.hasStoredAnonymous(agent, opts)) return;\n const convo = await this.manager.startAnonymous(agent, opts);\n if (this._conversationPath) return;\n this._conversationPath = convo.conversationPath;\n this._conversationType = convo.type;\n this.ensureSubscription();\n await this.loadMessages();\n } catch (err) {\n logger.debug('[ConversationStore] No visitor conversation to resume', err);\n }\n }\n\n // ==========================================================================\n // Subscription\n // ==========================================================================\n\n /**\n * Subscribe to state changes.\n * Returns an unsubscribe function.\n */\n subscribe(callback: Subscriber): () => void {\n this.subscribers.add(callback);\n callback(this.getSnapshot());\n return () => { this.subscribers.delete(callback); };\n }\n\n /**\n * Get the current state snapshot.\n */\n getSnapshot(): ConversationStoreSnapshot {\n return {\n conversation: this._conversationPath\n ? { conversationPath: this._conversationPath, type: this._conversationType || 'ai_chat' }\n : null,\n messages: [...this._messages],\n isStreaming: this._isStreaming,\n isWaiting: this._isWaiting,\n streamingText: this._streamingText,\n error: this._error,\n activeToolCalls: [...this._activeToolCalls],\n plans: projectPlansFromMessages(this._messages),\n isLoading: this._isLoading,\n conversationPath: this._conversationPath,\n };\n }\n\n // ==========================================================================\n // Actions\n // ==========================================================================\n\n /**\n * Send a user message and stream the AI response.\n *\n * If no conversation exists yet and `createOptions` was provided,\n * the conversation is created automatically on the first call.\n */\n async sendMessage(content: string): Promise<void> {\n if (this._isStreaming) {\n logger.warn('[ConversationStore] Cannot send message while streaming');\n return;\n }\n\n logger.info('[ConversationStore] Sending message', { path: this._conversationPath });\n this._error = null;\n\n // Create conversation if needed\n if (!this._conversationPath) {\n if (!this.createOptions) {\n throw new Error('No conversationPath and no createOptions provided');\n }\n try {\n const anonymous = this.anonymousOptions();\n const convo = anonymous\n ? await this.manager.startAnonymous(this.createOptions.participant, anonymous)\n : await this.manager.create({\n participant: this.createOptions.participant,\n input: this.createOptions.input,\n });\n this._conversationPath = convo.conversationPath;\n this._conversationType = convo.type;\n\n for (const event of convo.initialEvents ?? []) {\n this.onEvent?.(event);\n this.handleEvent(event);\n }\n\n this.notify();\n } catch (err) {\n this._error = err instanceof Error ? err.message : String(err);\n this.notify();\n return;\n }\n }\n\n // Add user message optimistically\n const userMessage: ChatMessage = {\n role: 'user',\n content,\n timestamp: new Date().toISOString(),\n };\n this._messages.push(userMessage);\n\n // Start streaming\n this._isStreaming = true;\n this._isWaiting = false;\n this._streamingText = '';\n this._activeToolCalls = [];\n this.resetStreamingTimer();\n this.startWatchdog();\n this.notify();\n\n // Use persistent SSE subscription + create message node\n try {\n this.ensureSubscription();\n await this.subscription!.waitUntilConnected();\n await this.manager.createUserMessage(this._conversationPath!, content);\n // Events arrive via the persistent subscription → handleEvent\n } catch (err) {\n logger.error('[ConversationStore] Send failed', err);\n this._error = err instanceof Error ? err.message : String(err);\n this._isStreaming = false;\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n }\n }\n\n /**\n * Load messages from the persisted node tree.\n * Useful for loading history on page reload.\n */\n async loadMessages(): Promise<ChatMessage[]> {\n if (!this._conversationPath) return [];\n this._isLoading = true;\n this.notify();\n try {\n const messages = await this.manager.getMessages(this._conversationPath);\n logger.debug(`[ConversationStore] Loaded ${messages.length} messages`);\n if (messages.length > 0) {\n this._messages = messages;\n // Rebuild tool call state from messages\n this._activeToolCalls = [];\n }\n return messages;\n } finally {\n this._isLoading = false;\n this.notify();\n }\n }\n\n /**\n * Approve a pending plan.\n */\n async approvePlan(planPath: string): Promise<void> {\n await this.manager.approvePlan(planPath);\n }\n\n /**\n * Reject a pending plan.\n */\n async rejectPlan(planPath: string, feedback?: string): Promise<void> {\n await this.manager.rejectPlan(planPath, feedback);\n }\n\n /**\n * Mark a single message as read.\n */\n async markMessageAsRead(messagePath: string): Promise<void> {\n await this.manager.markMessageAsRead(messagePath);\n }\n\n /**\n * Stop the current streaming response.\n */\n stop(): void {\n this._isStreaming = false;\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n }\n\n /**\n * Clean up the store and release resources.\n */\n destroy(): void {\n this.stop();\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.subscription?.unsubscribe();\n this.subscription = null;\n this.subscribers.clear();\n }\n\n /**\n * Get the conversation path.\n */\n getConversationPath(): string | null {\n return this._conversationPath;\n }\n\n // ==========================================================================\n // Internal\n // ==========================================================================\n\n private ensureSubscription(): void {\n if (this.subscription) return;\n if (!this._conversationPath) return;\n\n this.subscription = this.manager.subscribe(\n this._conversationPath,\n (event) => {\n this.onEvent?.(event);\n this.handleEvent(event);\n },\n );\n }\n\n private handleEvent(event: ChatEvent): void {\n logger.debug('[ConversationStore] SSE:', event.type);\n // Every SSE event resets the streaming inactivity timer\n if (this._isStreaming) {\n this.resetStreamingTimer();\n }\n switch (event.type) {\n case 'text_chunk':\n this.markStreamingActivity();\n this._streamingText += event.text;\n this.notify();\n break;\n\n case 'thought_chunk':\n this.markStreamingActivity();\n this.notify();\n break;\n\n case 'assistant_message':\n this._messages.push(event.message);\n this._streamingText = '';\n this.notify();\n break;\n\n case 'tool_call_started': {\n this.markStreamingActivity();\n logger.debug('[ConversationStore] Tool started:', event.functionName);\n const info: ToolCallInfo = {\n id: event.toolCallId,\n functionName: event.functionName,\n arguments: event.arguments,\n status: 'running',\n };\n this._activeToolCalls.push(info);\n this.notify();\n break;\n }\n\n case 'tool_call_completed': {\n this.markStreamingActivity();\n logger.debug('[ConversationStore] Tool completed:', event.toolCallId);\n const idx = this._activeToolCalls.findIndex(tc => tc.id === event.toolCallId);\n if (idx >= 0) {\n this._activeToolCalls[idx] = {\n ...this._activeToolCalls[idx],\n status: event.error ? 'failed' : 'completed',\n result: event.result,\n durationMs: event.durationMs,\n };\n }\n this.notify();\n break;\n }\n\n case 'waiting': {\n logger.info('[ConversationStore] Waiting for input');\n this.flushStreamingText(event.timestamp);\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n // Deferred reload: pick up canonical messages from node tree\n this.deferredReload();\n break;\n }\n\n case 'done': {\n if (isRecoveredDoneEvent(event)) {\n logger.debug('[ConversationStore] Ignoring recovered done event');\n this.reconcileRecoveredDone();\n break;\n }\n const doneEvent = event as import('../types/chat').ChatDoneEvent;\n if (doneEvent.dispatchPhase && doneEvent.dispatchPhase !== 'terminal') {\n logger.debug('[ConversationStore] Ignoring non-terminal done event');\n break;\n }\n logger.info('[ConversationStore] Turn done');\n this.flushStreamingText(event.timestamp);\n // Safety net: if streaming missed chunks, use the complete response from the event\n if (doneEvent.content && !this.lastMessageIsAssistant()) {\n this._messages.push({\n role: (doneEvent.role as ChatMessage['role']) || 'assistant',\n content: doneEvent.content,\n timestamp: event.timestamp,\n senderDisplayName: doneEvent.senderDisplayName,\n finishReason: doneEvent.finishReason,\n });\n }\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n break;\n }\n\n case 'completed': {\n this.flushStreamingText(event.timestamp);\n if (event.messages && event.messages.length > 0) {\n this._messages = event.messages;\n }\n this._isStreaming = false;\n this._isWaiting = false;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n break;\n }\n\n case 'failed':\n logger.error('[ConversationStore] Turn failed', event.error);\n this._error = event.error;\n this._isStreaming = false;\n // A failed turn ends any waiting state too - otherwise UIs keyed on\n // isWaiting show a thinking indicator forever after a failure.\n this._isWaiting = false;\n this._streamingText = '';\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n break;\n\n case 'conversation_created':\n this._conversationPath = event.conversationPath;\n this.notify();\n break;\n\n case 'message_saved':\n // A message was persisted — reload to pick it up, then notify\n this.silentReload().catch(() => {});\n this.notify();\n break;\n\n case 'message_delivered':\n this._messages.push(event.message);\n this.notify();\n break;\n\n case 'log': {\n const logEvent = event as ChatLogEvent;\n const currentLevel = getLogLevel();\n const levelMap: Record<string, LogLevel> = {\n debug: LogLevel.Debug,\n info: LogLevel.Info,\n warn: LogLevel.Warn,\n error: LogLevel.Error,\n };\n const eventLevel = levelMap[logEvent.level] ?? LogLevel.Debug;\n if (currentLevel >= eventLevel) {\n const fn = logEvent.level === 'error' ? console.error\n : logEvent.level === 'warn' ? console.warn\n : logEvent.level === 'debug' ? console.debug\n : console.info;\n fn('[server]', logEvent.message);\n }\n break;\n }\n }\n }\n\n /**\n * Agent activity observed while the store thought the turn was over.\n *\n * This happens when execution continues WITHOUT a user message — plan\n * approval in approve_then_auto / step_by_step (approvePlan() returns an\n * enqueue receipt and the agent starts executing), or agent-initiated\n * follow-ups. The events arrive over the persistent live subscription\n * (no history replay), so activity is always \"now\": re-enter streaming\n * state so UIs show progress and the end-of-turn handling (done/waiting\n * → deferred reload) runs again for this continuation.\n */\n private markStreamingActivity(): void {\n if (this._isStreaming) return;\n logger.debug('[ConversationStore] Agent activity while idle — re-entering streaming state');\n this._isStreaming = true;\n this._isWaiting = false;\n this._streamingText = '';\n this.resetStreamingTimer();\n this.startWatchdog();\n }\n\n /**\n * If accumulated streaming text hasn't been captured in an assistant_message,\n * synthesize one.\n */\n private flushStreamingText(timestamp: string): void {\n if (this._streamingText && !this.lastMessageIsAssistant()) {\n this._messages.push({\n role: 'assistant',\n content: this._streamingText,\n timestamp,\n });\n }\n this._streamingText = '';\n }\n\n private lastMessageIsAssistant(): boolean {\n const last = this._messages[this._messages.length - 1];\n return last?.role === 'assistant';\n }\n\n /**\n * After a turn completes, reload canonical messages from the node tree\n * to catch any events missed during SSE gaps.\n */\n private deferredReload(): void {\n if (!this._conversationPath) return;\n logger.debug('[ConversationStore] Scheduling deferred reload');\n const attemptReload = (delay: number, retriesLeft: number) => {\n setTimeout(async () => {\n try {\n const prevCount = this._messages.length;\n await this.silentReload();\n // If no new messages appeared and retries remain, try again with exponential backoff\n if (this._messages.length === prevCount && retriesLeft > 0) {\n attemptReload(delay * 2, retriesLeft - 1);\n }\n } catch {}\n }, delay);\n };\n attemptReload(500, 2); // 500ms, 1000ms, 2000ms\n }\n\n private async silentReload(): Promise<void> {\n if (!this._conversationPath) return;\n try {\n const fresh = await this.manager.getMessages(this._conversationPath);\n if (fresh.length === 0) return;\n\n // Try incremental merge: match existing messages by role+content\n // to preserve object references (prevents Svelte DOM recreation)\n let matchCount = 0;\n const limit = Math.min(this._messages.length, fresh.length);\n for (let i = 0; i < limit; i++) {\n if (this._messages[i].role !== fresh[i].role ||\n this._messages[i].content !== fresh[i].content) break;\n matchCount++;\n }\n\n if (matchCount === this._messages.length && fresh.length === matchCount) {\n // Same content, possibly different metadata — update silently\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n if (f.finishReason) e.finishReason = f.finishReason;\n }\n await this.rebuildToolCallState();\n return; // No visible change — don't notify\n }\n\n if (matchCount === this._messages.length && fresh.length > matchCount) {\n // Existing messages match prefix — append new ones\n for (let i = matchCount; i < fresh.length; i++) {\n this._messages.push(fresh[i]);\n }\n // Also update metadata on matched messages\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n }\n await this.rebuildToolCallState();\n this.notify(); // New messages to show\n return;\n }\n\n // Fresh is a prefix subset — outbox delivery pending, keep extras\n if (matchCount === fresh.length && fresh.length < this._messages.length) {\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n }\n await this.rebuildToolCallState();\n return; // Keep streaming-flushed extras — message_saved will trigger next reload\n }\n\n // Content mismatch — full replace\n this._messages = fresh;\n await this.rebuildToolCallState();\n this.notify();\n } catch (err) {\n logger.debug('[ConversationStore] Silent reload failed:', err);\n }\n }\n\n // ==========================================================================\n // Streaming timeout (Fix A) — auto-recover on SSE inactivity\n // ==========================================================================\n\n private resetStreamingTimer(): void {\n this.clearStreamingTimer();\n if (!this._isStreaming) return;\n this._streamingTimer = setTimeout(() => {\n if (this._isStreaming) {\n logger.warn('[ConversationStore] Streaming timeout — no SSE activity for', this._streamingTimeoutMs, 'ms');\n this.recoverFromHang();\n }\n }, this._streamingTimeoutMs);\n }\n\n private clearStreamingTimer(): void {\n if (this._streamingTimer) {\n clearTimeout(this._streamingTimer);\n this._streamingTimer = null;\n }\n }\n\n // ==========================================================================\n // Activity watchdog (Fix B) — periodic backend health check\n // ==========================================================================\n\n private startWatchdog(): void {\n this.stopWatchdog();\n if (!this._conversationPath) return;\n this._watchdogTimer = setInterval(async () => {\n if (!this._isStreaming || !this._conversationPath) {\n this.stopWatchdog();\n return;\n }\n try {\n const health = await this.manager.checkTurnHealth(this._conversationPath);\n if (health === 'done' && this._isStreaming) {\n logger.warn('[ConversationStore] Watchdog detected completed turn while still streaming — recovering');\n this.recoverFromHang();\n }\n } catch {\n // Ignore watchdog errors — it's a best-effort check\n }\n }, this._watchdogIntervalMs);\n }\n\n private stopWatchdog(): void {\n if (this._watchdogTimer) {\n clearInterval(this._watchdogTimer);\n this._watchdogTimer = null;\n }\n }\n\n // ==========================================================================\n // Hang recovery (shared by timeout + watchdog)\n // ==========================================================================\n\n private recoverFromHang(): void {\n logger.info('[ConversationStore] Recovering from hang');\n this.clearStreamingTimer();\n this.stopWatchdog();\n this._isStreaming = false;\n this._isWaiting = true;\n this._streamingText = '';\n this.notify();\n // Reload to pick up any persisted state we missed\n this.silentReload().catch(() => {});\n }\n\n /**\n * Reconcile a recovered done event against persisted state.\n * If the backend turn is actually complete, finalize the local state.\n * Otherwise keep streaming active and let normal events continue.\n */\n private reconcileRecoveredDone(): void {\n if (!this._conversationPath) return;\n void this.manager.checkTurnHealth(this._conversationPath).then((health) => {\n if (health !== 'done') return;\n if (!this._isStreaming) return;\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n }).catch(() => {\n // Best effort only; streaming watchdog still protects against hangs.\n });\n }\n\n // ==========================================================================\n // Tool call state rebuild (Fix C) — reconstruct from node tree on reload\n // ==========================================================================\n\n private async rebuildToolCallState(): Promise<void> {\n if (!this._conversationPath) {\n this._activeToolCalls = [];\n return;\n }\n try {\n const pendingCalls = await this.manager.getActiveToolCalls(this._conversationPath);\n if (pendingCalls.length > 0) {\n this._activeToolCalls = pendingCalls.map(tc => ({\n id: tc.id,\n functionName: tc.name,\n arguments: undefined,\n status: 'running' as const,\n }));\n // Keep streaming active if there are pending tool calls\n if (!this._isStreaming) {\n this._isStreaming = true;\n this.resetStreamingTimer();\n this.startWatchdog();\n }\n } else {\n this._activeToolCalls = [];\n // No pending tools — check if we should clear streaming\n if (this._isStreaming) {\n const lastMsg = this._messages[this._messages.length - 1];\n const isLastAssistantDone = lastMsg?.role === 'assistant' &&\n (lastMsg.dispatchPhase\n ? lastMsg.dispatchPhase === 'terminal'\n : (!!lastMsg.finishReason && lastMsg.finishReason !== 'tool_calls'));\n if (isLastAssistantDone) {\n this._isStreaming = false;\n this._isWaiting = true;\n this.clearStreamingTimer();\n this.stopWatchdog();\n }\n }\n }\n } catch {\n this._activeToolCalls = [];\n }\n }\n\n private notify(): void {\n const snapshot = this.getSnapshot();\n for (const sub of this.subscribers) {\n try { sub(snapshot); }\n catch (err) { logger.error('Error in conversation store subscriber:', err); }\n }\n }\n}\n","/**\n * User home path normalization.\n *\n * Some server responses (JWT claims, RAISIN_CURRENT_USER(), identity\n * endpoints) return the user home as a workspace-prefixed path like\n * `/raisin:access_control/users/internal/alice`, while others return the\n * workspace-relative form `/users/internal/alice`. SDK code that builds\n * child paths (inbox, chats, outbox) expects the workspace-relative form.\n */\n\n/** Workspace prefix that may leak into user home paths */\nconst ACCESS_CONTROL_PREFIX = '/raisin:access_control';\n\n/**\n * Normalize a user home path to its workspace-relative form.\n *\n * Strips a leading `/raisin:access_control` workspace prefix and trailing\n * slashes, so `/raisin:access_control/users/internal/alice/` becomes\n * `/users/internal/alice`. Paths without the prefix are returned unchanged\n * (modulo trailing-slash trimming). Returns `null` for empty input.\n *\n * @example\n * ```typescript\n * normalizeHomePath('/raisin:access_control/users/internal/alice');\n * // => '/users/internal/alice'\n * normalizeHomePath('/users/internal/alice');\n * // => '/users/internal/alice'\n * ```\n */\nexport function normalizeHomePath(home: string | null | undefined): string | null {\n if (home === null || home === undefined) return null;\n\n let path = home.trim();\n if (path.length === 0) return null;\n\n if (path === ACCESS_CONTROL_PREFIX) return '/';\n if (path.startsWith(`${ACCESS_CONTROL_PREFIX}/`)) {\n path = path.slice(ACCESS_CONTROL_PREFIX.length);\n }\n\n // Trim trailing slashes (but keep root \"/\")\n while (path.length > 1 && path.endsWith('/')) {\n path = path.slice(0, -1);\n }\n\n return path;\n}\n","/**\n * Multi-conversation list store for RaisinDB.\n *\n * Manages the list of conversations: loading, creating, deleting, marking\n * as read, and optional real-time updates via WebSocket events.\n *\n * Provides a factory (`getConversationStore`) that returns cached\n * per-conversation `ConversationStore` instances.\n *\n * @example Svelte 5:\n * ```typescript\n * const listStore = new ConversationListStore({ database: db, realtime: true });\n * let snapshot = $state(listStore.getSnapshot());\n * listStore.subscribe(s => { snapshot = s; });\n * await listStore.load();\n * ```\n */\n\nimport type { Database } from '../database';\nimport type { ConversationType, ConversationListItem } from '../types/chat';\nimport { ConversationStore, type ConversationStoreOptions } from './conversation-store';\nimport { logger } from '../logger';\nimport { normalizeHomePath } from '../utils/home-path';\n\n/**\n * Build the realtime subscription filters for a user's chats folder.\n *\n * The server matches subscription paths literally with glob semantics:\n * `*` matches exactly one path segment, `**` matches recursively, and there\n * is NO implicit prefix matching — a plain path only matches that exact\n * node. To observe conversations created/updated under the chats folder we\n * therefore need the recursive `/**` suffix.\n *\n * Exported for testing.\n * @internal\n */\nexport function buildChatsSubscriptionFilters(userHome: string): {\n workspace: string;\n path: string;\n event_types: string[];\n} {\n const home = normalizeHomePath(userHome) ?? userHome;\n return {\n workspace: 'raisin:access_control',\n path: `${home}/inbox/chats/**`,\n event_types: ['node:created', 'node:updated'],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Snapshot of the conversation list state */\nexport interface ConversationListSnapshot {\n conversations: ConversationListItem[];\n totalUnreadCount: number;\n isLoading: boolean;\n error: string | null;\n}\n\n/** Options for creating a ConversationListStore */\nexport interface ConversationListStoreOptions {\n database: Database;\n /** Filter by conversation type */\n type?: ConversationType;\n /** Subscribe to WebSocket events for new conversations */\n realtime?: boolean;\n}\n\ntype Subscriber = (snapshot: ConversationListSnapshot) => void;\n\n// ---------------------------------------------------------------------------\n// ConversationListStore\n// ---------------------------------------------------------------------------\n\nexport class ConversationListStore {\n private database: Database;\n private type?: ConversationType;\n private realtime: boolean;\n private subscribers = new Set<Subscriber>();\n\n // Internal state\n private _conversations: ConversationListItem[] = [];\n private _isLoading = false;\n private _error: string | null = null;\n\n // Cached ConversationStore instances\n private _stores = new Map<string, ConversationStore>();\n\n // Real-time subscription\n private _realtimeSubscription: { unsubscribe: () => void } | null = null;\n\n constructor(options: ConversationListStoreOptions) {\n this.database = options.database;\n this.type = options.type;\n this.realtime = options.realtime ?? false;\n }\n\n // ==========================================================================\n // Subscription\n // ==========================================================================\n\n subscribe(callback: Subscriber): () => void {\n this.subscribers.add(callback);\n callback(this.getSnapshot());\n return () => { this.subscribers.delete(callback); };\n }\n\n getSnapshot(): ConversationListSnapshot {\n return {\n conversations: [...this._conversations],\n totalUnreadCount: this._conversations.reduce((sum, c) => sum + (c.unreadCount ?? 0), 0),\n isLoading: this._isLoading,\n error: this._error,\n };\n }\n\n // ==========================================================================\n // Actions\n // ==========================================================================\n\n /**\n * Load conversations from the server.\n */\n async load(): Promise<void> {\n this._isLoading = true;\n this._error = null;\n this.notify();\n\n try {\n const conversations = await this.database.conversations.list({\n type: this.type,\n });\n logger.debug(`[ConversationListStore] Loaded ${conversations.length} conversations`);\n this._conversations = conversations;\n\n if (this.realtime) {\n this.startRealtimeSubscription();\n }\n } catch (err) {\n logger.error('[ConversationListStore] Failed to load conversations', err);\n this._error = err instanceof Error ? err.message : String(err);\n } finally {\n this._isLoading = false;\n this.notify();\n }\n }\n\n /**\n * Create a new conversation.\n */\n async createConversation(options: {\n participant: string;\n subject?: string;\n input?: Record<string, unknown>;\n }): Promise<ConversationListItem> {\n const convo = await this.database.conversations.create(options);\n\n const listItem: ConversationListItem = {\n id: convo.id,\n type: convo.type,\n conversationPath: convo.conversationPath,\n conversationWorkspace: convo.conversationWorkspace,\n agentRef: convo.agentRef,\n participants: convo.participants,\n unreadCount: 0,\n updatedAt: new Date().toISOString(),\n };\n\n this._conversations = [listItem, ...this._conversations];\n this.notify();\n return listItem;\n }\n\n /**\n * Delete a conversation.\n */\n async deleteConversation(conversationPath: string): Promise<void> {\n // Destroy cached store\n const store = this._stores.get(conversationPath);\n if (store) {\n store.destroy();\n this._stores.delete(conversationPath);\n }\n\n await this.database.conversations.delete(conversationPath);\n this._conversations = this._conversations.filter(c => c.conversationPath !== conversationPath);\n this.notify();\n }\n\n /**\n * Mark a conversation as read.\n */\n async markAsRead(conversationPath: string): Promise<void> {\n await this.database.conversations.markAsRead(conversationPath);\n this._conversations = this._conversations.map(c =>\n c.conversationPath === conversationPath ? { ...c, unreadCount: 0 } : c\n );\n this.notify();\n }\n\n /**\n * Get or create a ConversationStore for a specific conversation.\n * Caches instances so repeated calls return the same store.\n */\n getConversationStore(\n conversationPath: string,\n overrides?: Partial<ConversationStoreOptions>,\n ): ConversationStore {\n let store = this._stores.get(conversationPath);\n if (!store) {\n store = new ConversationStore({\n database: this.database,\n conversationPath,\n ...overrides,\n });\n this._stores.set(conversationPath, store);\n }\n return store;\n }\n\n /**\n * Clean up all resources.\n */\n destroy(): void {\n this._realtimeSubscription?.unsubscribe();\n this._realtimeSubscription = null;\n\n for (const store of this._stores.values()) {\n store.destroy();\n }\n this._stores.clear();\n this.subscribers.clear();\n }\n\n // ==========================================================================\n // Internal\n // ==========================================================================\n\n private async startRealtimeSubscription(): Promise<void> {\n if (this._realtimeSubscription) return;\n logger.debug('[ConversationListStore] Starting realtime subscription');\n\n try {\n const workspace = this.database.workspace('raisin:access_control');\n const events = workspace.events();\n\n // We need the user home path to subscribe to the right folder\n const result = await this.database.executeSql(\n `SELECT RAISIN_CURRENT_USER()->>'path'::String as home`,\n );\n const rows = result.rows as unknown as Record<string, unknown>[];\n const userHome = rows?.[0]?.home as string;\n if (!userHome) return;\n\n this._realtimeSubscription = await events.subscribe(\n buildChatsSubscriptionFilters(userHome),\n async (event) => {\n const eventData = event as any;\n const nodeId = eventData.payload?.node_id;\n if (!nodeId) return;\n\n try {\n const nodeResult = await this.database.executeSql(\n `SELECT id, path, node_type, properties, created_at, updated_at FROM 'raisin:access_control' WHERE id = $1 LIMIT 1`,\n [nodeId],\n );\n const row = (nodeResult.rows as unknown as Record<string, unknown>[])?.[0];\n if (!row || row.node_type !== 'raisin:Conversation') return;\n\n const convPath = row.path as string;\n const props = (row.properties ?? {}) as Record<string, unknown>;\n const item: ConversationListItem = {\n id: row.id as string,\n type: (props.conversation_type as ConversationType) ?? 'ai_chat',\n conversationPath: convPath,\n conversationWorkspace: 'raisin:access_control',\n agentRef: props.agent_ref as string | undefined,\n participants: props.participants as string[] | undefined,\n unreadCount: (props.unread_count as number) ?? 0,\n lastMessage: props.last_message as { content: string; sender_id: string; created_at: string } | undefined,\n updatedAt: (row.updated_at ?? row.created_at) as string,\n };\n\n const existingIdx = this._conversations.findIndex(c => c.conversationPath === convPath);\n if (existingIdx >= 0) {\n // Update existing conversation and move to top\n this._conversations.splice(existingIdx, 1);\n }\n this._conversations = [item, ...this._conversations];\n this.notify();\n } catch (err) {\n logger.debug('Failed to process conversation event:', err);\n }\n },\n );\n } catch (err) {\n logger.debug('Failed to start realtime subscription:', err);\n }\n }\n\n private notify(): void {\n const snapshot = this.getSnapshot();\n for (const sub of this.subscribers) {\n try { sub(snapshot); }\n catch (err) { logger.error('Error in conversation list store subscriber:', err); }\n }\n }\n}\n","/**\n * React adapters for the RaisinDB conversation stores.\n *\n * Since the SDK does not depend on React, the hooks accept React as a\n * parameter to avoid a hard dependency while providing full type safety.\n *\n * @example\n * ```tsx\n * import React from 'react';\n * import { useConversation, useConversationList } from '@raisindb/client';\n *\n * function Chat() {\n * const chat = useConversation(React, {\n * database: db,\n * conversationPath: '/...',\n * });\n * return (\n * <div>\n * {chat.messages.map((m, i) => <div key={i}>{m.content}</div>)}\n * {chat.isStreaming && <p>{chat.streamingText}</p>}\n * </div>\n * );\n * }\n * ```\n */\n\nimport {\n ConversationStore,\n type ConversationStoreOptions,\n type ConversationStoreSnapshot,\n} from '../stores/conversation-store';\nimport {\n ConversationListStore,\n type ConversationListStoreOptions,\n type ConversationListSnapshot,\n} from '../stores/conversation-list-store';\nimport type { ChatMessage, ConversationListItem } from '../types/chat';\n\n/** Minimal subset of the React API needed by the hooks */\nexport interface ReactLike {\n useState<T>(initial: T | (() => T)): [T, (value: T | ((prev: T) => T)) => void];\n useEffect(effect: () => void | (() => void), deps?: unknown[]): void;\n useRef<T>(initial: T): { current: T };\n useCallback<T extends (...args: never[]) => unknown>(fn: T, deps: unknown[]): T;\n}\n\n// ---------------------------------------------------------------------------\n// useConversation\n// ---------------------------------------------------------------------------\n\nconst INITIAL_SNAPSHOT: ConversationStoreSnapshot = {\n conversation: null,\n messages: [],\n isStreaming: false,\n isWaiting: false,\n streamingText: '',\n error: null,\n activeToolCalls: [],\n plans: [],\n isLoading: false,\n conversationPath: null,\n};\n\nexport interface UseConversationReturn extends ConversationStoreSnapshot {\n sendMessage: (content: string) => Promise<void>;\n approvePlan: (planPath: string) => Promise<void>;\n rejectPlan: (planPath: string, feedback?: string) => Promise<void>;\n stop: () => void;\n loadMessages: () => Promise<ChatMessage[]>;\n}\n\nexport function useConversation(\n react: ReactLike,\n options: ConversationStoreOptions,\n): UseConversationReturn {\n const { useState, useEffect, useRef, useCallback } = react;\n\n const [snapshot, setSnapshot] = useState<ConversationStoreSnapshot>(INITIAL_SNAPSHOT);\n const storeRef = useRef<ConversationStore | null>(null);\n\n useEffect(() => {\n const store = new ConversationStore(options);\n storeRef.current = store;\n\n const unsubscribe = store.subscribe((next) => setSnapshot(next));\n\n return () => {\n unsubscribe();\n store.destroy();\n storeRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options.conversationPath]);\n\n const sendMessage = useCallback(async (content: string) => {\n await storeRef.current?.sendMessage(content);\n }, []);\n\n const approvePlan = useCallback(async (planPath: string) => {\n await storeRef.current?.approvePlan(planPath);\n }, []);\n\n const rejectPlan = useCallback(async (planPath: string, feedback?: string) => {\n await storeRef.current?.rejectPlan(planPath, feedback);\n }, []);\n\n const stop = useCallback(() => { storeRef.current?.stop(); }, []);\n\n const loadMessages = useCallback(async () => {\n return (await storeRef.current?.loadMessages()) ?? [];\n }, []);\n\n return {\n ...snapshot,\n sendMessage,\n approvePlan,\n rejectPlan,\n stop,\n loadMessages,\n };\n}\n\n// ---------------------------------------------------------------------------\n// useConversationList\n// ---------------------------------------------------------------------------\n\n// isLoading starts TRUE: the hook always calls store.load() in its mount\n// effect, but the first render happens before that effect runs — reporting\n// \"not loading\" there makes consumers mis-resolve an empty list as \"loaded,\n// no conversations\" (e.g. failing to resume the latest conversation).\nconst INITIAL_LIST_SNAPSHOT: ConversationListSnapshot = {\n conversations: [],\n totalUnreadCount: 0,\n isLoading: true,\n error: null,\n};\n\nexport interface UseConversationListReturn extends ConversationListSnapshot {\n createConversation: (options: { participant: string; subject?: string }) => Promise<ConversationListItem>;\n deleteConversation: (conversationPath: string) => Promise<void>;\n markAsRead: (conversationPath: string) => Promise<void>;\n}\n\nexport function useConversationList(\n react: ReactLike,\n options: ConversationListStoreOptions,\n): UseConversationListReturn {\n const { useState, useEffect, useRef, useCallback } = react;\n\n const [snapshot, setSnapshot] = useState<ConversationListSnapshot>(INITIAL_LIST_SNAPSHOT);\n const storeRef = useRef<ConversationListStore | null>(null);\n\n useEffect(() => {\n const store = new ConversationListStore(options);\n storeRef.current = store;\n\n const unsubscribe = store.subscribe((next) => setSnapshot(next));\n store.load();\n\n return () => {\n unsubscribe();\n store.destroy();\n storeRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options.type, options.realtime]);\n\n const createConversation = useCallback(async (opts: { participant: string; subject?: string }) => {\n return storeRef.current!.createConversation(opts);\n }, []);\n\n const deleteConversation = useCallback(async (conversationPath: string) => {\n await storeRef.current?.deleteConversation(conversationPath);\n }, []);\n\n const markAsRead = useCallback(async (conversationPath: string) => {\n await storeRef.current?.markAsRead(conversationPath);\n }, []);\n\n return {\n ...snapshot,\n createConversation,\n deleteConversation,\n markAsRead,\n };\n}\n","/**\n * Error types for the RaisinDB JS SDK.\n *\n * Provides a structured error hierarchy for programmatic error handling.\n * All errors extend RaisinError and include a string `code` field for\n * easy matching in switch statements or error handlers.\n *\n * @example\n * ```typescript\n * import { RaisinError, RaisinConnectionError } from '@raisindb/client';\n *\n * try {\n * await flows.run('/flows/my-flow', {});\n * } catch (err) {\n * if (err instanceof RaisinConnectionError) {\n * console.log('Network issue:', err.code);\n * }\n * }\n * ```\n */\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\n/** Error codes for connection errors */\nexport type ConnectionErrorCode =\n | 'CONNECTION_FAILED'\n | 'CONNECTION_LOST'\n | 'SSE_STREAM_ERROR';\n\n/**\n * Error codes for authentication errors.\n *\n * Includes the well-known codes plus any server-provided code string\n * (e.g. identity endpoints return codes like `LOGIN_FAILED`).\n */\nexport type AuthErrorCode =\n | 'AUTH_UNAUTHORIZED'\n | 'AUTH_FORBIDDEN'\n | 'AUTH_TOKEN_EXPIRED'\n | 'LOGIN_FAILED'\n | 'REGISTRATION_FAILED'\n // eslint-disable-next-line @typescript-eslint/ban-types\n | (string & {});\n\n/** Error codes for flow execution errors */\nexport type FlowErrorCode =\n | 'FLOW_NOT_FOUND'\n | 'FLOW_EXECUTION_FAILED'\n | 'FLOW_INSTANCE_NOT_FOUND'\n | 'FLOW_RESUME_FAILED';\n\n/** Error codes for timeout errors */\nexport type TimeoutErrorCode =\n | 'REQUEST_TIMEOUT'\n | 'CONNECTION_TIMEOUT'\n | 'POLL_TIMEOUT'\n | 'SSE_INACTIVITY_TIMEOUT';\n\n/** Error codes for abort/cancellation errors */\nexport type AbortErrorCode = 'ABORTED';\n\n/** All error codes */\nexport type RaisinErrorCode =\n | ConnectionErrorCode\n | AuthErrorCode\n | FlowErrorCode\n | TimeoutErrorCode\n | AbortErrorCode;\n\n// ============================================================================\n// Error Classes\n// ============================================================================\n\n/**\n * Base error class for all RaisinDB SDK errors.\n *\n * All SDK errors extend this class, making it easy to catch any\n * RaisinDB-specific error with a single `instanceof` check.\n */\nexport class RaisinError extends Error {\n /** String error code for programmatic handling */\n readonly code: RaisinErrorCode;\n /** Optional additional details */\n readonly details?: unknown;\n\n constructor(message: string, code: RaisinErrorCode, details?: unknown) {\n super(message);\n this.name = 'RaisinError';\n this.code = code;\n this.details = details;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Thrown when a network or connection failure occurs.\n *\n * This includes SSE stream errors, fetch failures, and lost connections.\n */\nexport class RaisinConnectionError extends RaisinError {\n /** HTTP status code, if available */\n readonly status?: number;\n\n constructor(message: string, code: ConnectionErrorCode, options?: { status?: number; details?: unknown }) {\n super(message, code, options?.details);\n this.name = 'RaisinConnectionError';\n this.status = options?.status;\n }\n}\n\n/**\n * Thrown when an authentication or authorization failure occurs.\n *\n * This includes 401 Unauthorized, 403 Forbidden, and expired tokens.\n */\nexport class RaisinAuthError extends RaisinError {\n /** HTTP status code (401 or 403) */\n readonly status: number;\n\n constructor(message: string, code: AuthErrorCode, status: number, details?: unknown) {\n super(message, code, details);\n this.name = 'RaisinAuthError';\n this.status = status;\n }\n}\n\n/**\n * Thrown when a flow execution error occurs.\n *\n * This includes flow not found, execution failures, and resume errors.\n */\nexport class RaisinFlowError extends RaisinError {\n /** Flow instance ID, if available */\n readonly instanceId?: string;\n\n constructor(message: string, code: FlowErrorCode, instanceId?: string, details?: unknown) {\n super(message, code, details);\n this.name = 'RaisinFlowError';\n this.instanceId = instanceId;\n }\n}\n\n/**\n * Thrown when a request or operation times out.\n *\n * Includes the timeout duration for debugging.\n */\nexport class RaisinTimeoutError extends RaisinError {\n /** Timeout duration in milliseconds */\n readonly timeoutMs: number;\n\n constructor(message: string, code: TimeoutErrorCode, timeoutMs: number, details?: unknown) {\n super(message, code, details);\n this.name = 'RaisinTimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Thrown when an operation is aborted via AbortSignal.\n */\nexport class RaisinAbortError extends RaisinError {\n constructor(message = 'Operation was aborted') {\n super(message, 'ABORTED');\n this.name = 'RaisinAbortError';\n }\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Classify an HTTP response error into the appropriate RaisinError subclass.\n *\n * @internal\n */\nexport function classifyHttpError(\n status: number,\n message: string,\n details?: unknown,\n): RaisinError {\n if (status === 401) {\n return new RaisinAuthError(message, 'AUTH_UNAUTHORIZED', status, details);\n }\n if (status === 403) {\n return new RaisinAuthError(message, 'AUTH_FORBIDDEN', status, details);\n }\n if (status === 404) {\n return new RaisinFlowError(message, 'FLOW_NOT_FOUND', undefined, details);\n }\n return new RaisinConnectionError(message, 'CONNECTION_FAILED', { status, details });\n}\n","/**\n * SSE (Server-Sent Events) client for streaming flow execution events.\n *\n * Uses fetch-based approach for better control over connection lifecycle,\n * headers, and reconnection compared to the native EventSource API.\n */\n\nimport { logger } from '../logger';\nimport { ReconnectManager, type ReconnectOptions } from '../utils/reconnect';\nimport { RaisinConnectionError, RaisinAuthError, RaisinAbortError, RaisinTimeoutError } from '../errors';\n\n/**\n * Parsed SSE event from the wire format\n */\nexport interface SSEEvent<T = unknown> {\n /** SSE event type (from \"event:\" field) */\n type: string;\n /** Parsed event data */\n data: T;\n /** SSE event ID (from \"id:\" field, if present) */\n id?: string;\n}\n\n/**\n * SSE connection state\n */\nexport type SSEConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';\n\n/**\n * Callback for SSE connection state changes\n */\nexport type SSEStateCallback = (state: SSEConnectionState) => void;\n\n/**\n * Options for creating an SSE client\n */\nexport interface SSEClientOptions {\n /** HTTP headers to include (e.g., Authorization) */\n headers?: Record<string, string>;\n /** Event types to filter for (if empty, all events are passed through) */\n eventTypes?: string[];\n /** Reconnection options (uses ReconnectManager) */\n reconnect?: ReconnectOptions & {\n /** Whether to automatically reconnect on disconnect (default: true) */\n enabled?: boolean;\n };\n /** Custom fetch implementation */\n fetch?: typeof fetch;\n /** Connection state change callback */\n onStateChange?: SSEStateCallback;\n /** Last-Event-ID for resuming streams */\n lastEventId?: string;\n /** External AbortSignal for cancellation */\n signal?: AbortSignal;\n /** HTTP method (default: 'GET'). Use 'POST' to avoid path in URL params. */\n method?: 'GET' | 'POST';\n /** Request body for POST requests (JSON-serialized automatically) */\n body?: unknown;\n /**\n * Inactivity timeout in milliseconds (default: disabled).\n *\n * The timer is reset whenever any bytes arrive on the stream (events,\n * comments/keep-alives, partial frames). If the stream produces nothing\n * for this long — including a fetch that never responds — the connection\n * is torn down. With reconnection enabled the client reconnects; with\n * reconnection disabled the stream ends with a `RaisinTimeoutError`\n * (code `SSE_INACTIVITY_TIMEOUT`).\n */\n inactivityTimeoutMs?: number;\n}\n\n/**\n * Parse a single SSE message block into event type, data, and id.\n *\n * SSE wire format:\n * event: <type>\\n\n * data: <json>\\n\n * id: <id>\\n\n * \\n\n */\nfunction parseSSEBlock(block: string): { event: string; data: string; id?: string } | null {\n let event = 'message';\n let data = '';\n let id: string | undefined;\n\n const lines = block.split('\\n');\n for (const line of lines) {\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n // Accumulate data lines (SSE spec allows multi-line data)\n if (data) data += '\\n';\n data += line.slice(5).trim();\n } else if (line.startsWith('id:')) {\n id = line.slice(3).trim();\n }\n // Lines starting with \":\" are comments (keep-alive), ignore them\n // Empty lines are block separators, handled by the caller\n }\n\n if (!data) return null;\n\n return { event, data, id };\n}\n\n/**\n * Fetch-based SSE client with reconnection, filtering, and AsyncIterable support.\n *\n * @example\n * ```typescript\n * const sse = new SSEClient('https://api.example.com/events/flow/123', {\n * headers: { Authorization: `Bearer ${token}` },\n * eventTypes: ['step_started', 'step_completed', 'flow_completed'],\n * });\n *\n * // AsyncIterable usage\n * for await (const event of sse) {\n * console.log(event.type, event.data);\n * }\n *\n * // Callback usage\n * sse.connect((event) => {\n * console.log(event.type, event.data);\n * });\n *\n * // Cleanup\n * sse.close();\n * ```\n */\nexport class SSEClient<T = unknown> implements AsyncIterable<SSEEvent<T>> {\n private url: string;\n private options: SSEClientOptions;\n private abortController: AbortController | null = null;\n private reconnectManager: ReconnectManager;\n private fetchImpl: typeof fetch;\n private _state: SSEConnectionState = 'disconnected';\n private _lastEventId?: string;\n private eventTypeSet: Set<string> | null;\n private autoReconnect: boolean;\n private externalSignal?: AbortSignal;\n private inactivityTimeoutMs?: number;\n\n constructor(url: string, options: SSEClientOptions = {}) {\n this.url = url;\n this.options = options;\n this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);\n this._lastEventId = options.lastEventId;\n this.autoReconnect = options.reconnect?.enabled ?? true;\n this.externalSignal = options.signal;\n this.inactivityTimeoutMs = options.inactivityTimeoutMs;\n this.eventTypeSet = options.eventTypes?.length\n ? new Set(options.eventTypes)\n : null;\n\n this.reconnectManager = new ReconnectManager({\n initialDelay: options.reconnect?.initialDelay ?? 1000,\n maxDelay: options.reconnect?.maxDelay ?? 30000,\n backoffMultiplier: options.reconnect?.backoffMultiplier ?? 2,\n maxAttempts: options.reconnect?.maxAttempts ?? Infinity,\n });\n }\n\n /** Current connection state */\n get state(): SSEConnectionState {\n return this._state;\n }\n\n /**\n * Returns a promise that resolves once the SSE connection is established.\n *\n * Useful for ensuring the connection is ready before triggering events\n * on the server side (e.g., calling resume before consuming the stream).\n * If the client is already connected, resolves immediately.\n */\n waitUntilConnected(): Promise<void> {\n if (this._state === 'connected') return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const originalOnState = this.options.onStateChange;\n this.options.onStateChange = (state) => {\n originalOnState?.(state);\n if (state === 'connected') {\n // Restore original callback and resolve\n this.options.onStateChange = originalOnState;\n resolve();\n } else if (state === 'disconnected') {\n this.options.onStateChange = originalOnState;\n reject(new Error('SSE connection failed'));\n }\n };\n });\n }\n\n /** Last received event ID */\n get lastEventId(): string | undefined {\n return this._lastEventId;\n }\n\n private setState(state: SSEConnectionState): void {\n if (this._state !== state) {\n this._state = state;\n this.options.onStateChange?.(state);\n }\n }\n\n /**\n * Connect and consume events via callback.\n *\n * @param onEvent - Called for each matching SSE event\n * @param onError - Called when an error occurs (optional)\n */\n async connect(\n onEvent: (event: SSEEvent<T>) => void,\n onError?: (error: Error) => void,\n ): Promise<void> {\n this.close();\n await this.startStream(onEvent, onError);\n }\n\n /**\n * Close the SSE connection and stop reconnection attempts.\n */\n close(): void {\n this.reconnectManager.cancelReconnect();\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n this.setState('disconnected');\n }\n\n /**\n * AsyncIterable interface: allows `for await (const event of sseClient)`.\n *\n * The iterator ends when close() is called or the server closes the stream\n * (and auto-reconnect is disabled or max attempts are exhausted).\n */\n async *[Symbol.asyncIterator](): AsyncIterator<SSEEvent<T>> {\n // Use a queue and resolve-based approach for backpressure\n type QueueItem =\n | { done: false; value: SSEEvent<T> }\n | { done: true; error?: Error };\n\n const queue: QueueItem[] = [];\n let resolve: ((item: QueueItem) => void) | null = null;\n let finished = false;\n\n const push = (item: QueueItem) => {\n if (finished) return;\n if (resolve) {\n const r = resolve;\n resolve = null;\n r(item);\n } else {\n queue.push(item);\n }\n };\n\n const pull = (): Promise<QueueItem> => {\n const queued = queue.shift();\n if (queued) return Promise.resolve(queued);\n return new Promise<QueueItem>((r) => {\n resolve = r;\n });\n };\n\n // Start streaming in the background\n this.startStream(\n (event) => push({ done: false, value: event }),\n (error) => push({ done: true, error }),\n ).then(() => {\n push({ done: true });\n }).catch((error) => {\n push({ done: true, error });\n });\n\n try {\n while (!finished) {\n const item = await pull();\n if (item.done) {\n finished = true;\n if (item.error) {\n throw item.error;\n }\n return;\n }\n yield item.value;\n }\n } finally {\n finished = true;\n this.close();\n }\n }\n\n /**\n * Core streaming loop: connects, reads the SSE stream, and handles reconnection.\n */\n private async startStream(\n onEvent: (event: SSEEvent<T>) => void,\n onError?: (error: Error) => void,\n ): Promise<void> {\n while (true) {\n try {\n await this.readStream(onEvent);\n\n // Stream ended cleanly (server closed connection)\n if (!this.autoReconnect || this._state === 'disconnected') {\n return;\n }\n\n // Schedule reconnection\n this.setState('reconnecting');\n const scheduled = await this.waitForReconnect();\n if (!scheduled) {\n // Max attempts reached\n logger.warn('SSE max reconnection attempts reached');\n this.setState('disconnected');\n return;\n }\n } catch (error) {\n if (this._state === 'disconnected') {\n // close() was called, exit cleanly\n return;\n }\n\n const err = error instanceof Error ? error : new Error(String(error));\n\n // AbortError means close() was called or external AbortSignal fired\n if (err.name === 'AbortError' || err instanceof RaisinAbortError) {\n return;\n }\n\n // Inactivity timeout: reconnect silently if enabled, otherwise end\n // the stream with the typed timeout error.\n if (err instanceof RaisinTimeoutError && err.code === 'SSE_INACTIVITY_TIMEOUT') {\n logger.warn('SSE inactivity timeout:', err.message);\n if (!this.autoReconnect) {\n this.setState('disconnected');\n onError?.(err);\n throw err;\n }\n this.setState('reconnecting');\n const scheduled = await this.waitForReconnect();\n if (!scheduled) {\n logger.warn('SSE max reconnection attempts reached');\n this.setState('disconnected');\n onError?.(err);\n throw err;\n }\n continue;\n }\n\n logger.error('SSE connection error:', err.message);\n onError?.(err);\n\n if (!this.autoReconnect) {\n this.setState('disconnected');\n throw err;\n }\n\n // Schedule reconnection\n this.setState('reconnecting');\n const scheduled = await this.waitForReconnect();\n if (!scheduled) {\n logger.warn('SSE max reconnection attempts reached');\n this.setState('disconnected');\n throw err;\n }\n }\n }\n }\n\n /**\n * Perform a single fetch and read the response body as SSE stream.\n */\n private async readStream(onEvent: (event: SSEEvent<T>) => void): Promise<void> {\n this.abortController = new AbortController();\n this.setState('connecting');\n\n // If an external signal is provided, forward its abort to our internal controller\n if (this.externalSignal) {\n if (this.externalSignal.aborted) {\n this.abortController.abort();\n } else {\n const ctrl = this.abortController;\n this.externalSignal.addEventListener('abort', () => ctrl.abort(), { once: true });\n }\n }\n\n const headers: Record<string, string> = {\n Accept: 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...this.options.headers,\n };\n\n // Resume from last event ID if available\n if (this._lastEventId) {\n headers['Last-Event-ID'] = this._lastEventId;\n }\n\n const method = this.options.method ?? 'GET';\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: this.abortController.signal,\n };\n if (method === 'POST' && this.options.body !== undefined) {\n (fetchOptions.headers as Record<string, string>)['Content-Type'] = 'application/json';\n fetchOptions.body = JSON.stringify(this.options.body);\n }\n\n const response = await this.withInactivityTimeout(\n this.fetchImpl(this.url, fetchOptions),\n );\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n const msg = `SSE connection failed: ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`;\n if (response.status === 401 || response.status === 403) {\n throw new RaisinAuthError(\n msg,\n response.status === 401 ? 'AUTH_UNAUTHORIZED' : 'AUTH_FORBIDDEN',\n response.status,\n );\n }\n throw new RaisinConnectionError(msg, 'SSE_STREAM_ERROR', { status: response.status });\n }\n\n if (!response.body) {\n throw new RaisinConnectionError('SSE response has no body', 'SSE_STREAM_ERROR');\n }\n\n this.setState('connected');\n this.reconnectManager.reset();\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await this.withInactivityTimeout(reader.read());\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // SSE events are separated by double newlines\n const blocks = buffer.split('\\n\\n');\n // Last element is incomplete, keep it in the buffer\n buffer = blocks.pop() ?? '';\n\n for (const block of blocks) {\n const trimmed = block.trim();\n if (!trimmed) continue;\n\n const parsed = parseSSEBlock(trimmed);\n if (!parsed) continue;\n\n // Track event ID for reconnection\n if (parsed.id) {\n this._lastEventId = parsed.id;\n }\n\n // Apply event type filter\n if (this.eventTypeSet && !this.eventTypeSet.has(parsed.event)) {\n continue;\n }\n\n // Parse JSON data\n let data: T;\n try {\n data = JSON.parse(parsed.data) as T;\n } catch {\n // If data isn't valid JSON, pass it as-is\n data = parsed.data as unknown as T;\n }\n\n const sseEvent = {\n type: parsed.event,\n data,\n id: parsed.id,\n };\n onEvent(sseEvent);\n }\n }\n } finally {\n try {\n reader.releaseLock();\n } catch {\n // Releasing while a read is pending can throw in some implementations\n }\n }\n }\n\n /**\n * Race a stream operation against the inactivity timeout.\n *\n * If no bytes (or response) arrive within `inactivityTimeoutMs`, the\n * internal connection is aborted and the operation rejects with a\n * `RaisinTimeoutError` (code `SSE_INACTIVITY_TIMEOUT`). The timer is\n * effectively reset on every chunk because each `reader.read()` is\n * wrapped individually.\n */\n private withInactivityTimeout<T>(promise: Promise<T>): Promise<T> {\n const timeoutMs = this.inactivityTimeoutMs;\n if (!timeoutMs || timeoutMs <= 0) return promise;\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n // Swallow the eventual rejection of the underlying promise\n // (e.g. AbortError after we abort) to avoid unhandled rejections.\n promise.catch(() => undefined);\n this.abortController?.abort();\n reject(\n new RaisinTimeoutError(\n `SSE stream inactive for ${timeoutMs}ms`,\n 'SSE_INACTIVITY_TIMEOUT',\n timeoutMs,\n ),\n );\n }, timeoutMs);\n\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n }\n\n /**\n * Wait for the reconnect manager to schedule a reconnection attempt.\n * Returns true if reconnection was scheduled, false if max attempts reached.\n */\n private waitForReconnect(): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const scheduled = this.reconnectManager.scheduleReconnect(() => {\n resolve(true);\n });\n if (!scheduled) {\n resolve(false);\n }\n });\n }\n}\n","/**\n * FlowClient for executing and monitoring RaisinDB flows.\n *\n * Provides methods to:\n * - Run a flow by path and receive an instance ID\n * - Stream real-time execution events via SSE\n * - Resume a waiting flow with external data\n * - Respond to human tasks within a flow\n *\n * @example\n * ```typescript\n * import { RaisinHttpClient } from '@raisindb/client';\n * import { FlowClient } from '@raisindb/client/flow-client';\n *\n * const httpClient = new RaisinHttpClient('http://localhost:8081');\n * await httpClient.authenticate({ username: 'admin', password: 'admin' });\n *\n * const flows = new FlowClient(httpClient, 'my-repo');\n *\n * // Start a flow\n * const { instance_id } = await flows.run('/flows/my-flow', { key: 'value' });\n *\n * // Stream events\n * for await (const event of flows.streamEvents(instance_id)) {\n * console.log(event.type, event);\n * if (event.type === 'flow_completed') break;\n * }\n * ```\n */\n\nimport type { AuthManager } from './auth';\nimport type { FlowsApi } from './flows';\nimport type {\n FlowRunResponse,\n FlowExecutionEvent,\n FlowInstanceStatusResponse,\n FlowCompletedEvent,\n FlowFailedEvent,\n} from './types/flow';\nimport { SSEClient, type SSEEvent } from './streaming/sse-client';\nimport {\n classifyHttpError,\n RaisinAbortError,\n RaisinTimeoutError,\n} from './errors';\n\n/** Options for creating a FlowClient */\nexport interface FlowClientOptions {\n /** Request timeout in milliseconds (default: 60000 for long-running flows) */\n requestTimeout?: number;\n /** Custom fetch implementation */\n fetch?: typeof fetch;\n}\n\n/** Result from `runAndWait()` */\nexport interface FlowRunResult {\n /** Flow instance ID */\n instanceId: string;\n /** Terminal status */\n status: 'completed' | 'failed';\n /** Flow output (when completed) */\n output?: unknown;\n /** Error message (when failed) */\n error?: string;\n}\n\n/** Result from `runAndCollect()` */\nexport interface FlowCollectResult {\n /** Flow instance ID */\n instanceId: string;\n /** All collected events */\n events: FlowExecutionEvent[];\n}\n\n/**\n * Minimal interface the FlowClient needs from the HTTP client.\n * This avoids a hard dependency on the full RaisinHttpClient class.\n */\ninterface HttpClientLike {\n /** Get the auth manager for token access */\n getAuthManager(): AuthManager;\n}\n\n/**\n * Client for executing and monitoring RaisinDB flows.\n *\n * Uses the HTTP REST API for flow operations and SSE for real-time event streaming.\n */\nexport class FlowClient {\n private baseUrl: string;\n private repository: string;\n private authManager: AuthManager;\n private fetchImpl: typeof fetch;\n private requestTimeout: number;\n private flowsApi?: FlowsApi;\n\n /**\n * Create a new FlowClient.\n *\n * @param baseUrl - Base HTTP URL of the RaisinDB server (e.g., \"http://localhost:8081\")\n * @param repository - Repository name\n * @param authManager - Auth manager for token access\n * @param options - Additional options\n * @param flowsApi - Optional FlowsApi for WebSocket-based flow operations\n */\n constructor(\n baseUrl: string,\n repository: string,\n authManager: AuthManager,\n options: FlowClientOptions = {},\n flowsApi?: FlowsApi,\n ) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n this.repository = repository;\n this.authManager = authManager;\n this.fetchImpl = options.fetch ?? fetch;\n this.requestTimeout = options.requestTimeout ?? 60000;\n this.flowsApi = flowsApi;\n }\n\n /**\n * Create a FlowClient from an existing RaisinHttpClient.\n *\n * @param httpClient - An authenticated RaisinHttpClient instance\n * @param baseUrl - Base HTTP URL (same as the one used for the HTTP client)\n * @param repository - Repository name\n * @param options - Additional options\n */\n static fromHttpClient(\n httpClient: HttpClientLike,\n baseUrl: string,\n repository: string,\n options: FlowClientOptions = {},\n flowsApi?: FlowsApi,\n ): FlowClient {\n return new FlowClient(\n baseUrl,\n repository,\n httpClient.getAuthManager(),\n options,\n flowsApi,\n );\n }\n\n // ==========================================================================\n // Flow Execution\n // ==========================================================================\n\n /**\n * Execute a flow by path.\n *\n * Creates a FlowInstance and queues it for execution. Returns immediately\n * with the instance ID - use `streamEvents()` to monitor progress.\n *\n * @param flowPath - Path to the raisin:Flow node (e.g., \"/flows/my-flow\")\n * @param input - Input data passed to the flow\n * @returns Flow run response with instance_id and job_id\n *\n * @example\n * ```typescript\n * const result = await flows.run('/flows/process-order', {\n * orderId: 'ord-123',\n * items: [{ sku: 'ABC', qty: 2 }],\n * });\n * console.log('Instance:', result.instance_id);\n * ```\n */\n async run(\n flowPath: string,\n input: Record<string, unknown> = {},\n options?: { signal?: AbortSignal },\n ): Promise<FlowRunResponse> {\n if (this.flowsApi) {\n return this.flowsApi.run(flowPath, input);\n }\n const response = await this.request<FlowRunResponse>({\n method: 'POST',\n path: `/api/flows/${this.repository}/run`,\n body: { flow_path: flowPath, input },\n signal: options?.signal,\n });\n return response;\n }\n\n // ==========================================================================\n // Instance Status\n // ==========================================================================\n\n /**\n * Get the current status of a flow instance.\n *\n * Returns the instance status, variables, and metadata. Useful for\n * polling-based approaches where SSE is not reliable (e.g., initial\n * flow execution where events may be missed).\n *\n * @param instanceId - Flow instance ID (from `run()`)\n * @returns Instance status including variables and flow path\n *\n * @example\n * ```typescript\n * const status = await flows.getInstanceStatus('instance-123');\n * console.log('Status:', status.status); // 'waiting', 'completed', etc.\n * ```\n */\n async getInstanceStatus(\n instanceId: string,\n options?: { signal?: AbortSignal },\n ): Promise<FlowInstanceStatusResponse> {\n if (this.flowsApi) {\n return this.flowsApi.getInstanceStatus(instanceId);\n }\n return this.request<FlowInstanceStatusResponse>({\n method: 'GET',\n path: `/api/flows/${this.repository}/instances/${instanceId}`,\n signal: options?.signal,\n });\n }\n\n // ==========================================================================\n // Event Streaming (SSE)\n // ==========================================================================\n\n /**\n * Stream real-time execution events for a flow instance via SSE.\n *\n * Returns an async iterable that yields FlowExecutionEvent objects.\n * The stream automatically closes when a terminal event is received\n * (flow_completed or flow_failed).\n *\n * @param instanceId - Flow instance ID (from `run()`)\n * @returns Async iterable of flow execution events\n *\n * @example\n * ```typescript\n * const { instance_id } = await flows.run('/flows/my-flow', { key: 'value' });\n *\n * for await (const event of flows.streamEvents(instance_id)) {\n * switch (event.type) {\n * case 'step_started':\n * console.log(`Step ${event.node_id} started`);\n * break;\n * case 'step_completed':\n * console.log(`Step ${event.node_id} completed in ${event.duration_ms}ms`);\n * break;\n * case 'text_chunk':\n * process.stdout.write(event.text);\n * break;\n * case 'flow_completed':\n * console.log('Flow done:', event.output);\n * break;\n * case 'flow_failed':\n * console.error('Flow failed:', event.error);\n * break;\n * }\n * }\n * ```\n */\n async *streamEvents(\n instanceId: string,\n options?: { signal?: AbortSignal },\n ): AsyncIterable<FlowExecutionEvent> {\n if (this.flowsApi) {\n const sub = await this.flowsApi.subscribeEvents(instanceId);\n try {\n for await (const event of sub.events) {\n if (options?.signal?.aborted) throw new RaisinAbortError();\n yield event;\n if (event.type === 'flow_completed' || event.type === 'flow_failed') {\n return;\n }\n }\n } finally {\n await sub.unsubscribe();\n }\n return;\n }\n const url = `${this.baseUrl}/api/flows/${this.repository}/instances/${instanceId}/events`;\n const headers: Record<string, string> = {};\n\n const token = this.authManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n const sse = new SSEClient<FlowExecutionEvent>(url, {\n headers,\n eventTypes: ['flow-event', 'message'],\n reconnect: { enabled: false },\n fetch: this.fetchImpl,\n signal: options?.signal,\n });\n\n try {\n for await (const sseEvent of sse) {\n const event = sseEvent.data;\n yield event;\n\n // Stop on terminal events\n if (\n event.type === 'flow_completed' ||\n event.type === 'flow_failed'\n ) {\n return;\n }\n }\n } finally {\n sse.close();\n }\n }\n\n /**\n * Create an eagerly-connected event stream for a flow instance.\n *\n * Unlike `streamEvents()` (a lazy async generator), this method opens the\n * SSE connection immediately and waits until it is established before\n * returning. This is critical for resume flows: you must subscribe to\n * events BEFORE calling `resume()`, otherwise events emitted between\n * the resume and the SSE connection are lost.\n *\n * @param instanceId - Flow instance ID\n * @returns Object with an async iterable `events` and a `close()` method\n */\n async createEventStream(\n instanceId: string,\n options?: { signal?: AbortSignal },\n ): Promise<{\n events: AsyncIterable<FlowExecutionEvent>;\n close: () => void;\n }> {\n if (this.flowsApi) {\n const sub = await this.flowsApi.subscribeEvents(instanceId);\n return {\n events: sub.events,\n close: () => { sub.unsubscribe(); },\n };\n }\n const url = `${this.baseUrl}/api/flows/${this.repository}/instances/${instanceId}/events`;\n const headers: Record<string, string> = {};\n\n const token = this.authManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n const sse = new SSEClient<FlowExecutionEvent>(url, {\n headers,\n eventTypes: ['flow-event', 'message'],\n reconnect: { enabled: false },\n fetch: this.fetchImpl,\n signal: options?.signal,\n });\n\n // Kick-start the async generator to initiate the SSE connection.\n // Async generators are lazy — the body (which calls startStream)\n // only executes when .next() is called.\n const iterator = sse[Symbol.asyncIterator]();\n let pendingFirst: Promise<IteratorResult<SSEEvent<FlowExecutionEvent>>> | null = iterator.next();\n\n // Now that startStream is running, wait for the HTTP response\n await sse.waitUntilConnected();\n\n // Wrap in a terminal-event-aware iterable\n const events: AsyncIterable<FlowExecutionEvent> = {\n [Symbol.asyncIterator]() {\n return {\n async next() {\n let result;\n if (pendingFirst) {\n result = await pendingFirst;\n pendingFirst = null;\n } else {\n result = await iterator.next();\n }\n if (result.done) return { done: true as const, value: undefined };\n const event = result.value.data;\n return { done: false, value: event };\n },\n async return() {\n await iterator.return?.();\n sse.close();\n return { done: true as const, value: undefined };\n },\n };\n },\n };\n\n return { events, close: () => sse.close() };\n }\n\n // ==========================================================================\n // Flow Resume & Human Task\n // ==========================================================================\n\n /**\n * Resume a flow instance that is in a waiting state.\n *\n * Use this when a flow is waiting for external input (tool results,\n * external events, etc.). The resume data is passed to the waiting step.\n *\n * @param instanceId - Flow instance ID\n * @param data - Resume data to pass to the waiting step\n *\n * @example\n * ```typescript\n * await flows.resume('instance-123', {\n * tool_name: 'get_weather',\n * result: { temperature: 72, conditions: 'sunny' },\n * });\n * ```\n */\n async resume(\n instanceId: string,\n data: unknown,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n if (this.flowsApi) {\n await this.flowsApi.resume(instanceId, data);\n return;\n }\n await this.request<void>({\n method: 'POST',\n path: `/api/flows/${this.repository}/instances/${instanceId}/resume`,\n body: { resume_data: data },\n signal: options?.signal,\n });\n }\n\n /**\n * Respond to a human task within a flow.\n *\n * When a flow is waiting on a human_task step (approval, input, review),\n * use this method to submit the response. Completion is validated\n * (caller must be the task's assignee or an admin) and resumes the flow.\n *\n * Note: prefer `InboxApi` for full inbox functionality (listing tasks,\n * task details); this is a convenience passthrough to the same endpoint.\n *\n * @param instanceId - Flow instance ID (unused; kept for API stability)\n * @param taskId - The inbox task node ID (or path)\n * @param response - The response data\n *\n * @example\n * ```typescript\n * // Approve a task\n * await flows.respondToHumanTask('instance-123', 'task-abc', {\n * action: 'approve',\n * comment: 'Looks good!',\n * });\n * ```\n */\n async respondToHumanTask(\n _instanceId: string,\n taskId: string,\n response: unknown,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n await this.request<void>({\n method: 'POST',\n path: `/api/inbox/${this.repository}/tasks/${encodeURIComponent(taskId)}/complete`,\n body: { response },\n signal: options?.signal,\n });\n }\n\n // ==========================================================================\n // Convenience Methods\n // ==========================================================================\n\n /**\n * Run a flow and wait for the final result.\n *\n * Starts the flow, streams all events, and returns the terminal outcome.\n * This is a one-shot helper for fire-and-forget flows where you only\n * care about the final output.\n *\n * @param flowPath - Path to the raisin:Flow node\n * @param input - Input data passed to the flow\n * @param options - Optional abort signal\n * @returns The flow outcome including output or error\n *\n * @example\n * ```typescript\n * const result = await flows.runAndWait('/flows/process-order', { orderId: '123' });\n * if (result.status === 'completed') {\n * console.log('Output:', result.output);\n * }\n * ```\n */\n async runAndWait(\n flowPath: string,\n input: Record<string, unknown> = {},\n options?: { signal?: AbortSignal },\n ): Promise<FlowRunResult> {\n const { instance_id } = await this.run(flowPath, input, options);\n\n for await (const event of this.streamEvents(instance_id, options)) {\n if (event.type === 'flow_completed') {\n return {\n instanceId: instance_id,\n status: 'completed',\n output: (event as FlowCompletedEvent).output,\n };\n }\n if (event.type === 'flow_failed') {\n return {\n instanceId: instance_id,\n status: 'failed',\n error: (event as FlowFailedEvent).error,\n };\n }\n }\n\n return { instanceId: instance_id, status: 'failed', error: 'Stream ended without terminal event' };\n }\n\n /**\n * Run a flow and collect all events into an array.\n *\n * Starts the flow, streams all events, and returns them as a collected array.\n * Useful for testing, logging, or post-processing all flow events.\n *\n * @param flowPath - Path to the raisin:Flow node\n * @param input - Input data passed to the flow\n * @param options - Optional abort signal\n * @returns The instance ID and all collected events\n *\n * @example\n * ```typescript\n * const { events } = await flows.runAndCollect('/flows/etl-pipeline', { source: 'db' });\n * const steps = events.filter(e => e.type === 'step_completed');\n * console.log(`Completed ${steps.length} steps`);\n * ```\n */\n async runAndCollect(\n flowPath: string,\n input: Record<string, unknown> = {},\n options?: { signal?: AbortSignal },\n ): Promise<FlowCollectResult> {\n const { instance_id } = await this.run(flowPath, input, options);\n const events: FlowExecutionEvent[] = [];\n\n for await (const event of this.streamEvents(instance_id, options)) {\n events.push(event);\n }\n\n return { instanceId: instance_id, events };\n }\n\n // ==========================================================================\n // Internal HTTP request helper\n // ==========================================================================\n\n private async request<T>(options: {\n method: string;\n path: string;\n body?: unknown;\n signal?: AbortSignal;\n }): Promise<T> {\n const url = `${this.baseUrl}${options.path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n const token = this.authManager.getAccessToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.requestTimeout);\n\n // Forward external signal to our internal controller\n if (options.signal) {\n if (options.signal.aborted) {\n clearTimeout(timeoutId);\n throw new RaisinAbortError();\n }\n options.signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const response = await this.fetchImpl(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage: string;\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.message || errorJson.error || errorText;\n } catch {\n errorMessage =\n errorText || `HTTP ${response.status}: ${response.statusText}`;\n }\n\n throw classifyHttpError(response.status, errorMessage);\n }\n\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n return (await response.json()) as T;\n }\n\n return undefined as unknown as T;\n } catch (error) {\n clearTimeout(timeoutId);\n // Re-throw our own errors as-is\n if (error instanceof RaisinAbortError || error instanceof RaisinTimeoutError) {\n throw error;\n }\n if (error instanceof Error && error.name === 'AbortError') {\n // Distinguish external abort from internal timeout\n if (options.signal?.aborted) {\n throw new RaisinAbortError();\n }\n throw new RaisinTimeoutError(\n `Request timeout after ${this.requestTimeout}ms`,\n 'REQUEST_TIMEOUT',\n this.requestTimeout,\n );\n }\n throw error;\n }\n }\n}\n","/**\n * React flow adapter for RaisinDB.\n *\n * Provides a `useFlow` hook that wraps the FlowClient for executing\n * and monitoring RaisinDB flows in React applications. Returns reactive\n * state tracking flow events, status, and providing control actions.\n *\n * Since this SDK package does not depend on React, the hook accepts\n * React as a parameter via the `ReactLike` interface.\n *\n * @example\n * ```tsx\n * import React from 'react';\n * import { useFlow } from '@raisindb/client';\n *\n * function OrderProcessor() {\n * const flow = useFlow(React, {\n * baseUrl: 'http://localhost:8081',\n * repository: 'my-repo',\n * authManager,\n * });\n *\n * const handleRun = async () => {\n * await flow.run('/flows/process-order', { orderId: 'ord-123' });\n * };\n *\n * return (\n * <div>\n * <button onClick={handleRun} disabled={flow.isRunning}>Run</button>\n * <p>Status: {flow.status}</p>\n * {flow.events.map((e, i) => <div key={i}>{e.type}</div>)}\n * {flow.error && <p className=\"error\">{flow.error}</p>}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { FlowClient } from '../flow-client';\nimport type { FlowClientOptions } from '../flow-client';\nimport type { AuthManager } from '../auth';\nimport type { FlowsApi } from '../flows';\nimport type { Database } from '../database';\nimport type { FlowExecutionEvent } from '../types/flow';\nimport type { ReactLike } from './react-conversation';\n\n/** Options for creating the FlowClient within useFlow */\nexport interface UseFlowOptions {\n /**\n * Pre-configured Database instance (from `client.database()`).\n * When provided, `baseUrl`, `repository`, `authManager`, and `flowsApi` are derived\n * automatically — no manual wiring needed.\n */\n database?: Database;\n /** Base HTTP URL of the RaisinDB server */\n baseUrl?: string;\n /** Repository name */\n repository?: string;\n /** Auth manager for token access */\n authManager?: AuthManager;\n /** Additional FlowClient options */\n clientOptions?: FlowClientOptions;\n /** Optional FlowsApi for WebSocket-based flow operations */\n flowsApi?: FlowsApi;\n}\n\n/** Flow execution status */\nexport type FlowStatus = 'idle' | 'running' | 'waiting' | 'completed' | 'failed';\n\n/** Return value from the useFlow hook */\nexport interface UseFlowReturn {\n /** Collected flow execution events */\n events: FlowExecutionEvent[];\n /** Current flow status */\n status: FlowStatus;\n /** Whether a flow is currently running */\n isRunning: boolean;\n /** Current error, if any */\n error: string | null;\n /** Output from the completed flow */\n output: unknown | null;\n /** Current flow instance ID, if running */\n instanceId: string | null;\n /** Run a flow by path with optional input data */\n run: (flowPath: string, input?: Record<string, unknown>) => Promise<void>;\n /** Resume a waiting flow with data */\n resume: (data: unknown) => Promise<void>;\n /** Reset the hook state (clear events, status, error) */\n reset: () => void;\n}\n\n/**\n * React hook for executing and monitoring RaisinDB flows.\n *\n * Manages the full lifecycle: starting a flow, streaming events,\n * resuming waiting flows, and tracking completion status.\n * Auto-cleans up on component unmount.\n *\n * @param react - The React instance (pass `React` or `{ useState, useEffect, useRef, useCallback }`)\n * @param options - Flow client options (baseUrl, repository, authManager, etc.)\n * @returns Reactive flow state and action functions\n *\n * @example\n * ```tsx\n * import React from 'react';\n * import { useFlow } from '@raisindb/client';\n *\n * function DataPipeline() {\n * const flow = useFlow(React, {\n * baseUrl: 'http://localhost:8081',\n * repository: 'my-repo',\n * authManager,\n * });\n *\n * return (\n * <div>\n * <button onClick={() => flow.run('/flows/etl', { source: 'api' })}>\n * Start Pipeline\n * </button>\n * <p>Status: {flow.status}</p>\n * <p>Events: {flow.events.length}</p>\n * {flow.status === 'waiting' && (\n * <button onClick={() => flow.resume({ approved: true })}>\n * Approve\n * </button>\n * )}\n * {flow.output && <pre>{JSON.stringify(flow.output, null, 2)}</pre>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useFlow(react: ReactLike, options: UseFlowOptions): UseFlowReturn {\n const { useState, useEffect, useRef, useCallback } = react;\n\n const [events, setEvents] = useState<FlowExecutionEvent[]>([]);\n const [status, setStatus] = useState<FlowStatus>('idle');\n const [error, setError] = useState<string | null>(null);\n const [output, setOutput] = useState<unknown | null>(null);\n const [instanceId, setInstanceId] = useState<string | null>(null);\n\n const clientRef = useRef<FlowClient | null>(null);\n const abortRef = useRef(false);\n\n // Create the FlowClient once\n useEffect(() => {\n if (options.database) {\n clientRef.current = options.database.flow;\n } else if (options.baseUrl && options.repository && options.authManager) {\n clientRef.current = new FlowClient(\n options.baseUrl,\n options.repository,\n options.authManager,\n options.clientOptions,\n options.flowsApi,\n );\n }\n\n return () => {\n abortRef.current = true;\n clientRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options.database, options.baseUrl, options.repository]);\n\n const run = useCallback(async (flowPath: string, input: Record<string, unknown> = {}) => {\n const client = clientRef.current;\n if (!client) return;\n\n // Reset state\n abortRef.current = false;\n setEvents([]);\n setError(null);\n setOutput(null);\n setStatus('running');\n\n try {\n const result = await client.run(flowPath, input);\n setInstanceId(result.instance_id);\n\n for await (const event of client.streamEvents(result.instance_id)) {\n if (abortRef.current) break;\n\n setEvents((prev) => [...prev, event]);\n\n if (event.type === 'flow_waiting') {\n setStatus('waiting');\n } else if (event.type === 'flow_completed') {\n setOutput(event.output);\n setStatus('completed');\n } else if (event.type === 'flow_failed') {\n setError(event.error);\n setStatus('failed');\n }\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n setStatus('failed');\n }\n }, []);\n\n const resume = useCallback(async (data: unknown) => {\n const client = clientRef.current;\n if (!client || !instanceId) return;\n\n setStatus('running');\n setError(null);\n\n try {\n // Open event stream before resuming to avoid missing events\n const stream = await client.createEventStream(instanceId);\n await client.resume(instanceId, data);\n\n for await (const event of stream.events) {\n if (abortRef.current) break;\n\n setEvents((prev) => [...prev, event]);\n\n if (event.type === 'flow_waiting') {\n setStatus('waiting');\n stream.close();\n return;\n } else if (event.type === 'flow_completed') {\n setOutput(event.output);\n setStatus('completed');\n stream.close();\n return;\n } else if (event.type === 'flow_failed') {\n setError(event.error);\n setStatus('failed');\n stream.close();\n return;\n }\n }\n\n stream.close();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n setStatus('failed');\n }\n }, [instanceId]);\n\n const reset = useCallback(() => {\n abortRef.current = true;\n setEvents([]);\n setStatus('idle');\n setError(null);\n setOutput(null);\n setInstanceId(null);\n }, []);\n\n return {\n events,\n status,\n isRunning: status === 'running',\n error,\n output,\n instanceId,\n run,\n resume,\n reset,\n };\n}\n"],"mappings":"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","names":["scheduled","useConversation","useConversationList","useFlow"]}
|