@astralform/js 0.1.0 → 0.1.1

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
@@ -247,6 +247,11 @@ var AstralformClient = class {
247
247
  isEnabled: s.is_enabled
248
248
  }));
249
249
  }
250
+ async getConversationEvents(conversationId) {
251
+ return this.get(
252
+ `/v1/conversations/${encodeURIComponent(conversationId)}/events`
253
+ );
254
+ }
250
255
  async submitToolResult(request) {
251
256
  await this.post("/v1/tool-result", request);
252
257
  }
@@ -584,8 +589,7 @@ var ChatSession = class {
584
589
  };
585
590
  await this.processStream(request);
586
591
  }
587
- async processStream(request) {
588
- this.isStreaming = true;
592
+ resetStreamingState() {
589
593
  this.streamingContent = "";
590
594
  this.thinkingContent = "";
591
595
  this.isThinking = false;
@@ -594,6 +598,10 @@ var ChatSession = class {
594
598
  this.capsuleOutputs = [];
595
599
  this.todos = [];
596
600
  this.activeTools.clear();
601
+ }
602
+ async processStream(request) {
603
+ this.isStreaming = true;
604
+ this.resetStreamingState();
597
605
  this.abortController = new AbortController();
598
606
  try {
599
607
  await this.consumeJobStream(request);
@@ -653,39 +661,24 @@ var ChatSession = class {
653
661
  name: parsed.model_display_name
654
662
  });
655
663
  }
656
- if (parsed.agent_name) {
657
- this.emit({
658
- type: "agent_start",
659
- agentName: parsed.agent_name,
660
- agentDisplayName: parsed.agent_display_name
661
- });
662
- }
663
664
  break;
664
665
  case "content_block_delta":
665
666
  this.streamingContent += parsed.delta.text;
666
667
  this.emit({ type: "chunk", text: parsed.delta.text });
667
668
  break;
668
669
  case "tool_use_start": {
669
- const toolCall = {
670
- callId: parsed.call_id,
671
- toolName: parsed.tool,
672
- displayName: parsed.display_name,
673
- description: parsed.description,
674
- arguments: parsed.arguments,
675
- isClientTool: parsed.is_client_tool
676
- };
677
- this.activeTools.set(parsed.call_id, {
678
- toolName: parsed.tool,
679
- displayName: parsed.display_name,
680
- description: parsed.description,
681
- arguments: parsed.arguments,
682
- callId: parsed.call_id,
683
- status: parsed.is_client_tool ? "calling" : "executing",
684
- isClientTool: parsed.is_client_tool
685
- });
686
- this.emit({ type: "tool_call", request: toolCall });
670
+ this.applyEvent(parsed);
687
671
  if (parsed.is_client_tool) {
688
- const results = await this.executeClientTools([toolCall]);
672
+ const results = await this.executeClientTools([
673
+ {
674
+ callId: parsed.call_id,
675
+ toolName: parsed.tool,
676
+ displayName: parsed.display_name,
677
+ description: parsed.description,
678
+ arguments: parsed.arguments,
679
+ isClientTool: parsed.is_client_tool
680
+ }
681
+ ]);
689
682
  await this.client.submitToolResult({
690
683
  conversation_id: conversationId,
691
684
  message_id: messageId,
@@ -694,61 +687,6 @@ var ChatSession = class {
694
687
  }
695
688
  break;
696
689
  }
697
- case "tool_use_end": {
698
- const toolState = this.activeTools.get(parsed.call_id);
699
- if (toolState) {
700
- toolState.status = "completed";
701
- }
702
- this.emit({
703
- type: "tool_end",
704
- callId: parsed.call_id,
705
- toolName: parsed.tool
706
- });
707
- break;
708
- }
709
- case "agent_start":
710
- this.emit({
711
- type: "agent_start",
712
- agentName: parsed.agent_name,
713
- agentDisplayName: parsed.agent_display_name,
714
- avatarUrl: parsed.avatar_url
715
- });
716
- break;
717
- case "agent_end":
718
- this.emit({ type: "agent_end", agentName: parsed.agent_name });
719
- break;
720
- case "subagent_start":
721
- this.activeSubagents.set(parsed.tool_call_id, {
722
- agentName: parsed.agent_name,
723
- displayName: parsed.display_name,
724
- avatarUrl: parsed.avatar_url,
725
- description: parsed.description,
726
- content: "",
727
- isActive: true
728
- });
729
- this.emit({
730
- type: "subagent_start",
731
- agentName: parsed.agent_name,
732
- displayName: parsed.display_name,
733
- toolCallId: parsed.tool_call_id,
734
- avatarUrl: parsed.avatar_url,
735
- description: parsed.description
736
- });
737
- break;
738
- case "subagent_update": {
739
- const sub = this.activeSubagents.get(parsed.tool_call_id);
740
- if (sub) {
741
- sub.agentName = parsed.agent_name;
742
- sub.displayName = parsed.display_name;
743
- }
744
- this.emit({
745
- type: "subagent_update",
746
- agentName: parsed.agent_name,
747
- displayName: parsed.display_name,
748
- toolCallId: parsed.tool_call_id
749
- });
750
- break;
751
- }
752
690
  case "subagent_content_delta": {
753
691
  const subagent = this.activeSubagents.get(parsed.tool_call_id);
754
692
  if (subagent) {
@@ -762,19 +700,6 @@ var ChatSession = class {
762
700
  });
763
701
  break;
764
702
  }
765
- case "subagent_end": {
766
- const sub = this.activeSubagents.get(parsed.tool_call_id);
767
- if (sub) {
768
- sub.isActive = false;
769
- }
770
- this.emit({
771
- type: "subagent_end",
772
- agentName: parsed.agent_name,
773
- displayName: parsed.display_name,
774
- toolCallId: parsed.tool_call_id
775
- });
776
- break;
777
- }
778
703
  case "thinking_delta":
779
704
  this.thinkingContent += parsed.delta.text;
780
705
  this.isThinking = true;
@@ -784,45 +709,6 @@ var ChatSession = class {
784
709
  this.isThinking = false;
785
710
  this.emit({ type: "thinking_complete" });
786
711
  break;
787
- case "sources":
788
- this.sources.push(...parsed.sources);
789
- this.emit({ type: "sources", sources: parsed.sources });
790
- break;
791
- case "capsule_output": {
792
- const capsule = {
793
- toolName: parsed.tool_name,
794
- agentName: parsed.agent_name,
795
- command: parsed.command,
796
- output: parsed.output,
797
- durationMs: parsed.duration_ms
798
- };
799
- this.capsuleOutputs.push(capsule);
800
- this.emit({ type: "capsule_output", ...capsule });
801
- break;
802
- }
803
- case "todo_update":
804
- this.todos = parsed.todos;
805
- this.emit({ type: "todo_update", todos: parsed.todos });
806
- break;
807
- case "subagent_tool_use":
808
- this.emit({
809
- type: "subagent_tool_use",
810
- agentName: parsed.agent_name,
811
- toolName: parsed.tool,
812
- toolCallId: parsed.tool_call_id,
813
- result: parsed.result
814
- });
815
- break;
816
- case "asset_created":
817
- this.emit({
818
- type: "asset_created",
819
- assetId: parsed.asset_id,
820
- name: parsed.name,
821
- url: parsed.url,
822
- mediaType: parsed.media_type,
823
- sizeBytes: parsed.size_bytes
824
- });
825
- break;
826
712
  case "retry":
827
713
  this.emit({
828
714
  type: "retry",
@@ -840,11 +726,154 @@ var ChatSession = class {
840
726
  error: new AstralformError(parsed.message, parsed.code)
841
727
  });
842
728
  break;
729
+ default:
730
+ this.applyEvent(parsed);
843
731
  }
844
732
  }
845
733
  this.currentJobId = null;
846
734
  await this.completeStream(conversationId, messageId, stopTitle);
847
735
  }
736
+ /**
737
+ * Apply a single SSE event to session state and notify consumers.
738
+ * Shared between live streaming and historical event replay.
739
+ */
740
+ applyEvent(event) {
741
+ switch (event.type) {
742
+ case "tool_use_start": {
743
+ const request = {
744
+ callId: event.call_id,
745
+ toolName: event.tool,
746
+ displayName: event.display_name,
747
+ description: event.description,
748
+ arguments: event.arguments,
749
+ isClientTool: event.is_client_tool
750
+ };
751
+ this.activeTools.set(event.call_id, {
752
+ ...request,
753
+ status: event.is_client_tool ? "calling" : "executing"
754
+ });
755
+ this.emit({ type: "tool_call", request });
756
+ break;
757
+ }
758
+ case "tool_use_end": {
759
+ const toolState = this.activeTools.get(event.call_id);
760
+ if (toolState) {
761
+ toolState.status = "completed";
762
+ }
763
+ this.emit({
764
+ type: "tool_end",
765
+ callId: event.call_id,
766
+ toolName: event.tool,
767
+ result: event.result
768
+ });
769
+ break;
770
+ }
771
+ case "agent_start":
772
+ this.emit({
773
+ type: "agent_start",
774
+ agentName: event.agent_name,
775
+ agentDisplayName: event.agent_display_name,
776
+ avatarUrl: event.avatar_url
777
+ });
778
+ break;
779
+ case "agent_end":
780
+ this.emit({ type: "agent_end", agentName: event.agent_name });
781
+ break;
782
+ case "subagent_start":
783
+ this.activeSubagents.set(event.tool_call_id, {
784
+ agentName: event.agent_name,
785
+ displayName: event.display_name,
786
+ avatarUrl: event.avatar_url,
787
+ description: event.description,
788
+ content: "",
789
+ isActive: true
790
+ });
791
+ this.emit({
792
+ type: "subagent_start",
793
+ agentName: event.agent_name,
794
+ displayName: event.display_name,
795
+ toolCallId: event.tool_call_id,
796
+ avatarUrl: event.avatar_url,
797
+ description: event.description
798
+ });
799
+ break;
800
+ case "subagent_update": {
801
+ const sub = this.activeSubagents.get(event.tool_call_id);
802
+ if (sub) {
803
+ sub.agentName = event.agent_name;
804
+ sub.displayName = event.display_name;
805
+ }
806
+ this.emit({
807
+ type: "subagent_update",
808
+ agentName: event.agent_name,
809
+ displayName: event.display_name,
810
+ toolCallId: event.tool_call_id
811
+ });
812
+ break;
813
+ }
814
+ case "subagent_end": {
815
+ const sub = this.activeSubagents.get(event.tool_call_id);
816
+ if (sub) {
817
+ sub.isActive = false;
818
+ }
819
+ this.emit({
820
+ type: "subagent_end",
821
+ agentName: event.agent_name,
822
+ displayName: event.display_name,
823
+ toolCallId: event.tool_call_id
824
+ });
825
+ break;
826
+ }
827
+ case "subagent_tool_use":
828
+ this.emit({
829
+ type: "subagent_tool_use",
830
+ agentName: event.agent_name,
831
+ toolName: event.tool,
832
+ toolCallId: event.tool_call_id,
833
+ result: event.result
834
+ });
835
+ break;
836
+ case "sources":
837
+ this.sources.push(...event.sources);
838
+ this.emit({ type: "sources", sources: event.sources });
839
+ break;
840
+ case "capsule_output": {
841
+ const capsule = {
842
+ toolName: event.tool_name,
843
+ agentName: event.agent_name,
844
+ command: event.command,
845
+ output: event.output,
846
+ durationMs: event.duration_ms,
847
+ callId: event.call_id
848
+ };
849
+ this.capsuleOutputs.push(capsule);
850
+ this.emit({ type: "capsule_output", ...capsule });
851
+ break;
852
+ }
853
+ case "capsule_output_chunk":
854
+ this.emit({
855
+ type: "capsule_output_chunk",
856
+ callId: event.call_id,
857
+ stream: event.stream,
858
+ chunk: event.chunk
859
+ });
860
+ break;
861
+ case "todo_update":
862
+ this.todos = event.todos;
863
+ this.emit({ type: "todo_update", todos: event.todos });
864
+ break;
865
+ case "asset_created":
866
+ this.emit({
867
+ type: "asset_created",
868
+ assetId: event.asset_id,
869
+ name: event.name,
870
+ url: event.url,
871
+ mediaType: event.media_type,
872
+ sizeBytes: event.size_bytes
873
+ });
874
+ break;
875
+ }
876
+ }
848
877
  async executeClientTools(toolCalls) {
849
878
  const results = [];
850
879
  for (const call of toolCalls) {
@@ -921,12 +950,27 @@ var ChatSession = class {
921
950
  }
922
951
  async switchConversation(id) {
923
952
  this.conversationId = id;
924
- try {
925
- this.messages = await this.client.getMessages(id);
926
- } catch {
927
- this.messages = await this.storage.fetchMessages(id);
953
+ this.resetStreamingState();
954
+ const [messagesResult, eventsResult] = await Promise.allSettled([
955
+ this.client.getMessages(id).catch(() => this.storage.fetchMessages(id)),
956
+ this.client.getConversationEvents(id)
957
+ ]);
958
+ this.messages = messagesResult.status === "fulfilled" ? messagesResult.value : [];
959
+ if (eventsResult.status === "fulfilled") {
960
+ for (const ev of eventsResult.value) {
961
+ this.replayEvent(ev.event, ev.data);
962
+ }
928
963
  }
929
- this.streamingContent = "";
964
+ }
965
+ /**
966
+ * Replay a single persisted event to reconstruct session state.
967
+ * Skips text deltas (final content is already in messages[]).
968
+ */
969
+ replayEvent(eventType, data) {
970
+ if (eventType === "content_block_delta" || eventType === "thinking_delta" || eventType === "subagent_content_delta" || eventType === "thinking_complete") {
971
+ return;
972
+ }
973
+ this.applyEvent({ type: eventType, ...data });
930
974
  }
931
975
  async deleteConversation(id) {
932
976
  try {