@octavus/client-sdk 0.0.9 → 0.0.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/dist/index.d.ts CHANGED
@@ -18,6 +18,99 @@ interface Transport {
18
18
  /** Stop the current stream. Safe to call when no stream is active. */
19
19
  stop(): void;
20
20
  }
21
+ /**
22
+ * Connection states for socket transport.
23
+ *
24
+ * - `disconnected`: Not connected (initial state or after disconnect)
25
+ * - `connecting`: Connection attempt in progress
26
+ * - `connected`: Successfully connected and ready
27
+ * - `error`: Connection failed (check error in listener callback)
28
+ */
29
+ type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'error';
30
+ /**
31
+ * Callback for connection state changes.
32
+ */
33
+ type ConnectionStateListener = (state: ConnectionState, error?: Error) => void;
34
+ /**
35
+ * Socket transport with connection management capabilities.
36
+ *
37
+ * Extends the base Transport interface with methods for managing persistent
38
+ * WebSocket/SockJS connections. Use this when you need:
39
+ * - Eager connection (connect before first message)
40
+ * - Connection status UI indicators
41
+ * - Manual connection lifecycle control
42
+ *
43
+ * Created via `createSocketTransport()`.
44
+ */
45
+ interface SocketTransport extends Transport {
46
+ /**
47
+ * Current connection state.
48
+ *
49
+ * - `disconnected`: Not connected (initial state)
50
+ * - `connecting`: Connection in progress
51
+ * - `connected`: Ready to send/receive
52
+ * - `error`: Connection failed
53
+ */
54
+ readonly connectionState: ConnectionState;
55
+ /**
56
+ * Subscribe to connection state changes.
57
+ *
58
+ * The listener is called immediately with the current state, then again
59
+ * whenever the state changes.
60
+ *
61
+ * @param listener - Callback invoked on state changes
62
+ * @returns Unsubscribe function
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const unsubscribe = transport.onConnectionStateChange((state, error) => {
67
+ * setConnectionState(state);
68
+ * if (error) setConnectionError(error);
69
+ * });
70
+ * ```
71
+ */
72
+ onConnectionStateChange(listener: ConnectionStateListener): () => void;
73
+ /**
74
+ * Eagerly establish the connection.
75
+ *
76
+ * By default, socket transport connects lazily on first `trigger()`. Call
77
+ * this method to establish the connection early (e.g., on component mount):
78
+ * - Faster first message response
79
+ * - Show accurate connection status in UI
80
+ * - Handle connection errors before user interaction
81
+ *
82
+ * Safe to call multiple times - resolves immediately if already connected.
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * useEffect(() => {
87
+ * transport.connect()
88
+ * .then(() => console.log('Connected'))
89
+ * .catch((err) => console.error('Failed:', err));
90
+ *
91
+ * return () => transport.disconnect();
92
+ * }, [transport]);
93
+ * ```
94
+ */
95
+ connect(): Promise<void>;
96
+ /**
97
+ * Close the connection and clean up resources.
98
+ *
99
+ * The transport will reconnect automatically on next `trigger()` call.
100
+ */
101
+ disconnect(): void;
102
+ }
103
+ /**
104
+ * Check if a transport is a SocketTransport with connection management.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * if (isSocketTransport(transport)) {
109
+ * transport.connect(); // TypeScript knows this is available
110
+ * }
111
+ * ```
112
+ */
113
+ declare function isSocketTransport(transport: Transport): transport is SocketTransport;
21
114
 
22
115
  type ChatStatus = 'idle' | 'streaming' | 'error';
23
116
  /**
@@ -227,6 +320,22 @@ interface SocketTransportOptions {
227
320
  * The server should send StreamEvent format (same as SSE) over the socket.
228
321
  * Unknown events are safely ignored using Zod validation.
229
322
  *
323
+ * ## Connection Lifecycle
324
+ *
325
+ * By default, the socket connects **lazily** on the first `send()` call.
326
+ * Use `connect()` to establish the connection eagerly (e.g., on component mount):
327
+ *
328
+ * ```typescript
329
+ * // Eager connection for UI status indicators
330
+ * useEffect(() => {
331
+ * transport.connect()
332
+ * .then(() => console.log('Connected'))
333
+ * .catch((err) => console.error('Failed:', err));
334
+ *
335
+ * return () => transport.disconnect();
336
+ * }, [transport]);
337
+ * ```
338
+ *
230
339
  * @example Basic usage with WebSocket
231
340
  * ```typescript
232
341
  * const transport = createSocketTransport({
@@ -238,7 +347,7 @@ interface SocketTransportOptions {
238
347
  * });
239
348
  * ```
240
349
  *
241
- * @example With SockJS and custom events
350
+ * @example With SockJS and connection state
242
351
  * ```typescript
243
352
  * const transport = createSocketTransport({
244
353
  * connect: () => new Promise((resolve, reject) => {
@@ -246,18 +355,15 @@ interface SocketTransportOptions {
246
355
  * sock.onopen = () => resolve(sock);
247
356
  * sock.onerror = () => reject(new Error('Connection failed'));
248
357
  * }),
249
- * onMessage: (data) => {
250
- * const msg = data as { type: string };
251
- * if (msg.type === 'typing-indicator') {
252
- * setIsTyping(msg.isTyping);
253
- * }
254
- * },
255
- * onClose: () => {
256
- * console.log('Socket closed, attempting reconnect...');
257
- * },
358
+ * });
359
+ *
360
+ * // Subscribe to connection state changes
361
+ * transport.onConnectionStateChange((state, error) => {
362
+ * setConnectionState(state);
363
+ * if (error) setConnectionError(error);
258
364
  * });
259
365
  * ```
260
366
  */
261
- declare function createSocketTransport(options: SocketTransportOptions): Transport;
367
+ declare function createSocketTransport(options: SocketTransportOptions): SocketTransport;
262
368
 
263
- export { type ChatStatus, type HttpTransportOptions, OctavusChat, type OctavusChatOptions, type SocketLike, type SocketTransportOptions, type Transport, type UserMessageInput, createHttpTransport, createSocketTransport, isOtherThread, parseSSEStream };
369
+ export { type ChatStatus, type ConnectionState, type ConnectionStateListener, type HttpTransportOptions, OctavusChat, type OctavusChatOptions, type SocketLike, type SocketTransport, type SocketTransportOptions, type Transport, type UserMessageInput, createHttpTransport, createSocketTransport, isOtherThread, isSocketTransport, parseSSEStream };
package/dist/index.js CHANGED
@@ -457,6 +457,11 @@ function isOtherThread(part) {
457
457
  return Boolean(part.thread && part.thread !== "main");
458
458
  }
459
459
 
460
+ // src/transports/types.ts
461
+ function isSocketTransport(transport) {
462
+ return "connect" in transport && "disconnect" in transport && "connectionState" in transport && "onConnectionStateChange" in transport;
463
+ }
464
+
460
465
  // src/transports/http.ts
