@springbrand/chat-client 0.1.3-alpha.45 → 0.1.3-alpha.47

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springbrand/chat-client",
3
- "version": "0.1.3-alpha.45",
3
+ "version": "0.1.3-alpha.47",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",
@@ -27,7 +27,7 @@
27
27
  "@types/react-dom": "^19.2.5",
28
28
  "react-dom": "^19.2.8",
29
29
  "typescript": "^7.0.2",
30
- "@springbrand/agent-runtime": "0.2.0-alpha.61"
30
+ "@springbrand/agent-runtime": "0.2.0-alpha.63"
31
31
  },
32
32
  "scripts": {
33
33
  "typecheck": "tsc --noEmit"
@@ -26,6 +26,7 @@ import {
26
26
  type RuntimeConfigUpdateResult,
27
27
  type RuntimeQueuedSubmission,
28
28
  type RuntimeState,
29
+ type RuntimeTurnState,
29
30
  } from "@springbrand/agent-runtime/contracts";
30
31
  import { createSafeUIMessageAgentConnection } from "./ui-message-stream-guard";
31
32
  import { CURRENT_INBOX_AGENT_PATH } from "./chat-sessions";
@@ -90,26 +91,6 @@ type ChatApproval = {
90
91
  requestId: string;
91
92
  };
92
93
 
93
- type ChatQueuedSubmission = {
94
- submissionId: string;
95
- messageId: string;
96
- preview: string;
97
- position: number;
98
- createdAt: number;
99
- };
100
-
101
- type ChatTurn = {
102
- activeSubmissionId?: string;
103
- activeRequestId?: string;
104
- activeMessageId?: string;
105
- recoveryAttempt?: number;
106
- recoveryMax?: number;
107
- recoveryReason?: "no_meaningful_model_progress" | "transient_model_error";
108
- steerable: boolean;
109
- hasPendingSteer: boolean;
110
- queued: ChatQueuedSubmission[];
111
- };
112
-
113
94
  export interface ChatRuntime {
114
95
  messages: ChatMessage[];
115
96
  status: ChatStatus;
@@ -177,7 +158,7 @@ export interface ChatRuntime {
177
158
  isToolContinuation: boolean;
178
159
  approvals: ChatApproval[] | undefined;
179
160
  approvalsLoaded: boolean;
180
- turn: ChatTurn | undefined;
161
+ turn: RuntimeTurnState | undefined;
181
162
  turnActive: boolean;
182
163
  canSteer: boolean;
183
164
  }
@@ -558,34 +539,46 @@ export function useUniversalAgentChat(): ChatRuntime {
558
539
  : authoritativeStatus;
559
540
  const messagesRef = useRef(messages);
560
541
  messagesRef.current = messages;
561
- const replayFallbackMessageIdRef = useRef<string | undefined>(undefined);
542
+ // SDK callback identities can change without a new recovery request.
543
+ const replayActionsRef = useRef({ setMessages, clearError });
544
+ replayActionsRef.current = { setMessages, clearError };
562
545
  useEffect(() => {
563
546
  const activeMessageId = facetState?.turn?.activeMessageId;
564
547
  if (
565
548
  !activeMessageId ||
566
549
  sdkStatus !== "error" ||
567
550
  sdkIsRecovering ||
568
- isServerStreaming ||
569
- replayFallbackMessageIdRef.current === activeMessageId
551
+ isServerStreaming
570
552
  ) return;
571
553
  const url = chatAgent.getHttpUrl();
572
554
  if (!url) return;
573
- replayFallbackMessageIdRef.current = activeMessageId;
555
+ const baseline = messagesRef.current;
556
+ let cancelled = false;
574
557
  void loadInitialMessages(url, connection.credentials, "full")
575
558
  .then((snapshot) => {
576
- setMessages(snapshot);
577
- clearError();
559
+ if (cancelled) return;
560
+ // The SDK protects its own snapshots while streaming. This HTTP
561
+ // fallback must also reject a response older than the current store.
562
+ const actions = replayActionsRef.current;
563
+ let applied = false;
564
+ actions.setMessages((current) => {
565
+ if (current !== baseline) return current;
566
+ applied = true;
567
+ return snapshot;
568
+ });
569
+ if (applied) actions.clearError();
578
570
  })
579
571
  .catch(() => undefined);
572
+ return () => {
573
+ cancelled = true;
574
+ };
580
575
  }, [
581
576
  chatAgent,
582
- clearError,
583
577
  connection.credentials,
584
578
  facetState?.turn?.activeMessageId,
585
579
  isServerStreaming,
586
580
  sdkIsRecovering,
587
581
  sdkStatus,
588
- setMessages,
589
582
  ]);
590
583
  const turnTimingRef = useRef<ChatTurnTiming | null>(null);
591
584
 
@@ -781,7 +774,7 @@ export function useUniversalAgentChat(): ChatRuntime {
781
774
  markChatTurnPhase(turnTimingRef.current, "rpcReceipt");
782
775
  }
783
776
  if (receipt.kind === "rejected") {
784
- if (projection === "queue") rollback();
777
+ rollback();
785
778
  if (turnTimingRef.current?.messageId === message.id) {
786
779
  turnTimingRef.current = null;
787
780
  }
@@ -849,7 +842,7 @@ export function useUniversalAgentChat(): ChatRuntime {
849
842
  }
850
843
  return true;
851
844
  } catch (cause) {
852
- if (projection === "queue") rollback();
845
+ rollback();
853
846
  if (turnTimingRef.current?.messageId === message.id) {
854
847
  turnTimingRef.current = null;
855
848
  }