@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.cjs CHANGED
@@ -285,6 +285,11 @@ var AstralformClient = class {
285
285
  isEnabled: s.is_enabled
286
286
  }));
287
287
  }
288
+ async getConversationEvents(conversationId) {
289
+ return this.get(
290
+ `/v1/conversations/${encodeURIComponent(conversationId)}/events`
291
+ );
292
+ }
288
293
  async submitToolResult(request) {
289
294
  await this.post("/v1/tool-result", request);
290
295
  }
@@ -622,8 +627,7 @@ var ChatSession = class {
622
627
  };
623
628
  await this.processStream(request);
624
629
  }
625
- async processStream(request) {
626
- this.isStreaming = true;
630
+ resetStreamingState() {
627
631
  this.streamingContent = "";
628
632
  this.thinkingContent = "";
629
633
  this.isThinking = false;
@@ -632,6 +636,10 @@ var ChatSession = class {
632
636
  this.capsuleOutputs = [];
633
637
  this.todos = [];
634
638
  this.activeTools.clear();
639
+ }
640
+ async processStream(request) {
641
+ this.isStreaming = true;
642
+ this.resetStreamingState();
635
643
  this.abortController = new AbortController();
636
644
  try {
637
645
  await this.consumeJobStream(request);
@@ -691,39 +699,24 @@ var ChatSession = class {
691
699
  name: parsed.model_display_name
692
700
  });
693
701
  }
694
- if (parsed.agent_name) {
695
- this.emit({
696
- type: "agent_start",
697
- agentName: parsed.agent_name,
698
- agentDisplayName: parsed.agent_display_name
699
- });
700
- }
701
702
  break;
702
703
  case "content_block_delta":
703
704
  this.streamingContent += parsed.delta.text;
704
705
  this.emit({ type: "chunk", text: parsed.delta.text });
705
706
  break;
706
707
  case "tool_use_start": {
707
- const toolCall = {
708
- callId: parsed.call_id,
709
- toolName: parsed.tool,
710
- displayName: parsed.display_name,
711
- description: parsed.description,
712
- arguments: parsed.arguments,
713
- isClientTool: parsed.is_client_tool
714
- };
715
- this.activeTools.set(parsed.call_id, {
716
- toolName: parsed.tool,
717
- displayName: parsed.display_name,
718
- description: parsed.description,
719
- arguments: parsed.arguments,
720
- callId: parsed.call_id,
721
- status: parsed.is_client_tool ? "calling" : "executing",
722
- isClientTool: parsed.is_client_tool
723
- });
724
- this.emit({ type: "tool_call", request: toolCall });
708
+ this.applyEvent(parsed);
725
709
  if (parsed.is_client_tool) {
726
- const results = await this.executeClientTools([toolCall]);
710
+ const results = await this.executeClientTools([
711
+ {
712
+ callId: parsed.call_id,
713
+ toolName: parsed.tool,
714
+ displayName: parsed.display_name,
715
+ description: parsed.description,
716
+ arguments: parsed.arguments,
717
+ isClientTool: parsed.is_client_tool
718
+ }
719
+ ]);
727
720
  await this.client.submitToolResult({
728
721
  conversation_id: conversationId,
729
722
  message_id: messageId,
@@ -732,61 +725,6 @@ var ChatSession = class {
732
725
  }
733
726
  break;
734
727
  }
735
- case "tool_use_end": {
736
- const toolState = this.activeTools.get(parsed.call_id);
737
- if (toolState) {
738
- toolState.status = "completed";
739
- }
740
- this.emit({
741
- type: "tool_end",
742
- callId: parsed.call_id,
743
- toolName: parsed.tool
744
- });
745
- break;
746
- }
747
- case "agent_start":
748
- this.emit({
749
- type: "agent_start",
750
- agentName: parsed.agent_name,
751
- agentDisplayName: parsed.agent_display_name,
752
- avatarUrl: parsed.avatar_url
753
- });
754
- break;
755
- case "agent_end":
756
- this.emit({ type: "agent_end", agentName: parsed.agent_name });
757
- break;
758
- case "subagent_start":
759
- this.activeSubagents.set(parsed.tool_call_id, {
760
- agentName: parsed.agent_name,
761
- displayName: parsed.display_name,
762
- avatarUrl: parsed.avatar_url,
763
- description: parsed.description,
764
- content: "",
765
- isActive: true
766
- });
767
- this.emit({
768
- type: "subagent_start",
769
- agentName: parsed.agent_name,
770
- displayName: parsed.display_name,
771
- toolCallId: parsed.tool_call_id,
772
- avatarUrl: parsed.avatar_url,
773
- description: parsed.description
774
- });
775
- break;
776
- case "subagent_update": {
777
- const sub = this.activeSubagents.get(parsed.tool_call_id);
778
- if (sub) {
779
- sub.agentName = parsed.agent_name;
780
- sub.displayName = parsed.display_name;
781
- }
782
- this.emit({
783
- type: "subagent_update",
784
- agentName: parsed.agent_name,
785
- displayName: parsed.display_name,
786
- toolCallId: parsed.tool_call_id
787
- });
788
- break;
789
- }
790
728
  case "subagent_content_delta": {
791
729
  const subagent = this.activeSubagents.get(parsed.tool_call_id);
792
730
  if (subagent) {
@@ -800,19 +738,6 @@ var ChatSession = class {
800
738
  });
801
739
  break;
802
740
  }
803
- case "subagent_end": {
804
- const sub = this.activeSubagents.get(parsed.tool_call_id);
805
- if (sub) {
806
- sub.isActive = false;
807
- }
808
- this.emit({
809
- type: "subagent_end",
810
- agentName: parsed.agent_name,
811
- displayName: parsed.display_name,
812
- toolCallId: parsed.tool_call_id
813
- });
814
- break;
815
- }
816
741
  case "thinking_delta":
817
742
  this.thinkingContent += parsed.delta.text;
818
743
  this.isThinking = true;
@@ -822,45 +747,6 @@ var ChatSession = class {
822
747
  this.isThinking = false;
823
748
  this.emit({ type: "thinking_complete" });
824
749
  break;
825
- case "sources":
826
- this.sources.push(...parsed.sources);
827
- this.emit({ type: "sources", sources: parsed.sources });
828
- break;
829
- case "capsule_output": {
830
- const capsule = {
831
- toolName: parsed.tool_name,
832
- agentName: parsed.agent_name,
833
- command: parsed.command,
834
- output: parsed.output,
835
- durationMs: parsed.duration_ms
836
- };
837
- this.capsuleOutputs.push(capsule);
838
- this.emit({ type: "capsule_output", ...capsule });
839
- break;
840
- }
841
- case "todo_update":
842
- this.todos = parsed.todos;
843
- this.emit({ type: "todo_update", todos: parsed.todos });
844
- break;
845
- case "subagent_tool_use":
846
- this.emit({
847
- type: "subagent_tool_use",
848
- agentName: parsed.agent_name,
849
- toolName: parsed.tool,
850
- toolCallId: parsed.tool_call_id,
851
- result: parsed.result
852
- });
853
- break;
854
- case "asset_created":
855
- this.emit({
856
- type: "asset_created",
857
- assetId: parsed.asset_id,
858
- name: parsed.name,
859
- url: parsed.url,
860
- mediaType: parsed.media_type,
861
- sizeBytes: parsed.size_bytes
862
- });
863
- break;
864
750
  case "retry":
865
751
  this.emit({
866
752
  type: "retry",
@@ -878,11 +764,154 @@ var ChatSession = class {
878
764
  error: new AstralformError(parsed.message, parsed.code)
879
765
  });
880
766
  break;
767
+ default:
768
+ this.applyEvent(parsed);
881
769
  }
882
770
  }
883
771
  this.currentJobId = null;
884
772
  await this.completeStream(conversationId, messageId, stopTitle);
885
773
  }
774
+ /**
775
+ * Apply a single SSE event to session state and notify consumers.
776
+ * Shared between live streaming and historical event replay.
777
+ */
778
+ applyEvent(event) {
779
+ switch (event.type) {
780
+ case "tool_use_start": {
781
+ const request = {
782
+ callId: event.call_id,
783
+ toolName: event.tool,
784
+ displayName: event.display_name,
785
+ description: event.description,
786
+ arguments: event.arguments,
787
+ isClientTool: event.is_client_tool
788
+ };
789
+ this.activeTools.set(event.call_id, {
790
+ ...request,
791
+ status: event.is_client_tool ? "calling" : "executing"
792
+ });
793
+ this.emit({ type: "tool_call", request });
794
+ break;
795
+ }
796
+ case "tool_use_end": {
797
+ const toolState = this.activeTools.get(event.call_id);
798
+ if (toolState) {
799
+ toolState.status = "completed";
800
+ }
801
+ this.emit({
802
+ type: "tool_end",
803
+ callId: event.call_id,
804
+ toolName: event.tool,
805
+ result: event.result
806
+ });
807
+ break;
808
+ }
809
+ case "agent_start":
810
+ this.emit({
811
+ type: "agent_start",
812
+ agentName: event.agent_name,
813
+ agentDisplayName: event.agent_display_name,
814
+ avatarUrl: event.avatar_url
815
+ });
816
+ break;
817
+ case "agent_end":
818
+ this.emit({ type: "agent_end", agentName: event.agent_name });
819
+ break;
820
+ case "subagent_start":
821
+ this.activeSubagents.set(event.tool_call_id, {
822
+ agentName: event.agent_name,
823
+ displayName: event.display_name,
824
+ avatarUrl: event.avatar_url,
825
+ description: event.description,
826
+ content: "",
827
+ isActive: true
828
+ });
829
+ this.emit({
830
+ type: "subagent_start",
831
+ agentName: event.agent_name,
832
+ displayName: event.display_name,
833
+ toolCallId: event.tool_call_id,
834
+ avatarUrl: event.avatar_url,
835
+ description: event.description
836
+ });
837
+ break;
838
+ case "subagent_update": {
839
+ const sub = this.activeSubagents.get(event.tool_call_id);
840
+ if (sub) {
841
+ sub.agentName = event.agent_name;
842
+ sub.displayName = event.display_name;
843
+ }
844
+ this.emit({
845
+ type: "subagent_update",
846
+ agentName: event.agent_name,
847
+ displayName: event.display_name,
848
+ toolCallId: event.tool_call_id
849
+ });
850
+ break;
851
+ }
852
+ case "subagent_end": {
853
+ const sub = this.activeSubagents.get(event.tool_call_id);
854
+ if (sub) {
855
+ sub.isActive = false;
856
+ }
857
+ this.emit({
858
+ type: "subagent_end",
859
+ agentName: event.agent_name,
860
+ displayName: event.display_name,
861
+ toolCallId: event.tool_call_id
862
+ });
863
+ break;
864
+ }
865
+ case "subagent_tool_use":
866
+ this.emit({
867
+ type: "subagent_tool_use",
868
+ agentName: event.agent_name,
869
+ toolName: event.tool,
870
+ toolCallId: event.tool_call_id,
871
+ result: event.result
872
+ });
873
+ break;
874
+ case "sources":
875
+ this.sources.push(...event.sources);
876
+ this.emit({ type: "sources", sources: event.sources });
877
+ break;
878
+ case "capsule_output": {
879
+ const capsule = {
880
+ toolName: event.tool_name,
881
+ agentName: event.agent_name,
882
+ command: event.command,
883
+ output: event.output,
884
+ durationMs: event.duration_ms,
885
+ callId: event.call_id
886
+ };
887
+ this.capsuleOutputs.push(capsule);
888
+ this.emit({ type: "capsule_output", ...capsule });
889
+ break;
890
+ }
891
+ case "capsule_output_chunk":
892
+ this.emit({
893
+ type: "capsule_output_chunk",
894
+ callId: event.call_id,
895
+ stream: event.stream,
896
+ chunk: event.chunk
897
+ });
898
+ break;
899
+ case "todo_update":
900
+ this.todos = event.todos;
901
+ this.emit({ type: "todo_update", todos: event.todos });
902
+ break;
903
+ case "asset_created":
904
+ this.emit({
905
+ type: "asset_created",
906
+ assetId: event.asset_id,
907
+ name: event.name,
908
+ url: event.url,
909
+ mediaType: event.media_type,
910
+ sizeBytes: event.size_bytes
911
+ });
912
+ break;
913
+ }
914
+ }
886
915
  async executeClientTools(toolCalls) {
887
916
  const results = [];
888
917
  for (const call of toolCalls) {
@@ -959,12 +988,27 @@ var ChatSession = class {
959
988
  }
960
989
  async switchConversation(id) {
961
990
  this.conversationId = id;
962
- try {
963
- this.messages = await this.client.getMessages(id);
964
- } catch {
965
- this.messages = await this.storage.fetchMessages(id);
991
+ this.resetStreamingState();
992
+ const [messagesResult, eventsResult] = await Promise.allSettled([
993
+ this.client.getMessages(id).catch(() => this.storage.fetchMessages(id)),
994
+ this.client.getConversationEvents(id)
995
+ ]);
996
+ this.messages = messagesResult.status === "fulfilled" ? messagesResult.value : [];
997
+ if (eventsResult.status === "fulfilled") {
998
+ for (const ev of eventsResult.value) {
999
+ this.replayEvent(ev.event, ev.data);
1000
+ }
966
1001
  }
967
- this.streamingContent = "";
1002
+ }
1003
+ /**
1004
+ * Replay a single persisted event to reconstruct session state.
1005
+ * Skips text deltas (final content is already in messages[]).
1006
+ */
1007
+ replayEvent(eventType, data) {
1008
+ if (eventType === "content_block_delta" || eventType === "thinking_delta" || eventType === "subagent_content_delta" || eventType === "thinking_complete") {
1009
+ return;
1010
+ }
1011
+ this.applyEvent({ type: eventType, ...data });
968
1012
  }
969
1013
  async deleteConversation(id) {
970
1014
  try {