@octavus/react 0.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # @octavus/react
2
+
3
+ React bindings for Octavus agents.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @octavus/react
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ This package provides React hooks for interacting with Octavus agents. It wraps `@octavus/client-sdk` with React state management using `useSyncExternalStore` for proper React 18+ integration.
14
+
15
+ ## Quick Start
16
+
17
+ ```tsx
18
+ import { useMemo } from 'react';
19
+ import { useOctavusChat, createHttpTransport } from '@octavus/react';
20
+
21
+ function Chat({ sessionId }: { sessionId: string }) {
22
+ const transport = useMemo(
23
+ () =>
24
+ createHttpTransport({
25
+ request: (payload, options) =>
26
+ fetch('/api/trigger', {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify({ sessionId, ...payload }),
30
+ signal: options?.signal,
31
+ }),
32
+ }),
33
+ [sessionId],
34
+ );
35
+
36
+ const { messages, status, send } = useOctavusChat({ transport });
37
+
38
+ const handleSend = async (message: string) => {
39
+ await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });
40
+ };
41
+
42
+ return (
43
+ <div>
44
+ {messages.map((msg) => (
45
+ <Message key={msg.id} message={msg} />
46
+ ))}
47
+ <ChatInput onSend={handleSend} disabled={status === 'streaming'} />
48
+ </div>
49
+ );
50
+ }
51
+ ```
52
+
53
+ ## useOctavusChat Hook
54
+
55
+ ### Options
56
+
57
+ ```typescript
58
+ const { messages, status, error, send, stop } = useOctavusChat({
59
+ transport, // Required: HTTP or Socket transport
60
+ initialMessages: [], // Optional: restore messages from server
61
+ onError: (error) => {}, // Optional: error callback
62
+ onFinish: () => {}, // Optional: completion callback
63
+ onResourceUpdate: (name, value) => {}, // Optional: resource update callback
64
+ });
65
+ ```
66
+
67
+ ### Return Values
68
+
69
+ | Property | Type | Description |
70
+ | ------------- | ---------------------------------- | -------------------------------------------------- |
71
+ | `messages` | `UIMessage[]` | All messages including the currently streaming one |
72
+ | `status` | `'idle' \| 'streaming' \| 'error'` | Current chat status |
73
+ | `error` | `OctavusError \| null` | Structured error if status is 'error' |
74
+ | `send` | `function` | Send a message to the agent |
75
+ | `stop` | `function` | Stop streaming and finalize partial content |
76
+ | `uploadFiles` | `function` | Upload files with progress tracking |
77
+
78
+ ### Socket Transport Extensions
79
+
80
+ When using `createSocketTransport`, additional properties are available:
81
+
82
+ | Property | Type | Description |
83
+ | ----------------- | -------------------- | ---------------------------------------------------------- |
84
+ | `connectionState` | `ConnectionState` | `'disconnected' \| 'connecting' \| 'connected' \| 'error'` |
85
+ | `connectionError` | `Error \| undefined` | Connection error if state is 'error' |
86
+ | `connect` | `function` | Eagerly establish connection |
87
+ | `disconnect` | `function` | Close the connection |
88
+
89
+ ## Transports
90
+
91
+ ### HTTP Transport
92
+
93
+ ```typescript
94
+ import { createHttpTransport } from '@octavus/react';
95
+
96
+ const transport = createHttpTransport({
97
+ request: (payload, options) =>
98
+ fetch('/api/trigger', {
99
+ method: 'POST',
100
+ headers: { 'Content-Type': 'application/json' },
101
+ body: JSON.stringify({ sessionId, ...payload }),
102
+ signal: options?.signal,
103
+ }),
104
+ });
105
+ ```
106
+
107
+ ### Socket Transport
108
+
109
+ ```typescript
110
+ import { createSocketTransport } from '@octavus/react';
111
+
112
+ const transport = createSocketTransport({
113
+ connect: () =>
114
+ new Promise((resolve, reject) => {
115
+ const ws = new WebSocket(`wss://api.example.com/stream?sessionId=${sessionId}`);
116
+ ws.onopen = () => resolve(ws);
117
+ ws.onerror = () => reject(new Error('Connection failed'));
118
+ }),
119
+ });
120
+ ```
121
+
122
+ ## File Uploads
123
+
124
+ ```tsx
125
+ const { send, uploadFiles } = useOctavusChat({ transport, requestUploadUrls });
126
+
127
+ // Upload with progress tracking
128
+ const handleFileSelect = async (files: FileList) => {
129
+ const fileRefs = await uploadFiles(files, (index, progress) => {
130
+ console.log(`File ${index}: ${progress}%`);
131
+ });
132
+
133
+ await send('user-message', { FILES: fileRefs }, { userMessage: { files: fileRefs } });
134
+ };
135
+ ```
136
+
137
+ ## Error Handling
138
+
139
+ ```tsx
140
+ import { useOctavusChat, isRateLimitError, isProviderError } from '@octavus/react';
141
+
142
+ function Chat() {
143
+ const { error, status } = useOctavusChat({
144
+ transport,
145
+ onError: (error) => {
146
+ if (isRateLimitError(error)) {
147
+ toast(`Rate limited. Retry in ${error.retryAfter}s`);
148
+ } else if (isProviderError(error)) {
149
+ toast(`Provider error: ${error.provider?.name}`);
150
+ }
151
+ },
152
+ });
153
+
154
+ return status === 'error' && <ErrorBanner error={error} />;
155
+ }
156
+ ```
157
+
158
+ ## Re-exports
159
+
160
+ This package re-exports everything from `@octavus/client-sdk` and `@octavus/core`, so you don't need to install them separately.
161
+
162
+ ## Related Packages
163
+
164
+ - [`@octavus/client-sdk`](https://www.npmjs.com/package/@octavus/client-sdk) - Framework-agnostic client SDK
165
+ - [`@octavus/server-sdk`](https://www.npmjs.com/package/@octavus/server-sdk) - Server-side SDK
166
+ - [`@octavus/core`](https://www.npmjs.com/package/@octavus/core) - Shared types
167
+
168
+ ## License
169
+
170
+ MIT
package/dist/index.d.ts CHANGED
@@ -1,14 +1,18 @@
1
- import { OctavusChatOptions, UIMessage, ChatStatus, ConnectionState, UserMessageInput, FileReference } from '@octavus/client-sdk';
1
+ import { OctavusChatOptions, UIMessage, ChatStatus, OctavusError, ConnectionState, InteractiveTool, UserMessageInput, FileReference } from '@octavus/client-sdk';
2
2
  export * from '@octavus/client-sdk';
3
- export { ChatStatus, OctavusChatOptions, UserMessageInput } from '@octavus/client-sdk';
3
+ export { AppError, ChatStatus, ClientToolContext, ClientToolHandler, ConflictError, ForbiddenError, InteractiveTool, MAIN_THREAD, NotFoundError, OCTAVUS_SKILL_TOOLS, OctavusChat, OctavusChatOptions, OctavusError, UserMessageInput, ValidationError, createApiErrorEvent, createErrorEvent, createHttpTransport, createInternalErrorEvent, createSocketTransport, errorToStreamEvent, generateId, getSkillSlugFromToolCall, isAbortError, isAuthenticationError, isFileReference, isFileReferenceArray, isMainThread, isOctavusSkillTool, isOtherThread, isProviderError, isRateLimitError, isRetryableError, isSocketTransport, isToolError, isValidationError, parseSSEStream, resolveThread, safeParseStreamEvent, safeParseUIMessage, safeParseUIMessages, threadForPart, uploadFiles } from '@octavus/client-sdk';
4
4
 
5
5
  interface UseOctavusChatReturn {
6
6
  /** All messages including the currently streaming one */
7
7
  messages: UIMessage[];
8
8
  /** Current status of the chat */
9
9
  status: ChatStatus;
10
- /** Error if status is 'error' */
11
- error: Error | null;
10
+ /**
11
+ * Error if status is 'error'.
12
+ * Contains structured error information including type, source, and retryability.
13
+ * Use type guards like `isRateLimitError()` or `isProviderError()` to check specific error types.
14
+ */
15
+ error: OctavusError | null;
12
16
  /**
13
17
  * Socket connection state (socket transport only).
14
18
  * For HTTP transport, this is always `undefined`.
@@ -23,6 +27,25 @@ interface UseOctavusChatReturn {
23
27
  * Connection error if `connectionState` is 'error'.
24
28
  */
25
29
  connectionError: Error | undefined;
30
+ /**
31
+ * Pending interactive tool calls keyed by tool name.
32
+ * Each tool has bound `submit()` and `cancel()` methods.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * const feedbackTools = pendingClientTools['request-feedback'] ?? [];
37
+ *
38
+ * {feedbackTools.map(tool => (
39
+ * <FeedbackModal
40
+ * key={tool.toolCallId}
41
+ * {...tool.args}
42
+ * onSubmit={(result) => tool.submit(result)}
43
+ * onCancel={() => tool.cancel()}
44
+ * />
45
+ * ))}
46
+ * ```
47
+ */
48
+ pendingClientTools: Record<string, InteractiveTool[]>;
26
49
  /**
27
50
  * Trigger the agent and optionally add a user message to the chat.
28
51
  *
@@ -84,11 +107,12 @@ interface UseOctavusChatReturn {
84
107
  * function Chat({ sessionId }) {
85
108
  * const transport = useMemo(
86
109
  * () => createHttpTransport({
87
- * triggerRequest: (triggerName, input) =>
110
+ * request: (payload, options) =>
88
111
  * fetch('/api/trigger', {
89
112
  * method: 'POST',
90
113
  * headers: { 'Content-Type': 'application/json' },
91
- * body: JSON.stringify({ sessionId, triggerName, input }),
114
+ * body: JSON.stringify({ sessionId, ...payload }),
115
+ * signal: options?.signal,
92
116
  * }),
93
117
  * }),
94
118
  * [sessionId],
package/dist/index.js CHANGED
@@ -18,9 +18,15 @@ function useOctavusChat(options) {
18
18
  const getMessagesSnapshot = useCallback(() => chat.messages, [chat]);
19
19
  const getStatusSnapshot = useCallback(() => chat.status, [chat]);
20
20
  const getErrorSnapshot = useCallback(() => chat.error, [chat]);
21
+ const getPendingClientToolsSnapshot = useCallback(() => chat.pendingClientTools, [chat]);
21
22
  const messages = useSyncExternalStore(subscribe, getMessagesSnapshot, getMessagesSnapshot);
22
23
  const status = useSyncExternalStore(subscribe, getStatusSnapshot, getStatusSnapshot);
23
24
  const error = useSyncExternalStore(subscribe, getErrorSnapshot, getErrorSnapshot);
25
+ const pendingClientTools = useSyncExternalStore(
26
+ subscribe,
27
+ getPendingClientToolsSnapshot,
28
+ getPendingClientToolsSnapshot
29
+ );
24
30
  const socketTransport = isSocketTransport(transport) ? transport : null;
25
31
  const [connectionState, setConnectionState] = useState(
26
32
  socketTransport?.connectionState
@@ -43,7 +49,7 @@ function useOctavusChat(options) {
43
49
  [chat]
44
50
  );
45
51
  const stop = useCallback(() => chat.stop(), [chat]);
46
- const uploadFiles = useCallback(
52
+ const uploadFiles2 = useCallback(
47
53
  (files, onProgress) => chat.uploadFiles(files, onProgress),
48
54
  [chat]
49
55
  );
@@ -58,17 +64,93 @@ function useOctavusChat(options) {
58
64
  error,
59
65
  connectionState,
60
66
  connectionError,
67
+ pendingClientTools,
61
68
  send,
62
69
  stop,
63
70
  connect: socketTransport ? connect : void 0,
64
71
  disconnect: socketTransport ? disconnect : void 0,
65
- uploadFiles
72
+ uploadFiles: uploadFiles2
66
73
  };
67
74
  }
68
75
 
69
76
  // src/index.ts
70
- export * from "@octavus/client-sdk";
77
+ import {
78
+ OctavusChat as OctavusChat2,
79
+ uploadFiles,
80
+ parseSSEStream,
81
+ createHttpTransport,
82
+ createSocketTransport,
83
+ isSocketTransport as isSocketTransport2,
84
+ AppError,
85
+ NotFoundError,
86
+ ValidationError,
87
+ ConflictError,
88
+ ForbiddenError,
89
+ OctavusError,
90
+ isRateLimitError,
91
+ isAuthenticationError,
92
+ isProviderError,
93
+ isToolError,
94
+ isRetryableError,
95
+ isValidationError,
96
+ createErrorEvent,
97
+ errorToStreamEvent,
98
+ createInternalErrorEvent,
99
+ createApiErrorEvent,
100
+ generateId,
101
+ isAbortError,
102
+ MAIN_THREAD,
103
+ resolveThread,
104
+ isMainThread,
105
+ threadForPart,
106
+ isOtherThread,
107
+ isFileReference,
108
+ isFileReferenceArray,
109
+ safeParseStreamEvent,
110
+ safeParseUIMessage,
111
+ safeParseUIMessages,
112
+ OCTAVUS_SKILL_TOOLS,
113
+ isOctavusSkillTool,
114
+ getSkillSlugFromToolCall
115
+ } from "@octavus/client-sdk";
71
116
  export {
117
+ AppError,
118
+ ConflictError,
119
+ ForbiddenError,
120
+ MAIN_THREAD,
121
+ NotFoundError,
122
+ OCTAVUS_SKILL_TOOLS,
123
+ OctavusChat2 as OctavusChat,
124
+ OctavusError,
125
+ ValidationError,
126
+ createApiErrorEvent,
127
+ createErrorEvent,
128
+ createHttpTransport,
129
+ createInternalErrorEvent,
130
+ createSocketTransport,
131
+ errorToStreamEvent,
132
+ generateId,
133
+ getSkillSlugFromToolCall,
134
+ isAbortError,
135
+ isAuthenticationError,
136
+ isFileReference,
137
+ isFileReferenceArray,
138
+ isMainThread,
139
+ isOctavusSkillTool,
140
+ isOtherThread,
141
+ isProviderError,
142
+ isRateLimitError,
143
+ isRetryableError,
144
+ isSocketTransport2 as isSocketTransport,
145
+ isToolError,
146
+ isValidationError,
147
+ parseSSEStream,
148
+ resolveThread,
149
+ safeParseStreamEvent,
150
+ safeParseUIMessage,
151
+ safeParseUIMessages,
152
+ threadForPart,
153
+ uploadFiles,
72
154
  useOctavusChat
73
155
  };
74
156
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/use-octavus-chat.ts","../src/index.ts"],"sourcesContent":["'use client';\n\nimport { useRef, useCallback, useSyncExternalStore, useState, useEffect } from 'react';\nimport {\n OctavusChat,\n isSocketTransport,\n type OctavusChatOptions,\n type ChatStatus,\n type UserMessageInput,\n type UIMessage,\n type Transport,\n type ConnectionState,\n type FileReference,\n type UploadFilesOptions,\n type UploadUrlsResponse,\n} from '@octavus/client-sdk';\n\nexport type {\n OctavusChatOptions,\n ChatStatus,\n UserMessageInput,\n FileReference,\n UploadFilesOptions,\n UploadUrlsResponse,\n};\n\nexport interface UseOctavusChatReturn {\n /** All messages including the currently streaming one */\n messages: UIMessage[];\n /** Current status of the chat */\n status: ChatStatus;\n /** Error if status is 'error' */\n error: Error | null;\n /**\n * Socket connection state (socket transport only).\n * For HTTP transport, this is always `undefined`.\n *\n * - `disconnected`: Not connected (initial state before first send)\n * - `connecting`: Connection attempt in progress\n * - `connected`: Successfully connected\n * - `error`: Connection failed (check `connectionError`)\n */\n connectionState: ConnectionState | undefined;\n /**\n * Connection error if `connectionState` is 'error'.\n */\n connectionError: Error | undefined;\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 send: (\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ) => Promise<void>;\n /** Stop the current streaming and finalize any partial message */\n stop: () => void;\n /**\n * Eagerly connect to the socket (socket transport only).\n * Returns a promise that resolves when connected or rejects on error.\n * Safe to call multiple times - subsequent calls resolve immediately if already connected.\n *\n * For HTTP transport, this is `undefined`.\n */\n connect: (() => Promise<void>) | undefined;\n /**\n * Disconnect the socket (socket transport only).\n * The transport will reconnect automatically on next send().\n *\n * For HTTP transport, this is `undefined`.\n */\n disconnect: (() => void) | undefined;\n /**\n * Upload files directly without sending a message.\n * Useful for showing upload progress before sending.\n *\n * @param files - Files to upload\n * @param onProgress - Optional progress callback\n * @returns Array of file references\n *\n * @example\n * ```typescript\n * const fileRefs = await uploadFiles(fileInput.files, (i, progress) => {\n * console.log(`File ${i}: ${progress}%`);\n * });\n * // Later...\n * await send('user-message', { FILES: fileRefs }, { userMessage: { files: fileRefs } });\n * ```\n */\n uploadFiles: (\n files: FileList | File[],\n onProgress?: (fileIndex: number, progress: number) => void,\n ) => Promise<FileReference[]>;\n}\n\n/**\n * React hook for interacting with Octavus agents.\n * Provides chat state management and streaming support.\n *\n * When the transport changes (e.g., sessionId changes), the hook automatically\n * reinitializes with a fresh chat instance. Use `initialMessages` if you need\n * to preserve messages across transport changes.\n *\n * @example Basic usage with HTTP transport\n * ```tsx\n * import { useOctavusChat, createHttpTransport } from '@octavus/react';\n *\n * function Chat({ sessionId }) {\n * const transport = useMemo(\n * () => createHttpTransport({\n * triggerRequest: (triggerName, input) =>\n * fetch('/api/trigger', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * }),\n * }),\n * [sessionId],\n * );\n *\n * const { messages, status, send } = useOctavusChat({ transport });\n *\n * return (\n * <div>\n * {messages.map((msg) => (\n * <Message key={msg.id} message={msg} />\n * ))}\n * <button onClick={() => send('user-message', { USER_MESSAGE: 'Hello' })}>\n * Send\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @example Socket transport with eager connection\n * ```tsx\n * import { useOctavusChat, createSocketTransport } from '@octavus/react';\n *\n * function Chat() {\n * const transport = useMemo(\n * () => createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const sock = new SockJS('/octavus');\n * sock.onopen = () => resolve(sock);\n * sock.onerror = () => reject(new Error('Connection failed'));\n * }),\n * }),\n * [],\n * );\n *\n * const { messages, status, send, connectionState, connect, disconnect } =\n * useOctavusChat({ transport });\n *\n * // Eager connection on mount\n * useEffect(() => {\n * connect?.();\n * return () => disconnect?.();\n * }, [connect, disconnect]);\n *\n * return (\n * <div>\n * <StatusIndicator state={connectionState} />\n * {messages.map((msg) => <Message key={msg.id} message={msg} />)}\n * </div>\n * );\n * }\n * ```\n */\nexport function useOctavusChat(options: OctavusChatOptions): UseOctavusChatReturn {\n const chatRef = useRef<OctavusChat | null>(null);\n const transportRef = useRef<Transport | null>(null);\n\n if (transportRef.current !== options.transport) {\n chatRef.current?.stop();\n chatRef.current = new OctavusChat(options);\n transportRef.current = options.transport;\n }\n\n const chat = chatRef.current!;\n const transport = options.transport;\n\n const subscribe = useCallback((callback: () => void) => chat.subscribe(callback), [chat]);\n const getMessagesSnapshot = useCallback(() => chat.messages, [chat]);\n const getStatusSnapshot = useCallback(() => chat.status, [chat]);\n const getErrorSnapshot = useCallback(() => chat.error, [chat]);\n\n const messages = useSyncExternalStore(subscribe, getMessagesSnapshot, getMessagesSnapshot);\n const status = useSyncExternalStore(subscribe, getStatusSnapshot, getStatusSnapshot);\n const error = useSyncExternalStore(subscribe, getErrorSnapshot, getErrorSnapshot);\n\n const socketTransport = isSocketTransport(transport) ? transport : null;\n const [connectionState, setConnectionState] = useState<ConnectionState | undefined>(\n socketTransport?.connectionState,\n );\n const [connectionError, setConnectionError] = useState<Error | undefined>(undefined);\n\n useEffect(() => {\n if (!socketTransport) {\n setConnectionState(undefined);\n setConnectionError(undefined);\n return;\n }\n\n const unsubscribe = socketTransport.onConnectionStateChange((state, err) => {\n setConnectionState(state);\n setConnectionError(err);\n });\n\n return unsubscribe;\n }, [socketTransport]);\n\n const send = useCallback(\n (\n triggerName: string,\n input?: Record<string, unknown>,\n sendOptions?: { userMessage?: UserMessageInput },\n ) => chat.send(triggerName, input, sendOptions),\n [chat],\n );\n\n const stop = useCallback(() => chat.stop(), [chat]);\n\n const uploadFiles = useCallback(\n (files: FileList | File[], onProgress?: (fileIndex: number, progress: number) => void) =>\n chat.uploadFiles(files, onProgress),\n [chat],\n );\n\n // Stable references for connect/disconnect (socket transport only)\n const connect = useCallback(\n () => socketTransport?.connect() ?? Promise.resolve(),\n [socketTransport],\n );\n const disconnect = useCallback(() => socketTransport?.disconnect(), [socketTransport]);\n\n return {\n messages,\n status,\n error,\n connectionState,\n connectionError,\n send,\n stop,\n connect: socketTransport ? connect : undefined,\n disconnect: socketTransport ? disconnect : undefined,\n uploadFiles,\n };\n}\n","export {\n useOctavusChat,\n type UseOctavusChatReturn,\n type OctavusChatOptions,\n type ChatStatus,\n type UserMessageInput,\n} from './hooks/use-octavus-chat';\n\n// Re-export everything from client-sdk so consumers only need @octavus/react\nexport * from '@octavus/client-sdk';\n"],"mappings":";AAEA,SAAS,QAAQ,aAAa,sBAAsB,UAAU,iBAAiB;AAC/E;AAAA,EACE;AAAA,EACA;AAAA,OAUK;AA8JA,SAAS,eAAe,SAAmD;AAChF,QAAM,UAAU,OAA2B,IAAI;AAC/C,QAAM,eAAe,OAAyB,IAAI;AAElD,MAAI,aAAa,YAAY,QAAQ,WAAW;AAC9C,YAAQ,SAAS,KAAK;AACtB,YAAQ,UAAU,IAAI,YAAY,OAAO;AACzC,iBAAa,UAAU,QAAQ;AAAA,EACjC;AAEA,QAAM,OAAO,QAAQ;AACrB,QAAM,YAAY,QAAQ;AAE1B,QAAM,YAAY,YAAY,CAAC,aAAyB,KAAK,UAAU,QAAQ,GAAG,CAAC,IAAI,CAAC;AACxF,QAAM,sBAAsB,YAAY,MAAM,KAAK,UAAU,CAAC,IAAI,CAAC;AACnE,QAAM,oBAAoB,YAAY,MAAM,KAAK,QAAQ,CAAC,IAAI,CAAC;AAC/D,QAAM,mBAAmB,YAAY,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;AAE7D,QAAM,WAAW,qBAAqB,WAAW,qBAAqB,mBAAmB;AACzF,QAAM,SAAS,qBAAqB,WAAW,mBAAmB,iBAAiB;AACnF,QAAM,QAAQ,qBAAqB,WAAW,kBAAkB,gBAAgB;AAEhF,QAAM,kBAAkB,kBAAkB,SAAS,IAAI,YAAY;AACnE,QAAM,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAC5C,iBAAiB;AAAA,EACnB;AACA,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAA4B,MAAS;AAEnF,YAAU,MAAM;AACd,QAAI,CAAC,iBAAiB;AACpB,yBAAmB,MAAS;AAC5B,yBAAmB,MAAS;AAC5B;AAAA,IACF;AAEA,UAAM,cAAc,gBAAgB,wBAAwB,CAAC,OAAO,QAAQ;AAC1E,yBAAmB,KAAK;AACxB,yBAAmB,GAAG;AAAA,IACxB,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,OAAO;AAAA,IACX,CACE,aACA,OACA,gBACG,KAAK,KAAK,aAAa,OAAO,WAAW;AAAA,IAC9C,CAAC,IAAI;AAAA,EACP;AAEA,QAAM,OAAO,YAAY,MAAM,KAAK,KAAK,GAAG,CAAC,IAAI,CAAC;AAElD,QAAM,cAAc;AAAA,IAClB,CAAC,OAA0B,eACzB,KAAK,YAAY,OAAO,UAAU;AAAA,IACpC,CAAC,IAAI;AAAA,EACP;AAGA,QAAM,UAAU;AAAA,IACd,MAAM,iBAAiB,QAAQ,KAAK,QAAQ,QAAQ;AAAA,IACpD,CAAC,eAAe;AAAA,EAClB;AACA,QAAM,aAAa,YAAY,MAAM,iBAAiB,WAAW,GAAG,CAAC,eAAe,CAAC;AAErF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,kBAAkB,UAAU;AAAA,IACrC,YAAY,kBAAkB,aAAa;AAAA,IAC3C;AAAA,EACF;AACF;;;ACnPA,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/hooks/use-octavus-chat.ts","../src/index.ts"],"sourcesContent":["'use client';\n\nimport { useRef, useCallback, useSyncExternalStore, useState, useEffect } from 'react';\nimport {\n OctavusChat,\n type OctavusError,\n isSocketTransport,\n type OctavusChatOptions,\n type ChatStatus,\n type UserMessageInput,\n type UIMessage,\n type Transport,\n type ConnectionState,\n type FileReference,\n type UploadFilesOptions,\n type UploadUrlsResponse,\n type ClientToolContext,\n type ClientToolHandler,\n type InteractiveTool,\n} from '@octavus/client-sdk';\n\nexport type {\n OctavusChatOptions,\n ChatStatus,\n UserMessageInput,\n FileReference,\n UploadFilesOptions,\n UploadUrlsResponse,\n ClientToolContext,\n ClientToolHandler,\n InteractiveTool,\n};\n\nexport interface UseOctavusChatReturn {\n /** All messages including the currently streaming one */\n messages: UIMessage[];\n /** Current status of the chat */\n status: ChatStatus;\n /**\n * Error if status is 'error'.\n * Contains structured error information including type, source, and retryability.\n * Use type guards like `isRateLimitError()` or `isProviderError()` to check specific error types.\n */\n error: OctavusError | null;\n /**\n * Socket connection state (socket transport only).\n * For HTTP transport, this is always `undefined`.\n *\n * - `disconnected`: Not connected (initial state before first send)\n * - `connecting`: Connection attempt in progress\n * - `connected`: Successfully connected\n * - `error`: Connection failed (check `connectionError`)\n */\n connectionState: ConnectionState | undefined;\n /**\n * Connection error if `connectionState` is 'error'.\n */\n connectionError: Error | undefined;\n /**\n * Pending interactive tool calls keyed by tool name.\n * Each tool has bound `submit()` and `cancel()` methods.\n *\n * @example\n * ```tsx\n * const feedbackTools = pendingClientTools['request-feedback'] ?? [];\n *\n * {feedbackTools.map(tool => (\n * <FeedbackModal\n * key={tool.toolCallId}\n * {...tool.args}\n * onSubmit={(result) => tool.submit(result)}\n * onCancel={() => tool.cancel()}\n * />\n * ))}\n * ```\n */\n pendingClientTools: Record<string, InteractiveTool[]>;\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 send: (\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ) => Promise<void>;\n /** Stop the current streaming and finalize any partial message */\n stop: () => void;\n /**\n * Eagerly connect to the socket (socket transport only).\n * Returns a promise that resolves when connected or rejects on error.\n * Safe to call multiple times - subsequent calls resolve immediately if already connected.\n *\n * For HTTP transport, this is `undefined`.\n */\n connect: (() => Promise<void>) | undefined;\n /**\n * Disconnect the socket (socket transport only).\n * The transport will reconnect automatically on next send().\n *\n * For HTTP transport, this is `undefined`.\n */\n disconnect: (() => void) | undefined;\n /**\n * Upload files directly without sending a message.\n * Useful for showing upload progress before sending.\n *\n * @param files - Files to upload\n * @param onProgress - Optional progress callback\n * @returns Array of file references\n *\n * @example\n * ```typescript\n * const fileRefs = await uploadFiles(fileInput.files, (i, progress) => {\n * console.log(`File ${i}: ${progress}%`);\n * });\n * // Later...\n * await send('user-message', { FILES: fileRefs }, { userMessage: { files: fileRefs } });\n * ```\n */\n uploadFiles: (\n files: FileList | File[],\n onProgress?: (fileIndex: number, progress: number) => void,\n ) => Promise<FileReference[]>;\n}\n\n/**\n * React hook for interacting with Octavus agents.\n * Provides chat state management and streaming support.\n *\n * When the transport changes (e.g., sessionId changes), the hook automatically\n * reinitializes with a fresh chat instance. Use `initialMessages` if you need\n * to preserve messages across transport changes.\n *\n * @example Basic usage with HTTP transport\n * ```tsx\n * import { useOctavusChat, createHttpTransport } from '@octavus/react';\n *\n * function Chat({ sessionId }) {\n * const transport = useMemo(\n * () => createHttpTransport({\n * request: (payload, options) =>\n * fetch('/api/trigger', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, ...payload }),\n * signal: options?.signal,\n * }),\n * }),\n * [sessionId],\n * );\n *\n * const { messages, status, send } = useOctavusChat({ transport });\n *\n * return (\n * <div>\n * {messages.map((msg) => (\n * <Message key={msg.id} message={msg} />\n * ))}\n * <button onClick={() => send('user-message', { USER_MESSAGE: 'Hello' })}>\n * Send\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @example Socket transport with eager connection\n * ```tsx\n * import { useOctavusChat, createSocketTransport } from '@octavus/react';\n *\n * function Chat() {\n * const transport = useMemo(\n * () => createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const sock = new SockJS('/octavus');\n * sock.onopen = () => resolve(sock);\n * sock.onerror = () => reject(new Error('Connection failed'));\n * }),\n * }),\n * [],\n * );\n *\n * const { messages, status, send, connectionState, connect, disconnect } =\n * useOctavusChat({ transport });\n *\n * // Eager connection on mount\n * useEffect(() => {\n * connect?.();\n * return () => disconnect?.();\n * }, [connect, disconnect]);\n *\n * return (\n * <div>\n * <StatusIndicator state={connectionState} />\n * {messages.map((msg) => <Message key={msg.id} message={msg} />)}\n * </div>\n * );\n * }\n * ```\n */\nexport function useOctavusChat(options: OctavusChatOptions): UseOctavusChatReturn {\n const chatRef = useRef<OctavusChat | null>(null);\n const transportRef = useRef<Transport | null>(null);\n\n if (transportRef.current !== options.transport) {\n chatRef.current?.stop();\n chatRef.current = new OctavusChat(options);\n transportRef.current = options.transport;\n }\n\n const chat = chatRef.current!;\n const transport = options.transport;\n\n const subscribe = useCallback((callback: () => void) => chat.subscribe(callback), [chat]);\n const getMessagesSnapshot = useCallback(() => chat.messages, [chat]);\n const getStatusSnapshot = useCallback(() => chat.status, [chat]);\n const getErrorSnapshot = useCallback(() => chat.error, [chat]);\n const getPendingClientToolsSnapshot = useCallback(() => chat.pendingClientTools, [chat]);\n\n const messages = useSyncExternalStore(subscribe, getMessagesSnapshot, getMessagesSnapshot);\n const status = useSyncExternalStore(subscribe, getStatusSnapshot, getStatusSnapshot);\n const error = useSyncExternalStore(subscribe, getErrorSnapshot, getErrorSnapshot);\n const pendingClientTools = useSyncExternalStore(\n subscribe,\n getPendingClientToolsSnapshot,\n getPendingClientToolsSnapshot,\n );\n\n const socketTransport = isSocketTransport(transport) ? transport : null;\n const [connectionState, setConnectionState] = useState<ConnectionState | undefined>(\n socketTransport?.connectionState,\n );\n const [connectionError, setConnectionError] = useState<Error | undefined>(undefined);\n\n useEffect(() => {\n if (!socketTransport) {\n setConnectionState(undefined);\n setConnectionError(undefined);\n return;\n }\n\n const unsubscribe = socketTransport.onConnectionStateChange((state, err) => {\n setConnectionState(state);\n setConnectionError(err);\n });\n\n return unsubscribe;\n }, [socketTransport]);\n\n const send = useCallback(\n (\n triggerName: string,\n input?: Record<string, unknown>,\n sendOptions?: { userMessage?: UserMessageInput },\n ) => chat.send(triggerName, input, sendOptions),\n [chat],\n );\n\n const stop = useCallback(() => chat.stop(), [chat]);\n\n const uploadFiles = useCallback(\n (files: FileList | File[], onProgress?: (fileIndex: number, progress: number) => void) =>\n chat.uploadFiles(files, onProgress),\n [chat],\n );\n\n // Stable references for connect/disconnect (socket transport only)\n const connect = useCallback(\n () => socketTransport?.connect() ?? Promise.resolve(),\n [socketTransport],\n );\n const disconnect = useCallback(() => socketTransport?.disconnect(), [socketTransport]);\n\n return {\n messages,\n status,\n error,\n connectionState,\n connectionError,\n pendingClientTools,\n send,\n stop,\n connect: socketTransport ? connect : undefined,\n disconnect: socketTransport ? disconnect : undefined,\n uploadFiles,\n };\n}\n","export {\n useOctavusChat,\n type UseOctavusChatReturn,\n type OctavusChatOptions,\n type ChatStatus,\n type UserMessageInput,\n type ClientToolContext,\n type ClientToolHandler,\n type InteractiveTool,\n} from './hooks/use-octavus-chat';\n\nexport type * from '@octavus/client-sdk';\nexport {\n // Chat\n OctavusChat,\n // Files\n uploadFiles,\n // Stream\n parseSSEStream,\n // Transports\n createHttpTransport,\n createSocketTransport,\n isSocketTransport,\n // Error classes\n AppError,\n NotFoundError,\n ValidationError,\n ConflictError,\n ForbiddenError,\n OctavusError,\n // Error type guards\n isRateLimitError,\n isAuthenticationError,\n isProviderError,\n isToolError,\n isRetryableError,\n isValidationError,\n // Error event helpers\n createErrorEvent,\n errorToStreamEvent,\n createInternalErrorEvent,\n createApiErrorEvent,\n // Utilities\n generateId,\n isAbortError,\n // Thread helpers\n MAIN_THREAD,\n resolveThread,\n isMainThread,\n threadForPart,\n isOtherThread,\n // Type guards\n isFileReference,\n isFileReferenceArray,\n // Safe parse helpers\n safeParseStreamEvent,\n safeParseUIMessage,\n safeParseUIMessages,\n // Skills\n OCTAVUS_SKILL_TOOLS,\n isOctavusSkillTool,\n getSkillSlugFromToolCall,\n} from '@octavus/client-sdk';\n"],"mappings":"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","names":["uploadFiles","OctavusChat","isSocketTransport"]}
package/package.json CHANGED
@@ -1,16 +1,27 @@
1
1
  {
2
2
  "name": "@octavus/react",
3
- "version": "0.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "React bindings for Octavus agents",
5
5
  "license": "MIT",
6
6
  "author": "Octavus AI <dev@octavus.ai>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/octavus-ai/js-sdk.git",
10
+ "directory": "packages/react"
11
+ },
12
+ "homepage": "https://octavus.ai",
13
+ "bugs": {
14
+ "url": "https://github.com/octavus-ai/js-sdk/issues"
15
+ },
7
16
  "keywords": [
8
17
  "octavus",
9
18
  "ai",
10
19
  "agents",
11
20
  "sdk",
12
21
  "react",
13
- "streaming"
22
+ "hooks",
23
+ "streaming",
24
+ "chat"
14
25
  ],
15
26
  "type": "module",
16
27
  "sideEffects": false,
@@ -29,7 +40,7 @@
29
40
  "access": "public"
30
41
  },
31
42
  "dependencies": {
32
- "@octavus/client-sdk": "^0.2.0"
43
+ "@octavus/client-sdk": "^2.0.0"
33
44
  },
34
45
  "peerDependencies": {
35
46
  "react": ">=18.0.0"