@octavus/client-sdk 2.1.0 → 2.3.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
@@ -161,7 +161,8 @@ function createEmptyStreamingState() {
161
161
  currentTextPartIndex: null,
162
162
  currentReasoningPartIndex: null,
163
163
  currentObjectPartIndex: null,
164
- accumulatedJson: ""
164
+ accumulatedJson: "",
165
+ activeWorkers: /* @__PURE__ */ new Map()
165
166
  };
166
167
  }
167
168
  function buildMessageFromState(state, status) {
@@ -173,6 +174,36 @@ function buildMessageFromState(state, status) {
173
174
  createdAt: /* @__PURE__ */ new Date()
174
175
  };
175
176
  }
177
+ function finalizeParts(parts, workerError) {
178
+ return parts.map((part) => {
179
+ if (part.type === "text" || part.type === "reasoning") {
180
+ if (part.status === "streaming") {
181
+ return { ...part, status: "done" };
182
+ }
183
+ }
184
+ if (part.type === "object" && part.status === "streaming") {
185
+ return { ...part, status: "done" };
186
+ }
187
+ if (part.type === "tool-call") {
188
+ if (part.status === "pending" || part.status === "running") {
189
+ return { ...part, status: "cancelled" };
190
+ }
191
+ }
192
+ if (part.type === "operation" && part.status === "running") {
193
+ return { ...part, status: "cancelled" };
194
+ }
195
+ if (part.type === "worker" && part.status === "running") {
196
+ return {
197
+ ...part,
198
+ status: "error",
199
+ error: workerError,
200
+ parts: finalizeParts(part.parts)
201
+ // Recursive for nested parts
202
+ };
203
+ }
204
+ return part;
205
+ });
206
+ }
176
207
  var OctavusChat = class {
177
208
  // Private state
178
209
  _messages;
@@ -197,6 +228,9 @@ var OctavusChat = class {
197
228
  // Flag indicating automatic client tools have completed and are ready to continue
198
229
  // We wait for the finish event before actually continuing to avoid race conditions
199
230
  _readyToContinue = false;
231
+ // Flag indicating the finish event with client-tool-calls reason has been received
232
+ // Used to handle the race condition where finish arrives before async tools complete
233
+ _finishEventReceived = false;
200
234
  // Listener sets for reactive frameworks
201
235
  listeners = /* @__PURE__ */ new Set();
202
236
  constructor(options) {
@@ -350,6 +384,7 @@ var OctavusChat = class {
350
384
  this._serverToolResults = [];
351
385
  this._pendingExecutionId = null;
352
386
  this._readyToContinue = false;
387
+ this._finishEventReceived = false;
353
388
  this.updatePendingClientToolsCache();
354
389
  try {
355
390
  for await (const event of this.transport.trigger(triggerName, processedInput)) {
@@ -369,25 +404,7 @@ var OctavusChat = class {
369
404
  const messages = [...this._messages];
370
405
  const lastMsg = messages[messages.length - 1];
371
406
  if (state.parts.length > 0) {
372
- const finalParts = state.parts.map((part) => {
373
- if (part.type === "text" || part.type === "reasoning") {
374
- if (part.status === "streaming") {
375
- return { ...part, status: "done" };
376
- }
377
- }
378
- if (part.type === "object" && part.status === "streaming") {
379
- return { ...part, status: "done" };
380
- }
381
- if (part.type === "tool-call") {
382
- if (part.status === "pending" || part.status === "running") {
383
- return { ...part, status: "cancelled" };
384
- }
385
- }
386
- if (part.type === "operation" && part.status === "running") {
387
- return { ...part, status: "cancelled" };
388
- }
389
- return part;
390
- });
407
+ const finalParts = finalizeParts(state.parts, "Stream error");
391
408
  const finalMessage = {
392
409
  id: state.messageId,
393
410
  role: "assistant",
@@ -464,7 +481,9 @@ var OctavusChat = class {
464
481
  result: error ? void 0 : result,
465
482
  error,
466
483
  outputVariable: pendingTool.outputVariable,
467
- blockIndex: pendingTool.blockIndex
484
+ blockIndex: pendingTool.blockIndex,
485
+ thread: pendingTool.thread,
486
+ workerId: pendingTool.workerId
468
487
  };
469
488
  this._completedToolResults.push(toolResult);
470
489
  if (error) {
@@ -490,37 +509,12 @@ var OctavusChat = class {
490
509
  this._serverToolResults = [];
491
510
  this._pendingExecutionId = null;
492
511
  this._readyToContinue = false;
512
+ this._finishEventReceived = false;
493
513
  this.updatePendingClientToolsCache();
494
514
  this.transport.stop();
495
515
  const state = this.streamingState;
496
516
  if (state && state.parts.length > 0) {
497
- const finalParts = state.parts.map((part) => {
498
- if (part.type === "tool-call") {
499
- const toolPart = part;
500
- if (toolPart.status === "pending" || toolPart.status === "running") {
501
- return { ...toolPart, status: "cancelled" };
502
- }
503
- }
504
- if (part.type === "operation") {
505
- const opPart = part;
506
- if (opPart.status === "running") {
507
- return { ...opPart, status: "cancelled" };
508
- }
509
- }
510
- if (part.type === "text" || part.type === "reasoning") {
511
- const textPart = part;
512
- if (textPart.status === "streaming") {
513
- return { ...textPart, status: "done" };
514
- }
515
- }
516
- if (part.type === "object") {
517
- const objPart = part;
518
- if (objPart.status === "streaming") {
519
- return { ...objPart, status: "done" };
520
- }
521
- }
522
- return part;
523
- });
517
+ const finalParts = finalizeParts(state.parts, "Stopped by user");
524
518
  const finalMessage = {
525
519
  id: state.messageId,
526
520
  role: "assistant",
@@ -547,8 +541,13 @@ var OctavusChat = class {
547
541
  handleStreamEvent(event, state) {
548
542
  switch (event.type) {
549
543
  case "start":
544
+ if (event.executionId) {
545
+ this.options.onStart?.(event.executionId);
546
+ }
550
547
  break;
551
548
  case "block-start": {
549
+ const workerId = event.workerId;
550
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
552
551
  const block = {
553
552
  blockId: event.blockId,
554
553
  blockName: event.blockName,
@@ -575,7 +574,13 @@ var OctavusChat = class {
575
574
  status: "running",
576
575
  thread: threadForPart(thread)
577
576
  };
578
- state.parts.push(operationPart);
577
+ if (workerState) {
578
+ const workerPart = state.parts[workerState.partIndex];
579
+ workerPart.parts.push(operationPart);
580
+ state.parts[workerState.partIndex] = { ...workerPart };
581
+ } else {
582
+ state.parts.push(operationPart);
583
+ }
579
584
  }
580
585
  state.currentTextPartIndex = null;
581
586
  state.currentReasoningPartIndex = null;
@@ -583,12 +588,26 @@ var OctavusChat = class {
583
588
  break;
584
589
  }
585
590
  case "block-end": {
586
- const operationPartIndex = state.parts.findIndex(
587
- (p) => p.type === "operation" && p.operationId === event.blockId
588
- );
589
- if (operationPartIndex >= 0) {
590
- const part = state.parts[operationPartIndex];
591
- state.parts[operationPartIndex] = { ...part, status: "done" };
591
+ const workerId = event.workerId;
592
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
593
+ if (workerState) {
594
+ const workerPart = state.parts[workerState.partIndex];
595
+ const operationPartIndex = workerPart.parts.findIndex(
596
+ (p) => p.type === "operation" && p.operationId === event.blockId
597
+ );
598
+ if (operationPartIndex >= 0) {
599
+ const part = workerPart.parts[operationPartIndex];
600
+ workerPart.parts[operationPartIndex] = { ...part, status: "done" };
601
+ state.parts[workerState.partIndex] = { ...workerPart };
602
+ }
603
+ } else {
604
+ const operationPartIndex = state.parts.findIndex(
605
+ (p) => p.type === "operation" && p.operationId === event.blockId
606
+ );
607
+ if (operationPartIndex >= 0) {
608
+ const part = state.parts[operationPartIndex];
609
+ state.parts[operationPartIndex] = { ...part, status: "done" };
610
+ }
592
611
  }
593
612
  if (state.activeBlock?.blockId === event.blockId) {
594
613
  state.activeBlock = null;
@@ -597,31 +616,63 @@ var OctavusChat = class {
597
616
  break;
598
617
  }
599
618
  case "reasoning-start": {
619
+ const workerId = event.workerId;
620
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
600
621
  const reasoningPart = {
601
622
  type: "reasoning",
602
623
  text: "",
603
624
  status: "streaming",
604
625
  thread: threadForPart(state.activeBlock?.thread)
605
626
  };
606
- state.parts.push(reasoningPart);
607
- state.currentReasoningPartIndex = state.parts.length - 1;
627
+ if (workerState) {
628
+ const workerPart = state.parts[workerState.partIndex];
629
+ workerPart.parts.push(reasoningPart);
630
+ workerState.currentReasoningPartIndex = workerPart.parts.length - 1;
631
+ state.parts[workerState.partIndex] = { ...workerPart };
632
+ } else {
633
+ state.parts.push(reasoningPart);
634
+ state.currentReasoningPartIndex = state.parts.length - 1;
635
+ }
608
636
  this.updateStreamingMessage();
609
637
  break;
610
638
  }
611
639
  case "reasoning-delta": {
612
- if (state.currentReasoningPartIndex !== null) {
613
- const part = state.parts[state.currentReasoningPartIndex];
614
- part.text += event.delta;
615
- state.parts[state.currentReasoningPartIndex] = { ...part };
616
- }
617
- if (state.activeBlock) {
618
- state.activeBlock.reasoning += event.delta;
640
+ const workerId = event.workerId;
641
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
642
+ if (workerState) {
643
+ if (workerState.currentReasoningPartIndex !== null) {
644
+ const workerPart = state.parts[workerState.partIndex];
645
+ const part = workerPart.parts[workerState.currentReasoningPartIndex];
646
+ part.text += event.delta;
647
+ workerPart.parts[workerState.currentReasoningPartIndex] = { ...part };
648
+ state.parts[workerState.partIndex] = { ...workerPart };
649
+ }
650
+ } else {
651
+ if (state.currentReasoningPartIndex !== null) {
652
+ const part = state.parts[state.currentReasoningPartIndex];
653
+ part.text += event.delta;
654
+ state.parts[state.currentReasoningPartIndex] = { ...part };
655
+ }
656
+ if (state.activeBlock) {
657
+ state.activeBlock.reasoning += event.delta;
658
+ }
619
659
  }
620
660
  this.updateStreamingMessage();
621
661
  break;
622
662
  }
623
663
  case "reasoning-end": {
624
- if (state.currentReasoningPartIndex !== null) {
664
+ const workerId = event.workerId;
665
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
666
+ if (workerState) {
667
+ if (workerState.currentReasoningPartIndex !== null) {
668
+ const workerPart = state.parts[workerState.partIndex];
669
+ const part = workerPart.parts[workerState.currentReasoningPartIndex];
670
+ part.status = "done";
671
+ workerPart.parts[workerState.currentReasoningPartIndex] = { ...part };
672
+ state.parts[workerState.partIndex] = { ...workerPart };
673
+ workerState.currentReasoningPartIndex = null;
674
+ }
675
+ } else if (state.currentReasoningPartIndex !== null) {
625
676
  const part = state.parts[state.currentReasoningPartIndex];
626
677
  part.status = "done";
627
678
  state.parts[state.currentReasoningPartIndex] = { ...part };
@@ -631,9 +682,11 @@ var OctavusChat = class {
631
682
  break;
632
683
  }
633
684
  case "text-start": {
685
+ const workerId = event.workerId;
686
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
634
687
  const thread = threadForPart(state.activeBlock?.thread);
635
688
  const shouldAddPart = state.activeBlock?.outputToChat !== false || thread !== void 0;
636
- if (shouldAddPart) {
689
+ if (workerState || shouldAddPart) {
637
690
  if (event.responseType) {
638
691
  const objectPart = {
639
692
  type: "object",
@@ -644,10 +697,19 @@ var OctavusChat = class {
644
697
  status: "streaming",
645
698
  thread
646
699
  };
647
- state.parts.push(objectPart);
648
- state.currentObjectPartIndex = state.parts.length - 1;
649
- state.accumulatedJson = "";
650
- state.currentTextPartIndex = null;
700
+ if (workerState) {
701
+ const workerPart = state.parts[workerState.partIndex];
702
+ workerPart.parts.push(objectPart);
703
+ workerState.currentObjectPartIndex = workerPart.parts.length - 1;
704
+ workerState.accumulatedJson = "";
705
+ workerState.currentTextPartIndex = null;
706
+ state.parts[workerState.partIndex] = { ...workerPart };
707
+ } else {
708
+ state.parts.push(objectPart);
709
+ state.currentObjectPartIndex = state.parts.length - 1;
710
+ state.accumulatedJson = "";
711
+ state.currentTextPartIndex = null;
712
+ }
651
713
  } else {
652
714
  const textPart = {
653
715
  type: "text",
@@ -655,36 +717,89 @@ var OctavusChat = class {
655
717
  status: "streaming",
656
718
  thread
657
719
  };
658
- state.parts.push(textPart);
659
- state.currentTextPartIndex = state.parts.length - 1;
660
- state.currentObjectPartIndex = null;
720
+ if (workerState) {
721
+ const workerPart = state.parts[workerState.partIndex];
722
+ workerPart.parts.push(textPart);
723
+ workerState.currentTextPartIndex = workerPart.parts.length - 1;
724
+ workerState.currentObjectPartIndex = null;
725
+ state.parts[workerState.partIndex] = { ...workerPart };
726
+ } else {
727
+ state.parts.push(textPart);
728
+ state.currentTextPartIndex = state.parts.length - 1;
729
+ state.currentObjectPartIndex = null;
730
+ }
661
731
  }
662
732
  }
663
733
  this.updateStreamingMessage();
664
734
  break;
665
735
  }
666
736
  case "text-delta": {
667
- if (state.currentObjectPartIndex !== null) {
668
- state.accumulatedJson += event.delta;
669
- const part = state.parts[state.currentObjectPartIndex];
670
- const parsed = parsePartialJson(state.accumulatedJson);
671
- if (parsed !== void 0) {
672
- part.partial = parsed;
673
- state.parts[state.currentObjectPartIndex] = { ...part };
737
+ const workerId = event.workerId;
738
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
739
+ if (workerState) {
740
+ const workerPart = state.parts[workerState.partIndex];
741
+ if (workerState.currentObjectPartIndex !== null) {
742
+ workerState.accumulatedJson += event.delta;
743
+ const part = workerPart.parts[workerState.currentObjectPartIndex];
744
+ const parsed = parsePartialJson(workerState.accumulatedJson);
745
+ if (parsed !== void 0) {
746
+ part.partial = parsed;
747
+ workerPart.parts[workerState.currentObjectPartIndex] = { ...part };
748
+ }
749
+ } else if (workerState.currentTextPartIndex !== null) {
750
+ const part = workerPart.parts[workerState.currentTextPartIndex];
751
+ part.text += event.delta;
752
+ workerPart.parts[workerState.currentTextPartIndex] = { ...part };
753
+ }
754
+ state.parts[workerState.partIndex] = { ...workerPart };
755
+ } else {
756
+ if (state.currentObjectPartIndex !== null) {
757
+ state.accumulatedJson += event.delta;
758
+ const part = state.parts[state.currentObjectPartIndex];
759
+ const parsed = parsePartialJson(state.accumulatedJson);
760
+ if (parsed !== void 0) {
761
+ part.partial = parsed;
762
+ state.parts[state.currentObjectPartIndex] = { ...part };
763
+ }
764
+ } else if (state.currentTextPartIndex !== null) {
765
+ const part = state.parts[state.currentTextPartIndex];
766
+ part.text += event.delta;
767
+ state.parts[state.currentTextPartIndex] = { ...part };
768
+ }
769
+ if (state.activeBlock) {
770
+ state.activeBlock.text += event.delta;
674
771
  }
675
- } else if (state.currentTextPartIndex !== null) {
676
- const part = state.parts[state.currentTextPartIndex];
677
- part.text += event.delta;
678
- state.parts[state.currentTextPartIndex] = { ...part };
679
- }
680
- if (state.activeBlock) {
681
- state.activeBlock.text += event.delta;
682
772
  }
683
773
  this.updateStreamingMessage();
684
774
  break;
685
775
  }
686
776
  case "text-end": {
687
- if (state.currentObjectPartIndex !== null) {
777
+ const workerId = event.workerId;
778
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
779
+ if (workerState) {
780
+ const workerPart = state.parts[workerState.partIndex];
781
+ if (workerState.currentObjectPartIndex !== null) {
782
+ const part = workerPart.parts[workerState.currentObjectPartIndex];
783
+ try {
784
+ const finalObject = JSON.parse(workerState.accumulatedJson);
785
+ part.object = finalObject;
786
+ part.partial = finalObject;
787
+ part.status = "done";
788
+ } catch {
789
+ part.status = "error";
790
+ part.error = "Failed to parse response as JSON";
791
+ }
792
+ workerPart.parts[workerState.currentObjectPartIndex] = { ...part };
793
+ workerState.currentObjectPartIndex = null;
794
+ workerState.accumulatedJson = "";
795
+ } else if (workerState.currentTextPartIndex !== null) {
796
+ const part = workerPart.parts[workerState.currentTextPartIndex];
797
+ part.status = "done";
798
+ workerPart.parts[workerState.currentTextPartIndex] = { ...part };
799
+ workerState.currentTextPartIndex = null;
800
+ }
801
+ state.parts[workerState.partIndex] = { ...workerPart };
802
+ } else if (state.currentObjectPartIndex !== null) {
688
803
  const part = state.parts[state.currentObjectPartIndex];
689
804
  try {
690
805
  const finalObject = JSON.parse(state.accumulatedJson);
@@ -708,6 +823,8 @@ var OctavusChat = class {
708
823
  break;
709
824
  }
710
825
  case "tool-input-start": {
826
+ const workerId = event.workerId;
827
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
711
828
  const toolPart = {
712
829
  type: "tool-call",
713
830
  toolCallId: event.toolCallId,
@@ -717,24 +834,49 @@ var OctavusChat = class {
717
834
  status: "pending",
718
835
  thread: threadForPart(state.activeBlock?.thread)
719
836
  };
720
- state.parts.push(toolPart);
721
- if (state.activeBlock) {
722
- state.activeBlock.toolCalls.set(event.toolCallId, toolPart);
837
+ if (workerState) {
838
+ const workerPart = state.parts[workerState.partIndex];
839
+ workerPart.parts.push(toolPart);
840
+ state.parts[workerState.partIndex] = { ...workerPart };
841
+ } else {
842
+ state.parts.push(toolPart);
843
+ if (state.activeBlock) {
844
+ state.activeBlock.toolCalls.set(event.toolCallId, toolPart);
845
+ }
723
846
  }
724
847
  this.updateStreamingMessage();
725
848
  break;
726
849
  }
727
850
  case "tool-input-delta": {
728
- const toolPartIndex = state.parts.findIndex(
729
- (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
730
- );
731
- if (toolPartIndex >= 0) {
732
- try {
733
- const part = state.parts[toolPartIndex];
734
- part.args = JSON.parse(event.inputTextDelta);
735
- state.parts[toolPartIndex] = { ...part };
736
- this.updateStreamingMessage();
737
- } catch {
851
+ const workerId = event.workerId;
852
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
853
+ if (workerState) {
854
+ const workerPart = state.parts[workerState.partIndex];
855
+ const toolPartIndex = workerPart.parts.findIndex(
856
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
857
+ );
858
+ if (toolPartIndex >= 0) {
859
+ try {
860
+ const part = workerPart.parts[toolPartIndex];
861
+ part.args = JSON.parse(event.inputTextDelta);
862
+ workerPart.parts[toolPartIndex] = { ...part };
863
+ state.parts[workerState.partIndex] = { ...workerPart };
864
+ this.updateStreamingMessage();
865
+ } catch {
866
+ }
867
+ }
868
+ } else {
869
+ const toolPartIndex = state.parts.findIndex(
870
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
871
+ );
872
+ if (toolPartIndex >= 0) {
873
+ try {
874
+ const part = state.parts[toolPartIndex];
875
+ part.args = JSON.parse(event.inputTextDelta);
876
+ state.parts[toolPartIndex] = { ...part };
877
+ this.updateStreamingMessage();
878
+ } catch {
879
+ }
738
880
  }
739
881
  }
740
882
  break;
@@ -742,45 +884,98 @@ var OctavusChat = class {
742
884
  case "tool-input-end":
743
885
  break;
744
886
  case "tool-input-available": {
745
- const toolPartIndex = state.parts.findIndex(
746
- (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
747
- );
748
- if (toolPartIndex >= 0) {
749
- const part = state.parts[toolPartIndex];
750
- part.args = event.input;
751
- part.status = "running";
752
- state.parts[toolPartIndex] = { ...part };
753
- this.updateStreamingMessage();
887
+ const workerId = event.workerId;
888
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
889
+ if (workerState) {
890
+ const workerPart = state.parts[workerState.partIndex];
891
+ const toolPartIndex = workerPart.parts.findIndex(
892
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
893
+ );
894
+ if (toolPartIndex >= 0) {
895
+ const part = workerPart.parts[toolPartIndex];
896
+ part.args = event.input;
897
+ part.status = "running";
898
+ workerPart.parts[toolPartIndex] = { ...part };
899
+ state.parts[workerState.partIndex] = { ...workerPart };
900
+ this.updateStreamingMessage();
901
+ }
902
+ } else {
903
+ const toolPartIndex = state.parts.findIndex(
904
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
905
+ );
906
+ if (toolPartIndex >= 0) {
907
+ const part = state.parts[toolPartIndex];
908
+ part.args = event.input;
909
+ part.status = "running";
910
+ state.parts[toolPartIndex] = { ...part };
911
+ this.updateStreamingMessage();
912
+ }
754
913
  }
755
914
  break;
756
915
  }
757
916
  case "tool-output-available": {
758
- const toolPartIndex = state.parts.findIndex(
759
- (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
760
- );
761
- if (toolPartIndex >= 0) {
762
- const part = state.parts[toolPartIndex];
763
- part.result = event.output;
764
- part.status = "done";
765
- state.parts[toolPartIndex] = { ...part };
766
- this.updateStreamingMessage();
917
+ const workerId = event.workerId;
918
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
919
+ if (workerState) {
920
+ const workerPart = state.parts[workerState.partIndex];
921
+ const toolPartIndex = workerPart.parts.findIndex(
922
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
923
+ );
924
+ if (toolPartIndex >= 0) {
925
+ const part = workerPart.parts[toolPartIndex];
926
+ part.result = event.output;
927
+ part.status = "done";
928
+ workerPart.parts[toolPartIndex] = { ...part };
929
+ state.parts[workerState.partIndex] = { ...workerPart };
930
+ this.updateStreamingMessage();
931
+ }
932
+ } else {
933
+ const toolPartIndex = state.parts.findIndex(
934
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
935
+ );
936
+ if (toolPartIndex >= 0) {
937
+ const part = state.parts[toolPartIndex];
938
+ part.result = event.output;
939
+ part.status = "done";
940
+ state.parts[toolPartIndex] = { ...part };
941
+ this.updateStreamingMessage();
942
+ }
767
943
  }
768
944
  break;
769
945
  }
770
946
  case "tool-output-error": {
771
- const toolPartIndex = state.parts.findIndex(
772
- (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
773
- );
774
- if (toolPartIndex >= 0) {
775
- const part = state.parts[toolPartIndex];
776
- part.error = event.error;
777
- part.status = "error";
778
- state.parts[toolPartIndex] = { ...part };
779
- this.updateStreamingMessage();
947
+ const workerId = event.workerId;
948
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
949
+ if (workerState) {
950
+ const workerPart = state.parts[workerState.partIndex];
951
+ const toolPartIndex = workerPart.parts.findIndex(
952
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
953
+ );
954
+ if (toolPartIndex >= 0) {
955
+ const part = workerPart.parts[toolPartIndex];
956
+ part.error = event.error;
957
+ part.status = "error";
958
+ workerPart.parts[toolPartIndex] = { ...part };
959
+ state.parts[workerState.partIndex] = { ...workerPart };
960
+ this.updateStreamingMessage();
961
+ }
962
+ } else {
963
+ const toolPartIndex = state.parts.findIndex(
964
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
965
+ );
966
+ if (toolPartIndex >= 0) {
967
+ const part = state.parts[toolPartIndex];
968
+ part.error = event.error;
969
+ part.status = "error";
970
+ state.parts[toolPartIndex] = { ...part };
971
+ this.updateStreamingMessage();
972
+ }
780
973
  }
781
974
  break;
782
975
  }
783
976
  case "source": {
977
+ const workerId = event.workerId;
978
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
784
979
  const thread = threadForPart(state.activeBlock?.thread);
785
980
  let sourcePart;
786
981
  if (event.sourceType === "url") {
@@ -803,11 +998,19 @@ var OctavusChat = class {
803
998
  thread
804
999
  };
805
1000
  }
806
- state.parts.push(sourcePart);
1001
+ if (workerState) {
1002
+ const workerPart = state.parts[workerState.partIndex];
1003
+ workerPart.parts.push(sourcePart);
1004
+ state.parts[workerState.partIndex] = { ...workerPart };
1005
+ } else {
1006
+ state.parts.push(sourcePart);
1007
+ }
807
1008
  this.updateStreamingMessage();
808
1009
  break;
809
1010
  }
810
1011
  case "file-available": {
1012
+ const workerId = event.workerId;
1013
+ const workerState = workerId ? state.activeWorkers.get(workerId) : void 0;
811
1014
  const filePart = {
812
1015
  type: "file",
813
1016
  id: event.id,
@@ -818,19 +1021,83 @@ var OctavusChat = class {
818
1021
  toolCallId: event.toolCallId,
819
1022
  thread: threadForPart(state.activeBlock?.thread)
820
1023
  };
821
- state.parts.push(filePart);
1024
+ if (workerState) {
1025
+ const workerPart = state.parts[workerState.partIndex];
1026
+ workerPart.parts.push(filePart);
1027
+ state.parts[workerState.partIndex] = { ...workerPart };
1028
+ } else {
1029
+ state.parts.push(filePart);
1030
+ }
822
1031
  this.updateStreamingMessage();
823
1032
  break;
824
1033
  }
825
1034
  case "resource-update":
826
1035
  this.options.onResourceUpdate?.(event.name, event.value);
827
1036
  break;
1037
+ case "worker-start": {
1038
+ const existingIndex = state.parts.findIndex(
1039
+ (p) => p.type === "worker" && p.workerId === event.workerId
1040
+ );
1041
+ let partIndex;
1042
+ if (existingIndex !== -1) {
1043
+ const existingPart = state.parts[existingIndex];
1044
+ existingPart.status = "running";
1045
+ partIndex = existingIndex;
1046
+ } else {
1047
+ const workerPart = {
1048
+ type: "worker",
1049
+ workerId: event.workerId,
1050
+ workerSlug: event.workerSlug,
1051
+ description: event.description,
1052
+ parts: [],
1053
+ status: "running"
1054
+ };
1055
+ state.parts.push(workerPart);
1056
+ partIndex = state.parts.length - 1;
1057
+ }
1058
+ const workerState = {
1059
+ partIndex,
1060
+ currentTextPartIndex: null,
1061
+ currentReasoningPartIndex: null,
1062
+ currentObjectPartIndex: null,
1063
+ accumulatedJson: ""
1064
+ };
1065
+ state.activeWorkers.set(event.workerId, workerState);
1066
+ this.updateStreamingMessage();
1067
+ break;
1068
+ }
1069
+ case "worker-result": {
1070
+ const workerState = state.activeWorkers.get(event.workerId);
1071
+ if (workerState !== void 0) {
1072
+ const part = state.parts[workerState.partIndex];
1073
+ part.output = event.output;
1074
+ part.error = event.error;
1075
+ part.status = event.error ? "error" : "done";
1076
+ part.parts = part.parts.map((p) => {
1077
+ if (p.type === "text" || p.type === "reasoning") {
1078
+ if (p.status === "streaming") {
1079
+ return { ...p, status: "done" };
1080
+ }
1081
+ }
1082
+ if (p.type === "object" && p.status === "streaming") {
1083
+ return { ...p, status: "done" };
1084
+ }
1085
+ return p;
1086
+ });
1087
+ state.parts[workerState.partIndex] = { ...part };
1088
+ state.activeWorkers.delete(event.workerId);
1089
+ }
1090
+ this.updateStreamingMessage();
1091
+ break;
1092
+ }
828
1093
  case "finish": {
829
1094
  if (event.finishReason === "client-tool-calls") {
1095
+ this._finishEventReceived = true;
830
1096
  if (this._pendingToolsByCallId.size > 0) {
831
1097
  this.setStatus("awaiting-input");
832
1098
  } else if (this._readyToContinue) {
833
1099
  this._readyToContinue = false;
1100
+ this._finishEventReceived = false;
834
1101
  void this.continueWithClientToolResults();
835
1102
  }
836
1103
  return;
@@ -987,7 +1254,9 @@ var OctavusChat = class {
987
1254
  args: tc.args,
988
1255
  source: tc.source,
989
1256
  outputVariable: tc.outputVariable,
990
- blockIndex: tc.blockIndex
1257
+ blockIndex: tc.blockIndex,
1258
+ thread: tc.thread,
1259
+ workerId: tc.workerId
991
1260
  };
992
1261
  this._pendingToolsByCallId.set(tc.toolCallId, toolState);
993
1262
  const existing = this._pendingToolsByName.get(tc.toolName) ?? [];
@@ -1019,7 +1288,9 @@ var OctavusChat = class {
1019
1288
  toolName: tc.toolName,
1020
1289
  result,
1021
1290
  outputVariable: tc.outputVariable,
1022
- blockIndex: tc.blockIndex
1291
+ blockIndex: tc.blockIndex,
1292
+ thread: tc.thread,
1293
+ workerId: tc.workerId
1023
1294
  });
1024
1295
  this.emitToolOutputAvailable(tc.toolCallId, result);
1025
1296
  } catch (err) {
@@ -1029,7 +1300,9 @@ var OctavusChat = class {
1029
1300
  toolName: tc.toolName,
1030
1301
  error: errorMessage,
1031
1302
  outputVariable: tc.outputVariable,
1032
- blockIndex: tc.blockIndex
1303
+ blockIndex: tc.blockIndex,
1304
+ thread: tc.thread,
1305
+ workerId: tc.workerId
1033
1306
  });
1034
1307
  this.emitToolOutputError(tc.toolCallId, errorMessage);
1035
1308
  }
@@ -1040,13 +1313,20 @@ var OctavusChat = class {
1040
1313
  toolName: tc.toolName,
1041
1314
  error: errorMessage,
1042
1315
  outputVariable: tc.outputVariable,
1043
- blockIndex: tc.blockIndex
1316
+ blockIndex: tc.blockIndex,
1317
+ thread: tc.thread,
1318
+ workerId: tc.workerId
1044
1319
  });
1045
1320
  this.emitToolOutputError(tc.toolCallId, errorMessage);
1046
1321
  }
1047
1322
  }
1048
1323
  if (this._pendingToolsByCallId.size === 0 && this._completedToolResults.length > 0) {
1049
1324
  this._readyToContinue = true;
1325
+ if (this._finishEventReceived) {
1326
+ this._readyToContinue = false;
1327
+ this._finishEventReceived = false;
1328
+ void this.continueWithClientToolResults();
1329
+ }
1050
1330
  }
1051
1331
  }
1052
1332
  };