461
466
  function createHttpTransport(options) {
462
467
  let abortController = null;
@@ -493,6 +498,15 @@ function createSocketTransport(options) {
493
498
  let eventQueue = [];
494
499
  let eventResolver = null;
495
500
  let isStreaming = false;
501
+ let connectionState = "disconnected";
502
+ let connectionError;
503
+ let connectionPromise = null;
504
+ const connectionListeners = /* @__PURE__ */ new Set();
505
+ function setConnectionState(state, error) {
506
+ connectionState = state;
507
+ connectionError = error;
508
+ connectionListeners.forEach((listener) => listener(state, error));
509
+ }
496
510
  function enqueueEvent(event) {
497
511
  if (eventResolver) {
498
512
  eventResolver(event);
@@ -512,8 +526,7 @@ function createSocketTransport(options) {
512
526
  eventResolver = resolve;
513
527
  });
514
528
  }
515
- async function connect() {
516
- const sock = await options.connect();
529
+ function setupSocketHandlers(sock) {
517
530
  sock.onmessage = (e) => {
518
531
  try {
519
532
  const data = typeof e.data === "string" ? JSON.parse(e.data) : e.data;
@@ -530,6 +543,9 @@ function createSocketTransport(options) {
530
543
  }
531
544
  };
532
545
  sock.onclose = () => {
546
+ socket = null;
547
+ connectionPromise = null;
548
+ setConnectionState("disconnected");
533
549
  options.onClose?.();
534
550
  isStreaming = false;
535
551
  if (eventResolver) {
@@ -537,13 +553,65 @@ function createSocketTransport(options) {
537
553
  eventResolver = null;
538
554
  }
539
555
  };
540
- return sock;
556
+ }
557
+ async function ensureConnected() {
558
+ if (socket?.readyState === SOCKET_OPEN) {
559
+ return;
560
+ }
561
+ if (connectionPromise) {
562
+ await connectionPromise;
563
+ return;
564
+ }
565
+ setConnectionState("connecting");
566
+ connectionPromise = (async () => {
567
+ try {
568
+ const sock = await options.connect();
569
+ socket = sock;
570
+ setupSocketHandlers(sock);
571
+ setConnectionState("connected");
572
+ } catch (err) {
573
+ socket = null;
574
+ connectionPromise = null;
575
+ const error = err instanceof Error ? err : new Error("Connection failed");
576
+ setConnectionState("error", error);
577
+ throw error;
578
+ }
579
+ })();
580
+ await connectionPromise;
541
581
  }
542
582
  return {
543
- async *trigger(triggerName, input) {
544
- if (socket?.readyState !== SOCKET_OPEN) {
545
- socket = await connect();
583
+ // =========================================================================
584
+ // Connection Management
585
+ // =========================================================================
586
+ get connectionState() {
587
+ return connectionState;
588
+ },
589
+ onConnectionStateChange(listener) {
590
+ connectionListeners.add(listener);
591
+ listener(connectionState, connectionError);
592
+ return () => connectionListeners.delete(listener);
593
+ },
594
+ async connect() {
595
+ await ensureConnected();
596
+ },
597
+ disconnect() {
598
+ if (socket) {
599
+ socket.close();
600
+ socket = null;
546
601
  }
602
+ connectionPromise = null;
603
+ isStreaming = false;
604
+ if (eventResolver) {
605
+ eventResolver(null);
606
+ eventResolver = null;
607
+ }
608
+ setConnectionState("disconnected");
609
+ },
610
+ // =========================================================================
611
+ // Streaming
612
+ // =========================================================================
613
+ async *trigger(triggerName, input) {
614
+ await ensureConnected();
547
615
  eventQueue = [];
548
616
  isStreaming = true;
549
617
  socket.send(
@@ -577,6 +645,7 @@ export {
577
645
  createHttpTransport,
578
646
  createSocketTransport,
579
647
  isOtherThread,
648
+ isSocketTransport,
580
649
  parseSSEStream
581
650
  };
582
651
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/chat.ts","../src/stream/reader.ts","../src/utils/message-helpers.ts","../src/transports/http.ts","../src/transports/socket.ts"],"sourcesContent":["import {\n generateId,\n type UIMessage,\n type UIMessagePart,\n type UITextPart,\n type UIReasoningPart,\n type UIToolCallPart,\n type UIOperationPart,\n type UISourcePart,\n type DisplayMode,\n type StreamEvent,\n} from '@octavus/core';\nimport type { Transport } from './transports/types';\n\n/** Block types that are internal operations (not LLM-driven) */\nconst OPERATION_BLOCK_TYPES = new Set(['set-resource', 'serialize-thread']);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ChatStatus = 'idle' | 'streaming' | 'error';\n\n/**\n * Input for creating a user message.\n * Currently supports text content. Will be extended to support attachments, images, etc.\n */\nexport interface UserMessageInput {\n /** Text content of the message */\n content: string;\n}\n\nexport interface OctavusChatOptions {\n /**\n * Transport for streaming events.\n * Use `createHttpTransport` for HTTP/SSE or `createSocketTransport` for WebSocket/SockJS.\n */\n transport: Transport;\n\n /** Initial messages (for session refresh) */\n initialMessages?: UIMessage[];\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n /** Callback when streaming finishes */\n onFinish?: () => void;\n /** Callback when a resource is updated */\n onResourceUpdate?: (name: string, value: unknown) => void;\n}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\ninterface BlockState {\n blockId: string;\n blockName: string;\n blockType: string;\n display: DisplayMode;\n description?: string;\n outputToChat: boolean;\n thread?: string;\n reasoning: string;\n text: string;\n toolCalls: Map<string, UIToolCallPart>;\n}\n\ninterface StreamingState {\n messageId: string;\n parts: UIMessagePart[];\n activeBlock: BlockState | null;\n blocks: Map<string, BlockState>;\n // Track current text/reasoning part indices for delta updates\n currentTextPartIndex: number | null;\n currentReasoningPartIndex: number | null;\n}\n\ntype Listener = () => void;\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction createUserMessage(input: UserMessageInput): UIMessage {\n return {\n id: generateId(),\n role: 'user',\n parts: [{ type: 'text', text: input.content, status: 'done' }],\n status: 'done',\n createdAt: new Date(),\n };\n}\n\nfunction createEmptyStreamingState(): StreamingState {\n return {\n messageId: generateId(),\n parts: [],\n activeBlock: null,\n blocks: new Map(),\n currentTextPartIndex: null,\n currentReasoningPartIndex: null,\n };\n}\n\nfunction buildMessageFromState(state: StreamingState, status: 'streaming' | 'done'): UIMessage {\n return {\n id: state.messageId,\n role: 'assistant',\n parts: [...state.parts],\n status,\n createdAt: new Date(),\n };\n}\n\n// =============================================================================\n// OctavusChat Class\n// =============================================================================\n\n/**\n * Framework-agnostic chat client for Octavus agents.\n * Manages chat state and streaming, allowing reactive frameworks to subscribe to updates.\n *\n * @example HTTP transport (Next.js, etc.)\n * ```typescript\n * import { OctavusChat, createHttpTransport } from '@octavus/client-sdk';\n *\n * const chat = new OctavusChat({\n * transport: createHttpTransport({\n * triggerRequest: (triggerName, input) =>\n * fetch('/api/trigger', {\n * method: 'POST',\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * }),\n * }),\n * });\n * ```\n *\n * @example Socket transport (WebSocket, SockJS, Meteor)\n * ```typescript\n * import { OctavusChat, createSocketTransport } from '@octavus/client-sdk';\n *\n * const chat = new OctavusChat({\n * transport: createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * }),\n * }),\n * });\n * ```\n */\nexport class OctavusChat {\n // Private state\n private _messages: UIMessage[];\n private _status: ChatStatus = 'idle';\n private _error: Error | null = null;\n private options: OctavusChatOptions;\n private transport: Transport;\n private streamingState: StreamingState | null = null;\n\n // Listener sets for reactive frameworks\n private listeners = new Set<Listener>();\n\n constructor(options: OctavusChatOptions) {\n this.options = options;\n this._messages = options.initialMessages ?? [];\n this.transport = options.transport;\n }\n\n // =========================================================================\n // Public Getters\n // =========================================================================\n\n get messages(): UIMessage[] {\n return this._messages;\n }\n\n get status(): ChatStatus {\n return this._status;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n // =========================================================================\n // Subscription Methods (for reactive frameworks)\n // =========================================================================\n\n /**\n * Subscribe to state changes. The callback is called whenever messages, status, or error changes.\n * @returns Unsubscribe function\n */\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private notifyListeners(): void {\n this.listeners.forEach((l) => l());\n }\n\n // =========================================================================\n // Private Setters (notify listeners)\n // =========================================================================\n\n private setMessages(messages: UIMessage[]): void {\n this._messages = messages;\n this.notifyListeners();\n }\n\n private setStatus(status: ChatStatus): void {\n this._status = status;\n this.notifyListeners();\n }\n\n private setError(error: Error | null): void {\n this._error = error;\n this.notifyListeners();\n }\n\n // =========================================================================\n // Public Methods\n // =========================================================================\n\n /**\n * Trigger the agent and optionally add a user message to the chat.\n *\n * @param triggerName - The trigger name defined in the agent's protocol.yaml\n * @param input - Input parameters for the trigger (variable substitutions)\n * @param options.userMessage - If provided, adds a user message to the chat before triggering\n */\n async send(\n triggerName: string,\n input?: Record<string, unknown>,\n sendOptions?: { userMessage?: UserMessageInput },\n ): Promise<void> {\n this.transport.stop();\n\n if (sendOptions?.userMessage !== undefined) {\n const userMsg = createUserMessage(sendOptions.userMessage);\n this.setMessages([...this._messages, userMsg]);\n }\n\n this.setStatus('streaming');\n this.setError(null);\n this.streamingState = createEmptyStreamingState();\n\n try {\n for await (const event of this.transport.trigger(triggerName, input)) {\n if (this.streamingState === null) break;\n\n this.handleStreamEvent(event, this.streamingState);\n }\n } catch (err) {\n const errorObj = err instanceof Error ? err : new Error('Unknown error');\n this.setError(errorObj);\n this.setStatus('error');\n this.streamingState = null;\n this.options.onError?.(errorObj);\n }\n }\n\n /** Stop the current streaming and finalize any partial message */\n stop(): void {\n this.transport.stop();\n\n const state = this.streamingState;\n if (state && state.parts.length > 0) {\n const finalMessage = buildMessageFromState(state, 'done');\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n }\n\n this.streamingState = null;\n this.setStatus('idle');\n }\n\n // =========================================================================\n // Private Helpers\n // =========================================================================\n\n private handleStreamEvent(event: StreamEvent, state: StreamingState): void {\n switch (event.type) {\n case 'start':\n break;\n\n case 'block-start': {\n const block: BlockState = {\n blockId: event.blockId,\n blockName: event.blockName,\n blockType: event.blockType,\n display: event.display,\n description: event.description,\n outputToChat: event.outputToChat ?? true,\n thread: event.thread,\n reasoning: '',\n text: '',\n toolCalls: new Map(),\n };\n state.blocks.set(event.blockId, block);\n state.activeBlock = block;\n\n const isOperation = OPERATION_BLOCK_TYPES.has(event.blockType);\n const isHidden = event.display === 'hidden';\n if (isOperation && !isHidden) {\n const thread = event.thread;\n const operationPart: UIOperationPart = {\n type: 'operation',\n operationId: event.blockId,\n name: event.description ?? event.blockName,\n operationType: event.blockType,\n status: 'running',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(operationPart);\n }\n\n state.currentTextPartIndex = null;\n state.currentReasoningPartIndex = null;\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'block-end': {\n const operationPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'operation' && p.operationId === event.blockId,\n );\n if (operationPartIndex >= 0) {\n const part = state.parts[operationPartIndex] as UIOperationPart;\n state.parts[operationPartIndex] = { ...part, status: 'done' };\n }\n\n if (state.activeBlock?.blockId === event.blockId) {\n state.activeBlock = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-start': {\n const thread = state.activeBlock?.thread;\n const reasoningPart: UIReasoningPart = {\n type: 'reasoning',\n text: '',\n status: 'streaming',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(reasoningPart);\n state.currentReasoningPartIndex = state.parts.length - 1;\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-delta': {\n if (state.currentReasoningPartIndex !== null) {\n const part = state.parts[state.currentReasoningPartIndex] as UIReasoningPart;\n part.text += event.delta;\n state.parts[state.currentReasoningPartIndex] = { ...part };\n }\n\n if (state.activeBlock) {\n state.activeBlock.reasoning += event.delta;\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-end': {\n if (state.currentReasoningPartIndex !== null) {\n const part = state.parts[state.currentReasoningPartIndex] as UIReasoningPart;\n part.status = 'done';\n state.parts[state.currentReasoningPartIndex] = { ...part };\n state.currentReasoningPartIndex = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-start': {\n const thread = state.activeBlock?.thread;\n const isOtherThread = Boolean(thread && thread !== 'main');\n const shouldAddPart = state.activeBlock?.outputToChat !== false || isOtherThread;\n\n if (shouldAddPart) {\n const textPart: UITextPart = {\n type: 'text',\n text: '',\n status: 'streaming',\n thread: isOtherThread ? thread : undefined,\n };\n state.parts.push(textPart);\n state.currentTextPartIndex = state.parts.length - 1;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-delta': {\n if (state.currentTextPartIndex !== null) {\n const part = state.parts[state.currentTextPartIndex] as UITextPart;\n part.text += event.delta;\n state.parts[state.currentTextPartIndex] = { ...part };\n }\n\n if (state.activeBlock) {\n state.activeBlock.text += event.delta;\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-end': {\n if (state.currentTextPartIndex !== null) {\n const part = state.parts[state.currentTextPartIndex] as UITextPart;\n part.status = 'done';\n state.parts[state.currentTextPartIndex] = { ...part };\n state.currentTextPartIndex = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'tool-input-start': {\n const thread = state.activeBlock?.thread;\n const toolPart: UIToolCallPart = {\n type: 'tool-call',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n displayName: event.title,\n args: {},\n status: 'pending',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(toolPart);\n\n if (state.activeBlock) {\n state.activeBlock.toolCalls.set(event.toolCallId, toolPart);\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'tool-input-delta': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n try {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.args = JSON.parse(event.inputTextDelta) as Record<string, unknown>;\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n } catch {\n // Partial JSON, ignore\n }\n }\n break;\n }\n\n case 'tool-input-end':\n // Input streaming ended, wait for tool-input-available\n break;\n\n case 'tool-input-available': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.args = event.input as Record<string, unknown>;\n part.status = 'running';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'tool-output-available': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.result = event.output;\n part.status = 'done';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'tool-output-error': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.error = event.errorText;\n part.status = 'error';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'source': {\n // Add source (URL or document) as a part - aligned with Vercel AI SDK\n const thread = state.activeBlock?.thread;\n const threadValue = thread && thread !== 'main' ? thread : undefined;\n\n let sourcePart: UISourcePart;\n if (event.sourceType === 'url') {\n sourcePart = {\n type: 'source',\n sourceType: 'url',\n id: event.id,\n url: event.url,\n title: event.title,\n thread: threadValue,\n };\n } else {\n sourcePart = {\n type: 'source',\n sourceType: 'document',\n id: event.id,\n mediaType: event.mediaType,\n title: event.title,\n filename: event.filename,\n thread: threadValue,\n };\n }\n\n state.parts.push(sourcePart);\n this.updateStreamingMessage();\n break;\n }\n\n case 'resource-update':\n this.options.onResourceUpdate?.(event.name, event.value);\n break;\n\n case 'finish': {\n const finalMessage = buildMessageFromState(state, 'done');\n\n finalMessage.parts = finalMessage.parts.map((part) => {\n if (part.type === 'text' || part.type === 'reasoning') {\n return { ...part, status: 'done' as const };\n }\n return part;\n });\n\n if (finalMessage.parts.length > 0) {\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n }\n\n this.setStatus('idle');\n this.streamingState = null;\n this.options.onFinish?.();\n break;\n }\n\n case 'error':\n throw new Error(event.errorText);\n\n case 'tool-request':\n // Handled by server-sdk, not relevant for UI\n break;\n }\n }\n\n private updateStreamingMessage(): void {\n const state = this.streamingState;\n if (!state) return;\n\n const msg = buildMessageFromState(state, 'streaming');\n const messages = [...this._messages];\n\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = msg;\n } else {\n messages.push(msg);\n }\n\n this.setMessages(messages);\n }\n}\n","import { safeParseStreamEvent, type StreamEvent } from '@octavus/core';\n\n/**\n * Parse SSE stream events\n */\nexport async function* parseSSEStream(\n response: Response,\n): AsyncGenerator<StreamEvent, void, unknown> {\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error('Response body is not readable');\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n let reading = true;\n while (reading) {\n const { done, value } = await reader.read();\n\n if (done) {\n reading = false;\n continue;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ') && line !== 'data: [DONE]') {\n try {\n const parsed = safeParseStreamEvent(JSON.parse(line.slice(6)));\n if (parsed.success) {\n yield parsed.data;\n }\n // Skip malformed events silently\n } catch {\n // Skip malformed JSON - no logging in production\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type { UIMessagePart } from '@octavus/core';\n\n/** Non-main thread content (e.g., \"summary\") is typically displayed differently. */\nexport function isOtherThread(part: UIMessagePart): boolean {\n return Boolean(part.thread && part.thread !== 'main');\n}\n","import { parseSSEStream } from '@/stream/reader';\nimport type { Transport } from './types';\n\n/**\n * Options for creating an HTTP transport.\n */\nexport interface HttpTransportOptions {\n /**\n * Function to make the trigger request.\n * Called each time `send()` is invoked on the chat.\n *\n * @param triggerName - The trigger name (e.g., 'user-message')\n * @param input - Input parameters for the trigger\n * @returns Response with SSE stream body\n */\n triggerRequest: (triggerName: string, input?: Record<string, unknown>) => Promise<Response>;\n}\n\n/**\n * Create an HTTP transport using native fetch() and SSE parsing.\n * This is the default transport for Next.js and other HTTP-based applications.\n *\n * @example\n * ```typescript\n * const transport = createHttpTransport({\n * triggerRequest: (triggerName, input) =>\n * fetch('/api/octavus', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * }),\n * });\n * ```\n */\nexport function createHttpTransport(options: HttpTransportOptions): Transport {\n let abortController: AbortController | null = null;\n\n return {\n async *trigger(triggerName, input) {\n abortController = new AbortController();\n\n const response = await options.triggerRequest(triggerName, input);\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => `Request failed: ${response.status}`);\n throw new Error(errorText);\n }\n\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n\n for await (const event of parseSSEStream(response)) {\n if (abortController.signal.aborted) {\n break;\n }\n yield event;\n }\n },\n\n stop() {\n abortController?.abort();\n abortController = null;\n },\n };\n}\n","import { safeParseStreamEvent, type StreamEvent } from '@octavus/core';\nimport type { Transport } from './types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Socket interface compatible with both WebSocket and SockJS.\n *\n * Uses MessageEvent for the message handler, which both WebSocket and SockJS support.\n * The `| undefined` union accommodates SockJS's optional property typing.\n */\nexport interface SocketLike {\n send(data: string): void;\n close(): void;\n readyState: number;\n onmessage: ((event: MessageEvent) => void) | null | undefined;\n onclose: ((event: CloseEvent) => void) | null | undefined;\n}\n\n/** WebSocket readyState constants */\nconst SOCKET_OPEN = 1;\n\n// =============================================================================\n// Transport Options\n// =============================================================================\n\n/**\n * Options for creating a socket transport.\n */\nexport interface SocketTransportOptions {\n /**\n * Function to create and connect the socket.\n * Works directly with WebSocket and SockJS - no wrappers needed.\n *\n * @example Native WebSocket\n * ```typescript\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket('wss://api.example.com/stream?sessionId=xxx');\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * })\n * ```\n *\n * @example SockJS\n * ```typescript\n * connect: () => new Promise((resolve, reject) => {\n * const sock = new SockJS('/chat-service');\n * sock.onopen = () => resolve(sock);\n * sock.onerror = () => reject(new Error('Connection failed'));\n * })\n * ```\n */\n connect: () => Promise<SocketLike>;\n\n /**\n * Called for every message received (parsed as JSON).\n * Use this to handle custom (non-Octavus) events.\n * Octavus StreamEvents are handled automatically by the transport.\n *\n * @example\n * ```typescript\n * onMessage: (data) => {\n * const msg = data as { type: string };\n * if (msg.type === 'typing-indicator') {\n * setIsTyping(true);\n * }\n * if (msg.type === 'presence-update') {\n * updatePresence(msg.users);\n * }\n * }\n * ```\n */\n onMessage?: (data: unknown) => void;\n\n /**\n * Called when the socket connection closes.\n * Use this for cleanup or reconnection logic.\n */\n onClose?: () => void;\n}\n\n/**\n * Create a socket transport that works with any WebSocket-like connection.\n * Supports native WebSocket, SockJS, or any compatible socket implementation.\n *\n * The server should send StreamEvent format (same as SSE) over the socket.\n * Unknown events are safely ignored using Zod validation.\n *\n * @example Basic usage with WebSocket\n * ```typescript\n * const transport = createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * }),\n * });\n * ```\n *\n * @example With SockJS and custom events\n * ```typescript\n * const transport = createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const sock = new SockJS('/octavus-stream');\n * sock.onopen = () => resolve(sock);\n * sock.onerror = () => reject(new Error('Connection failed'));\n * }),\n * onMessage: (data) => {\n * const msg = data as { type: string };\n * if (msg.type === 'typing-indicator') {\n * setIsTyping(msg.isTyping);\n * }\n * },\n * onClose: () => {\n * console.log('Socket closed, attempting reconnect...');\n * },\n * });\n * ```\n */\nexport function createSocketTransport(options: SocketTransportOptions): Transport {\n let socket: SocketLike | null = null;\n let eventQueue: StreamEvent[] = [];\n let eventResolver: ((event: StreamEvent | null) => void) | null = null;\n let isStreaming = false;\n\n function enqueueEvent(event: StreamEvent) {\n if (eventResolver) {\n eventResolver(event);\n eventResolver = null;\n } else {\n eventQueue.push(event);\n }\n }\n\n function nextEvent(): Promise<StreamEvent | null> {\n if (eventQueue.length > 0) {\n return Promise.resolve(eventQueue.shift()!);\n }\n if (!isStreaming) {\n return Promise.resolve(null);\n }\n return new Promise((resolve) => {\n eventResolver = resolve;\n });\n }\n\n async function connect(): Promise<SocketLike> {\n const sock = await options.connect();\n\n sock.onmessage = (e: MessageEvent) => {\n try {\n const data: unknown = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;\n\n options.onMessage?.(data);\n\n const result = safeParseStreamEvent(data);\n if (result.success) {\n const event = result.data;\n enqueueEvent(event);\n\n if (event.type === 'finish' || event.type === 'error') {\n isStreaming = false;\n }\n }\n } catch {\n // Malformed JSON, skip\n }\n };\n\n sock.onclose = () => {\n options.onClose?.();\n\n isStreaming = false;\n if (eventResolver) {\n eventResolver(null);\n eventResolver = null;\n }\n };\n\n return sock;\n }\n\n return {\n async *trigger(triggerName, input) {\n if (socket?.readyState !== SOCKET_OPEN) {\n socket = await connect();\n }\n\n eventQueue = [];\n isStreaming = true;\n\n socket.send(\n JSON.stringify({\n type: 'trigger',\n triggerName,\n input,\n }),\n );\n\n while (true) {\n const event = await nextEvent();\n if (event === null) break;\n yield event;\n if (event.type === 'finish' || event.type === 'error') break;\n }\n },\n\n stop() {\n if (socket?.readyState === SOCKET_OPEN) {\n socket.send(JSON.stringify({ type: 'stop' }));\n }\n isStreaming = false;\n if (eventResolver) {\n eventResolver(null);\n eventResolver = null;\n }\n },\n };\n}\n"],"mappings":"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","names":["isOtherThread","safeParseStreamEvent"]}
1
+ {"version":3,"sources":["../src/chat.ts","../src/stream/reader.ts","../src/utils/message-helpers.ts","../src/transports/types.ts","../src/transports/http.ts","../src/transports/socket.ts"],"sourcesContent":["import {\n generateId,\n type UIMessage,\n type UIMessagePart,\n type UITextPart,\n type UIReasoningPart,\n type UIToolCallPart,\n type UIOperationPart,\n type UISourcePart,\n type DisplayMode,\n type StreamEvent,\n} from '@octavus/core';\nimport type { Transport } from './transports/types';\n\n/** Block types that are internal operations (not LLM-driven) */\nconst OPERATION_BLOCK_TYPES = new Set(['set-resource', 'serialize-thread']);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ChatStatus = 'idle' | 'streaming' | 'error';\n\n/**\n * Input for creating a user message.\n * Currently supports text content. Will be extended to support attachments, images, etc.\n */\nexport interface UserMessageInput {\n /** Text content of the message */\n content: string;\n}\n\nexport interface OctavusChatOptions {\n /**\n * Transport for streaming events.\n * Use `createHttpTransport` for HTTP/SSE or `createSocketTransport` for WebSocket/SockJS.\n */\n transport: Transport;\n\n /** Initial messages (for session refresh) */\n initialMessages?: UIMessage[];\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n /** Callback when streaming finishes */\n onFinish?: () => void;\n /** Callback when a resource is updated */\n onResourceUpdate?: (name: string, value: unknown) => void;\n}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\ninterface BlockState {\n blockId: string;\n blockName: string;\n blockType: string;\n display: DisplayMode;\n description?: string;\n outputToChat: boolean;\n thread?: string;\n reasoning: string;\n text: string;\n toolCalls: Map<string, UIToolCallPart>;\n}\n\ninterface StreamingState {\n messageId: string;\n parts: UIMessagePart[];\n activeBlock: BlockState | null;\n blocks: Map<string, BlockState>;\n // Track current text/reasoning part indices for delta updates\n currentTextPartIndex: number | null;\n currentReasoningPartIndex: number | null;\n}\n\ntype Listener = () => void;\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction createUserMessage(input: UserMessageInput): UIMessage {\n return {\n id: generateId(),\n role: 'user',\n parts: [{ type: 'text', text: input.content, status: 'done' }],\n status: 'done',\n createdAt: new Date(),\n };\n}\n\nfunction createEmptyStreamingState(): StreamingState {\n return {\n messageId: generateId(),\n parts: [],\n activeBlock: null,\n blocks: new Map(),\n currentTextPartIndex: null,\n currentReasoningPartIndex: null,\n };\n}\n\nfunction buildMessageFromState(state: StreamingState, status: 'streaming' | 'done'): UIMessage {\n return {\n id: state.messageId,\n role: 'assistant',\n parts: [...state.parts],\n status,\n createdAt: new Date(),\n };\n}\n\n// =============================================================================\n// OctavusChat Class\n// =============================================================================\n\n/**\n * Framework-agnostic chat client for Octavus agents.\n * Manages chat state and streaming, allowing reactive frameworks to subscribe to updates.\n *\n * @example HTTP transport (Next.js, etc.)\n * ```typescript\n * import { OctavusChat, createHttpTransport } from '@octavus/client-sdk';\n *\n * const chat = new OctavusChat({\n * transport: createHttpTransport({\n * triggerRequest: (triggerName, input) =>\n * fetch('/api/trigger', {\n * method: 'POST',\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * }),\n * }),\n * });\n * ```\n *\n * @example Socket transport (WebSocket, SockJS, Meteor)\n * ```typescript\n * import { OctavusChat, createSocketTransport } from '@octavus/client-sdk';\n *\n * const chat = new OctavusChat({\n * transport: createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * }),\n * }),\n * });\n * ```\n */\nexport class OctavusChat {\n // Private state\n private _messages: UIMessage[];\n private _status: ChatStatus = 'idle';\n private _error: Error | null = null;\n private options: OctavusChatOptions;\n private transport: Transport;\n private streamingState: StreamingState | null = null;\n\n // Listener sets for reactive frameworks\n private listeners = new Set<Listener>();\n\n constructor(options: OctavusChatOptions) {\n this.options = options;\n this._messages = options.initialMessages ?? [];\n this.transport = options.transport;\n }\n\n // =========================================================================\n // Public Getters\n // =========================================================================\n\n get messages(): UIMessage[] {\n return this._messages;\n }\n\n get status(): ChatStatus {\n return this._status;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n // =========================================================================\n // Subscription Methods (for reactive frameworks)\n // =========================================================================\n\n /**\n * Subscribe to state changes. The callback is called whenever messages, status, or error changes.\n * @returns Unsubscribe function\n */\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private notifyListeners(): void {\n this.listeners.forEach((l) => l());\n }\n\n // =========================================================================\n // Private Setters (notify listeners)\n // =========================================================================\n\n private setMessages(messages: UIMessage[]): void {\n this._messages = messages;\n this.notifyListeners();\n }\n\n private setStatus(status: ChatStatus): void {\n this._status = status;\n this.notifyListeners();\n }\n\n private setError(error: Error | null): void {\n this._error = error;\n this.notifyListeners();\n }\n\n // =========================================================================\n // Public Methods\n // =========================================================================\n\n /**\n * Trigger the agent and optionally add a user message to the chat.\n *\n * @param triggerName - The trigger name defined in the agent's protocol.yaml\n * @param input - Input parameters for the trigger (variable substitutions)\n * @param options.userMessage - If provided, adds a user message to the chat before triggering\n */\n async send(\n triggerName: string,\n input?: Record<string, unknown>,\n sendOptions?: { userMessage?: UserMessageInput },\n ): Promise<void> {\n this.transport.stop();\n\n if (sendOptions?.userMessage !== undefined) {\n const userMsg = createUserMessage(sendOptions.userMessage);\n this.setMessages([...this._messages, userMsg]);\n }\n\n this.setStatus('streaming');\n this.setError(null);\n this.streamingState = createEmptyStreamingState();\n\n try {\n for await (const event of this.transport.trigger(triggerName, input)) {\n if (this.streamingState === null) break;\n\n this.handleStreamEvent(event, this.streamingState);\n }\n } catch (err) {\n const errorObj = err instanceof Error ? err : new Error('Unknown error');\n this.setError(errorObj);\n this.setStatus('error');\n this.streamingState = null;\n this.options.onError?.(errorObj);\n }\n }\n\n /** Stop the current streaming and finalize any partial message */\n stop(): void {\n this.transport.stop();\n\n const state = this.streamingState;\n if (state && state.parts.length > 0) {\n const finalMessage = buildMessageFromState(state, 'done');\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n }\n\n this.streamingState = null;\n this.setStatus('idle');\n }\n\n // =========================================================================\n // Private Helpers\n // =========================================================================\n\n private handleStreamEvent(event: StreamEvent, state: StreamingState): void {\n switch (event.type) {\n case 'start':\n break;\n\n case 'block-start': {\n const block: BlockState = {\n blockId: event.blockId,\n blockName: event.blockName,\n blockType: event.blockType,\n display: event.display,\n description: event.description,\n outputToChat: event.outputToChat ?? true,\n thread: event.thread,\n reasoning: '',\n text: '',\n toolCalls: new Map(),\n };\n state.blocks.set(event.blockId, block);\n state.activeBlock = block;\n\n const isOperation = OPERATION_BLOCK_TYPES.has(event.blockType);\n const isHidden = event.display === 'hidden';\n if (isOperation && !isHidden) {\n const thread = event.thread;\n const operationPart: UIOperationPart = {\n type: 'operation',\n operationId: event.blockId,\n name: event.description ?? event.blockName,\n operationType: event.blockType,\n status: 'running',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(operationPart);\n }\n\n state.currentTextPartIndex = null;\n state.currentReasoningPartIndex = null;\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'block-end': {\n const operationPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'operation' && p.operationId === event.blockId,\n );\n if (operationPartIndex >= 0) {\n const part = state.parts[operationPartIndex] as UIOperationPart;\n state.parts[operationPartIndex] = { ...part, status: 'done' };\n }\n\n if (state.activeBlock?.blockId === event.blockId) {\n state.activeBlock = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-start': {\n const thread = state.activeBlock?.thread;\n const reasoningPart: UIReasoningPart = {\n type: 'reasoning',\n text: '',\n status: 'streaming',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(reasoningPart);\n state.currentReasoningPartIndex = state.parts.length - 1;\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-delta': {\n if (state.currentReasoningPartIndex !== null) {\n const part = state.parts[state.currentReasoningPartIndex] as UIReasoningPart;\n part.text += event.delta;\n state.parts[state.currentReasoningPartIndex] = { ...part };\n }\n\n if (state.activeBlock) {\n state.activeBlock.reasoning += event.delta;\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-end': {\n if (state.currentReasoningPartIndex !== null) {\n const part = state.parts[state.currentReasoningPartIndex] as UIReasoningPart;\n part.status = 'done';\n state.parts[state.currentReasoningPartIndex] = { ...part };\n state.currentReasoningPartIndex = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-start': {\n const thread = state.activeBlock?.thread;\n const isOtherThread = Boolean(thread && thread !== 'main');\n const shouldAddPart = state.activeBlock?.outputToChat !== false || isOtherThread;\n\n if (shouldAddPart) {\n const textPart: UITextPart = {\n type: 'text',\n text: '',\n status: 'streaming',\n thread: isOtherThread ? thread : undefined,\n };\n state.parts.push(textPart);\n state.currentTextPartIndex = state.parts.length - 1;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-delta': {\n if (state.currentTextPartIndex !== null) {\n const part = state.parts[state.currentTextPartIndex] as UITextPart;\n part.text += event.delta;\n state.parts[state.currentTextPartIndex] = { ...part };\n }\n\n if (state.activeBlock) {\n state.activeBlock.text += event.delta;\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-end': {\n if (state.currentTextPartIndex !== null) {\n const part = state.parts[state.currentTextPartIndex] as UITextPart;\n part.status = 'done';\n state.parts[state.currentTextPartIndex] = { ...part };\n state.currentTextPartIndex = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'tool-input-start': {\n const thread = state.activeBlock?.thread;\n const toolPart: UIToolCallPart = {\n type: 'tool-call',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n displayName: event.title,\n args: {},\n status: 'pending',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(toolPart);\n\n if (state.activeBlock) {\n state.activeBlock.toolCalls.set(event.toolCallId, toolPart);\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'tool-input-delta': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n try {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.args = JSON.parse(event.inputTextDelta) as Record<string, unknown>;\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n } catch {\n // Partial JSON, ignore\n }\n }\n break;\n }\n\n case 'tool-input-end':\n // Input streaming ended, wait for tool-input-available\n break;\n\n case 'tool-input-available': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.args = event.input as Record<string, unknown>;\n part.status = 'running';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'tool-output-available': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.result = event.output;\n part.status = 'done';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'tool-output-error': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.error = event.errorText;\n part.status = 'error';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'source': {\n // Add source (URL or document) as a part - aligned with Vercel AI SDK\n const thread = state.activeBlock?.thread;\n const threadValue = thread && thread !== 'main' ? thread : undefined;\n\n let sourcePart: UISourcePart;\n if (event.sourceType === 'url') {\n sourcePart = {\n type: 'source',\n sourceType: 'url',\n id: event.id,\n url: event.url,\n title: event.title,\n thread: threadValue,\n };\n } else {\n sourcePart = {\n type: 'source',\n sourceType: 'document',\n id: event.id,\n mediaType: event.mediaType,\n title: event.title,\n filename: event.filename,\n thread: threadValue,\n };\n }\n\n state.parts.push(sourcePart);\n this.updateStreamingMessage();\n break;\n }\n\n case 'resource-update':\n this.options.onResourceUpdate?.(event.name, event.value);\n break;\n\n case 'finish': {\n const finalMessage = buildMessageFromState(state, 'done');\n\n finalMessage.parts = finalMessage.parts.map((part) => {\n if (part.type === 'text' || part.type === 'reasoning') {\n return { ...part, status: 'done' as const };\n }\n return part;\n });\n\n if (finalMessage.parts.length > 0) {\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n }\n\n this.setStatus('idle');\n this.streamingState = null;\n this.options.onFinish?.();\n break;\n }\n\n case 'error':\n throw new Error(event.errorText);\n\n case 'tool-request':\n // Handled by server-sdk, not relevant for UI\n break;\n }\n }\n\n private updateStreamingMessage(): void {\n const state = this.streamingState;\n if (!state) return;\n\n const msg = buildMessageFromState(state, 'streaming');\n const messages = [...this._messages];\n\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = msg;\n } else {\n messages.push(msg);\n }\n\n this.setMessages(messages);\n }\n}\n","import { safeParseStreamEvent, type StreamEvent } from '@octavus/core';\n\n/**\n * Parse SSE stream events\n */\nexport async function* parseSSEStream(\n response: Response,\n): AsyncGenerator<StreamEvent, void, unknown> {\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error('Response body is not readable');\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n let reading = true;\n while (reading) {\n const { done, value } = await reader.read();\n\n if (done) {\n reading = false;\n continue;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ') && line !== 'data: [DONE]') {\n try {\n const parsed = safeParseStreamEvent(JSON.parse(line.slice(6)));\n if (parsed.success) {\n yield parsed.data;\n }\n // Skip malformed events silently\n } catch {\n // Skip malformed JSON - no logging in production\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type { UIMessagePart } from '@octavus/core';\n\n/** Non-main thread content (e.g., \"summary\") is typically displayed differently. */\nexport function isOtherThread(part: UIMessagePart): boolean {\n return Boolean(part.thread && part.thread !== 'main');\n}\n","import type { StreamEvent } from '@octavus/core';\n\n// =============================================================================\n// Base Transport Interface\n// =============================================================================\n\n/**\n * Transport interface for delivering events from server to client.\n *\n * Abstracts the connection mechanism (HTTP/SSE or WebSocket) behind a unified\n * async iterator interface. Use `createHttpTransport` or `createSocketTransport`\n * to create an implementation.\n */\nexport interface Transport {\n /**\n * Trigger the agent and stream events.\n * @param triggerName - The trigger name defined in the agent's protocol\n * @param input - Input parameters for variable substitution\n */\n trigger(triggerName: string, input?: Record<string, unknown>): AsyncIterable<StreamEvent>;\n\n /** Stop the current stream. Safe to call when no stream is active. */\n stop(): void;\n}\n\n// =============================================================================\n// Socket Transport (extends Transport with connection management)\n// =============================================================================\n\n/**\n * Connection states for socket transport.\n *\n * - `disconnected`: Not connected (initial state or after disconnect)\n * - `connecting`: Connection attempt in progress\n * - `connected`: Successfully connected and ready\n * - `error`: Connection failed (check error in listener callback)\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'error';\n\n/**\n * Callback for connection state changes.\n */\nexport type ConnectionStateListener = (state: ConnectionState, error?: Error) => void;\n\n/**\n * Socket transport with connection management capabilities.\n *\n * Extends the base Transport interface with methods for managing persistent\n * WebSocket/SockJS connections. Use this when you need:\n * - Eager connection (connect before first message)\n * - Connection status UI indicators\n * - Manual connection lifecycle control\n *\n * Created via `createSocketTransport()`.\n */\nexport interface SocketTransport extends Transport {\n /**\n * Current connection state.\n *\n * - `disconnected`: Not connected (initial state)\n * - `connecting`: Connection in progress\n * - `connected`: Ready to send/receive\n * - `error`: Connection failed\n */\n readonly connectionState: ConnectionState;\n\n /**\n * Subscribe to connection state changes.\n *\n * The listener is called immediately with the current state, then again\n * whenever the state changes.\n *\n * @param listener - Callback invoked on state changes\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = transport.onConnectionStateChange((state, error) => {\n * setConnectionState(state);\n * if (error) setConnectionError(error);\n * });\n * ```\n */\n onConnectionStateChange(listener: ConnectionStateListener): () => void;\n\n /**\n * Eagerly establish the connection.\n *\n * By default, socket transport connects lazily on first `trigger()`. Call\n * this method to establish the connection early (e.g., on component mount):\n * - Faster first message response\n * - Show accurate connection status in UI\n * - Handle connection errors before user interaction\n *\n * Safe to call multiple times - resolves immediately if already connected.\n *\n * @example\n * ```tsx\n * useEffect(() => {\n * transport.connect()\n * .then(() => console.log('Connected'))\n * .catch((err) => console.error('Failed:', err));\n *\n * return () => transport.disconnect();\n * }, [transport]);\n * ```\n */\n connect(): Promise<void>;\n\n /**\n * Close the connection and clean up resources.\n *\n * The transport will reconnect automatically on next `trigger()` call.\n */\n disconnect(): void;\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Check if a transport is a SocketTransport with connection management.\n *\n * @example\n * ```typescript\n * if (isSocketTransport(transport)) {\n * transport.connect(); // TypeScript knows this is available\n * }\n * ```\n */\nexport function isSocketTransport(transport: Transport): transport is SocketTransport {\n return (\n 'connect' in transport &&\n 'disconnect' in transport &&\n 'connectionState' in transport &&\n 'onConnectionStateChange' in transport\n );\n}\n","import { parseSSEStream } from '@/stream/reader';\nimport type { Transport } from './types';\n\n/**\n * Options for creating an HTTP transport.\n */\nexport interface HttpTransportOptions {\n /**\n * Function to make the trigger request.\n * Called each time `send()` is invoked on the chat.\n *\n * @param triggerName - The trigger name (e.g., 'user-message')\n * @param input - Input parameters for the trigger\n * @returns Response with SSE stream body\n */\n triggerRequest: (triggerName: string, input?: Record<string, unknown>) => Promise<Response>;\n}\n\n/**\n * Create an HTTP transport using native fetch() and SSE parsing.\n * This is the default transport for Next.js and other HTTP-based applications.\n *\n * @example\n * ```typescript\n * const transport = createHttpTransport({\n * triggerRequest: (triggerName, input) =>\n * fetch('/api/octavus', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * }),\n * });\n * ```\n */\nexport function createHttpTransport(options: HttpTransportOptions): Transport {\n let abortController: AbortController | null = null;\n\n return {\n async *trigger(triggerName, input) {\n abortController = new AbortController();\n\n const response = await options.triggerRequest(triggerName, input);\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => `Request failed: ${response.status}`);\n throw new Error(errorText);\n }\n\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n\n for await (const event of parseSSEStream(response)) {\n if (abortController.signal.aborted) {\n break;\n }\n yield event;\n }\n },\n\n stop() {\n abortController?.abort();\n abortController = null;\n },\n };\n}\n","import { safeParseStreamEvent, type StreamEvent } from '@octavus/core';\nimport type { SocketTransport, ConnectionState, ConnectionStateListener } from './types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Socket interface compatible with both WebSocket and SockJS.\n *\n * Uses MessageEvent for the message handler, which both WebSocket and SockJS support.\n * The `| undefined` union accommodates SockJS's optional property typing.\n */\nexport interface SocketLike {\n send(data: string): void;\n close(): void;\n readyState: number;\n onmessage: ((event: MessageEvent) => void) | null | undefined;\n onclose: ((event: CloseEvent) => void) | null | undefined;\n}\n\n/** WebSocket readyState constants */\nconst SOCKET_OPEN = 1;\n\n// =============================================================================\n// Transport Options\n// =============================================================================\n\n/**\n * Options for creating a socket transport.\n */\nexport interface SocketTransportOptions {\n /**\n * Function to create and connect the socket.\n * Works directly with WebSocket and SockJS - no wrappers needed.\n *\n * @example Native WebSocket\n * ```typescript\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket('wss://api.example.com/stream?sessionId=xxx');\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * })\n * ```\n *\n * @example SockJS\n * ```typescript\n * connect: () => new Promise((resolve, reject) => {\n * const sock = new SockJS('/chat-service');\n * sock.onopen = () => resolve(sock);\n * sock.onerror = () => reject(new Error('Connection failed'));\n * })\n * ```\n */\n connect: () => Promise<SocketLike>;\n\n /**\n * Called for every message received (parsed as JSON).\n * Use this to handle custom (non-Octavus) events.\n * Octavus StreamEvents are handled automatically by the transport.\n *\n * @example\n * ```typescript\n * onMessage: (data) => {\n * const msg = data as { type: string };\n * if (msg.type === 'typing-indicator') {\n * setIsTyping(true);\n * }\n * if (msg.type === 'presence-update') {\n * updatePresence(msg.users);\n * }\n * }\n * ```\n */\n onMessage?: (data: unknown) => void;\n\n /**\n * Called when the socket connection closes.\n * Use this for cleanup or reconnection logic.\n */\n onClose?: () => void;\n}\n\n/**\n * Create a socket transport that works with any WebSocket-like connection.\n * Supports native WebSocket, SockJS, or any compatible socket implementation.\n *\n * The server should send StreamEvent format (same as SSE) over the socket.\n * Unknown events are safely ignored using Zod validation.\n *\n * ## Connection Lifecycle\n *\n * By default, the socket connects **lazily** on the first `send()` call.\n * Use `connect()` to establish the connection eagerly (e.g., on component mount):\n *\n * ```typescript\n * // Eager connection for UI status indicators\n * useEffect(() => {\n * transport.connect()\n * .then(() => console.log('Connected'))\n * .catch((err) => console.error('Failed:', err));\n *\n * return () => transport.disconnect();\n * }, [transport]);\n * ```\n *\n * @example Basic usage with WebSocket\n * ```typescript\n * const transport = createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * }),\n * });\n * ```\n *\n * @example With SockJS and connection state\n * ```typescript\n * const transport = createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const sock = new SockJS('/octavus-stream');\n * sock.onopen = () => resolve(sock);\n * sock.onerror = () => reject(new Error('Connection failed'));\n * }),\n * });\n *\n * // Subscribe to connection state changes\n * transport.onConnectionStateChange((state, error) => {\n * setConnectionState(state);\n * if (error) setConnectionError(error);\n * });\n * ```\n */\nexport function createSocketTransport(options: SocketTransportOptions): SocketTransport {\n let socket: SocketLike | null = null;\n let eventQueue: StreamEvent[] = [];\n let eventResolver: ((event: StreamEvent | null) => void) | null = null;\n let isStreaming = false;\n\n let connectionState: ConnectionState = 'disconnected';\n let connectionError: Error | undefined;\n let connectionPromise: Promise<void> | null = null;\n const connectionListeners = new Set<ConnectionStateListener>();\n\n function setConnectionState(state: ConnectionState, error?: Error) {\n connectionState = state;\n connectionError = error;\n connectionListeners.forEach((listener) => listener(state, error));\n }\n\n function enqueueEvent(event: StreamEvent) {\n if (eventResolver) {\n eventResolver(event);\n eventResolver = null;\n } else {\n eventQueue.push(event);\n }\n }\n\n function nextEvent(): Promise<StreamEvent | null> {\n if (eventQueue.length > 0) {\n return Promise.resolve(eventQueue.shift()!);\n }\n if (!isStreaming) {\n return Promise.resolve(null);\n }\n return new Promise((resolve) => {\n eventResolver = resolve;\n });\n }\n\n function setupSocketHandlers(sock: SocketLike): void {\n sock.onmessage = (e: MessageEvent) => {\n try {\n const data: unknown = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;\n\n options.onMessage?.(data);\n\n const result = safeParseStreamEvent(data);\n if (result.success) {\n const event = result.data;\n enqueueEvent(event);\n\n if (event.type === 'finish' || event.type === 'error') {\n isStreaming = false;\n }\n }\n } catch {\n // Malformed JSON, skip\n }\n };\n\n sock.onclose = () => {\n socket = null;\n connectionPromise = null;\n setConnectionState('disconnected');\n options.onClose?.();\n\n isStreaming = false;\n if (eventResolver) {\n eventResolver(null);\n eventResolver = null;\n }\n };\n }\n\n async function ensureConnected(): Promise<void> {\n // Already connected\n if (socket?.readyState === SOCKET_OPEN) {\n return;\n }\n\n // Connection in progress - wait for it\n if (connectionPromise) {\n await connectionPromise;\n return;\n }\n\n // Start new connection\n setConnectionState('connecting');\n\n connectionPromise = (async () => {\n try {\n const sock = await options.connect();\n socket = sock;\n setupSocketHandlers(sock);\n setConnectionState('connected');\n } catch (err) {\n socket = null;\n connectionPromise = null;\n const error = err instanceof Error ? err : new Error('Connection failed');\n setConnectionState('error', error);\n throw error;\n }\n })();\n\n await connectionPromise;\n }\n\n return {\n // =========================================================================\n // Connection Management\n // =========================================================================\n\n get connectionState(): ConnectionState {\n return connectionState;\n },\n\n onConnectionStateChange(listener: ConnectionStateListener): () => void {\n connectionListeners.add(listener);\n // Immediately notify with current state\n listener(connectionState, connectionError);\n return () => connectionListeners.delete(listener);\n },\n\n async connect(): Promise<void> {\n await ensureConnected();\n },\n\n disconnect(): void {\n if (socket) {\n socket.close();\n socket = null;\n }\n connectionPromise = null;\n isStreaming = false;\n if (eventResolver) {\n eventResolver(null);\n eventResolver = null;\n }\n setConnectionState('disconnected');\n },\n\n // =========================================================================\n // Streaming\n // =========================================================================\n\n async *trigger(triggerName, input) {\n await ensureConnected();\n\n eventQueue = [];\n isStreaming = true;\n\n socket!.send(\n JSON.stringify({\n type: 'trigger',\n triggerName,\n input,\n }),\n );\n\n while (true) {\n const event = await nextEvent();\n if (event === null) break;\n yield event;\n if (event.type === 'finish' || event.type === 'error') break;\n }\n },\n\n stop() {\n if (socket?.readyState === SOCKET_OPEN) {\n socket.send(JSON.stringify({ type: 'stop' }));\n }\n isStreaming = false;\n if (eventResolver) {\n eventResolver(null);\n eventResolver = null;\n }\n },\n };\n}\n"],"mappings":"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","names":["isOtherThread","safeParseStreamEvent"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octavus/client-sdk",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Framework-agnostic client SDK for Octavus agents",
5
5
  "license": "MIT",
6
6
  "author": "Octavus AI <dev@octavus.ai>",
@@ -28,7 +28,7 @@
28
28
  "access": "public"
29
29
  },
30
30
  "dependencies": {
31
- "@octavus/core": "^0.0.9"
31
+ "@octavus/core": "^0.0.10"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsup": "^8.3.5",