@octavus/client-sdk 2.19.0 → 2.21.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/dist/index.cjs +2050 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +884 -0
- package/package.json +12 -5
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
import { StreamEvent, ToolResult, FileReference, UIMessage, OctavusError } from '@octavus/core';
|
|
2
|
+
export * from '@octavus/core';
|
|
3
|
+
export { AppError, ConflictError, ForbiddenError, MAIN_THREAD, NotFoundError, OCTAVUS_SKILL_TOOLS, OctavusError, ValidationError, createApiErrorEvent, createErrorEvent, createInternalErrorEvent, errorToStreamEvent, generateId, getSkillSlugFromToolCall, isAbortError, isAuthenticationError, isFileReference, isFileReferenceArray, isMainThread, isOctavusSkillTool, isOtherThread, isProviderError, isRateLimitError, isRetryableError, isToolError, isValidationError, resolveThread, safeParseStreamEvent, safeParseUIMessage, safeParseUIMessages, threadForPart } from '@octavus/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for trigger execution (e.g., retry with rollback).
|
|
7
|
+
*/
|
|
8
|
+
interface TriggerOptions {
|
|
9
|
+
/**
|
|
10
|
+
* ID of the last message to keep in session state.
|
|
11
|
+
* Messages after this are removed before execution.
|
|
12
|
+
* Use `null` to truncate all messages (retry from empty history).
|
|
13
|
+
*/
|
|
14
|
+
rollbackAfterMessageId?: string | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Transport interface for delivering events from server to client.
|
|
18
|
+
*
|
|
19
|
+
* Abstracts the connection mechanism (HTTP/SSE or WebSocket) behind a unified
|
|
20
|
+
* async iterator interface. Use `createHttpTransport` or `createSocketTransport`
|
|
21
|
+
* to create an implementation.
|
|
22
|
+
*/
|
|
23
|
+
interface Transport {
|
|
24
|
+
/**
|
|
25
|
+
* Trigger the agent and stream events.
|
|
26
|
+
* @param triggerName - The trigger name defined in the agent's protocol
|
|
27
|
+
* @param input - Input parameters for variable substitution
|
|
28
|
+
* @param options - Optional trigger options (e.g., rollback for retry)
|
|
29
|
+
*/
|
|
30
|
+
trigger(triggerName: string, input?: Record<string, unknown>, options?: TriggerOptions): AsyncIterable<StreamEvent>;
|
|
31
|
+
/**
|
|
32
|
+
* Continue execution with tool results after client-side tool handling.
|
|
33
|
+
*
|
|
34
|
+
* @param executionId - The execution ID from the client-tool-request event
|
|
35
|
+
* @param results - All tool results (server + client) to send
|
|
36
|
+
*/
|
|
37
|
+
continueWithToolResults(executionId: string, results: ToolResult[]): AsyncIterable<StreamEvent>;
|
|
38
|
+
/**
|
|
39
|
+
* Observe an already-active execution without triggering a new one.
|
|
40
|
+
* Returns events from the current execution.
|
|
41
|
+
*
|
|
42
|
+
* Only applicable to transports where execution happens independently of the
|
|
43
|
+
* client connection (e.g., polling). HTTP and WebSocket transports do not need
|
|
44
|
+
* to implement this.
|
|
45
|
+
*/
|
|
46
|
+
observe?(): AsyncIterable<StreamEvent>;
|
|
47
|
+
/** Stop the current stream. Safe to call when no stream is active. */
|
|
48
|
+
stop(): void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Connection states for socket transport.
|
|
52
|
+
*
|
|
53
|
+
* - `disconnected`: Not connected (initial state or after disconnect)
|
|
54
|
+
* - `connecting`: Connection attempt in progress
|
|
55
|
+
* - `connected`: Successfully connected and ready
|
|
56
|
+
* - `error`: Connection failed (check error in listener callback)
|
|
57
|
+
*/
|
|
58
|
+
type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'error';
|
|
59
|
+
/**
|
|
60
|
+
* Callback for connection state changes.
|
|
61
|
+
*/
|
|
62
|
+
type ConnectionStateListener = (state: ConnectionState, error?: Error) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Socket transport with connection management capabilities.
|
|
65
|
+
*
|
|
66
|
+
* Extends the base Transport interface with methods for managing persistent
|
|
67
|
+
* WebSocket/SockJS connections. Use this when you need:
|
|
68
|
+
* - Eager connection (connect before first message)
|
|
69
|
+
* - Connection status UI indicators
|
|
70
|
+
* - Manual connection lifecycle control
|
|
71
|
+
*
|
|
72
|
+
* Created via `createSocketTransport()`.
|
|
73
|
+
*/
|
|
74
|
+
interface SocketTransport extends Transport {
|
|
75
|
+
/**
|
|
76
|
+
* Current connection state.
|
|
77
|
+
*
|
|
78
|
+
* - `disconnected`: Not connected (initial state)
|
|
79
|
+
* - `connecting`: Connection in progress
|
|
80
|
+
* - `connected`: Ready to send/receive
|
|
81
|
+
* - `error`: Connection failed
|
|
82
|
+
*/
|
|
83
|
+
readonly connectionState: ConnectionState;
|
|
84
|
+
/**
|
|
85
|
+
* Subscribe to connection state changes.
|
|
86
|
+
*
|
|
87
|
+
* The listener is called immediately with the current state, then again
|
|
88
|
+
* whenever the state changes.
|
|
89
|
+
*
|
|
90
|
+
* @param listener - Callback invoked on state changes
|
|
91
|
+
* @returns Unsubscribe function
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const unsubscribe = transport.onConnectionStateChange((state, error) => {
|
|
96
|
+
* setConnectionState(state);
|
|
97
|
+
* if (error) setConnectionError(error);
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
onConnectionStateChange(listener: ConnectionStateListener): () => void;
|
|
102
|
+
/**
|
|
103
|
+
* Eagerly establish the connection.
|
|
104
|
+
*
|
|
105
|
+
* By default, socket transport connects lazily on first `trigger()`. Call
|
|
106
|
+
* this method to establish the connection early (e.g., on component mount):
|
|
107
|
+
* - Faster first message response
|
|
108
|
+
* - Show accurate connection status in UI
|
|
109
|
+
* - Handle connection errors before user interaction
|
|
110
|
+
*
|
|
111
|
+
* Safe to call multiple times - resolves immediately if already connected.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```tsx
|
|
115
|
+
* useEffect(() => {
|
|
116
|
+
* transport.connect()
|
|
117
|
+
* .then(() => console.log('Connected'))
|
|
118
|
+
* .catch((err) => console.error('Failed:', err));
|
|
119
|
+
*
|
|
120
|
+
* return () => transport.disconnect();
|
|
121
|
+
* }, [transport]);
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
connect(): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Close the connection and clean up resources.
|
|
127
|
+
*
|
|
128
|
+
* The transport will reconnect automatically on next `trigger()` call.
|
|
129
|
+
*/
|
|
130
|
+
disconnect(): void;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if a transport is a SocketTransport with connection management.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* if (isSocketTransport(transport)) {
|
|
138
|
+
* transport.connect(); // TypeScript knows this is available
|
|
139
|
+
* }
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
declare function isSocketTransport(transport: Transport): transport is SocketTransport;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Response from the upload URLs endpoint
|
|
146
|
+
*/
|
|
147
|
+
interface UploadUrlsResponse {
|
|
148
|
+
files: {
|
|
149
|
+
id: string;
|
|
150
|
+
uploadUrl: string;
|
|
151
|
+
downloadUrl: string;
|
|
152
|
+
}[];
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Options for uploading files
|
|
156
|
+
*/
|
|
157
|
+
interface UploadFilesOptions {
|
|
158
|
+
/**
|
|
159
|
+
* Function to request upload URLs from the platform.
|
|
160
|
+
* Consumer apps must implement this to authenticate with the platform.
|
|
161
|
+
*
|
|
162
|
+
* @param files - Array of file metadata to request URLs for
|
|
163
|
+
* @returns Response with presigned upload and download URLs
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* requestUploadUrls: async (files) => {
|
|
168
|
+
* const response = await fetch('/api/upload-urls', {
|
|
169
|
+
* method: 'POST',
|
|
170
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
171
|
+
* body: JSON.stringify({ sessionId, files }),
|
|
172
|
+
* });
|
|
173
|
+
* return response.json();
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
requestUploadUrls: (files: {
|
|
178
|
+
filename: string;
|
|
179
|
+
mediaType: string;
|
|
180
|
+
size: number;
|
|
181
|
+
}[]) => Promise<UploadUrlsResponse>;
|
|
182
|
+
/**
|
|
183
|
+
* Callback for upload progress (0-100 per file).
|
|
184
|
+
* Called multiple times during upload with real-time progress.
|
|
185
|
+
*
|
|
186
|
+
* @param fileIndex - Index of the file being uploaded
|
|
187
|
+
* @param progress - Progress percentage (0-100)
|
|
188
|
+
*/
|
|
189
|
+
onProgress?: (fileIndex: number, progress: number) => void;
|
|
190
|
+
/** Upload timeout per file in milliseconds. Default: 60000 (60s). Set to 0 to disable. */
|
|
191
|
+
timeoutMs?: number;
|
|
192
|
+
/** Max retry attempts per file after initial failure. Default: 2. Set to 0 to disable retries. */
|
|
193
|
+
maxRetries?: number;
|
|
194
|
+
/** Delay between retries in milliseconds. Default: 1000 (1s). */
|
|
195
|
+
retryDelayMs?: number;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Upload files to the Octavus platform.
|
|
199
|
+
*
|
|
200
|
+
* This function:
|
|
201
|
+
* 1. Requests presigned upload URLs from the platform
|
|
202
|
+
* 2. Uploads each file directly to S3 with progress tracking
|
|
203
|
+
* 3. Returns file references that can be used in trigger input
|
|
204
|
+
*
|
|
205
|
+
* Uploads include automatic timeout (default 60s) and retry (default 2 retries)
|
|
206
|
+
* for transient failures like network errors or server issues.
|
|
207
|
+
*
|
|
208
|
+
* @param files - Files to upload (from file input or drag/drop)
|
|
209
|
+
* @param options - Upload configuration
|
|
210
|
+
* @returns Array of file references with download URLs
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* const fileRefs = await uploadFiles(fileInputRef.current.files, {
|
|
215
|
+
* requestUploadUrls: async (files) => {
|
|
216
|
+
* const response = await fetch('/api/upload-urls', {
|
|
217
|
+
* method: 'POST',
|
|
218
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
219
|
+
* body: JSON.stringify({ sessionId, files }),
|
|
220
|
+
* });
|
|
221
|
+
* return response.json();
|
|
222
|
+
* },
|
|
223
|
+
* onProgress: (fileIndex, progress) => {
|
|
224
|
+
* console.log(`File ${fileIndex}: ${progress}%`);
|
|
225
|
+
* },
|
|
226
|
+
* });
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
declare function uploadFiles(files: FileList | File[], options: UploadFilesOptions): Promise<FileReference[]>;
|
|
230
|
+
|
|
231
|
+
type ChatStatus = 'idle' | 'streaming' | 'error' | 'awaiting-input';
|
|
232
|
+
/**
|
|
233
|
+
* Context provided to client tool handlers.
|
|
234
|
+
*/
|
|
235
|
+
interface ClientToolContext {
|
|
236
|
+
/** Unique identifier for this tool call */
|
|
237
|
+
toolCallId: string;
|
|
238
|
+
/** Name of the tool being called */
|
|
239
|
+
toolName: string;
|
|
240
|
+
/** Signal for cancellation if user stops generation */
|
|
241
|
+
signal: AbortSignal;
|
|
242
|
+
/**
|
|
243
|
+
* Register a file produced by this tool (e.g., a screenshot).
|
|
244
|
+
* Files are sent to the platform alongside the tool result so the LLM
|
|
245
|
+
* can see them as visual content rather than just a JSON URL.
|
|
246
|
+
*/
|
|
247
|
+
addFile: (file: FileReference) => void;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Handler function for client-side tool execution.
|
|
251
|
+
* Can be:
|
|
252
|
+
* - An async function that executes automatically and returns a result
|
|
253
|
+
* - The string 'interactive' to indicate the tool requires user interaction
|
|
254
|
+
*/
|
|
255
|
+
type ClientToolHandler = ((args: Record<string, unknown>, ctx: ClientToolContext) => Promise<unknown>) | 'interactive';
|
|
256
|
+
/**
|
|
257
|
+
* Interactive tool call awaiting user interaction.
|
|
258
|
+
* The `submit` and `cancel` methods are pre-bound to this tool call's ID.
|
|
259
|
+
*/
|
|
260
|
+
interface InteractiveTool {
|
|
261
|
+
/** Unique identifier for this tool call */
|
|
262
|
+
toolCallId: string;
|
|
263
|
+
/** Name of the tool being called */
|
|
264
|
+
toolName: string;
|
|
265
|
+
/** Arguments passed to the tool */
|
|
266
|
+
args: Record<string, unknown>;
|
|
267
|
+
/**
|
|
268
|
+
* Submit a result for this tool call.
|
|
269
|
+
* Call this when the user has provided input.
|
|
270
|
+
*
|
|
271
|
+
* @param result - The result from user interaction
|
|
272
|
+
*/
|
|
273
|
+
submit: (result: unknown) => void;
|
|
274
|
+
/**
|
|
275
|
+
* Cancel this tool call with an optional reason.
|
|
276
|
+
* Call this when the user dismisses the UI without providing input.
|
|
277
|
+
*
|
|
278
|
+
* @param reason - Optional reason for cancellation (default: 'User cancelled')
|
|
279
|
+
*/
|
|
280
|
+
cancel: (reason?: string) => void;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Input for creating a user message.
|
|
284
|
+
* Supports text content, structured object content, and file attachments.
|
|
285
|
+
*/
|
|
286
|
+
interface UserMessageInput {
|
|
287
|
+
/**
|
|
288
|
+
* Content of the message. Can be:
|
|
289
|
+
* - string: Creates a text part
|
|
290
|
+
* - object: Creates an object part (uses `type` field as typeName if present)
|
|
291
|
+
*/
|
|
292
|
+
content?: string | Record<string, unknown>;
|
|
293
|
+
/**
|
|
294
|
+
* File attachments (shorthand). Can be:
|
|
295
|
+
* - FileList: From file input element (will be uploaded via uploadFiles)
|
|
296
|
+
* - File[]: Array of File objects (will be uploaded via uploadFiles)
|
|
297
|
+
* - FileReference[]: Already uploaded files (used directly)
|
|
298
|
+
*/
|
|
299
|
+
files?: FileList | File[] | FileReference[];
|
|
300
|
+
}
|
|
301
|
+
interface OctavusChatOptions {
|
|
302
|
+
/**
|
|
303
|
+
* Transport for streaming events.
|
|
304
|
+
* Use `createHttpTransport` for HTTP/SSE or `createSocketTransport` for WebSocket/SockJS.
|
|
305
|
+
*/
|
|
306
|
+
transport: Transport;
|
|
307
|
+
/**
|
|
308
|
+
* Function to request upload URLs from the platform.
|
|
309
|
+
* Required if you want to use file uploads with FileList/File[].
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* requestUploadUrls: async (files) => {
|
|
314
|
+
* const response = await fetch('/api/upload-urls', {
|
|
315
|
+
* method: 'POST',
|
|
316
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
317
|
+
* body: JSON.stringify({ sessionId, files }),
|
|
318
|
+
* });
|
|
319
|
+
* return response.json();
|
|
320
|
+
* }
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
requestUploadUrls?: UploadFilesOptions['requestUploadUrls'];
|
|
324
|
+
/** Upload timeout and retry configuration. Defaults: 60s timeout, 2 retries, 1s delay. */
|
|
325
|
+
uploadOptions?: Pick<UploadFilesOptions, 'timeoutMs' | 'maxRetries' | 'retryDelayMs'>;
|
|
326
|
+
/**
|
|
327
|
+
* Client-side tool handlers.
|
|
328
|
+
* Register handlers for tools that should execute in the browser.
|
|
329
|
+
*
|
|
330
|
+
* - If a tool has a handler function: executes automatically
|
|
331
|
+
* - If a tool is marked as 'interactive': appears in `pendingClientTools` with bound `submit()`/`cancel()`
|
|
332
|
+
*
|
|
333
|
+
* @example Automatic client tool
|
|
334
|
+
* ```typescript
|
|
335
|
+
* clientTools: {
|
|
336
|
+
* 'get-browser-location': async () => {
|
|
337
|
+
* const pos = await new Promise((resolve, reject) => {
|
|
338
|
+
* navigator.geolocation.getCurrentPosition(resolve, reject);
|
|
339
|
+
* });
|
|
340
|
+
* return { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
|
341
|
+
* },
|
|
342
|
+
* }
|
|
343
|
+
* ```
|
|
344
|
+
*
|
|
345
|
+
* @example Interactive client tool (user input required)
|
|
346
|
+
* ```typescript
|
|
347
|
+
* clientTools: {
|
|
348
|
+
* 'request-feedback': 'interactive',
|
|
349
|
+
* }
|
|
350
|
+
* // Then render UI based on pendingClientTools['request-feedback']
|
|
351
|
+
* // and call tool.submit(result) or tool.cancel()
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
clientTools?: Record<string, ClientToolHandler>;
|
|
355
|
+
/** Initial messages (for session refresh) */
|
|
356
|
+
initialMessages?: UIMessage[];
|
|
357
|
+
/**
|
|
358
|
+
* Callback when an error occurs.
|
|
359
|
+
* Receives an OctavusError with structured error information.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```typescript
|
|
363
|
+
* onError: (error) => {
|
|
364
|
+
* console.error('Chat error:', {
|
|
365
|
+
* type: error.errorType,
|
|
366
|
+
* message: error.message,
|
|
367
|
+
* retryable: error.retryable,
|
|
368
|
+
* provider: error.provider,
|
|
369
|
+
* });
|
|
370
|
+
*
|
|
371
|
+
* // Handle specific error types
|
|
372
|
+
* if (isRateLimitError(error)) {
|
|
373
|
+
* showRetryButton(error.retryAfter);
|
|
374
|
+
* }
|
|
375
|
+
* }
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
onError?: (error: OctavusError) => void;
|
|
379
|
+
/** Callback when streaming finishes successfully */
|
|
380
|
+
onFinish?: () => void;
|
|
381
|
+
/** Callback when streaming is stopped by user */
|
|
382
|
+
onStop?: () => void;
|
|
383
|
+
/** Callback when a resource is updated */
|
|
384
|
+
onResourceUpdate?: (name: string, value: unknown) => void;
|
|
385
|
+
/**
|
|
386
|
+
* Callback when execution starts with the session/execution ID.
|
|
387
|
+
* Useful for tracking the current execution for activity logs.
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```typescript
|
|
391
|
+
* onStart: (sessionId) => {
|
|
392
|
+
* setCurrentSessionId(sessionId);
|
|
393
|
+
* }
|
|
394
|
+
* ```
|
|
395
|
+
*/
|
|
396
|
+
onStart?: (sessionId: string) => void;
|
|
397
|
+
}
|
|
398
|
+
type Listener = () => void;
|
|
399
|
+
/**
|
|
400
|
+
* Framework-agnostic chat client for Octavus agents.
|
|
401
|
+
* Manages chat state and streaming, allowing reactive frameworks to subscribe to updates.
|
|
402
|
+
*
|
|
403
|
+
* @example HTTP transport (Next.js, etc.)
|
|
404
|
+
* ```typescript
|
|
405
|
+
* import { OctavusChat, createHttpTransport } from '@octavus/client-sdk';
|
|
406
|
+
*
|
|
407
|
+
* const chat = new OctavusChat({
|
|
408
|
+
* transport: createHttpTransport({
|
|
409
|
+
* request: (payload, options) =>
|
|
410
|
+
* fetch('/api/trigger', {
|
|
411
|
+
* method: 'POST',
|
|
412
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
413
|
+
* body: JSON.stringify({ sessionId, ...payload }),
|
|
414
|
+
* signal: options?.signal,
|
|
415
|
+
* }),
|
|
416
|
+
* }),
|
|
417
|
+
* });
|
|
418
|
+
* ```
|
|
419
|
+
*
|
|
420
|
+
* @example Socket transport (WebSocket, SockJS, Meteor)
|
|
421
|
+
* ```typescript
|
|
422
|
+
* import { OctavusChat, createSocketTransport } from '@octavus/client-sdk';
|
|
423
|
+
*
|
|
424
|
+
* const chat = new OctavusChat({
|
|
425
|
+
* transport: createSocketTransport({
|
|
426
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
427
|
+
* const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);
|
|
428
|
+
* ws.onopen = () => resolve(ws);
|
|
429
|
+
* ws.onerror = () => reject(new Error('Connection failed'));
|
|
430
|
+
* }),
|
|
431
|
+
* }),
|
|
432
|
+
* });
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
declare class OctavusChat {
|
|
436
|
+
private _messages;
|
|
437
|
+
private _status;
|
|
438
|
+
private _error;
|
|
439
|
+
private options;
|
|
440
|
+
private transport;
|
|
441
|
+
private streamingState;
|
|
442
|
+
private _pendingToolsByName;
|
|
443
|
+
private _pendingToolsByCallId;
|
|
444
|
+
private _pendingClientToolsCache;
|
|
445
|
+
private _completedToolResults;
|
|
446
|
+
private _clientToolAbortController;
|
|
447
|
+
private _serverToolResults;
|
|
448
|
+
private _pendingExecutionId;
|
|
449
|
+
private _readyToContinue;
|
|
450
|
+
private _finishEventReceived;
|
|
451
|
+
private _rollbackSynced;
|
|
452
|
+
private _lastTrigger;
|
|
453
|
+
private listeners;
|
|
454
|
+
constructor(options: OctavusChatOptions);
|
|
455
|
+
/**
|
|
456
|
+
* Update mutable options (callbacks and tool handlers) without recreating the instance.
|
|
457
|
+
* Used by the React hook to keep options fresh across renders, but can also be
|
|
458
|
+
* called directly by non-React consumers.
|
|
459
|
+
*
|
|
460
|
+
* `transport` and `initialMessages` are excluded since they're only consumed at construction time.
|
|
461
|
+
*/
|
|
462
|
+
updateOptions(updates: Partial<Omit<OctavusChatOptions, 'transport' | 'initialMessages'>>): void;
|
|
463
|
+
get messages(): UIMessage[];
|
|
464
|
+
get status(): ChatStatus;
|
|
465
|
+
/**
|
|
466
|
+
* The current error, if any.
|
|
467
|
+
* Contains structured error information including type, source, and retryability.
|
|
468
|
+
*/
|
|
469
|
+
get error(): OctavusError | null;
|
|
470
|
+
/**
|
|
471
|
+
* Pending interactive tool calls keyed by tool name.
|
|
472
|
+
* Each tool has bound `submit()` and `cancel()` methods.
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* ```tsx
|
|
476
|
+
* const feedbackTools = pendingClientTools['request-feedback'] ?? [];
|
|
477
|
+
*
|
|
478
|
+
* {feedbackTools.map(tool => (
|
|
479
|
+
* <FeedbackModal
|
|
480
|
+
* key={tool.toolCallId}
|
|
481
|
+
* {...tool.args}
|
|
482
|
+
* onSubmit={(result) => tool.submit(result)}
|
|
483
|
+
* onCancel={() => tool.cancel()}
|
|
484
|
+
* />
|
|
485
|
+
* ))}
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
get pendingClientTools(): Record<string, InteractiveTool[]>;
|
|
489
|
+
/**
|
|
490
|
+
* Whether `retry()` can be called.
|
|
491
|
+
* True when a trigger has been sent and the chat is not currently streaming or awaiting input.
|
|
492
|
+
*/
|
|
493
|
+
get canRetry(): boolean;
|
|
494
|
+
/**
|
|
495
|
+
* Replace the message list with externally-provided messages.
|
|
496
|
+
*
|
|
497
|
+
* Use this to sync with server-authoritative state (e.g., after an execution
|
|
498
|
+
* completes or when an observer detects new messages from another client).
|
|
499
|
+
*
|
|
500
|
+
* Must NOT be called while streaming — only when status is `idle` or `error`.
|
|
501
|
+
*/
|
|
502
|
+
replaceMessages(messages: UIMessage[]): void;
|
|
503
|
+
/**
|
|
504
|
+
* Subscribe to state changes. The callback is called whenever messages, status, or error changes.
|
|
505
|
+
* @returns Unsubscribe function
|
|
506
|
+
*/
|
|
507
|
+
subscribe(listener: Listener): () => void;
|
|
508
|
+
private notifyListeners;
|
|
509
|
+
private setMessages;
|
|
510
|
+
private setStatus;
|
|
511
|
+
private setError;
|
|
512
|
+
private updatePendingClientToolsCache;
|
|
513
|
+
/**
|
|
514
|
+
* Trigger the agent and optionally add a user message to the chat.
|
|
515
|
+
*
|
|
516
|
+
* @param triggerName - The trigger name defined in the agent's protocol.yaml
|
|
517
|
+
* @param input - Input parameters for the trigger (variable substitutions)
|
|
518
|
+
* @param options.userMessage - If provided, adds a user message to the chat before triggering
|
|
519
|
+
*
|
|
520
|
+
* @example Send a text message
|
|
521
|
+
* ```typescript
|
|
522
|
+
* await chat.send('user-message',
|
|
523
|
+
* { USER_MESSAGE: message },
|
|
524
|
+
* { userMessage: { content: message } }
|
|
525
|
+
* );
|
|
526
|
+
* ```
|
|
527
|
+
*
|
|
528
|
+
* @example Send a message with file attachments
|
|
529
|
+
* ```typescript
|
|
530
|
+
* await chat.send('user-message',
|
|
531
|
+
* { USER_MESSAGE: message, FILES: fileRefs },
|
|
532
|
+
* { userMessage: { content: message, files: fileRefs } }
|
|
533
|
+
* );
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
536
|
+
send(triggerName: string, input?: Record<string, unknown>, sendOptions?: {
|
|
537
|
+
userMessage?: UserMessageInput;
|
|
538
|
+
}): Promise<void>;
|
|
539
|
+
/**
|
|
540
|
+
* Retry the last trigger from the same starting point.
|
|
541
|
+
* Rolls back messages to the state before the last trigger, re-adds the user message
|
|
542
|
+
* (if any), and re-executes. Files are not re-uploaded.
|
|
543
|
+
*
|
|
544
|
+
* No-op if no trigger has been sent yet.
|
|
545
|
+
*
|
|
546
|
+
* @example
|
|
547
|
+
* ```typescript
|
|
548
|
+
* // After an error or unsatisfactory result
|
|
549
|
+
* if (chat.canRetry) {
|
|
550
|
+
* await chat.retry();
|
|
551
|
+
* }
|
|
552
|
+
* ```
|
|
553
|
+
*/
|
|
554
|
+
retry(): Promise<void>;
|
|
555
|
+
/**
|
|
556
|
+
* Observe an already-active execution without triggering a new one.
|
|
557
|
+
*
|
|
558
|
+
* Only supported by transports that implement `observe()` (e.g., polling transport).
|
|
559
|
+
* Use this when the page loads and the session is already streaming — the transport
|
|
560
|
+
* will start consuming events without dispatching a new trigger.
|
|
561
|
+
*
|
|
562
|
+
* When using with `initialMessages`, exclude any in-progress assistant message
|
|
563
|
+
* from the initial messages to avoid duplication — the event stream will rebuild it.
|
|
564
|
+
*/
|
|
565
|
+
observe(): Promise<void>;
|
|
566
|
+
private _executeTrigger;
|
|
567
|
+
/**
|
|
568
|
+
* Shared streaming logic for `send()`, `retry()`, and `observe()`.
|
|
569
|
+
* Sets up streaming state, consumes an event stream, and handles errors.
|
|
570
|
+
*/
|
|
571
|
+
private _consumeStream;
|
|
572
|
+
/**
|
|
573
|
+
* Upload files directly without sending a message.
|
|
574
|
+
* Useful for showing upload progress before sending.
|
|
575
|
+
*
|
|
576
|
+
* @param files - Files to upload
|
|
577
|
+
* @param onProgress - Optional progress callback
|
|
578
|
+
* @returns Array of file references
|
|
579
|
+
*
|
|
580
|
+
* @example
|
|
581
|
+
* ```typescript
|
|
582
|
+
* const fileRefs = await chat.uploadFiles(fileInput.files, (i, progress) => {
|
|
583
|
+
* console.log(`File ${i}: ${progress}%`);
|
|
584
|
+
* });
|
|
585
|
+
* // Later...
|
|
586
|
+
* await chat.send('user-message', { FILES: fileRefs }, { userMessage: { files: fileRefs } });
|
|
587
|
+
* ```
|
|
588
|
+
*/
|
|
589
|
+
uploadFiles(files: FileList | File[], onProgress?: (fileIndex: number, progress: number) => void): Promise<FileReference[]>;
|
|
590
|
+
/**
|
|
591
|
+
* Internal: Submit a result for a pending tool.
|
|
592
|
+
* Called by bound submit/cancel methods on InteractiveTool.
|
|
593
|
+
*/
|
|
594
|
+
private submitToolResult;
|
|
595
|
+
/** Stop the current streaming and finalize any partial message */
|
|
596
|
+
stop(): void;
|
|
597
|
+
/**
|
|
598
|
+
* IMMUTABILITY RULES — all event handlers must follow these patterns:
|
|
599
|
+
*
|
|
600
|
+
* 1. Never mutate an existing part/message object. Some environments (e.g.
|
|
601
|
+
* React Native with Reanimated) freeze objects between renders, so
|
|
602
|
+
* mutations silently fail or throw.
|
|
603
|
+
*
|
|
604
|
+
* 2. Always create a new object via spread and assign it back:
|
|
605
|
+
* GOOD: state.parts[i] = { ...part, text: part.text + delta };
|
|
606
|
+
* BAD: part.text += delta; state.parts[i] = { ...part };
|
|
607
|
+
*
|
|
608
|
+
* 3. For nested worker parts, copy the parts array too:
|
|
609
|
+
* const updatedParts = [...workerPart.parts];
|
|
610
|
+
* updatedParts[i] = { ...part, status: 'done' };
|
|
611
|
+
* state.parts[wi] = { ...workerPart, parts: updatedParts };
|
|
612
|
+
*
|
|
613
|
+
* 4. For the messages array, copy before mutating:
|
|
614
|
+
* const messages = [...this._messages];
|
|
615
|
+
* messages[i] = newMessage; // or messages.pop()
|
|
616
|
+
* this.setMessages(messages);
|
|
617
|
+
*/
|
|
618
|
+
private handleStreamEvent;
|
|
619
|
+
private updateStreamingMessage;
|
|
620
|
+
/**
|
|
621
|
+
* Emit a tool-output-available event for a client tool result.
|
|
622
|
+
*/
|
|
623
|
+
private emitToolOutputAvailable;
|
|
624
|
+
/**
|
|
625
|
+
* Emit a tool-output-error event for a client tool result.
|
|
626
|
+
*/
|
|
627
|
+
private emitToolOutputError;
|
|
628
|
+
/**
|
|
629
|
+
* Continue execution with collected client tool results.
|
|
630
|
+
*/
|
|
631
|
+
private continueWithClientToolResults;
|
|
632
|
+
/**
|
|
633
|
+
* Handle client tool request event.
|
|
634
|
+
*
|
|
635
|
+
* IMPORTANT: Interactive tools must be registered synchronously (before any await)
|
|
636
|
+
* to avoid a race condition where the finish event is processed before tools are added.
|
|
637
|
+
*/
|
|
638
|
+
private handleClientToolRequest;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Parse SSE stream events.
|
|
643
|
+
*
|
|
644
|
+
* @param response - The HTTP response with SSE body
|
|
645
|
+
* @param signal - Optional abort signal to cancel reading
|
|
646
|
+
*/
|
|
647
|
+
declare function parseSSEStream(response: Response, signal?: AbortSignal): AsyncGenerator<StreamEvent, void, unknown>;
|
|
648
|
+
|
|
649
|
+
/** Start a new trigger execution */
|
|
650
|
+
interface TriggerRequest {
|
|
651
|
+
type: 'trigger';
|
|
652
|
+
triggerName: string;
|
|
653
|
+
input?: Record<string, unknown>;
|
|
654
|
+
rollbackAfterMessageId?: string | null;
|
|
655
|
+
}
|
|
656
|
+
/** Continue execution after client-side tool handling */
|
|
657
|
+
interface ContinueRequest {
|
|
658
|
+
type: 'continue';
|
|
659
|
+
executionId: string;
|
|
660
|
+
toolResults: ToolResult[];
|
|
661
|
+
}
|
|
662
|
+
/** All request types supported by the HTTP transport */
|
|
663
|
+
type HttpRequest = TriggerRequest | ContinueRequest;
|
|
664
|
+
/** Request options passed to the request callback */
|
|
665
|
+
interface HttpRequestOptions {
|
|
666
|
+
/** Abort signal to cancel the request */
|
|
667
|
+
signal?: AbortSignal;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Options for creating an HTTP transport.
|
|
671
|
+
*/
|
|
672
|
+
interface HttpTransportOptions {
|
|
673
|
+
/**
|
|
674
|
+
* Function to make requests to your backend.
|
|
675
|
+
* Receives a discriminated union with `type` to identify the request kind.
|
|
676
|
+
*
|
|
677
|
+
* @param request - The request payload (check `request.type` for the kind)
|
|
678
|
+
* @param options - Request options including abort signal
|
|
679
|
+
* @returns Response with SSE stream body
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```typescript
|
|
683
|
+
* request: (payload, options) =>
|
|
684
|
+
* fetch('/api/trigger', {
|
|
685
|
+
* method: 'POST',
|
|
686
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
687
|
+
* body: JSON.stringify({ sessionId, ...payload }),
|
|
688
|
+
* signal: options?.signal,
|
|
689
|
+
* })
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
request: (request: HttpRequest, options?: HttpRequestOptions) => Promise<Response>;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Create an HTTP transport using native fetch() and SSE parsing.
|
|
696
|
+
* This is the default transport for Next.js and other HTTP-based applications.
|
|
697
|
+
*
|
|
698
|
+
* @example
|
|
699
|
+
* ```typescript
|
|
700
|
+
* const transport = createHttpTransport({
|
|
701
|
+
* request: (payload, options) =>
|
|
702
|
+
* fetch('/api/trigger', {
|
|
703
|
+
* method: 'POST',
|
|
704
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
705
|
+
* body: JSON.stringify({ sessionId, ...payload }),
|
|
706
|
+
* signal: options?.signal,
|
|
707
|
+
* }),
|
|
708
|
+
* });
|
|
709
|
+
* ```
|
|
710
|
+
*/
|
|
711
|
+
declare function createHttpTransport(options: HttpTransportOptions): Transport;
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Socket interface compatible with both WebSocket and SockJS.
|
|
715
|
+
*
|
|
716
|
+
* Uses MessageEvent for the message handler, which both WebSocket and SockJS support.
|
|
717
|
+
* The `| undefined` union accommodates SockJS's optional property typing.
|
|
718
|
+
*/
|
|
719
|
+
interface SocketLike {
|
|
720
|
+
send(data: string): void;
|
|
721
|
+
close(): void;
|
|
722
|
+
readyState: number;
|
|
723
|
+
onmessage: ((event: MessageEvent) => void) | null | undefined;
|
|
724
|
+
onclose: ((event: CloseEvent) => void) | null | undefined;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Options for creating a socket transport.
|
|
728
|
+
*/
|
|
729
|
+
interface SocketTransportOptions {
|
|
730
|
+
/**
|
|
731
|
+
* Function to create and connect the socket.
|
|
732
|
+
* Works directly with WebSocket and SockJS - no wrappers needed.
|
|
733
|
+
*
|
|
734
|
+
* @example Native WebSocket
|
|
735
|
+
* ```typescript
|
|
736
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
737
|
+
* const ws = new WebSocket('wss://api.example.com/stream?sessionId=xxx');
|
|
738
|
+
* ws.onopen = () => resolve(ws);
|
|
739
|
+
* ws.onerror = () => reject(new Error('Connection failed'));
|
|
740
|
+
* })
|
|
741
|
+
* ```
|
|
742
|
+
*
|
|
743
|
+
* @example SockJS
|
|
744
|
+
* ```typescript
|
|
745
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
746
|
+
* const sock = new SockJS('/chat-service');
|
|
747
|
+
* sock.onopen = () => resolve(sock);
|
|
748
|
+
* sock.onerror = () => reject(new Error('Connection failed'));
|
|
749
|
+
* })
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
connect: () => Promise<SocketLike>;
|
|
753
|
+
/**
|
|
754
|
+
* Called for every message received (parsed as JSON).
|
|
755
|
+
* Use this to handle custom (non-Octavus) events.
|
|
756
|
+
* Octavus StreamEvents are handled automatically by the transport.
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```typescript
|
|
760
|
+
* onMessage: (data) => {
|
|
761
|
+
* const msg = data as { type: string };
|
|
762
|
+
* if (msg.type === 'typing-indicator') {
|
|
763
|
+
* setIsTyping(true);
|
|
764
|
+
* }
|
|
765
|
+
* if (msg.type === 'presence-update') {
|
|
766
|
+
* updatePresence(msg.users);
|
|
767
|
+
* }
|
|
768
|
+
* }
|
|
769
|
+
* ```
|
|
770
|
+
*/
|
|
771
|
+
onMessage?: (data: unknown) => void;
|
|
772
|
+
/**
|
|
773
|
+
* Called when the socket connection closes.
|
|
774
|
+
* Use this for cleanup or reconnection logic.
|
|
775
|
+
*/
|
|
776
|
+
onClose?: () => void;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Create a socket transport that works with any WebSocket-like connection.
|
|
780
|
+
* Supports native WebSocket, SockJS, or any compatible socket implementation.
|
|
781
|
+
*
|
|
782
|
+
* The server should send StreamEvent format (same as SSE) over the socket.
|
|
783
|
+
* Unknown events are safely ignored using Zod validation.
|
|
784
|
+
*
|
|
785
|
+
* ## Connection Lifecycle
|
|
786
|
+
*
|
|
787
|
+
* By default, the socket connects **lazily** on the first `send()` call.
|
|
788
|
+
* Use `connect()` to establish the connection eagerly (e.g., on component mount):
|
|
789
|
+
*
|
|
790
|
+
* ```typescript
|
|
791
|
+
* // Eager connection for UI status indicators
|
|
792
|
+
* useEffect(() => {
|
|
793
|
+
* transport.connect()
|
|
794
|
+
* .then(() => console.log('Connected'))
|
|
795
|
+
* .catch((err) => console.error('Failed:', err));
|
|
796
|
+
*
|
|
797
|
+
* return () => transport.disconnect();
|
|
798
|
+
* }, [transport]);
|
|
799
|
+
* ```
|
|
800
|
+
*
|
|
801
|
+
* @example Basic usage with WebSocket
|
|
802
|
+
* ```typescript
|
|
803
|
+
* const transport = createSocketTransport({
|
|
804
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
805
|
+
* const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);
|
|
806
|
+
* ws.onopen = () => resolve(ws);
|
|
807
|
+
* ws.onerror = () => reject(new Error('Connection failed'));
|
|
808
|
+
* }),
|
|
809
|
+
* });
|
|
810
|
+
* ```
|
|
811
|
+
*
|
|
812
|
+
* @example With SockJS and connection state
|
|
813
|
+
* ```typescript
|
|
814
|
+
* const transport = createSocketTransport({
|
|
815
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
816
|
+
* const sock = new SockJS('/octavus-stream');
|
|
817
|
+
* sock.onopen = () => resolve(sock);
|
|
818
|
+
* sock.onerror = () => reject(new Error('Connection failed'));
|
|
819
|
+
* }),
|
|
820
|
+
* });
|
|
821
|
+
*
|
|
822
|
+
* // Subscribe to connection state changes
|
|
823
|
+
* transport.onConnectionStateChange((state, error) => {
|
|
824
|
+
* setConnectionState(state);
|
|
825
|
+
* if (error) setConnectionError(error);
|
|
826
|
+
* });
|
|
827
|
+
* ```
|
|
828
|
+
*/
|
|
829
|
+
declare function createSocketTransport(options: SocketTransportOptions): SocketTransport;
|
|
830
|
+
|
|
831
|
+
interface PollResult {
|
|
832
|
+
events: unknown[];
|
|
833
|
+
cursor: number;
|
|
834
|
+
status: 'idle' | 'streaming' | 'error';
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Options for creating a polling transport.
|
|
838
|
+
*/
|
|
839
|
+
interface PollingTransportOptions {
|
|
840
|
+
/**
|
|
841
|
+
* Dispatch a trigger to the backend.
|
|
842
|
+
* Called when `send()` initiates a new execution.
|
|
843
|
+
* Return `{ error }` to surface the error to the chat.
|
|
844
|
+
*/
|
|
845
|
+
onTrigger: (triggerName: string, input?: Record<string, unknown>) => Promise<{
|
|
846
|
+
error?: string;
|
|
847
|
+
}>;
|
|
848
|
+
/**
|
|
849
|
+
* Poll for execution events.
|
|
850
|
+
* Called repeatedly during streaming at `pollIntervalMs` intervals.
|
|
851
|
+
* The cursor tracks read position — pass 0 on first call.
|
|
852
|
+
*/
|
|
853
|
+
onPoll: (cursor: number) => Promise<PollResult>;
|
|
854
|
+
/** Called when the user stops the execution. */
|
|
855
|
+
onStop: () => void;
|
|
856
|
+
/** Milliseconds between poll calls. Defaults to 500. */
|
|
857
|
+
pollIntervalMs?: number;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Create a polling transport for backends that use a poll-based event delivery
|
|
861
|
+
* model instead of SSE or WebSocket streaming.
|
|
862
|
+
*
|
|
863
|
+
* The transport dispatches a trigger via `onTrigger`, then polls `onPoll` at
|
|
864
|
+
* a fixed interval, yielding events as they arrive. From `OctavusChat`'s
|
|
865
|
+
* perspective this looks identical to an SSE stream.
|
|
866
|
+
*
|
|
867
|
+
* Supports `observe()` for resuming observation of an already-active execution
|
|
868
|
+
* (e.g., after a page refresh while the agent is streaming). `observe()` skips
|
|
869
|
+
* the trigger and goes straight to polling.
|
|
870
|
+
*
|
|
871
|
+
* @example
|
|
872
|
+
* ```typescript
|
|
873
|
+
* const transport = createPollingTransport({
|
|
874
|
+
* onTrigger: (triggerName, input) => triggerServerAction(input?.USER_MESSAGE),
|
|
875
|
+
* onPoll: (cursor) => pollServerAction(cursor),
|
|
876
|
+
* onStop: () => stopServerAction(),
|
|
877
|
+
* });
|
|
878
|
+
*
|
|
879
|
+
* const { send, messages } = useOctavusChat({ transport });
|
|
880
|
+
* ```
|
|
881
|
+
*/
|
|
882
|
+
declare function createPollingTransport(options: PollingTransportOptions): Transport;
|
|
883
|
+
|
|
884
|
+
export { type ChatStatus, type ClientToolContext, type ClientToolHandler, type ConnectionState, type ConnectionStateListener, type ContinueRequest, type HttpRequest, type HttpRequestOptions, type HttpTransportOptions, type InteractiveTool, OctavusChat, type OctavusChatOptions, type PollResult, type PollingTransportOptions, type SocketLike, type SocketTransport, type SocketTransportOptions, type Transport, type TriggerOptions, type TriggerRequest, type UploadFilesOptions, type UploadUrlsResponse, type UserMessageInput, createHttpTransport, createPollingTransport, createSocketTransport, isSocketTransport, parseSSEStream, uploadFiles };
|