@astralform/js 0.1.0 → 0.1.2

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.d.ts CHANGED
@@ -29,11 +29,14 @@ interface ToolUseStartEvent {
29
29
  description?: string;
30
30
  arguments: Record<string, unknown>;
31
31
  is_client_tool: boolean;
32
+ tool_category?: string;
33
+ icon_url?: string;
32
34
  }
33
35
  interface ToolUseEndEvent {
34
36
  type: "tool_use_end";
35
37
  call_id: string;
36
38
  tool: string;
39
+ result?: string;
37
40
  }
38
41
  interface AgentStartEvent {
39
42
  type: "agent_start";
@@ -98,6 +101,13 @@ interface CapsuleOutputEvent {
98
101
  command?: string;
99
102
  output: string;
100
103
  duration_ms?: number;
104
+ call_id?: string;
105
+ }
106
+ interface CapsuleOutputChunkEvent {
107
+ type: "capsule_output_chunk";
108
+ call_id: string;
109
+ stream: "stdout" | "stderr";
110
+ chunk: string;
101
111
  }
102
112
  interface TodoUpdateEvent {
103
113
  type: "todo_update";
@@ -128,13 +138,29 @@ interface AssetCreatedEvent {
128
138
  media_type: string;
129
139
  size_bytes: number;
130
140
  }
141
+ interface ActivitySSEEvent {
142
+ type: "activity";
143
+ activity_id: string;
144
+ status: "started" | "completed";
145
+ category: string;
146
+ title: string;
147
+ detail?: string;
148
+ sources?: Array<{
149
+ title: string;
150
+ url: string;
151
+ snippet?: string;
152
+ }>;
153
+ tool_name?: string;
154
+ agent_name?: string;
155
+ duration_ms?: number;
156
+ }
131
157
  interface RetryEvent {
132
158
  type: "retry";
133
159
  attempt: number;
134
160
  max_attempts: number;
135
161
  delay_seconds: number;
136
162
  }
137
- type SSEEvent = MessageStartEvent | ContentBlockDeltaEvent | ToolUseStartEvent | ToolUseEndEvent | AgentStartEvent | AgentEndEvent | SubagentStartEvent | SubagentContentDeltaEvent | SubagentUpdateEvent | SubagentEndEvent | SubagentToolUseEvent | ThinkingDeltaEvent | ThinkingCompleteEvent | SourcesEvent | CapsuleOutputEvent | TodoUpdateEvent | MessageStopEvent | AssetCreatedEvent | RetryEvent | SSEErrorEvent;
163
+ type SSEEvent = MessageStartEvent | ContentBlockDeltaEvent | ToolUseStartEvent | ToolUseEndEvent | AgentStartEvent | AgentEndEvent | SubagentStartEvent | SubagentContentDeltaEvent | SubagentUpdateEvent | SubagentEndEvent | SubagentToolUseEvent | ThinkingDeltaEvent | ThinkingCompleteEvent | SourcesEvent | CapsuleOutputEvent | CapsuleOutputChunkEvent | TodoUpdateEvent | MessageStopEvent | AssetCreatedEvent | ActivitySSEEvent | RetryEvent | SSEErrorEvent;
138
164
  type ChatEvent = {
139
165
  type: "connected";
140
166
  } | {
@@ -154,6 +180,7 @@ type ChatEvent = {
154
180
  type: "tool_end";
155
181
  callId: string;
156
182
  toolName: string;
183
+ result?: string;
157
184
  } | {
158
185
  type: "agent_start";
159
186
  agentName: string;
@@ -202,6 +229,12 @@ type ChatEvent = {
202
229
  command?: string;
203
230
  output: string;
204
231
  durationMs?: number;
232
+ callId?: string;
233
+ } | {
234
+ type: "capsule_output_chunk";
235
+ callId: string;
236
+ stream: "stdout" | "stderr";
237
+ chunk: string;
205
238
  } | {
206
239
  type: "todo_update";
207
240
  todos: TodoItem[];
@@ -229,6 +262,21 @@ type ChatEvent = {
229
262
  attempt: number;
230
263
  maxAttempts: number;
231
264
  delaySeconds: number;
265
+ } | {
266
+ type: "activity";
267
+ activityId: string;
268
+ status: "started" | "completed";
269
+ category: string;
270
+ title: string;
271
+ detail?: string;
272
+ sources?: Array<{
273
+ title: string;
274
+ url: string;
275
+ snippet?: string;
276
+ }>;
277
+ toolName?: string;
278
+ agentName?: string;
279
+ durationMs?: number;
232
280
  } | {
233
281
  type: "model_info";
234
282
  name: string;
@@ -299,6 +347,7 @@ interface CapsuleOutput {
299
347
  command?: string;
300
348
  output: string;
301
349
  durationMs?: number;
350
+ callId?: string;
302
351
  }
303
352
  interface Source {
304
353
  title: string;
@@ -324,6 +373,7 @@ interface ChatStreamRequest {
324
373
  resend_from?: string;
325
374
  upload_ids?: string[];
326
375
  agent_name?: string;
376
+ enable_search?: boolean;
327
377
  }
328
378
  interface ToolResultRequest {
329
379
  conversation_id: string;
@@ -348,6 +398,8 @@ interface ToolCallRequest {
348
398
  description?: string;
349
399
  arguments: Record<string, unknown>;
350
400
  isClientTool: boolean;
401
+ toolCategory?: string;
402
+ iconUrl?: string;
351
403
  }
352
404
  interface StreamJobSSEOptions {
353
405
  url: string;
@@ -359,11 +411,17 @@ interface ChatStreamEvent {
359
411
  event: string;
360
412
  data: string;
361
413
  }
414
+ interface ConversationEvent {
415
+ seq: number;
416
+ event: string;
417
+ data: Record<string, unknown>;
418
+ }
362
419
  interface SendOptions {
363
420
  conversationId?: string;
364
421
  enabledClientTools?: string[];
365
422
  uploadIds?: string[];
366
423
  agentName?: string;
424
+ enableSearch?: boolean;
367
425
  }
368
426
  interface ConversationAsset {
369
427
  id: string;
@@ -400,6 +458,7 @@ declare class AstralformClient {
400
458
  deleteConversation(id: string): Promise<void>;
401
459
  getAgents(): Promise<AgentInfo[]>;
402
460
  getSkills(): Promise<SkillInfo[]>;
461
+ getConversationEvents(conversationId: string): Promise<ConversationEvent[]>;
403
462
  submitToolResult(request: ToolResultRequest): Promise<void>;
404
463
  private mapAsset;
405
464
  uploadFile(conversationId: string, file: Blob, filename?: string): Promise<ConversationAsset>;
@@ -478,17 +537,28 @@ declare class ChatSession {
478
537
  connect(): Promise<void>;
479
538
  send(content: string, options?: SendOptions): Promise<void>;
480
539
  resendFromCheckpoint(messageId: string, newContent: string): Promise<void>;
540
+ private resetStreamingState;
481
541
  private processStream;
482
542
  /** Last received sequence number for resumable reconnection */
483
543
  private lastSeq;
484
544
  /** Current job ID for cancellation */
485
545
  private currentJobId;
486
546
  private consumeJobStream;
547
+ /**
548
+ * Apply a single SSE event to session state and notify consumers.
549
+ * Shared between live streaming and historical event replay.
550
+ */
551
+ private applyEvent;
487
552
  private executeClientTools;
488
553
  private completeStream;
489
554
  disconnect(): void;
490
555
  createNewConversation(): Promise<string>;
491
556
  switchConversation(id: string): Promise<void>;
557
+ /**
558
+ * Replay a single persisted event to reconstruct session state.
559
+ * Skips text deltas (final content is already in messages[]).
560
+ */
561
+ private replayEvent;
492
562
  deleteConversation(id: string): Promise<void>;
493
563
  toggleClientTool(name: string): boolean;
494
564
  }
@@ -523,4 +593,4 @@ declare function generateId(): string;
523
593
  */
524
594
  declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<ChatStreamEvent>;
525
595
 
526
- export { type AgentEndEvent, type AgentInfo, type AgentStartEvent, AstralformClient, type AstralformConfig, AstralformError, AuthenticationError, type CapsuleOutput, type CapsuleOutputEvent, type ChatEvent, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContentBlockDeltaEvent, type Conversation, type ConversationAsset, InMemoryStorage, type JobCreateResponse, LLMNotConfiguredError, type Message, type MessageStartEvent, type MessageStopEvent, type ProjectStatus, RateLimitError, type RetryEvent, type SSEErrorEvent, type SSEEvent, type SendOptions, ServerError, type SkillInfo, type Source, type SourcesEvent, StreamAbortedError, type StreamJobSSEOptions, type SubagentContentDeltaEvent, type SubagentEndEvent, type SubagentStartEvent, type SubagentState, type SubagentToolUseEvent, type ThinkingCompleteEvent, type ThinkingDeltaEvent, type TodoItem, type TodoUpdateEvent, type ToolCallRequest, type ToolDefinition, type ToolHandler, ToolRegistry, type ToolResult, type ToolResultRequest, type ToolState, type ToolUseEndEvent, type ToolUseStartEvent, generateId, streamJobSSE };
596
+ export { type AgentEndEvent, type AgentInfo, type AgentStartEvent, AstralformClient, type AstralformConfig, AstralformError, AuthenticationError, type CapsuleOutput, type CapsuleOutputChunkEvent, type CapsuleOutputEvent, type ChatEvent, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContentBlockDeltaEvent, type Conversation, type ConversationAsset, type ConversationEvent, InMemoryStorage, type JobCreateResponse, LLMNotConfiguredError, type Message, type MessageStartEvent, type MessageStopEvent, type ProjectStatus, RateLimitError, type RetryEvent, type SSEErrorEvent, type SSEEvent, type SendOptions, ServerError, type SkillInfo, type Source, type SourcesEvent, StreamAbortedError, type StreamJobSSEOptions, type SubagentContentDeltaEvent, type SubagentEndEvent, type SubagentStartEvent, type SubagentState, type SubagentToolUseEvent, type ThinkingCompleteEvent, type ThinkingDeltaEvent, type TodoItem, type TodoUpdateEvent, type ToolCallRequest, type ToolDefinition, type ToolHandler, ToolRegistry, type ToolResult, type ToolResultRequest, type ToolState, type ToolUseEndEvent, type ToolUseStartEvent, generateId, streamJobSSE };
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
  }
@@ -569,7 +574,8 @@ var ChatSession = class {
569
574
  options?.enabledClientTools ?? this.enabledClientTools
570
575
  ),
571
576
  upload_ids: options?.uploadIds,
572
- agent_name: options?.agentName
577
+ agent_name: options?.agentName,
578
+ enable_search: options?.enableSearch
573
579
  };
574
580
  await this.processStream(request);
575
581
  }
@@ -584,8 +590,7 @@ var ChatSession = class {
584
590
  };
585
591
  await this.processStream(request);
586
592
  }
587
- async processStream(request) {
588
- this.isStreaming = true;
593
+ resetStreamingState() {
589
594
  this.streamingContent = "";
590
595
  this.thinkingContent = "";
591
596
  this.isThinking = false;
@@ -594,6 +599,10 @@ var ChatSession = class {
594
599
  this.capsuleOutputs = [];
595
600
  this.todos = [];
596
601
  this.activeTools.clear();
602
+ }
603
+ async processStream(request) {
604
+ this.isStreaming = true;
605
+ this.resetStreamingState();
597
606
  this.abortController = new AbortController();
598
607
  try {
599
608
  await this.consumeJobStream(request);
@@ -653,39 +662,26 @@ var ChatSession = class {
653
662
  name: parsed.model_display_name
654
663
  });
655
664
  }
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
665
  break;
664
666
  case "content_block_delta":
665
667
  this.streamingContent += parsed.delta.text;
666
668
  this.emit({ type: "chunk", text: parsed.delta.text });
667
669
  break;
668
670
  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 });
671
+ this.applyEvent(parsed);
687
672
  if (parsed.is_client_tool) {
688
- const results = await this.executeClientTools([toolCall]);
673
+ const results = await this.executeClientTools([
674
+ {
675
+ callId: parsed.call_id,
676
+ toolName: parsed.tool,
677
+ displayName: parsed.display_name,
678
+ description: parsed.description,
679
+ arguments: parsed.arguments,
680
+ isClientTool: parsed.is_client_tool,
681
+ toolCategory: parsed.tool_category,
682
+ iconUrl: parsed.icon_url
683
+ }
684
+ ]);
689
685
  await this.client.submitToolResult({
690
686
  conversation_id: conversationId,
691
687
  message_id: messageId,
@@ -694,61 +690,6 @@ var ChatSession = class {
694
690
  }
695
691
  break;
696
692
  }
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
693
  case "subagent_content_delta": {
753
694
  const subagent = this.activeSubagents.get(parsed.tool_call_id);
754
695
  if (subagent) {
@@ -762,19 +703,6 @@ var ChatSession = class {
762
703
  });
763
704
  break;
764
705
  }
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
706
  case "thinking_delta":
779
707
  this.thinkingContent += parsed.delta.text;
780
708
  this.isThinking = true;
@@ -784,45 +712,6 @@ var ChatSession = class {
784
712
  this.isThinking = false;
785
713
  this.emit({ type: "thinking_complete" });
786
714
  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
715
  case "retry":
827
716
  this.emit({
828
717
  type: "retry",
@@ -840,11 +729,170 @@ var ChatSession = class {
840
729
  error: new AstralformError(parsed.message, parsed.code)
841
730
  });
842
731
  break;
732
+ default:
733
+ this.applyEvent(parsed);
843
734
  }
844
735
  }
845
736
  this.currentJobId = null;
846
737
  await this.completeStream(conversationId, messageId, stopTitle);
847
738
  }
739
+ /**
740
+ * Apply a single SSE event to session state and notify consumers.
741
+ * Shared between live streaming and historical event replay.
742
+ */
743
+ applyEvent(event) {
744
+ switch (event.type) {
745
+ case "tool_use_start": {
746
+ const request = {
747
+ callId: event.call_id,
748
+ toolName: event.tool,
749
+ displayName: event.display_name,
750
+ description: event.description,
751
+ arguments: event.arguments,
752
+ isClientTool: event.is_client_tool,
753
+ toolCategory: event.tool_category,
754
+ iconUrl: event.icon_url
755
+ };
756
+ this.activeTools.set(event.call_id, {
757
+ ...request,
758
+ status: event.is_client_tool ? "calling" : "executing"
759
+ });
760
+ this.emit({ type: "tool_call", request });
761
+ break;
762
+ }
763
+ case "tool_use_end": {
764
+ const toolState = this.activeTools.get(event.call_id);
765
+ if (toolState) {
766
+ toolState.status = "completed";
767
+ }
768
+ this.emit({
769
+ type: "tool_end",
770
+ callId: event.call_id,
771
+ toolName: event.tool,
772
+ result: event.result
773
+ });
774
+ break;
775
+ }
776
+ case "agent_start":
777
+ this.emit({
778
+ type: "agent_start",
779
+ agentName: event.agent_name,
780
+ agentDisplayName: event.agent_display_name,
781
+ avatarUrl: event.avatar_url
782
+ });
783
+ break;
784
+ case "agent_end":
785
+ this.emit({ type: "agent_end", agentName: event.agent_name });
786
+ break;
787
+ case "subagent_start":
788
+ this.activeSubagents.set(event.tool_call_id, {
789
+ agentName: event.agent_name,
790
+ displayName: event.display_name,
791
+ avatarUrl: event.avatar_url,
792
+ description: event.description,
793
+ content: "",
794
+ isActive: true
795
+ });
796
+ this.emit({
797
+ type: "subagent_start",
798
+ agentName: event.agent_name,
799
+ displayName: event.display_name,
800
+ toolCallId: event.tool_call_id,
801
+ avatarUrl: event.avatar_url,
802
+ description: event.description
803
+ });
804
+ break;
805
+ case "subagent_update": {
806
+ const sub = this.activeSubagents.get(event.tool_call_id);
807
+ if (sub) {
808
+ sub.agentName = event.agent_name;
809
+ sub.displayName = event.display_name;
810
+ }
811
+ this.emit({
812
+ type: "subagent_update",
813
+ agentName: event.agent_name,
814
+ displayName: event.display_name,
815
+ toolCallId: event.tool_call_id
816
+ });
817
+ break;
818
+ }
819
+ case "subagent_end": {
820
+ const sub = this.activeSubagents.get(event.tool_call_id);
821
+ if (sub) {
822
+ sub.isActive = false;
823
+ }
824
+ this.emit({
825
+ type: "subagent_end",
826
+ agentName: event.agent_name,
827
+ displayName: event.display_name,
828
+ toolCallId: event.tool_call_id
829
+ });
830
+ break;
831
+ }
832
+ case "subagent_tool_use":
833
+ this.emit({
834
+ type: "subagent_tool_use",
835
+ agentName: event.agent_name,
836
+ toolName: event.tool,
837
+ toolCallId: event.tool_call_id,
838
+ result: event.result
839
+ });
840
+ break;
841
+ case "sources":
842
+ this.sources.push(...event.sources);
843
+ this.emit({ type: "sources", sources: event.sources });
844
+ break;
845
+ case "capsule_output": {
846
+ const capsule = {
847
+ toolName: event.tool_name,
848
+ agentName: event.agent_name,
849
+ command: event.command,
850
+ output: event.output,
851
+ durationMs: event.duration_ms,
852
+ callId: event.call_id
853
+ };
854
+ this.capsuleOutputs.push(capsule);
855
+ this.emit({ type: "capsule_output", ...capsule });
856
+ break;
857
+ }
858
+ case "capsule_output_chunk":
859
+ this.emit({
860
+ type: "capsule_output_chunk",
861
+ callId: event.call_id,
862
+ stream: event.stream,
863
+ chunk: event.chunk
864
+ });
865
+ break;
866
+ case "todo_update":
867
+ this.todos = event.todos;
868
+ this.emit({ type: "todo_update", todos: event.todos });
869
+ break;
870
+ case "asset_created":
871
+ this.emit({
872
+ type: "asset_created",
873
+ assetId: event.asset_id,
874
+ name: event.name,
875
+ url: event.url,
876
+ mediaType: event.media_type,
877
+ sizeBytes: event.size_bytes
878
+ });
879
+ break;
880
+ case "activity":
881
+ this.emit({
882
+ type: "activity",
883
+ activityId: event.activity_id,
884
+ status: event.status,
885
+ category: event.category,
886
+ title: event.title,
887
+ detail: event.detail,
888
+ sources: event.sources,
889
+ toolName: event.tool_name,
890
+ agentName: event.agent_name,
891
+ durationMs: event.duration_ms
892
+ });
893
+ break;
894
+ }
895
+ }
848
896
  async executeClientTools(toolCalls) {
849
897
  const results = [];
850
898
  for (const call of toolCalls) {
@@ -921,12 +969,27 @@ var ChatSession = class {
921
969
  }
922
970
  async switchConversation(id) {
923
971
  this.conversationId = id;
924
- try {
925
- this.messages = await this.client.getMessages(id);
926
- } catch {
927
- this.messages = await this.storage.fetchMessages(id);
972
+ this.resetStreamingState();
973
+ const [messagesResult, eventsResult] = await Promise.allSettled([
974
+ this.client.getMessages(id).catch(() => this.storage.fetchMessages(id)),
975
+ this.client.getConversationEvents(id)
976
+ ]);
977
+ this.messages = messagesResult.status === "fulfilled" ? messagesResult.value : [];
978
+ if (eventsResult.status === "fulfilled") {
979
+ for (const ev of eventsResult.value) {
980
+ this.replayEvent(ev.event, ev.data);
981
+ }
928
982
  }
929
- this.streamingContent = "";
983
+ }
984
+ /**
985
+ * Replay a single persisted event to reconstruct session state.
986
+ * Skips text deltas (final content is already in messages[]).
987
+ */
988
+ replayEvent(eventType, data) {
989
+ if (eventType === "content_block_delta" || eventType === "thinking_delta" || eventType === "subagent_content_delta" || eventType === "thinking_complete") {
990
+ return;
991
+ }
992
+ this.applyEvent({ type: eventType, ...data });
930
993
  }
931
994
  async deleteConversation(id) {
932
995
  try {