@octavus/client-sdk 1.0.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 CHANGED
@@ -21,11 +21,11 @@ import { OctavusChat, createHttpTransport } from '@octavus/client-sdk';
21
21
 
22
22
  // Create transport
23
23
  const transport = createHttpTransport({
24
- triggerRequest: (triggerName, input, options) =>
25
- fetch('/api/octavus', {
24
+ request: (payload, options) =>
25
+ fetch('/api/trigger', {
26
26
  method: 'POST',
27
27
  headers: { 'Content-Type': 'application/json' },
28
- body: JSON.stringify({ sessionId, triggerName, input }),
28
+ body: JSON.stringify({ sessionId, ...payload }),
29
29
  signal: options?.signal,
30
30
  }),
31
31
  });
@@ -56,11 +56,11 @@ Best for Next.js, Express, and HTTP-based applications:
56
56
  import { createHttpTransport } from '@octavus/client-sdk';
57
57
 
58
58
  const transport = createHttpTransport({
59
- triggerRequest: (triggerName, input, options) =>
60
- fetch('/api/octavus', {
59
+ request: (payload, options) =>
60
+ fetch('/api/trigger', {
61
61
  method: 'POST',
62
62
  headers: { 'Content-Type': 'application/json' },
63
- body: JSON.stringify({ sessionId, triggerName, input }),
63
+ body: JSON.stringify({ sessionId, ...payload }),
64
64
  signal: options?.signal,
65
65
  }),
66
66
  });
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { StreamEvent, FileReference, UIMessage, OctavusError } from '@octavus/core';
1
+ import { StreamEvent, ToolResult, FileReference, UIMessage, OctavusError } from '@octavus/core';
2
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';
3
4
 
4
5
  /**
5
6
  * Transport interface for delivering events from server to client.
@@ -15,6 +16,13 @@ interface Transport {
15
16
  * @param input - Input parameters for variable substitution
16
17
  */
17
18
  trigger(triggerName: string, input?: Record<string, unknown>): AsyncIterable<StreamEvent>;
19
+ /**
20
+ * Continue execution with tool results after client-side tool handling.
21
+ *
22
+ * @param executionId - The execution ID from the client-tool-request event
23
+ * @param results - All tool results (server + client) to send
24
+ */
25
+ continueWithToolResults(executionId: string, results: ToolResult[]): AsyncIterable<StreamEvent>;
18
26
  /** Stop the current stream. Safe to call when no stream is active. */
19
27
  stop(): void;
20
28
  }
@@ -190,7 +198,51 @@ interface UploadFilesOptions {
190
198
  */
191
199
  declare function uploadFiles(files: FileList | File[], options: UploadFilesOptions): Promise<FileReference[]>;
192
200
 
193
- type ChatStatus = 'idle' | 'streaming' | 'error';
201
+ type ChatStatus = 'idle' | 'streaming' | 'error' | 'awaiting-input';
202
+ /**
203
+ * Context provided to client tool handlers.
204
+ */
205
+ interface ClientToolContext {
206
+ /** Unique identifier for this tool call */
207
+ toolCallId: string;
208
+ /** Name of the tool being called */
209
+ toolName: string;
210
+ /** Signal for cancellation if user stops generation */
211
+ signal: AbortSignal;
212
+ }
213
+ /**
214
+ * Handler function for client-side tool execution.
215
+ * Can be:
216
+ * - An async function that executes automatically and returns a result
217
+ * - The string 'interactive' to indicate the tool requires user interaction
218
+ */
219
+ type ClientToolHandler = ((args: Record<string, unknown>, ctx: ClientToolContext) => Promise<unknown>) | 'interactive';
220
+ /**
221
+ * Interactive tool call awaiting user interaction.
222
+ * The `submit` and `cancel` methods are pre-bound to this tool call's ID.
223
+ */
224
+ interface InteractiveTool {
225
+ /** Unique identifier for this tool call */
226
+ toolCallId: string;
227
+ /** Name of the tool being called */
228
+ toolName: string;
229
+ /** Arguments passed to the tool */
230
+ args: Record<string, unknown>;
231
+ /**
232
+ * Submit a result for this tool call.
233
+ * Call this when the user has provided input.
234
+ *
235
+ * @param result - The result from user interaction
236
+ */
237
+ submit: (result: unknown) => void;
238
+ /**
239
+ * Cancel this tool call with an optional reason.
240
+ * Call this when the user dismisses the UI without providing input.
241
+ *
242
+ * @param reason - Optional reason for cancellation (default: 'User cancelled')
243
+ */
244
+ cancel: (reason?: string) => void;
245
+ }
194
246
  /**
195
247
  * Input for creating a user message.
196
248
  * Supports text content, structured object content, and file attachments.
@@ -233,6 +285,35 @@ interface OctavusChatOptions {
233
285
  * ```
234
286
  */
235
287
  requestUploadUrls?: UploadFilesOptions['requestUploadUrls'];
288
+ /**
289
+ * Client-side tool handlers.
290
+ * Register handlers for tools that should execute in the browser.
291
+ *
292
+ * - If a tool has a handler function: executes automatically
293
+ * - If a tool is marked as 'interactive': appears in `pendingClientTools` with bound `submit()`/`cancel()`
294
+ *
295
+ * @example Automatic client tool
296
+ * ```typescript
297
+ * clientTools: {
298
+ * 'get-browser-location': async () => {
299
+ * const pos = await new Promise((resolve, reject) => {
300
+ * navigator.geolocation.getCurrentPosition(resolve, reject);
301
+ * });
302
+ * return { lat: pos.coords.latitude, lng: pos.coords.longitude };
303
+ * },
304
+ * }
305
+ * ```
306
+ *
307
+ * @example Interactive client tool (user input required)
308
+ * ```typescript
309
+ * clientTools: {
310
+ * 'request-feedback': 'interactive',
311
+ * }
312
+ * // Then render UI based on pendingClientTools['request-feedback']
313
+ * // and call tool.submit(result) or tool.cancel()
314
+ * ```
315
+ */
316
+ clientTools?: Record<string, ClientToolHandler>;
236
317
  /** Initial messages (for session refresh) */
237
318
  initialMessages?: UIMessage[];
238
319
  /**
@@ -275,10 +356,12 @@ type Listener = () => void;
275
356
  *
276
357
  * const chat = new OctavusChat({
277
358
  * transport: createHttpTransport({
278
- * triggerRequest: (triggerName, input) =>
359
+ * request: (payload, options) =>
279
360
  * fetch('/api/trigger', {
280
361
  * method: 'POST',
281
- * body: JSON.stringify({ sessionId, triggerName, input }),
362
+ * headers: { 'Content-Type': 'application/json' },
363
+ * body: JSON.stringify({ sessionId, ...payload }),
364
+ * signal: options?.signal,
282
365
  * }),
283
366
  * }),
284
367
  * });
@@ -306,6 +389,13 @@ declare class OctavusChat {
306
389
  private options;
307
390
  private transport;
308
391
  private streamingState;
392
+ private _pendingToolsByName;
393
+ private _pendingToolsByCallId;
394
+ private _pendingClientToolsCache;
395
+ private _completedToolResults;
396
+ private _clientToolAbortController;
397
+ private _serverToolResults;
398
+ private _pendingExecutionId;
309
399
  private listeners;
310
400
  constructor(options: OctavusChatOptions);
311
401
  get messages(): UIMessage[];
@@ -315,6 +405,25 @@ declare class OctavusChat {
315
405
  * Contains structured error information including type, source, and retryability.
316
406
  */
317
407
  get error(): OctavusError | null;
408
+ /**
409
+ * Pending interactive tool calls keyed by tool name.
410
+ * Each tool has bound `submit()` and `cancel()` methods.
411
+ *
412
+ * @example
413
+ * ```tsx
414
+ * const feedbackTools = pendingClientTools['request-feedback'] ?? [];
415
+ *
416
+ * {feedbackTools.map(tool => (
417
+ * <FeedbackModal
418
+ * key={tool.toolCallId}
419
+ * {...tool.args}
420
+ * onSubmit={(result) => tool.submit(result)}
421
+ * onCancel={() => tool.cancel()}
422
+ * />
423
+ * ))}
424
+ * ```
425
+ */
426
+ get pendingClientTools(): Record<string, InteractiveTool[]>;
318
427
  /**
319
428
  * Subscribe to state changes. The callback is called whenever messages, status, or error changes.
320
429
  * @returns Unsubscribe function
@@ -324,6 +433,7 @@ declare class OctavusChat {
324
433
  private setMessages;
325
434
  private setStatus;
326
435
  private setError;
436
+ private updatePendingClientToolsCache;
327
437
  /**
328
438
  * Trigger the agent and optionally add a user message to the chat.
329
439
  *
@@ -368,10 +478,34 @@ declare class OctavusChat {
368
478
  * ```
369
479
  */
370
480
  uploadFiles(files: FileList | File[], onProgress?: (fileIndex: number, progress: number) => void): Promise<FileReference[]>;
481
+ /**
482
+ * Internal: Submit a result for a pending tool.
483
+ * Called by bound submit/cancel methods on InteractiveTool.
484
+ */
485
+ private submitToolResult;
371
486
  /** Stop the current streaming and finalize any partial message */
372
487
  stop(): void;
373
488
  private handleStreamEvent;
374
489
  private updateStreamingMessage;
490
+ /**
491
+ * Emit a tool-output-available event for a client tool result.
492
+ */
493
+ private emitToolOutputAvailable;
494
+ /**
495
+ * Emit a tool-output-error event for a client tool result.
496
+ */
497
+ private emitToolOutputError;
498
+ /**
499
+ * Continue execution with collected client tool results.
500
+ */
501
+ private continueWithClientToolResults;
502
+ /**
503
+ * Handle client tool request event.
504
+ *
505
+ * IMPORTANT: Interactive tools must be registered synchronously (before any await)
506
+ * to avoid a race condition where the finish event is processed before tools are added.
507
+ */
508
+ private handleClientToolRequest;
375
509
  }
376
510
 
377
511
  /**
@@ -382,10 +516,22 @@ declare class OctavusChat {
382
516
  */
383
517
  declare function parseSSEStream(response: Response, signal?: AbortSignal): AsyncGenerator<StreamEvent, void, unknown>;
384
518
 
385
- /**
386
- * Request options passed to the triggerRequest function.
387
- */
388
- interface TriggerRequestOptions {
519
+ /** Start a new trigger execution */
520
+ interface TriggerRequest {
521
+ type: 'trigger';
522
+ triggerName: string;
523
+ input?: Record<string, unknown>;
524
+ }
525
+ /** Continue execution after client-side tool handling */
526
+ interface ContinueRequest {
527
+ type: 'continue';
528
+ executionId: string;
529
+ toolResults: ToolResult[];
530
+ }
531
+ /** All request types supported by the HTTP transport */
532
+ type HttpRequest = TriggerRequest | ContinueRequest;
533
+ /** Request options passed to the request callback */
534
+ interface HttpRequestOptions {
389
535
  /** Abort signal to cancel the request */
390
536
  signal?: AbortSignal;
391
537
  }
@@ -394,26 +540,25 @@ interface TriggerRequestOptions {
394
540
  */
395
541
  interface HttpTransportOptions {
396
542
  /**
397
- * Function to make the trigger request.
398
- * Called each time `send()` is invoked on the chat.
543
+ * Function to make requests to your backend.
544
+ * Receives a discriminated union with `type` to identify the request kind.
399
545
  *
400
- * @param triggerName - The trigger name (e.g., 'user-message')
401
- * @param input - Input parameters for the trigger
402
- * @param options - Optional request options including abort signal
546
+ * @param request - The request payload (check `request.type` for the kind)
547
+ * @param options - Request options including abort signal
403
548
  * @returns Response with SSE stream body
404
549
  *
405
550
  * @example
406
551
  * ```typescript
407
- * triggerRequest: (triggerName, input, options) =>
408
- * fetch('/api/octavus', {
552
+ * request: (payload, options) =>
553
+ * fetch('/api/trigger', {
409
554
  * method: 'POST',
410
555
  * headers: { 'Content-Type': 'application/json' },
411
- * body: JSON.stringify({ sessionId, triggerName, input }),
556
+ * body: JSON.stringify({ sessionId, ...payload }),
412
557
  * signal: options?.signal,
413
- * }),
558
+ * })
414
559
  * ```
415
560
  */
416
- triggerRequest: (triggerName: string, input?: Record<string, unknown>, options?: TriggerRequestOptions) => Promise<Response>;
561
+ request: (request: HttpRequest, options?: HttpRequestOptions) => Promise<Response>;
417
562
  }
418
563
  /**
419
564
  * Create an HTTP transport using native fetch() and SSE parsing.
@@ -422,11 +567,11 @@ interface HttpTransportOptions {
422
567
  * @example
423
568
  * ```typescript
424
569
  * const transport = createHttpTransport({
425
- * triggerRequest: (triggerName, input, options) =>
426
- * fetch('/api/octavus', {
570
+ * request: (payload, options) =>
571
+ * fetch('/api/trigger', {
427
572
  * method: 'POST',
428
573
  * headers: { 'Content-Type': 'application/json' },
429
- * body: JSON.stringify({ sessionId, triggerName, input }),
574
+ * body: JSON.stringify({ sessionId, ...payload }),
430
575
  * signal: options?.signal,
431
576
  * }),
432
577
  * });
@@ -552,4 +697,4 @@ interface SocketTransportOptions {
552
697
  */
553
698
  declare function createSocketTransport(options: SocketTransportOptions): SocketTransport;
554
699
 
555
- export { type ChatStatus, type ConnectionState, type ConnectionStateListener, type HttpTransportOptions, OctavusChat, type OctavusChatOptions, type SocketLike, type SocketTransport, type SocketTransportOptions, type Transport, type TriggerRequestOptions, type UploadFilesOptions, type UploadUrlsResponse, type UserMessageInput, createHttpTransport, createSocketTransport, isSocketTransport, parseSSEStream, uploadFiles };
700
+ 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 SocketLike, type SocketTransport, type SocketTransportOptions, type Transport, type TriggerRequest, type UploadFilesOptions, type UploadUrlsResponse, type UserMessageInput, createHttpTransport, createSocketTransport, isSocketTransport, parseSSEStream, uploadFiles };
package/dist/index.js CHANGED
@@ -181,6 +181,19 @@ var OctavusChat = class {
181
181
  options;
182
182
  transport;
183
183
  streamingState = null;
184
+ // Client tool state
185
+ // Keyed by toolName -> array of pending tools for that name
186
+ _pendingToolsByName = /* @__PURE__ */ new Map();
187
+ // Keyed by toolCallId -> pending tool state (for internal lookup when submitting)
188
+ _pendingToolsByCallId = /* @__PURE__ */ new Map();
189
+ // Cache for React useSyncExternalStore compatibility
190
+ _pendingClientToolsCache = {};
191
+ _completedToolResults = [];
192
+ _clientToolAbortController = null;
193
+ // Server tool results from mixed server+client tools (for continuation)
194
+ _serverToolResults = [];
195
+ // Execution ID for continuation (from client-tool-request event)
196
+ _pendingExecutionId = null;
184
197
  // Listener sets for reactive frameworks
185
198
  listeners = /* @__PURE__ */ new Set();
186
199
  constructor(options) {
@@ -204,6 +217,27 @@ var OctavusChat = class {
204
217
  get error() {
205
218
  return this._error;
206
219
  }
220
+ /**
221
+ * Pending interactive tool calls keyed by tool name.
222
+ * Each tool has bound `submit()` and `cancel()` methods.
223
+ *
224
+ * @example
225
+ * ```tsx
226
+ * const feedbackTools = pendingClientTools['request-feedback'] ?? [];
227
+ *
228
+ * {feedbackTools.map(tool => (
229
+ * <FeedbackModal
230
+ * key={tool.toolCallId}
231
+ * {...tool.args}
232
+ * onSubmit={(result) => tool.submit(result)}
233
+ * onCancel={() => tool.cancel()}
234
+ * />
235
+ * ))}
236
+ * ```
237
+ */
238
+ get pendingClientTools() {
239
+ return this._pendingClientToolsCache;
240
+ }
207
241
  // =========================================================================
208
242
  // Subscription Methods (for reactive frameworks)
209
243
  // =========================================================================
@@ -233,6 +267,19 @@ var OctavusChat = class {
233
267
  this._error = error;
234
268
  this.notifyListeners();
235
269
  }
270
+ updatePendingClientToolsCache() {
271
+ const cache = {};
272
+ for (const [toolName, tools] of this._pendingToolsByName.entries()) {
273
+ cache[toolName] = tools.map((tool) => ({
274
+ toolCallId: tool.toolCallId,
275
+ toolName: tool.toolName,
276
+ args: tool.args,
277
+ submit: (result) => this.submitToolResult(tool.toolCallId, result),
278
+ cancel: (reason) => this.submitToolResult(tool.toolCallId, void 0, reason ?? "User cancelled")
279
+ }));
280
+ }
281
+ this._pendingClientToolsCache = cache;
282
+ }
236
283
  // =========================================================================
237
284
  // Public Methods
238
285
  // =========================================================================
@@ -294,6 +341,12 @@ var OctavusChat = class {
294
341
  this.setStatus("streaming");
295
342
  this.setError(null);
296
343
  this.streamingState = createEmptyStreamingState();
344
+ this._pendingToolsByName.clear();
345
+ this._pendingToolsByCallId.clear();
346
+ this._completedToolResults = [];
347
+ this._serverToolResults = [];
348
+ this._pendingExecutionId = null;
349
+ this.updatePendingClientToolsCache();
297
350
  try {
298
351
  for await (const event of this.transport.trigger(triggerName, processedInput)) {
299
352
  if (this.streamingState === null) break;
@@ -307,6 +360,48 @@ var OctavusChat = class {
307
360
  retryable: false,
308
361
  cause: err
309
362
  });
363
+ const state = this.streamingState;
364
+ if (state !== null) {
365
+ const messages = [...this._messages];
366
+ const lastMsg = messages[messages.length - 1];
367
+ if (state.parts.length > 0) {
368
+ const finalParts = state.parts.map((part) => {
369
+ if (part.type === "text" || part.type === "reasoning") {
370
+ if (part.status === "streaming") {
371
+ return { ...part, status: "done" };
372
+ }
373
+ }
374
+ if (part.type === "object" && part.status === "streaming") {
375
+ return { ...part, status: "done" };
376
+ }
377
+ if (part.type === "tool-call") {
378
+ if (part.status === "pending" || part.status === "running") {
379
+ return { ...part, status: "cancelled" };
380
+ }
381
+ }
382
+ if (part.type === "operation" && part.status === "running") {
383
+ return { ...part, status: "cancelled" };
384
+ }
385
+ return part;
386
+ });
387
+ const finalMessage = {
388
+ id: state.messageId,
389
+ role: "assistant",
390
+ parts: finalParts,
391
+ status: "done",
392
+ createdAt: /* @__PURE__ */ new Date()
393
+ };
394
+ if (lastMsg?.id === state.messageId) {
395
+ messages[messages.length - 1] = finalMessage;
396
+ } else {
397
+ messages.push(finalMessage);
398
+ }
399
+ this.setMessages(messages);
400
+ } else if (lastMsg?.id === state.messageId) {
401
+ messages.pop();
402
+ this.setMessages(messages);
403
+ }
404
+ }
310
405
  this.setError(errorObj);
311
406
  this.setStatus("error");
312
407
  this.streamingState = null;
@@ -339,11 +434,58 @@ var OctavusChat = class {
339
434
  onProgress
340
435
  });
341
436
  }
437
+ /**
438
+ * Internal: Submit a result for a pending tool.
439
+ * Called by bound submit/cancel methods on InteractiveTool.
440
+ */
441
+ submitToolResult(toolCallId, result, error) {
442
+ const pendingTool = this._pendingToolsByCallId.get(toolCallId);
443
+ if (!pendingTool) {
444
+ return;
445
+ }
446
+ this._pendingToolsByCallId.delete(toolCallId);
447
+ const toolsForName = this._pendingToolsByName.get(pendingTool.toolName);
448
+ if (toolsForName) {
449
+ const filtered = toolsForName.filter((t) => t.toolCallId !== toolCallId);
450
+ if (filtered.length === 0) {
451
+ this._pendingToolsByName.delete(pendingTool.toolName);
452
+ } else {
453
+ this._pendingToolsByName.set(pendingTool.toolName, filtered);
454
+ }
455
+ }
456
+ this.updatePendingClientToolsCache();
457
+ const toolResult = {
458
+ toolCallId,
459
+ toolName: pendingTool.toolName,
460
+ result: error ? void 0 : result,
461
+ error,
462
+ outputVariable: pendingTool.outputVariable,
463
+ blockIndex: pendingTool.blockIndex
464
+ };
465
+ this._completedToolResults.push(toolResult);
466
+ if (error) {
467
+ this.emitToolOutputError(toolCallId, error);
468
+ } else {
469
+ this.emitToolOutputAvailable(toolCallId, result);
470
+ }
471
+ if (this._pendingToolsByCallId.size === 0) {
472
+ void this.continueWithClientToolResults();
473
+ }
474
+ this.notifyListeners();
475
+ }
342
476
  /** Stop the current streaming and finalize any partial message */
343
477
  stop() {
344
- if (this._status !== "streaming") {
478
+ if (this._status !== "streaming" && this._status !== "awaiting-input") {
345
479
  return;
346
480
  }
481
+ this._clientToolAbortController?.abort();
482
+ this._clientToolAbortController = null;
483
+ this._pendingToolsByName.clear();
484
+ this._pendingToolsByCallId.clear();
485
+ this._completedToolResults = [];
486
+ this._serverToolResults = [];
487
+ this._pendingExecutionId = null;
488
+ this.updatePendingClientToolsCache();
347
489
  this.transport.stop();
348
490
  const state = this.streamingState;
349
491
  if (state && state.parts.length > 0) {
@@ -679,6 +821,12 @@ var OctavusChat = class {
679
821
  this.options.onResourceUpdate?.(event.name, event.value);
680
822
  break;
681
823
  case "finish": {
824
+ if (event.finishReason === "client-tool-calls") {
825
+ if (this._pendingToolsByCallId.size > 0) {
826
+ this.setStatus("awaiting-input");
827
+ }
828
+ return;
829
+ }
682
830
  const finalMessage = buildMessageFromState(state, "done");
683
831
  finalMessage.parts = finalMessage.parts.map((part) => {
684
832
  if (part.type === "text" || part.type === "reasoning") {
@@ -718,6 +866,11 @@ var OctavusChat = class {
718
866
  }
719
867
  case "tool-request":
720
868
  break;
869
+ case "client-tool-request":
870
+ this._pendingExecutionId = event.executionId;
871
+ this._serverToolResults = event.serverToolResults ?? [];
872
+ void this.handleClientToolRequest(event.toolCalls, state);
873
+ break;
721
874
  }
722
875
  }
723
876
  updateStreamingMessage() {
@@ -733,6 +886,161 @@ var OctavusChat = class {
733
886
  }
734
887
  this.setMessages(messages);
735
888
  }
889
+ /**
890
+ * Emit a tool-output-available event for a client tool result.
891
+ */
892
+ emitToolOutputAvailable(toolCallId, output) {
893
+ const state = this.streamingState;
894
+ if (!state) return;
895
+ const toolPartIndex = state.parts.findIndex(
896
+ (p) => p.type === "tool-call" && p.toolCallId === toolCallId
897
+ );
898
+ if (toolPartIndex >= 0) {
899
+ const part = state.parts[toolPartIndex];
900
+ part.result = output;
901
+ part.status = "done";
902
+ state.parts[toolPartIndex] = { ...part };
903
+ this.updateStreamingMessage();
904
+ }
905
+ }
906
+ /**
907
+ * Emit a tool-output-error event for a client tool result.
908
+ */
909
+ emitToolOutputError(toolCallId, error) {
910
+ const state = this.streamingState;
911
+ if (!state) return;
912
+ const toolPartIndex = state.parts.findIndex(
913
+ (p) => p.type === "tool-call" && p.toolCallId === toolCallId
914
+ );
915
+ if (toolPartIndex >= 0) {
916
+ const part = state.parts[toolPartIndex];
917
+ part.error = error;
918
+ part.status = "error";
919
+ state.parts[toolPartIndex] = { ...part };
920
+ this.updateStreamingMessage();
921
+ }
922
+ }
923
+ /**
924
+ * Continue execution with collected client tool results.
925
+ */
926
+ async continueWithClientToolResults() {
927
+ if (this._completedToolResults.length === 0) return;
928
+ if (this._pendingExecutionId === null) {
929
+ const errorObj = new OctavusError({
930
+ errorType: "internal_error",
931
+ message: "Cannot continue execution: execution ID was lost.",
932
+ source: "client",
933
+ retryable: false
934
+ });
935
+ this.setError(errorObj);
936
+ this.setStatus("error");
937
+ this.options.onError?.(errorObj);
938
+ return;
939
+ }
940
+ const allResults = [...this._serverToolResults, ...this._completedToolResults];
941
+ const executionId = this._pendingExecutionId;
942
+ this._serverToolResults = [];
943
+ this._completedToolResults = [];
944
+ this._pendingExecutionId = null;
945
+ this.setStatus("streaming");
946
+ try {
947
+ for await (const event of this.transport.continueWithToolResults(executionId, allResults)) {
948
+ if (this.streamingState === null) break;
949
+ this.handleStreamEvent(event, this.streamingState);
950
+ }
951
+ } catch (err) {
952
+ const errorObj = OctavusError.isInstance(err) ? err : new OctavusError({
953
+ errorType: "internal_error",
954
+ message: err instanceof Error ? err.message : "Unknown error",
955
+ source: "client",
956
+ retryable: false,
957
+ cause: err
958
+ });
959
+ this.setError(errorObj);
960
+ this.setStatus("error");
961
+ this.streamingState = null;
962
+ this.options.onError?.(errorObj);
963
+ }
964
+ }
965
+ /**
966
+ * Handle client tool request event.
967
+ *
968
+ * IMPORTANT: Interactive tools must be registered synchronously (before any await)
969
+ * to avoid a race condition where the finish event is processed before tools are added.
970
+ */
971
+ async handleClientToolRequest(toolCalls, state) {
972
+ this._clientToolAbortController = new AbortController();
973
+ for (const tc of toolCalls) {
974
+ const handler = this.options.clientTools?.[tc.toolName];
975
+ if (handler === "interactive") {
976
+ const toolState = {
977
+ toolCallId: tc.toolCallId,
978
+ toolName: tc.toolName,
979
+ args: tc.args,
980
+ source: tc.source,
981
+ outputVariable: tc.outputVariable,
982
+ blockIndex: tc.blockIndex
983
+ };
984
+ this._pendingToolsByCallId.set(tc.toolCallId, toolState);
985
+ const existing = this._pendingToolsByName.get(tc.toolName) ?? [];
986
+ this._pendingToolsByName.set(tc.toolName, [...existing, toolState]);
987
+ }
988
+ }
989
+ if (this._pendingToolsByCallId.size > 0) {
990
+ this.updatePendingClientToolsCache();
991
+ }
992
+ for (const tc of toolCalls) {
993
+ const handler = this.options.clientTools?.[tc.toolName];
994
+ if (handler === "interactive") {
995
+ const toolPartIndex = state.parts.findIndex(
996
+ (p) => p.type === "tool-call" && p.toolCallId === tc.toolCallId
997
+ );
998
+ if (toolPartIndex >= 0) {
999
+ const part = state.parts[toolPartIndex];
1000
+ state.parts[toolPartIndex] = { ...part };
1001
+ }
1002
+ } else if (handler) {
1003
+ try {
1004
+ const result = await handler(tc.args, {
1005
+ toolCallId: tc.toolCallId,
1006
+ toolName: tc.toolName,
1007
+ signal: this._clientToolAbortController.signal
1008
+ });
1009
+ this._completedToolResults.push({
1010
+ toolCallId: tc.toolCallId,
1011
+ toolName: tc.toolName,
1012
+ result,
1013
+ outputVariable: tc.outputVariable,
1014
+ blockIndex: tc.blockIndex
1015
+ });
1016
+ this.emitToolOutputAvailable(tc.toolCallId, result);
1017
+ } catch (err) {
1018
+ const errorMessage = err instanceof Error ? err.message : "Tool execution failed";
1019
+ this._completedToolResults.push({
1020
+ toolCallId: tc.toolCallId,
1021
+ toolName: tc.toolName,
1022
+ error: errorMessage,
1023
+ outputVariable: tc.outputVariable,
1024
+ blockIndex: tc.blockIndex
1025
+ });
1026
+ this.emitToolOutputError(tc.toolCallId, errorMessage);
1027
+ }
1028
+ } else {
1029
+ const errorMessage = `No client handler for tool: ${tc.toolName}`;
1030
+ this._completedToolResults.push({
1031
+ toolCallId: tc.toolCallId,
1032
+ toolName: tc.toolName,
1033
+ error: errorMessage,
1034
+ outputVariable: tc.outputVariable,
1035
+ blockIndex: tc.blockIndex
1036
+ });
1037
+ this.emitToolOutputError(tc.toolCallId, errorMessage);
1038
+ }
1039
+ }
1040
+ if (this._pendingToolsByCallId.size === 0 && this._completedToolResults.length > 0) {
1041
+ await this.continueWithClientToolResults();
1042
+ }
1043
+ }
736
1044
  };
737
1045
 
738
1046
  // src/stream/reader.ts
@@ -793,32 +1101,45 @@ function isSocketTransport(transport) {
793
1101
  import { isAbortError as isAbortError2 } from "@octavus/core";
794
1102
  function createHttpTransport(options) {
795
1103
  let abortController = null;
1104
+ async function* streamResponse(responsePromise) {
1105
+ try {
1106
+ const response = await responsePromise;
1107
+ if (!response.ok) {
1108
+ const errorText = await response.text().catch(() => `Request failed: ${response.status}`);
1109
+ throw new Error(errorText);
1110
+ }
1111
+ if (!response.body) {
1112
+ throw new Error("Response body is empty");
1113
+ }
1114
+ for await (const event of parseSSEStream(response, abortController.signal)) {
1115
+ if (abortController?.signal.aborted) {
1116
+ break;
1117
+ }
1118
+ yield event;
1119
+ }
1120
+ } catch (err) {
1121
+ if (isAbortError2(err)) {
1122
+ return;
1123
+ }
1124
+ throw err;
1125
+ }
1126
+ }
796
1127
  return {
797
1128
  async *trigger(triggerName, input) {
798
1129
  abortController = new AbortController();
799
- try {
800
- const response = await options.triggerRequest(triggerName, input, {
801
- signal: abortController.signal
802
- });
803
- if (!response.ok) {
804
- const errorText = await response.text().catch(() => `Request failed: ${response.status}`);
805
- throw new Error(errorText);
806
- }
807
- if (!response.body) {
808
- throw new Error("Response body is empty");
809
- }
810
- for await (const event of parseSSEStream(response, abortController.signal)) {
811
- if (abortController.signal.aborted) {
812
- break;
813
- }
814
- yield event;
815
- }
816
- } catch (err) {
817
- if (isAbortError2(err)) {
818
- return;
819
- }
820
- throw err;
821
- }
1130
+ const response = options.request(
1131
+ { type: "trigger", triggerName, input },
1132
+ { signal: abortController.signal }
1133
+ );
1134
+ yield* streamResponse(response);
1135
+ },
1136
+ async *continueWithToolResults(executionId, toolResults) {
1137
+ abortController = new AbortController();
1138
+ const response = options.request(
1139
+ { type: "continue", executionId, toolResults },
1140
+ { signal: abortController.signal }
1141
+ );
1142
+ yield* streamResponse(response);
822
1143
  },
823
1144
  stop() {
824
1145
  abortController?.abort();
@@ -974,18 +1295,104 @@ function createSocketTransport(options) {
974
1295
  eventResolver(null);
975
1296
  eventResolver = null;
976
1297
  }
1298
+ },
1299
+ /**
1300
+ * Continue execution with tool results after client-side tool handling.
1301
+ * @param executionId - The execution ID from the client-tool-request event
1302
+ * @param toolResults - All tool results (server + client) to send
1303
+ */
1304
+ async *continueWithToolResults(executionId, toolResults) {
1305
+ await ensureConnected();
1306
+ eventQueue = [];
1307
+ isStreaming = true;
1308
+ socket.send(
1309
+ JSON.stringify({
1310
+ type: "continue",
1311
+ executionId,
1312
+ toolResults
1313
+ })
1314
+ );
1315
+ while (true) {
1316
+ const event = await nextEvent();
1317
+ if (event === null) break;
1318
+ yield event;
1319
+ if (event.type === "finish" || event.type === "error") break;
1320
+ }
977
1321
  }
978
1322
  };
979
1323
  }
980
1324
 
981
1325
  // src/index.ts
982
- export * from "@octavus/core";
1326
+ import {
1327
+ AppError,
1328
+ NotFoundError,
1329
+ ValidationError,
1330
+ ConflictError,
1331
+ ForbiddenError,
1332
+ OctavusError as OctavusError2,
1333
+ isRateLimitError,
1334
+ isAuthenticationError,
1335
+ isProviderError,
1336
+ isToolError,
1337
+ isRetryableError,
1338
+ isValidationError,
1339
+ createErrorEvent,
1340
+ errorToStreamEvent,
1341
+ createInternalErrorEvent,
1342
+ createApiErrorEvent,
1343
+ generateId as generateId2,
1344
+ isAbortError as isAbortError3,
1345
+ MAIN_THREAD,
1346
+ resolveThread,
1347
+ isMainThread,
1348
+ threadForPart as threadForPart2,
1349
+ isOtherThread,
1350
+ isFileReference,
1351
+ isFileReferenceArray as isFileReferenceArray2,
1352
+ safeParseStreamEvent as safeParseStreamEvent3,
1353
+ safeParseUIMessage,
1354
+ safeParseUIMessages,
1355
+ OCTAVUS_SKILL_TOOLS,
1356
+ isOctavusSkillTool,
1357
+ getSkillSlugFromToolCall
1358
+ } from "@octavus/core";
983
1359
  export {
1360
+ AppError,
1361
+ ConflictError,
1362
+ ForbiddenError,
1363
+ MAIN_THREAD,
1364
+ NotFoundError,
1365
+ OCTAVUS_SKILL_TOOLS,
984
1366
  OctavusChat,
1367
+ OctavusError2 as OctavusError,
1368
+ ValidationError,
1369
+ createApiErrorEvent,
1370
+ createErrorEvent,
985
1371
  createHttpTransport,
1372
+ createInternalErrorEvent,
986
1373
  createSocketTransport,
1374
+ errorToStreamEvent,
1375
+ generateId2 as generateId,
1376
+ getSkillSlugFromToolCall,
1377
+ isAbortError3 as isAbortError,
1378
+ isAuthenticationError,
1379
+ isFileReference,
1380
+ isFileReferenceArray2 as isFileReferenceArray,
1381
+ isMainThread,
1382
+ isOctavusSkillTool,
1383
+ isOtherThread,
1384
+ isProviderError,
1385
+ isRateLimitError,
1386
+ isRetryableError,
987
1387
  isSocketTransport,
1388
+ isToolError,
1389
+ isValidationError,
988
1390
  parseSSEStream,
1391
+ resolveThread,
1392
+ safeParseStreamEvent3 as safeParseStreamEvent,
1393
+ safeParseUIMessage,
1394
+ safeParseUIMessages,
1395
+ threadForPart2 as threadForPart,
989
1396
  uploadFiles
990
1397
  };
991
1398
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/chat.ts","../src/files.ts","../src/stream/reader.ts","../src/transports/types.ts","../src/transports/http.ts","../src/transports/socket.ts","../src/index.ts"],"sourcesContent":["import {\n generateId,\n threadForPart,\n isFileReferenceArray,\n OctavusError,\n type UIMessage,\n type UIMessagePart,\n type UITextPart,\n type UIReasoningPart,\n type UIToolCallPart,\n type UIOperationPart,\n type UISourcePart,\n type UIFilePart,\n type UIObjectPart,\n type DisplayMode,\n type StreamEvent,\n type FileReference,\n} from '@octavus/core';\nimport type { Transport } from './transports/types';\nimport { uploadFiles, type UploadFilesOptions } from './files';\n\n/** Block types that are internal operations (not LLM-driven) */\nconst OPERATION_BLOCK_TYPES = new Set(['set-resource', 'serialize-thread', 'generate-image']);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ChatStatus = 'idle' | 'streaming' | 'error';\n\n/**\n * Input for creating a user message.\n * Supports text content, structured object content, and file attachments.\n */\nexport interface UserMessageInput {\n /**\n * Content of the message. Can be:\n * - string: Creates a text part\n * - object: Creates an object part (uses `type` field as typeName if present)\n */\n content?: string | Record<string, unknown>;\n /**\n * File attachments (shorthand). Can be:\n * - FileList: From file input element (will be uploaded via uploadFiles)\n * - File[]: Array of File objects (will be uploaded via uploadFiles)\n * - FileReference[]: Already uploaded files (used directly)\n */\n files?: FileList | File[] | FileReference[];\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 /**\n * Function to request upload URLs from the platform.\n * Required if you want to use file uploads with FileList/File[].\n *\n * @example\n * ```typescript\n * requestUploadUrls: async (files) => {\n * const response = await fetch('/api/upload-urls', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, files }),\n * });\n * return response.json();\n * }\n * ```\n */\n requestUploadUrls?: UploadFilesOptions['requestUploadUrls'];\n\n /** Initial messages (for session refresh) */\n initialMessages?: UIMessage[];\n /**\n * Callback when an error occurs.\n * Receives an OctavusError with structured error information.\n *\n * @example\n * ```typescript\n * onError: (error) => {\n * console.error('Chat error:', {\n * type: error.errorType,\n * message: error.message,\n * retryable: error.retryable,\n * provider: error.provider,\n * });\n *\n * // Handle specific error types\n * if (isRateLimitError(error)) {\n * showRetryButton(error.retryAfter);\n * }\n * }\n * ```\n */\n onError?: (error: OctavusError) => void;\n /** Callback when streaming finishes successfully */\n onFinish?: () => void;\n /** Callback when streaming is stopped by user */\n onStop?: () => 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 currentTextPartIndex: number | null;\n currentReasoningPartIndex: number | null;\n currentObjectPartIndex: number | null;\n accumulatedJson: string;\n}\n\ntype Listener = () => void;\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Create a user message from input with optional file attachments.\n * Parts order: files first (for vision models), then content (text or object).\n */\nfunction createUserMessage(input: UserMessageInput, files?: FileReference[]): UIMessage {\n const parts: UIMessagePart[] = [];\n\n // Add file parts first (vision models expect images before text)\n if (files && files.length > 0) {\n for (const file of files) {\n parts.push({\n type: 'file',\n id: file.id,\n mediaType: file.mediaType,\n url: file.url,\n filename: file.filename,\n size: file.size,\n });\n }\n }\n\n // Add content part after files\n if (input.content !== undefined) {\n if (typeof input.content === 'string') {\n // String content → text part\n parts.push({ type: 'text', text: input.content, status: 'done' });\n } else {\n // Object content → object part\n // Use the object's `type` field as typeName if present, otherwise fallback to 'object'\n const typeName = (input.content as { type?: string }).type ?? 'object';\n parts.push({\n type: 'object',\n id: generateId(),\n typeName,\n object: input.content,\n status: 'done',\n });\n }\n }\n\n return {\n id: generateId(),\n role: 'user',\n parts,\n status: 'done',\n createdAt: new Date(),\n };\n}\n\n/**\n * Parse partial JSON by fixing incomplete structures (unclosed strings, brackets, braces).\n */\nfunction parsePartialJson(jsonText: string): unknown {\n if (!jsonText.trim()) {\n return undefined;\n }\n\n try {\n return JSON.parse(jsonText) as unknown;\n } catch {\n // Continue to fix incomplete JSON\n }\n\n let fixed = jsonText;\n\n // Count unclosed brackets/braces while tracking string boundaries\n let openBraces = 0;\n let openBrackets = 0;\n let inString = false;\n let escaped = false;\n\n for (const char of fixed) {\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (char === '\\\\') {\n escaped = true;\n continue;\n }\n\n if (char === '\"') {\n inString = !inString;\n continue;\n }\n\n if (!inString) {\n if (char === '{') openBraces += 1;\n else if (char === '}') openBraces -= 1;\n else if (char === '[') openBrackets += 1;\n else if (char === ']') openBrackets -= 1;\n }\n }\n\n // Close unclosed structures\n if (inString) {\n fixed += '\"';\n }\n while (openBrackets > 0) {\n fixed += ']';\n openBrackets -= 1;\n }\n while (openBraces > 0) {\n fixed += '}';\n openBraces -= 1;\n }\n\n try {\n return JSON.parse(fixed) as unknown;\n } catch {\n return undefined;\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 currentObjectPartIndex: null,\n accumulatedJson: '',\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: OctavusError | 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 /**\n * The current error, if any.\n * Contains structured error information including type, source, and retryability.\n */\n get error(): OctavusError | 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: OctavusError | 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 * @example Send a text message\n * ```typescript\n * await chat.send('user-message',\n * { USER_MESSAGE: message },\n * { userMessage: { content: message } }\n * );\n * ```\n *\n * @example Send a message with file attachments\n * ```typescript\n * await chat.send('user-message',\n * { USER_MESSAGE: message, FILES: fileRefs },\n * { userMessage: { content: message, files: fileRefs } }\n * );\n * ```\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 let fileRefs: FileReference[] | undefined;\n if (sendOptions?.userMessage?.files) {\n const files = sendOptions.userMessage.files;\n if (isFileReferenceArray(files)) {\n fileRefs = files;\n } else if (this.options.requestUploadUrls) {\n fileRefs = await uploadFiles(files, {\n requestUploadUrls: this.options.requestUploadUrls,\n });\n } else {\n throw new Error(\n 'File upload requires requestUploadUrls option. Either provide FileReference[] or configure requestUploadUrls.',\n );\n }\n }\n\n // Auto-upload FILES in trigger input if needed\n let processedInput = input;\n if (input?.FILES !== undefined && !isFileReferenceArray(input.FILES)) {\n if (this.options.requestUploadUrls) {\n const inputFiles = input.FILES as FileList | File[];\n const uploadedRefs =\n fileRefs ??\n (await uploadFiles(inputFiles, {\n requestUploadUrls: this.options.requestUploadUrls,\n }));\n processedInput = { ...input, FILES: uploadedRefs };\n fileRefs = fileRefs ?? uploadedRefs;\n }\n }\n\n // Optimistic UI: add user message before server responds\n if (sendOptions?.userMessage !== undefined) {\n const userMsg = createUserMessage(sendOptions.userMessage, fileRefs);\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, processedInput)) {\n if (this.streamingState === null) break;\n\n this.handleStreamEvent(event, this.streamingState);\n }\n } catch (err) {\n // Convert unknown errors to OctavusError\n const errorObj = OctavusError.isInstance(err)\n ? err\n : new OctavusError({\n errorType: 'internal_error',\n message: err instanceof Error ? err.message : 'Unknown error',\n source: 'client',\n retryable: false,\n cause: err,\n });\n this.setError(errorObj);\n this.setStatus('error');\n this.streamingState = null;\n this.options.onError?.(errorObj);\n }\n }\n\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 chat.uploadFiles(fileInput.files, (i, progress) => {\n * console.log(`File ${i}: ${progress}%`);\n * });\n * // Later...\n * await chat.send('user-message', { FILES: fileRefs }, { userMessage: { files: fileRefs } });\n * ```\n */\n async uploadFiles(\n files: FileList | File[],\n onProgress?: (fileIndex: number, progress: number) => void,\n ): Promise<FileReference[]> {\n if (!this.options.requestUploadUrls) {\n throw new Error('File upload requires requestUploadUrls option');\n }\n return await uploadFiles(files, {\n requestUploadUrls: this.options.requestUploadUrls,\n onProgress,\n });\n }\n\n /** Stop the current streaming and finalize any partial message */\n stop(): void {\n if (this._status !== 'streaming') {\n return;\n }\n\n this.transport.stop();\n\n const state = this.streamingState;\n if (state && state.parts.length > 0) {\n // Mark in-progress parts as cancelled/done\n const finalParts = state.parts.map((part): UIMessagePart => {\n if (part.type === 'tool-call') {\n const toolPart = part;\n // Mark pending/running tools as cancelled\n if (toolPart.status === 'pending' || toolPart.status === 'running') {\n return { ...toolPart, status: 'cancelled' };\n }\n }\n if (part.type === 'operation') {\n const opPart = part;\n // Mark running operations as cancelled\n if (opPart.status === 'running') {\n return { ...opPart, status: 'cancelled' };\n }\n }\n if (part.type === 'text' || part.type === 'reasoning') {\n const textPart = part;\n // Mark streaming text/reasoning as done (it's not an error)\n if (textPart.status === 'streaming') {\n return { ...textPart, status: 'done' };\n }\n }\n if (part.type === 'object') {\n const objPart = part;\n // Mark streaming objects as done\n if (objPart.status === 'streaming') {\n return { ...objPart, status: 'done' };\n }\n }\n return part;\n });\n\n const finalMessage: UIMessage = {\n id: state.messageId,\n role: 'assistant',\n parts: finalParts,\n status: 'done',\n createdAt: new Date(),\n };\n\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (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 this.options.onStop?.();\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: threadForPart(thread),\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 reasoningPart: UIReasoningPart = {\n type: 'reasoning',\n text: '',\n status: 'streaming',\n thread: threadForPart(state.activeBlock?.thread),\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 = threadForPart(state.activeBlock?.thread);\n const shouldAddPart = state.activeBlock?.outputToChat !== false || thread !== undefined;\n\n if (shouldAddPart) {\n // Structured output mode: accumulate JSON and parse progressively\n if (event.responseType) {\n const objectPart: UIObjectPart = {\n type: 'object',\n id: event.id,\n typeName: event.responseType,\n partial: undefined,\n object: undefined,\n status: 'streaming',\n thread,\n };\n state.parts.push(objectPart);\n state.currentObjectPartIndex = state.parts.length - 1;\n state.accumulatedJson = '';\n state.currentTextPartIndex = null;\n } else {\n const textPart: UITextPart = {\n type: 'text',\n text: '',\n status: 'streaming',\n thread,\n };\n state.parts.push(textPart);\n state.currentTextPartIndex = state.parts.length - 1;\n state.currentObjectPartIndex = null;\n }\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-delta': {\n if (state.currentObjectPartIndex !== null) {\n state.accumulatedJson += event.delta;\n const part = state.parts[state.currentObjectPartIndex] as UIObjectPart;\n const parsed = parsePartialJson(state.accumulatedJson);\n if (parsed !== undefined) {\n part.partial = parsed;\n state.parts[state.currentObjectPartIndex] = { ...part };\n }\n } else 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.currentObjectPartIndex !== null) {\n const part = state.parts[state.currentObjectPartIndex] as UIObjectPart;\n try {\n const finalObject = JSON.parse(state.accumulatedJson) as unknown;\n part.object = finalObject;\n part.partial = finalObject;\n part.status = 'done';\n } catch {\n // Keep partial data but mark as error\n part.status = 'error';\n part.error = 'Failed to parse response as JSON';\n }\n state.parts[state.currentObjectPartIndex] = { ...part };\n state.currentObjectPartIndex = null;\n state.accumulatedJson = '';\n } else 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 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: threadForPart(state.activeBlock?.thread),\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.error;\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\n const thread = threadForPart(state.activeBlock?.thread);\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,\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,\n };\n }\n\n state.parts.push(sourcePart);\n this.updateStreamingMessage();\n break;\n }\n\n case 'file-available': {\n // Add generated file as a part\n const filePart: UIFilePart = {\n type: 'file',\n id: event.id,\n mediaType: event.mediaType,\n url: event.url,\n filename: event.filename,\n size: event.size,\n toolCallId: event.toolCallId,\n thread: threadForPart(state.activeBlock?.thread),\n };\n state.parts.push(filePart);\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 if (part.type === 'object' && part.status === 'streaming') {\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?.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 // Create structured error from the error event\n throw new OctavusError({\n errorType: event.errorType,\n message: event.message,\n source: event.source,\n retryable: event.retryable,\n retryAfter: event.retryAfter,\n code: event.code,\n provider: event.provider,\n tool: event.tool,\n });\n }\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?.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 type { FileReference } from '@octavus/core';\n\n/**\n * Response from the upload URLs endpoint\n */\nexport interface UploadUrlsResponse {\n files: {\n id: string;\n uploadUrl: string;\n downloadUrl: string;\n }[];\n}\n\n/**\n * Options for uploading files\n */\nexport interface UploadFilesOptions {\n /**\n * Function to request upload URLs from the platform.\n * Consumer apps must implement this to authenticate with the platform.\n *\n * @param files - Array of file metadata to request URLs for\n * @returns Response with presigned upload and download URLs\n *\n * @example\n * ```typescript\n * requestUploadUrls: async (files) => {\n * const response = await fetch('/api/upload-urls', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, files }),\n * });\n * return response.json();\n * }\n * ```\n */\n requestUploadUrls: (\n files: { filename: string; mediaType: string; size: number }[],\n ) => Promise<UploadUrlsResponse>;\n\n /**\n * Callback for upload progress (0-100 per file).\n * Called multiple times during upload with real-time progress.\n *\n * @param fileIndex - Index of the file being uploaded\n * @param progress - Progress percentage (0-100)\n */\n onProgress?: (fileIndex: number, progress: number) => void;\n}\n\n/**\n * Upload a single file to S3 with progress tracking.\n * Uses XMLHttpRequest for upload progress events (fetch doesn't support this).\n */\nfunction uploadFileWithProgress(\n url: string,\n file: File,\n onProgress?: (progress: number) => void,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n const progress = Math.round((event.loaded / event.total) * 100);\n onProgress?.(progress);\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new Error('Upload failed: network error'));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new Error('Upload aborted'));\n });\n\n xhr.open('PUT', url);\n xhr.setRequestHeader('Content-Type', file.type || 'application/octet-stream');\n xhr.send(file);\n });\n}\n\n/**\n * Upload files to the Octavus platform.\n *\n * This function:\n * 1. Requests presigned upload URLs from the platform\n * 2. Uploads each file directly to S3 with progress tracking\n * 3. Returns file references that can be used in trigger input\n *\n * @param files - Files to upload (from file input or drag/drop)\n * @param options - Upload configuration\n * @returns Array of file references with download URLs\n *\n * @example\n * ```typescript\n * const fileRefs = await uploadFiles(fileInputRef.current.files, {\n * requestUploadUrls: async (files) => {\n * const response = await fetch('/api/upload-urls', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, files }),\n * });\n * return response.json();\n * },\n * onProgress: (fileIndex, progress) => {\n * console.log(`File ${fileIndex}: ${progress}%`);\n * },\n * });\n * ```\n */\nexport async function uploadFiles(\n files: FileList | File[],\n options: UploadFilesOptions,\n): Promise<FileReference[]> {\n const fileArray = Array.from(files);\n\n if (fileArray.length === 0) {\n return [];\n }\n\n const { files: uploadInfos } = await options.requestUploadUrls(\n fileArray.map((f) => ({\n filename: f.name,\n mediaType: f.type || 'application/octet-stream',\n size: f.size,\n })),\n );\n\n const references: FileReference[] = [];\n\n for (let i = 0; i < fileArray.length; i++) {\n const file = fileArray[i]!;\n const uploadInfo = uploadInfos[i]!;\n\n await uploadFileWithProgress(uploadInfo.uploadUrl, file, (progress) => {\n options.onProgress?.(i, progress);\n });\n\n references.push({\n id: uploadInfo.id,\n mediaType: file.type || 'application/octet-stream',\n url: uploadInfo.downloadUrl,\n filename: file.name,\n size: file.size,\n });\n }\n\n return references;\n}\n","import { safeParseStreamEvent, isAbortError, type StreamEvent } from '@octavus/core';\n\n/**\n * Parse SSE stream events.\n *\n * @param response - The HTTP response with SSE body\n * @param signal - Optional abort signal to cancel reading\n */\nexport async function* parseSSEStream(\n response: Response,\n signal?: AbortSignal,\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 // Check if aborted before reading\n if (signal?.aborted) {\n return;\n }\n\n let readResult: ReadableStreamReadResult<Uint8Array>;\n try {\n readResult = await reader.read();\n } catch (err) {\n // Handle abort errors gracefully - exit without throwing\n if (isAbortError(err)) {\n return;\n }\n throw err;\n }\n\n const { done, value } = readResult;\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 { 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 { isAbortError } from '@octavus/core';\nimport { parseSSEStream } from '@/stream/reader';\nimport type { Transport } from './types';\n\n/**\n * Request options passed to the triggerRequest function.\n */\nexport interface TriggerRequestOptions {\n /** Abort signal to cancel the request */\n signal?: AbortSignal;\n}\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 * @param options - Optional request options including abort signal\n * @returns Response with SSE stream body\n *\n * @example\n * ```typescript\n * triggerRequest: (triggerName, input, options) =>\n * fetch('/api/octavus', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * signal: options?.signal,\n * }),\n * ```\n */\n triggerRequest: (\n triggerName: string,\n input?: Record<string, unknown>,\n options?: TriggerRequestOptions,\n ) => 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, options) =>\n * fetch('/api/octavus', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * signal: options?.signal,\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 try {\n const response = await options.triggerRequest(triggerName, input, {\n signal: abortController.signal,\n });\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, abortController.signal)) {\n if (abortController.signal.aborted) {\n break;\n }\n yield event;\n }\n } catch (err) {\n // Handle abort errors gracefully - don't throw, just exit\n if (isAbortError(err)) {\n return;\n }\n throw err;\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","export {\n OctavusChat,\n type OctavusChatOptions,\n type ChatStatus,\n type UserMessageInput,\n} from './chat';\n\nexport { uploadFiles, type UploadFilesOptions, type UploadUrlsResponse } from './files';\n\nexport { parseSSEStream } from './stream/reader';\n\n// Transport exports\nexport {\n createHttpTransport,\n createSocketTransport,\n isSocketTransport,\n type Transport,\n type SocketTransport,\n type ConnectionState,\n type ConnectionStateListener,\n type HttpTransportOptions,\n type TriggerRequestOptions,\n type SocketLike,\n type SocketTransportOptions,\n} from './transports';\n\n// Re-export everything from core so consumers don't need to install @octavus/core separately\nexport * from '@octavus/core';\n"],"mappings":"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","names":["isAbortError","isAbortError","safeParseStreamEvent"]}
1
+ {"version":3,"sources":["../src/chat.ts","../src/files.ts","../src/stream/reader.ts","../src/transports/types.ts","../src/transports/http.ts","../src/transports/socket.ts","../src/index.ts"],"sourcesContent":["import {\n generateId,\n threadForPart,\n isFileReferenceArray,\n OctavusError,\n type UIMessage,\n type UIMessagePart,\n type UITextPart,\n type UIReasoningPart,\n type UIToolCallPart,\n type UIOperationPart,\n type UISourcePart,\n type UIFilePart,\n type UIObjectPart,\n type DisplayMode,\n type StreamEvent,\n type FileReference,\n type PendingToolCall,\n type ToolResult,\n} from '@octavus/core';\nimport type { Transport } from './transports/types';\nimport { uploadFiles, type UploadFilesOptions } from './files';\n\n/** Block types that are internal operations (not LLM-driven) */\nconst OPERATION_BLOCK_TYPES = new Set(['set-resource', 'serialize-thread', 'generate-image']);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ChatStatus = 'idle' | 'streaming' | 'error' | 'awaiting-input';\n\n/**\n * Context provided to client tool handlers.\n */\nexport interface ClientToolContext {\n /** Unique identifier for this tool call */\n toolCallId: string;\n /** Name of the tool being called */\n toolName: string;\n /** Signal for cancellation if user stops generation */\n signal: AbortSignal;\n}\n\n/**\n * Handler function for client-side tool execution.\n * Can be:\n * - An async function that executes automatically and returns a result\n * - The string 'interactive' to indicate the tool requires user interaction\n */\nexport type ClientToolHandler =\n | ((args: Record<string, unknown>, ctx: ClientToolContext) => Promise<unknown>)\n | 'interactive';\n\n/**\n * Interactive tool call awaiting user interaction.\n * The `submit` and `cancel` methods are pre-bound to this tool call's ID.\n */\nexport interface InteractiveTool {\n /** Unique identifier for this tool call */\n toolCallId: string;\n /** Name of the tool being called */\n toolName: string;\n /** Arguments passed to the tool */\n args: Record<string, unknown>;\n /**\n * Submit a result for this tool call.\n * Call this when the user has provided input.\n *\n * @param result - The result from user interaction\n */\n submit: (result: unknown) => void;\n /**\n * Cancel this tool call with an optional reason.\n * Call this when the user dismisses the UI without providing input.\n *\n * @param reason - Optional reason for cancellation (default: 'User cancelled')\n */\n cancel: (reason?: string) => void;\n}\n\n/**\n * Internal pending tool state (before binding submit/cancel).\n */\ninterface PendingToolState {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n source?: 'llm' | 'block';\n outputVariable?: string;\n blockIndex?: number;\n}\n\n/**\n * Input for creating a user message.\n * Supports text content, structured object content, and file attachments.\n */\nexport interface UserMessageInput {\n /**\n * Content of the message. Can be:\n * - string: Creates a text part\n * - object: Creates an object part (uses `type` field as typeName if present)\n */\n content?: string | Record<string, unknown>;\n /**\n * File attachments (shorthand). Can be:\n * - FileList: From file input element (will be uploaded via uploadFiles)\n * - File[]: Array of File objects (will be uploaded via uploadFiles)\n * - FileReference[]: Already uploaded files (used directly)\n */\n files?: FileList | File[] | FileReference[];\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 /**\n * Function to request upload URLs from the platform.\n * Required if you want to use file uploads with FileList/File[].\n *\n * @example\n * ```typescript\n * requestUploadUrls: async (files) => {\n * const response = await fetch('/api/upload-urls', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, files }),\n * });\n * return response.json();\n * }\n * ```\n */\n requestUploadUrls?: UploadFilesOptions['requestUploadUrls'];\n\n /**\n * Client-side tool handlers.\n * Register handlers for tools that should execute in the browser.\n *\n * - If a tool has a handler function: executes automatically\n * - If a tool is marked as 'interactive': appears in `pendingClientTools` with bound `submit()`/`cancel()`\n *\n * @example Automatic client tool\n * ```typescript\n * clientTools: {\n * 'get-browser-location': async () => {\n * const pos = await new Promise((resolve, reject) => {\n * navigator.geolocation.getCurrentPosition(resolve, reject);\n * });\n * return { lat: pos.coords.latitude, lng: pos.coords.longitude };\n * },\n * }\n * ```\n *\n * @example Interactive client tool (user input required)\n * ```typescript\n * clientTools: {\n * 'request-feedback': 'interactive',\n * }\n * // Then render UI based on pendingClientTools['request-feedback']\n * // and call tool.submit(result) or tool.cancel()\n * ```\n */\n clientTools?: Record<string, ClientToolHandler>;\n\n /** Initial messages (for session refresh) */\n initialMessages?: UIMessage[];\n /**\n * Callback when an error occurs.\n * Receives an OctavusError with structured error information.\n *\n * @example\n * ```typescript\n * onError: (error) => {\n * console.error('Chat error:', {\n * type: error.errorType,\n * message: error.message,\n * retryable: error.retryable,\n * provider: error.provider,\n * });\n *\n * // Handle specific error types\n * if (isRateLimitError(error)) {\n * showRetryButton(error.retryAfter);\n * }\n * }\n * ```\n */\n onError?: (error: OctavusError) => void;\n /** Callback when streaming finishes successfully */\n onFinish?: () => void;\n /** Callback when streaming is stopped by user */\n onStop?: () => 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 currentTextPartIndex: number | null;\n currentReasoningPartIndex: number | null;\n currentObjectPartIndex: number | null;\n accumulatedJson: string;\n}\n\ntype Listener = () => void;\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Create a user message from input with optional file attachments.\n * Parts order: files first (for vision models), then content (text or object).\n */\nfunction createUserMessage(input: UserMessageInput, files?: FileReference[]): UIMessage {\n const parts: UIMessagePart[] = [];\n\n // Add file parts first (vision models expect images before text)\n if (files && files.length > 0) {\n for (const file of files) {\n parts.push({\n type: 'file',\n id: file.id,\n mediaType: file.mediaType,\n url: file.url,\n filename: file.filename,\n size: file.size,\n });\n }\n }\n\n // Add content part after files\n if (input.content !== undefined) {\n if (typeof input.content === 'string') {\n // String content → text part\n parts.push({ type: 'text', text: input.content, status: 'done' });\n } else {\n // Object content → object part\n // Use the object's `type` field as typeName if present, otherwise fallback to 'object'\n const typeName = (input.content as { type?: string }).type ?? 'object';\n parts.push({\n type: 'object',\n id: generateId(),\n typeName,\n object: input.content,\n status: 'done',\n });\n }\n }\n\n return {\n id: generateId(),\n role: 'user',\n parts,\n status: 'done',\n createdAt: new Date(),\n };\n}\n\n/**\n * Parse partial JSON by fixing incomplete structures (unclosed strings, brackets, braces).\n */\nfunction parsePartialJson(jsonText: string): unknown {\n if (!jsonText.trim()) {\n return undefined;\n }\n\n try {\n return JSON.parse(jsonText) as unknown;\n } catch {\n // Continue to fix incomplete JSON\n }\n\n let fixed = jsonText;\n\n // Count unclosed brackets/braces while tracking string boundaries\n let openBraces = 0;\n let openBrackets = 0;\n let inString = false;\n let escaped = false;\n\n for (const char of fixed) {\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (char === '\\\\') {\n escaped = true;\n continue;\n }\n\n if (char === '\"') {\n inString = !inString;\n continue;\n }\n\n if (!inString) {\n if (char === '{') openBraces += 1;\n else if (char === '}') openBraces -= 1;\n else if (char === '[') openBrackets += 1;\n else if (char === ']') openBrackets -= 1;\n }\n }\n\n // Close unclosed structures\n if (inString) {\n fixed += '\"';\n }\n while (openBrackets > 0) {\n fixed += ']';\n openBrackets -= 1;\n }\n while (openBraces > 0) {\n fixed += '}';\n openBraces -= 1;\n }\n\n try {\n return JSON.parse(fixed) as unknown;\n } catch {\n return undefined;\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 currentObjectPartIndex: null,\n accumulatedJson: '',\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 * 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 * });\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: OctavusError | null = null;\n private options: OctavusChatOptions;\n private transport: Transport;\n private streamingState: StreamingState | null = null;\n\n // Client tool state\n // Keyed by toolName -> array of pending tools for that name\n private _pendingToolsByName = new Map<string, PendingToolState[]>();\n // Keyed by toolCallId -> pending tool state (for internal lookup when submitting)\n private _pendingToolsByCallId = new Map<string, PendingToolState>();\n // Cache for React useSyncExternalStore compatibility\n private _pendingClientToolsCache: Record<string, InteractiveTool[]> = {};\n private _completedToolResults: ToolResult[] = [];\n private _clientToolAbortController: AbortController | null = null;\n // Server tool results from mixed server+client tools (for continuation)\n private _serverToolResults: ToolResult[] = [];\n // Execution ID for continuation (from client-tool-request event)\n private _pendingExecutionId: string | 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 /**\n * The current error, if any.\n * Contains structured error information including type, source, and retryability.\n */\n get error(): OctavusError | null {\n return this._error;\n }\n\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 get pendingClientTools(): Record<string, InteractiveTool[]> {\n return this._pendingClientToolsCache;\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: OctavusError | null): void {\n this._error = error;\n this.notifyListeners();\n }\n\n private updatePendingClientToolsCache(): void {\n const cache: Record<string, InteractiveTool[]> = {};\n for (const [toolName, tools] of this._pendingToolsByName.entries()) {\n cache[toolName] = tools.map((tool) => ({\n toolCallId: tool.toolCallId,\n toolName: tool.toolName,\n args: tool.args,\n submit: (result: unknown) => this.submitToolResult(tool.toolCallId, result),\n cancel: (reason?: string) =>\n this.submitToolResult(tool.toolCallId, undefined, reason ?? 'User cancelled'),\n }));\n }\n this._pendingClientToolsCache = cache;\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 * @example Send a text message\n * ```typescript\n * await chat.send('user-message',\n * { USER_MESSAGE: message },\n * { userMessage: { content: message } }\n * );\n * ```\n *\n * @example Send a message with file attachments\n * ```typescript\n * await chat.send('user-message',\n * { USER_MESSAGE: message, FILES: fileRefs },\n * { userMessage: { content: message, files: fileRefs } }\n * );\n * ```\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 let fileRefs: FileReference[] | undefined;\n if (sendOptions?.userMessage?.files) {\n const files = sendOptions.userMessage.files;\n if (isFileReferenceArray(files)) {\n fileRefs = files;\n } else if (this.options.requestUploadUrls) {\n fileRefs = await uploadFiles(files, {\n requestUploadUrls: this.options.requestUploadUrls,\n });\n } else {\n throw new Error(\n 'File upload requires requestUploadUrls option. Either provide FileReference[] or configure requestUploadUrls.',\n );\n }\n }\n\n // Auto-upload FILES in trigger input if needed\n let processedInput = input;\n if (input?.FILES !== undefined && !isFileReferenceArray(input.FILES)) {\n if (this.options.requestUploadUrls) {\n const inputFiles = input.FILES as FileList | File[];\n const uploadedRefs =\n fileRefs ??\n (await uploadFiles(inputFiles, {\n requestUploadUrls: this.options.requestUploadUrls,\n }));\n processedInput = { ...input, FILES: uploadedRefs };\n fileRefs = fileRefs ?? uploadedRefs;\n }\n }\n\n // Optimistic UI: add user message before server responds\n if (sendOptions?.userMessage !== undefined) {\n const userMsg = createUserMessage(sendOptions.userMessage, fileRefs);\n this.setMessages([...this._messages, userMsg]);\n }\n\n this.setStatus('streaming');\n this.setError(null);\n this.streamingState = createEmptyStreamingState();\n\n // Clear any previous client tool state\n this._pendingToolsByName.clear();\n this._pendingToolsByCallId.clear();\n this._completedToolResults = [];\n this._serverToolResults = [];\n this._pendingExecutionId = null;\n this.updatePendingClientToolsCache();\n\n try {\n for await (const event of this.transport.trigger(triggerName, processedInput)) {\n if (this.streamingState === null) break;\n\n this.handleStreamEvent(event, this.streamingState);\n }\n } catch (err) {\n // Convert unknown errors to OctavusError\n const errorObj = OctavusError.isInstance(err)\n ? err\n : new OctavusError({\n errorType: 'internal_error',\n message: err instanceof Error ? err.message : 'Unknown error',\n source: 'client',\n retryable: false,\n cause: err,\n });\n\n // Finalize any streaming message before setting error state\n const state = this.streamingState;\n if (state !== null) {\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n\n if (state.parts.length > 0) {\n // Mark in-progress parts as done/cancelled\n const finalParts = state.parts.map((part): UIMessagePart => {\n if (part.type === 'text' || part.type === 'reasoning') {\n if (part.status === 'streaming') {\n return { ...part, status: 'done' };\n }\n }\n if (part.type === 'object' && part.status === 'streaming') {\n return { ...part, status: 'done' };\n }\n if (part.type === 'tool-call') {\n if (part.status === 'pending' || part.status === 'running') {\n return { ...part, status: 'cancelled' };\n }\n }\n if (part.type === 'operation' && part.status === 'running') {\n return { ...part, status: 'cancelled' };\n }\n return part;\n });\n\n const finalMessage: UIMessage = {\n id: state.messageId,\n role: 'assistant',\n parts: finalParts,\n status: 'done',\n createdAt: new Date(),\n };\n\n if (lastMsg?.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n } else if (lastMsg?.id === state.messageId) {\n // No parts yet - remove the empty streaming message\n messages.pop();\n this.setMessages(messages);\n }\n }\n\n this.setError(errorObj);\n this.setStatus('error');\n this.streamingState = null;\n this.options.onError?.(errorObj);\n }\n }\n\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 chat.uploadFiles(fileInput.files, (i, progress) => {\n * console.log(`File ${i}: ${progress}%`);\n * });\n * // Later...\n * await chat.send('user-message', { FILES: fileRefs }, { userMessage: { files: fileRefs } });\n * ```\n */\n async uploadFiles(\n files: FileList | File[],\n onProgress?: (fileIndex: number, progress: number) => void,\n ): Promise<FileReference[]> {\n if (!this.options.requestUploadUrls) {\n throw new Error('File upload requires requestUploadUrls option');\n }\n return await uploadFiles(files, {\n requestUploadUrls: this.options.requestUploadUrls,\n onProgress,\n });\n }\n\n /**\n * Internal: Submit a result for a pending tool.\n * Called by bound submit/cancel methods on InteractiveTool.\n */\n private submitToolResult(toolCallId: string, result?: unknown, error?: string): void {\n const pendingTool = this._pendingToolsByCallId.get(toolCallId);\n if (!pendingTool) {\n // Tool not found - may have been cancelled or already resolved\n return;\n }\n\n // Remove from both maps\n this._pendingToolsByCallId.delete(toolCallId);\n const toolsForName = this._pendingToolsByName.get(pendingTool.toolName);\n if (toolsForName) {\n const filtered = toolsForName.filter((t) => t.toolCallId !== toolCallId);\n if (filtered.length === 0) {\n this._pendingToolsByName.delete(pendingTool.toolName);\n } else {\n this._pendingToolsByName.set(pendingTool.toolName, filtered);\n }\n }\n this.updatePendingClientToolsCache();\n\n const toolResult: ToolResult = {\n toolCallId,\n toolName: pendingTool.toolName,\n result: error ? undefined : result,\n error,\n outputVariable: pendingTool.outputVariable,\n blockIndex: pendingTool.blockIndex,\n };\n this._completedToolResults.push(toolResult);\n\n if (error) {\n this.emitToolOutputError(toolCallId, error);\n } else {\n this.emitToolOutputAvailable(toolCallId, result);\n }\n\n if (this._pendingToolsByCallId.size === 0) {\n void this.continueWithClientToolResults();\n }\n\n this.notifyListeners();\n }\n\n /** Stop the current streaming and finalize any partial message */\n stop(): void {\n if (this._status !== 'streaming' && this._status !== 'awaiting-input') {\n return;\n }\n\n this._clientToolAbortController?.abort();\n this._clientToolAbortController = null;\n this._pendingToolsByName.clear();\n this._pendingToolsByCallId.clear();\n this._completedToolResults = [];\n this._serverToolResults = [];\n this._pendingExecutionId = null;\n this.updatePendingClientToolsCache();\n\n this.transport.stop();\n\n const state = this.streamingState;\n if (state && state.parts.length > 0) {\n // Mark in-progress parts as cancelled/done\n const finalParts = state.parts.map((part): UIMessagePart => {\n if (part.type === 'tool-call') {\n const toolPart = part;\n // Mark pending/running tools as cancelled\n if (toolPart.status === 'pending' || toolPart.status === 'running') {\n return { ...toolPart, status: 'cancelled' };\n }\n }\n if (part.type === 'operation') {\n const opPart = part;\n // Mark running operations as cancelled\n if (opPart.status === 'running') {\n return { ...opPart, status: 'cancelled' };\n }\n }\n if (part.type === 'text' || part.type === 'reasoning') {\n const textPart = part;\n // Mark streaming text/reasoning as done (it's not an error)\n if (textPart.status === 'streaming') {\n return { ...textPart, status: 'done' };\n }\n }\n if (part.type === 'object') {\n const objPart = part;\n // Mark streaming objects as done\n if (objPart.status === 'streaming') {\n return { ...objPart, status: 'done' };\n }\n }\n return part;\n });\n\n const finalMessage: UIMessage = {\n id: state.messageId,\n role: 'assistant',\n parts: finalParts,\n status: 'done',\n createdAt: new Date(),\n };\n\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (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 this.options.onStop?.();\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: threadForPart(thread),\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 reasoningPart: UIReasoningPart = {\n type: 'reasoning',\n text: '',\n status: 'streaming',\n thread: threadForPart(state.activeBlock?.thread),\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 = threadForPart(state.activeBlock?.thread);\n const shouldAddPart = state.activeBlock?.outputToChat !== false || thread !== undefined;\n\n if (shouldAddPart) {\n // Structured output mode: accumulate JSON and parse progressively\n if (event.responseType) {\n const objectPart: UIObjectPart = {\n type: 'object',\n id: event.id,\n typeName: event.responseType,\n partial: undefined,\n object: undefined,\n status: 'streaming',\n thread,\n };\n state.parts.push(objectPart);\n state.currentObjectPartIndex = state.parts.length - 1;\n state.accumulatedJson = '';\n state.currentTextPartIndex = null;\n } else {\n const textPart: UITextPart = {\n type: 'text',\n text: '',\n status: 'streaming',\n thread,\n };\n state.parts.push(textPart);\n state.currentTextPartIndex = state.parts.length - 1;\n state.currentObjectPartIndex = null;\n }\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-delta': {\n if (state.currentObjectPartIndex !== null) {\n state.accumulatedJson += event.delta;\n const part = state.parts[state.currentObjectPartIndex] as UIObjectPart;\n const parsed = parsePartialJson(state.accumulatedJson);\n if (parsed !== undefined) {\n part.partial = parsed;\n state.parts[state.currentObjectPartIndex] = { ...part };\n }\n } else 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.currentObjectPartIndex !== null) {\n const part = state.parts[state.currentObjectPartIndex] as UIObjectPart;\n try {\n const finalObject = JSON.parse(state.accumulatedJson) as unknown;\n part.object = finalObject;\n part.partial = finalObject;\n part.status = 'done';\n } catch {\n // Keep partial data but mark as error\n part.status = 'error';\n part.error = 'Failed to parse response as JSON';\n }\n state.parts[state.currentObjectPartIndex] = { ...part };\n state.currentObjectPartIndex = null;\n state.accumulatedJson = '';\n } else 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 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: threadForPart(state.activeBlock?.thread),\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.error;\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\n const thread = threadForPart(state.activeBlock?.thread);\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,\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,\n };\n }\n\n state.parts.push(sourcePart);\n this.updateStreamingMessage();\n break;\n }\n\n case 'file-available': {\n // Add generated file as a part\n const filePart: UIFilePart = {\n type: 'file',\n id: event.id,\n mediaType: event.mediaType,\n url: event.url,\n filename: event.filename,\n size: event.size,\n toolCallId: event.toolCallId,\n thread: threadForPart(state.activeBlock?.thread),\n };\n state.parts.push(filePart);\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 // Handle client-tool-calls finish reason\n if (event.finishReason === 'client-tool-calls') {\n // Don't finalize message - we're waiting for client tools\n if (this._pendingToolsByCallId.size > 0) {\n this.setStatus('awaiting-input');\n }\n // If no interactive tools but we have completed results, continueWithClientToolResults\n // was already called from handleClientToolRequest\n return;\n }\n\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 if (part.type === 'object' && part.status === 'streaming') {\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?.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 // Create structured error from the error event\n throw new OctavusError({\n errorType: event.errorType,\n message: event.message,\n source: event.source,\n retryable: event.retryable,\n retryAfter: event.retryAfter,\n code: event.code,\n provider: event.provider,\n tool: event.tool,\n });\n }\n\n case 'tool-request':\n // Handled by server-sdk, not relevant for UI\n break;\n\n case 'client-tool-request':\n // Store execution ID and server tool results for continuation\n this._pendingExecutionId = event.executionId;\n this._serverToolResults = event.serverToolResults ?? [];\n // Handle client-side tool execution\n void this.handleClientToolRequest(event.toolCalls, state);\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?.id === state.messageId) {\n messages[messages.length - 1] = msg;\n } else {\n messages.push(msg);\n }\n\n this.setMessages(messages);\n }\n\n /**\n * Emit a tool-output-available event for a client tool result.\n */\n private emitToolOutputAvailable(toolCallId: string, output: unknown): void {\n const state = this.streamingState;\n if (!state) return;\n\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.result = output;\n part.status = 'done';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n }\n\n /**\n * Emit a tool-output-error event for a client tool result.\n */\n private emitToolOutputError(toolCallId: string, error: string): void {\n const state = this.streamingState;\n if (!state) return;\n\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.error = error;\n part.status = 'error';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n }\n\n /**\n * Continue execution with collected client tool results.\n */\n private async continueWithClientToolResults(): Promise<void> {\n if (this._completedToolResults.length === 0) return;\n\n if (this._pendingExecutionId === null) {\n // Context lost - this shouldn't happen, but handle gracefully\n const errorObj = new OctavusError({\n errorType: 'internal_error',\n message: 'Cannot continue execution: execution ID was lost.',\n source: 'client',\n retryable: false,\n });\n this.setError(errorObj);\n this.setStatus('error');\n this.options.onError?.(errorObj);\n return;\n }\n\n // Combine server results (from mixed tools scenario) with client results\n const allResults = [...this._serverToolResults, ...this._completedToolResults];\n const executionId = this._pendingExecutionId;\n this._serverToolResults = [];\n this._completedToolResults = [];\n this._pendingExecutionId = null;\n\n this.setStatus('streaming');\n\n try {\n // Use the transport's continuation method (works for both HTTP and Socket)\n for await (const event of this.transport.continueWithToolResults(executionId, allResults)) {\n if (this.streamingState === null) break;\n this.handleStreamEvent(event, this.streamingState);\n }\n } catch (err) {\n const errorObj = OctavusError.isInstance(err)\n ? err\n : new OctavusError({\n errorType: 'internal_error',\n message: err instanceof Error ? err.message : 'Unknown error',\n source: 'client',\n retryable: false,\n cause: err,\n });\n\n this.setError(errorObj);\n this.setStatus('error');\n this.streamingState = null;\n this.options.onError?.(errorObj);\n }\n }\n\n /**\n * Handle client tool request event.\n *\n * IMPORTANT: Interactive tools must be registered synchronously (before any await)\n * to avoid a race condition where the finish event is processed before tools are added.\n */\n private async handleClientToolRequest(\n toolCalls: PendingToolCall[],\n state: StreamingState,\n ): Promise<void> {\n this._clientToolAbortController = new AbortController();\n\n // FIRST PASS: Register all interactive tools synchronously (no await)\n // This ensures pending tools are populated before finish event is processed\n for (const tc of toolCalls) {\n const handler = this.options.clientTools?.[tc.toolName];\n if (handler === 'interactive') {\n const toolState: PendingToolState = {\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: tc.args,\n source: tc.source,\n outputVariable: tc.outputVariable,\n blockIndex: tc.blockIndex,\n };\n // Add to both maps\n this._pendingToolsByCallId.set(tc.toolCallId, toolState);\n const existing = this._pendingToolsByName.get(tc.toolName) ?? [];\n this._pendingToolsByName.set(tc.toolName, [...existing, toolState]);\n }\n }\n if (this._pendingToolsByCallId.size > 0) {\n this.updatePendingClientToolsCache();\n }\n\n // SECOND PASS: Execute automatic handlers and handle missing handlers\n for (const tc of toolCalls) {\n const handler = this.options.clientTools?.[tc.toolName];\n\n if (handler === 'interactive') {\n // Already registered above, just update UI state\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === tc.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n // Keep running status - user will see the tool is \"executing\" while modal is open\n state.parts[toolPartIndex] = { ...part };\n }\n } else if (handler) {\n try {\n const result = await handler(tc.args, {\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n signal: this._clientToolAbortController.signal,\n });\n\n this._completedToolResults.push({\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n result,\n outputVariable: tc.outputVariable,\n blockIndex: tc.blockIndex,\n });\n\n this.emitToolOutputAvailable(tc.toolCallId, result);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Tool execution failed';\n this._completedToolResults.push({\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n error: errorMessage,\n outputVariable: tc.outputVariable,\n blockIndex: tc.blockIndex,\n });\n\n this.emitToolOutputError(tc.toolCallId, errorMessage);\n }\n } else {\n // No handler registered - treat as error\n const errorMessage = `No client handler for tool: ${tc.toolName}`;\n this._completedToolResults.push({\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n error: errorMessage,\n outputVariable: tc.outputVariable,\n blockIndex: tc.blockIndex,\n });\n\n this.emitToolOutputError(tc.toolCallId, errorMessage);\n }\n }\n\n // If no interactive tools, auto-continue with results\n if (this._pendingToolsByCallId.size === 0 && this._completedToolResults.length > 0) {\n await this.continueWithClientToolResults();\n }\n }\n}\n","import type { FileReference } from '@octavus/core';\n\n/**\n * Response from the upload URLs endpoint\n */\nexport interface UploadUrlsResponse {\n files: {\n id: string;\n uploadUrl: string;\n downloadUrl: string;\n }[];\n}\n\n/**\n * Options for uploading files\n */\nexport interface UploadFilesOptions {\n /**\n * Function to request upload URLs from the platform.\n * Consumer apps must implement this to authenticate with the platform.\n *\n * @param files - Array of file metadata to request URLs for\n * @returns Response with presigned upload and download URLs\n *\n * @example\n * ```typescript\n * requestUploadUrls: async (files) => {\n * const response = await fetch('/api/upload-urls', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, files }),\n * });\n * return response.json();\n * }\n * ```\n */\n requestUploadUrls: (\n files: { filename: string; mediaType: string; size: number }[],\n ) => Promise<UploadUrlsResponse>;\n\n /**\n * Callback for upload progress (0-100 per file).\n * Called multiple times during upload with real-time progress.\n *\n * @param fileIndex - Index of the file being uploaded\n * @param progress - Progress percentage (0-100)\n */\n onProgress?: (fileIndex: number, progress: number) => void;\n}\n\n/**\n * Upload a single file to S3 with progress tracking.\n * Uses XMLHttpRequest for upload progress events (fetch doesn't support this).\n */\nfunction uploadFileWithProgress(\n url: string,\n file: File,\n onProgress?: (progress: number) => void,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n const progress = Math.round((event.loaded / event.total) * 100);\n onProgress?.(progress);\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new Error('Upload failed: network error'));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new Error('Upload aborted'));\n });\n\n xhr.open('PUT', url);\n xhr.setRequestHeader('Content-Type', file.type || 'application/octet-stream');\n xhr.send(file);\n });\n}\n\n/**\n * Upload files to the Octavus platform.\n *\n * This function:\n * 1. Requests presigned upload URLs from the platform\n * 2. Uploads each file directly to S3 with progress tracking\n * 3. Returns file references that can be used in trigger input\n *\n * @param files - Files to upload (from file input or drag/drop)\n * @param options - Upload configuration\n * @returns Array of file references with download URLs\n *\n * @example\n * ```typescript\n * const fileRefs = await uploadFiles(fileInputRef.current.files, {\n * requestUploadUrls: async (files) => {\n * const response = await fetch('/api/upload-urls', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, files }),\n * });\n * return response.json();\n * },\n * onProgress: (fileIndex, progress) => {\n * console.log(`File ${fileIndex}: ${progress}%`);\n * },\n * });\n * ```\n */\nexport async function uploadFiles(\n files: FileList | File[],\n options: UploadFilesOptions,\n): Promise<FileReference[]> {\n const fileArray = Array.from(files);\n\n if (fileArray.length === 0) {\n return [];\n }\n\n const { files: uploadInfos } = await options.requestUploadUrls(\n fileArray.map((f) => ({\n filename: f.name,\n mediaType: f.type || 'application/octet-stream',\n size: f.size,\n })),\n );\n\n const references: FileReference[] = [];\n\n for (let i = 0; i < fileArray.length; i++) {\n const file = fileArray[i]!;\n const uploadInfo = uploadInfos[i]!;\n\n await uploadFileWithProgress(uploadInfo.uploadUrl, file, (progress) => {\n options.onProgress?.(i, progress);\n });\n\n references.push({\n id: uploadInfo.id,\n mediaType: file.type || 'application/octet-stream',\n url: uploadInfo.downloadUrl,\n filename: file.name,\n size: file.size,\n });\n }\n\n return references;\n}\n","import { safeParseStreamEvent, isAbortError, type StreamEvent } from '@octavus/core';\n\n/**\n * Parse SSE stream events.\n *\n * @param response - The HTTP response with SSE body\n * @param signal - Optional abort signal to cancel reading\n */\nexport async function* parseSSEStream(\n response: Response,\n signal?: AbortSignal,\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 // Check if aborted before reading\n if (signal?.aborted) {\n return;\n }\n\n let readResult: ReadableStreamReadResult<Uint8Array>;\n try {\n readResult = await reader.read();\n } catch (err) {\n // Handle abort errors gracefully - exit without throwing\n if (isAbortError(err)) {\n return;\n }\n throw err;\n }\n\n const { done, value } = readResult;\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 { StreamEvent, ToolResult } 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 /**\n * Continue execution with tool results after client-side tool handling.\n *\n * @param executionId - The execution ID from the client-tool-request event\n * @param results - All tool results (server + client) to send\n */\n continueWithToolResults(executionId: string, results: ToolResult[]): 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 { isAbortError, type ToolResult } from '@octavus/core';\nimport { parseSSEStream } from '@/stream/reader';\nimport type { Transport } from './types';\n\n// =============================================================================\n// Request Types\n// =============================================================================\n\n/** Start a new trigger execution */\nexport interface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\n/** Continue execution after client-side tool handling */\nexport interface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n\n/** All request types supported by the HTTP transport */\nexport type HttpRequest = TriggerRequest | ContinueRequest;\n\n// =============================================================================\n// Transport Options\n// =============================================================================\n\n/** Request options passed to the request callback */\nexport interface HttpRequestOptions {\n /** Abort signal to cancel the request */\n signal?: AbortSignal;\n}\n\n/**\n * Options for creating an HTTP transport.\n */\nexport interface HttpTransportOptions {\n /**\n * Function to make requests to your backend.\n * Receives a discriminated union with `type` to identify the request kind.\n *\n * @param request - The request payload (check `request.type` for the kind)\n * @param options - Request options including abort signal\n * @returns Response with SSE stream body\n *\n * @example\n * ```typescript\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 */\n request: (request: HttpRequest, options?: HttpRequestOptions) => Promise<Response>;\n}\n\n// =============================================================================\n// Transport Implementation\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 * 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 * ```\n */\nexport function createHttpTransport(options: HttpTransportOptions): Transport {\n let abortController: AbortController | null = null;\n\n async function* streamResponse(responsePromise: Promise<Response>) {\n try {\n const response = await responsePromise;\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, abortController!.signal)) {\n if (abortController?.signal.aborted) {\n break;\n }\n yield event;\n }\n } catch (err) {\n if (isAbortError(err)) {\n return;\n }\n throw err;\n }\n }\n\n return {\n async *trigger(triggerName, input) {\n abortController = new AbortController();\n const response = options.request(\n { type: 'trigger', triggerName, input },\n { signal: abortController.signal },\n );\n yield* streamResponse(response);\n },\n\n async *continueWithToolResults(executionId, toolResults) {\n abortController = new AbortController();\n const response = options.request(\n { type: 'continue', executionId, toolResults },\n { signal: abortController.signal },\n );\n yield* streamResponse(response);\n },\n\n stop() {\n abortController?.abort();\n abortController = null;\n },\n };\n}\n","import { safeParseStreamEvent, type StreamEvent, type ToolResult } 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 // Note: clientToolResults not sent here - socket uses sendClientToolResults() for continuation\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 * Continue execution with tool results after client-side tool handling.\n * @param executionId - The execution ID from the client-tool-request event\n * @param toolResults - All tool results (server + client) to send\n */\n async *continueWithToolResults(executionId: string, toolResults: ToolResult[]) {\n await ensureConnected();\n\n eventQueue = [];\n isStreaming = true;\n\n socket!.send(\n JSON.stringify({\n type: 'continue',\n executionId,\n toolResults,\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}\n","export {\n OctavusChat,\n type OctavusChatOptions,\n type ChatStatus,\n type UserMessageInput,\n type ClientToolContext,\n type ClientToolHandler,\n type InteractiveTool,\n} from './chat';\n\nexport { uploadFiles, type UploadFilesOptions, type UploadUrlsResponse } from './files';\n\nexport { parseSSEStream } from './stream/reader';\n\n// Transport exports\nexport {\n createHttpTransport,\n createSocketTransport,\n isSocketTransport,\n type Transport,\n type SocketTransport,\n type ConnectionState,\n type ConnectionStateListener,\n type HttpTransportOptions,\n type HttpRequestOptions,\n type HttpRequest,\n type TriggerRequest,\n type ContinueRequest,\n type SocketLike,\n type SocketTransportOptions,\n} from './transports';\n\nexport type * from '@octavus/core';\nexport {\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/core';\n"],"mappings":"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","names":["isAbortError","isAbortError","safeParseStreamEvent","OctavusError","generateId","isAbortError","threadForPart","isFileReferenceArray","safeParseStreamEvent"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octavus/client-sdk",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Framework-agnostic client SDK for Octavus agents",
5
5
  "license": "MIT",
6
6
  "author": "Octavus AI <dev@octavus.ai>",
@@ -39,7 +39,7 @@
39
39
  "access": "public"
40
40
  },
41
41
  "dependencies": {
42
- "@octavus/core": "^1.0.0"
42
+ "@octavus/core": "^2.0.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "tsup": "^8.3.5",