@octavus/client-sdk 0.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  import {
3
3
  generateId,
4
4
  threadForPart,
5
- isFileReferenceArray
5
+ isFileReferenceArray,
6
+ OctavusError
6
7
  } from "@octavus/core";
7
8
 
8
9
  // src/files.ts
@@ -180,6 +181,19 @@ var OctavusChat = class {
180
181
  options;
181
182
  transport;
182
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;
183
197
  // Listener sets for reactive frameworks
184
198
  listeners = /* @__PURE__ */ new Set();
185
199
  constructor(options) {
@@ -196,9 +210,34 @@ var OctavusChat = class {
196
210
  get status() {
197
211
  return this._status;
198
212
  }
213
+ /**
214
+ * The current error, if any.
215
+ * Contains structured error information including type, source, and retryability.
216
+ */
199
217
  get error() {
200
218
  return this._error;
201
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
+ }
202
241
  // =========================================================================
203
242
  // Subscription Methods (for reactive frameworks)
204
243
  // =========================================================================
@@ -228,6 +267,19 @@ var OctavusChat = class {
228
267
  this._error = error;
229
268
  this.notifyListeners();
230
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
+ }
231
283
  // =========================================================================
232
284
  // Public Methods
233
285
  // =========================================================================
@@ -289,13 +341,67 @@ var OctavusChat = class {
289
341
  this.setStatus("streaming");
290
342
  this.setError(null);
291
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();
292
350
  try {
293
351
  for await (const event of this.transport.trigger(triggerName, processedInput)) {
294
352
  if (this.streamingState === null) break;
295
353
  this.handleStreamEvent(event, this.streamingState);
296
354
  }
297
355
  } catch (err) {
298
- const errorObj = err instanceof Error ? err : new Error("Unknown error");
356
+ const errorObj = OctavusError.isInstance(err) ? err : new OctavusError({
357
+ errorType: "internal_error",
358
+ message: err instanceof Error ? err.message : "Unknown error",
359
+ source: "client",
360
+ retryable: false,
361
+ cause: err
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
+ }
299
405
  this.setError(errorObj);
300
406
  this.setStatus("error");
301
407
  this.streamingState = null;
@@ -328,11 +434,58 @@ var OctavusChat = class {
328
434
  onProgress
329
435
  });
330
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
+ }
331
476
  /** Stop the current streaming and finalize any partial message */
332
477
  stop() {
333
- if (this._status !== "streaming") {
478
+ if (this._status !== "streaming" && this._status !== "awaiting-input") {
334
479
  return;
335
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();
336
489
  this.transport.stop();
337
490
  const state = this.streamingState;
338
491
  if (state && state.parts.length > 0) {
@@ -372,7 +525,7 @@ var OctavusChat = class {
372
525
  };
373
526
  const messages = [...this._messages];
374
527
  const lastMsg = messages[messages.length - 1];
375
- if (lastMsg && lastMsg.id === state.messageId) {
528
+ if (lastMsg?.id === state.messageId) {
376
529
  messages[messages.length - 1] = finalMessage;
377
530
  } else {
378
531
  messages.push(finalMessage);
@@ -615,7 +768,7 @@ var OctavusChat = class {
615
768
  );
616
769
  if (toolPartIndex >= 0) {
617
770
  const part = state.parts[toolPartIndex];
618
- part.error = event.errorText;
771
+ part.error = event.error;
619
772
  part.status = "error";
620
773
  state.parts[toolPartIndex] = { ...part };
621
774
  this.updateStreamingMessage();
@@ -668,6 +821,12 @@ var OctavusChat = class {
668
821
  this.options.onResourceUpdate?.(event.name, event.value);
669
822
  break;
670
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
+ }
671
830
  const finalMessage = buildMessageFromState(state, "done");
672
831
  finalMessage.parts = finalMessage.parts.map((part) => {
673
832
  if (part.type === "text" || part.type === "reasoning") {
@@ -681,7 +840,7 @@ var OctavusChat = class {
681
840
  if (finalMessage.parts.length > 0) {
682
841
  const messages = [...this._messages];
683
842
  const lastMsg = messages[messages.length - 1];
684
- if (lastMsg && lastMsg.id === state.messageId) {
843
+ if (lastMsg?.id === state.messageId) {
685
844
  messages[messages.length - 1] = finalMessage;
686
845
  } else {
687
846
  messages.push(finalMessage);
@@ -693,10 +852,25 @@ var OctavusChat = class {
693
852
  this.options.onFinish?.();
694
853
  break;
695
854
  }
696
- case "error":
697
- throw new Error(event.errorText);
855
+ case "error": {
856
+ throw new OctavusError({
857
+ errorType: event.errorType,
858
+ message: event.message,
859
+ source: event.source,
860
+ retryable: event.retryable,
861
+ retryAfter: event.retryAfter,
862
+ code: event.code,
863
+ provider: event.provider,
864
+ tool: event.tool
865
+ });
866
+ }
698
867
  case "tool-request":
699
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;
700
874
  }
701
875
  }
702
876
  updateStreamingMessage() {
@@ -705,13 +879,168 @@ var OctavusChat = class {
705
879
  const msg = buildMessageFromState(state, "streaming");
706
880
  const messages = [...this._messages];
707
881
  const lastMsg = messages[messages.length - 1];
708
- if (lastMsg && lastMsg.id === state.messageId) {
882
+ if (lastMsg?.id === state.messageId) {
709
883
  messages[messages.length - 1] = msg;
710
884
  } else {
711
885
  messages.push(msg);
712
886
  }
713
887
  this.setMessages(messages);
714
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
+ }
715
1044
  };
716
1045
 
717
1046
  // src/stream/reader.ts
@@ -763,9 +1092,6 @@ async function* parseSSEStream(response, signal) {
763
1092
  }
764
1093
  }
765
1094
 
766
- // src/index.ts
767
- import { isOtherThread, isFileReference, isFileReferenceArray as isFileReferenceArray2 } from "@octavus/core";
768
-
769
1095
  // src/transports/types.ts
770
1096
  function isSocketTransport(transport) {
771
1097
  return "connect" in transport && "disconnect" in transport && "connectionState" in transport && "onConnectionStateChange" in transport;
@@ -775,32 +1101,45 @@ function isSocketTransport(transport) {
775
1101
  import { isAbortError as isAbortError2 } from "@octavus/core";
776
1102
  function createHttpTransport(options) {
777
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
+ }
778
1127
  return {
779
1128
  async *trigger(triggerName, input) {
780
1129
  abortController = new AbortController();
781
- try {
782
- const response = await options.triggerRequest(triggerName, input, {
783
- signal: abortController.signal
784
- });
785
- if (!response.ok) {
786
- const errorText = await response.text().catch(() => `Request failed: ${response.status}`);
787
- throw new Error(errorText);
788
- }
789
- if (!response.body) {
790
- throw new Error("Response body is empty");
791
- }
792
- for await (const event of parseSSEStream(response, abortController.signal)) {
793
- if (abortController.signal.aborted) {
794
- break;
795
- }
796
- yield event;
797
- }
798
- } catch (err) {
799
- if (isAbortError2(err)) {
800
- return;
801
- }
802
- throw err;
803
- }
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);
804
1143
  },
805
1144
  stop() {
806
1145
  abortController?.abort();
@@ -956,18 +1295,104 @@ function createSocketTransport(options) {
956
1295
  eventResolver(null);
957
1296
  eventResolver = null;
958
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
+ }
959
1321
  }
960
1322
  };
961
1323
  }
1324
+
1325
+ // src/index.ts
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";
962
1359
  export {
1360
+ AppError,
1361
+ ConflictError,
1362
+ ForbiddenError,
1363
+ MAIN_THREAD,
1364
+ NotFoundError,
1365
+ OCTAVUS_SKILL_TOOLS,
963
1366
  OctavusChat,
1367
+ OctavusError2 as OctavusError,
1368
+ ValidationError,
1369
+ createApiErrorEvent,
1370
+ createErrorEvent,
964
1371
  createHttpTransport,
1372
+ createInternalErrorEvent,
965
1373
  createSocketTransport,
1374
+ errorToStreamEvent,
1375
+ generateId2 as generateId,
1376
+ getSkillSlugFromToolCall,
1377
+ isAbortError3 as isAbortError,
1378
+ isAuthenticationError,
966
1379
  isFileReference,
967
1380
  isFileReferenceArray2 as isFileReferenceArray,
1381
+ isMainThread,
1382
+ isOctavusSkillTool,
968
1383
  isOtherThread,
1384
+ isProviderError,
1385
+ isRateLimitError,
1386
+ isRetryableError,
969
1387
  isSocketTransport,
1388
+ isToolError,
1389
+ isValidationError,
970
1390
  parseSSEStream,
1391
+ resolveThread,
1392
+ safeParseStreamEvent3 as safeParseStreamEvent,
1393
+ safeParseUIMessage,
1394
+ safeParseUIMessages,
1395
+ threadForPart2 as threadForPart,
971
1396
  uploadFiles
972
1397
  };
973
1398
  //# sourceMappingURL=index.js.map