@paymanai/payman-typescript-ask-sdk 4.0.24 → 4.0.26

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.mts CHANGED
@@ -124,6 +124,8 @@ type UserActionRequest = {
124
124
  subAction?: UserActionSubAction | (string & {});
125
125
  verificationType?: VerificationType;
126
126
  expirySeconds?: number;
127
+ /** Wall-clock expiry (epoch ms). Set when the prompt is first received. */
128
+ expiresAt?: number;
127
129
  message?: string;
128
130
  requestedSchema?: RequestedSchema;
129
131
  metadata?: Record<string, string>;
@@ -199,6 +201,14 @@ type MessageDisplay = {
199
201
  isStreaming?: boolean;
200
202
  streamingContent?: string;
201
203
  currentMessage?: string;
204
+ /**
205
+ * True while work events (tool calls, LLM-call boundaries, keep-alive
206
+ * heartbeats) are arriving AFTER answer text already started streaming —
207
+ * e.g. the agent-builder streams an instant acknowledgment and then does
208
+ * its real work. Tells the UI to keep a live status line below the
209
+ * already-rendered text instead of showing just a frozen cursor.
210
+ */
211
+ hasTrailingActivity?: boolean;
202
212
  streamProgress?: StreamProgress;
203
213
  steps?: StreamingStep[];
204
214
  isCancelled?: boolean;
@@ -556,6 +566,16 @@ type V2EventProcessorState = {
556
566
  currentExecutingStepId?: string;
557
567
  /** Server-computed wall-clock duration captured from RUN_COMPLETED's `totalTimeMs`. */
558
568
  totalElapsedMs?: number;
569
+ /**
570
+ * True once work events (tool calls, LLM-call boundaries, keep-alive
571
+ * heartbeats) arrive AFTER answer text has started streaming. The
572
+ * agent-builder streams an instant acknowledgment first and then does
573
+ * its real work — this tells the UI to keep a live status line below
574
+ * the already-rendered text. Plain answer streaming (formatter deltas
575
+ * with no trailing work) never sets it. Sticky until the run ends so
576
+ * the status line doesn't flicker during think-gaps between tools.
577
+ */
578
+ trailingActivity: boolean;
559
579
  };
560
580
  declare function createInitialV2State(): V2EventProcessorState;
561
581
  declare function processStreamEventV2(rawEvent: StreamEvent, state: V2EventProcessorState): V2EventProcessorState;
@@ -639,6 +659,12 @@ declare function cancelUserAction(config: ChatConfig, userActionId: string): Pro
639
659
  declare function resendUserAction(config: ChatConfig, userActionId: string): Promise<UserActionResponse>;
640
660
  declare function expireUserAction(config: ChatConfig, userActionId: string): Promise<UserActionResponse>;
641
661
 
662
+ /** Wall-clock deadline (epoch ms) for a prompt with a positive TTL. */
663
+ declare function resolveUserActionExpiresAt(req: UserActionRequest, existing?: ActiveUserAction): number | undefined;
664
+ /** Seconds remaining before expiry, or undefined when the prompt has no TTL. */
665
+ declare function getUserActionSecondsLeft(prompt: ActiveUserAction): number | undefined;
666
+ declare function isUserActionExpired(prompt: ActiveUserAction): boolean;
667
+
642
668
  declare function migrateActiveStream(oldUserId: string, newUserId: string): void;
643
669
 
644
- export { type APIConfig, type ActiveUserAction, type AgentStage, type AnalysisMode, type ChatCallbacks, type ChatConfig, type ChunkDisplay, type FieldWidget, type JsonSchemaField, type JsonSchemaOption, type MessageDisplay, type MessageFeedbackState, type MessageRole, type RequestedSchema, type SendMessageOptions, type SessionParams, type StreamEvent, type StreamOptions, type StreamProgress, type StreamingStep, type UseChatV2Return, type UseVoiceReturn, type UserActionKind, type UserActionRequest, UserActionStaleError, type UserActionState, type UserActionStatus, type UserActionSubAction, type UserNotification, type V2EventProcessorState, type VerificationType, type VoiceCallbacks, type VoiceConfig, type VoicePermissions, type VoiceResult, type VoiceState, buildContent, cancelUserAction, classifyField, classifyUserActionKind, coerceValue, createInitialV2State, defaultValueFor, expireUserAction, generateId, getOptions, isNestedOrUnsupported, isRequired, migrateActiveStream, processStreamEventV2, renderableFields, resendUserAction, streamWorkflowEvents, submitUserAction, useChatV2, useVoice, validateField, validateForm };
670
+ export { type APIConfig, type ActiveUserAction, type AgentStage, type AnalysisMode, type ChatCallbacks, type ChatConfig, type ChunkDisplay, type FieldWidget, type JsonSchemaField, type JsonSchemaOption, type MessageDisplay, type MessageFeedbackState, type MessageRole, type RequestedSchema, type SendMessageOptions, type SessionParams, type StreamEvent, type StreamOptions, type StreamProgress, type StreamingStep, type UseChatV2Return, type UseVoiceReturn, type UserActionKind, type UserActionRequest, UserActionStaleError, type UserActionState, type UserActionStatus, type UserActionSubAction, type UserNotification, type V2EventProcessorState, type VerificationType, type VoiceCallbacks, type VoiceConfig, type VoicePermissions, type VoiceResult, type VoiceState, buildContent, cancelUserAction, classifyField, classifyUserActionKind, coerceValue, createInitialV2State, defaultValueFor, expireUserAction, generateId, getOptions, getUserActionSecondsLeft, isNestedOrUnsupported, isRequired, isUserActionExpired, migrateActiveStream, processStreamEventV2, renderableFields, resendUserAction, resolveUserActionExpiresAt, streamWorkflowEvents, submitUserAction, useChatV2, useVoice, validateField, validateForm };
package/dist/index.d.ts CHANGED
@@ -124,6 +124,8 @@ type UserActionRequest = {
124
124
  subAction?: UserActionSubAction | (string & {});
125
125
  verificationType?: VerificationType;
126
126
  expirySeconds?: number;
127
+ /** Wall-clock expiry (epoch ms). Set when the prompt is first received. */
128
+ expiresAt?: number;
127
129
  message?: string;
128
130
  requestedSchema?: RequestedSchema;
129
131
  metadata?: Record<string, string>;
@@ -199,6 +201,14 @@ type MessageDisplay = {
199
201
  isStreaming?: boolean;
200
202
  streamingContent?: string;
201
203
  currentMessage?: string;
204
+ /**
205
+ * True while work events (tool calls, LLM-call boundaries, keep-alive
206
+ * heartbeats) are arriving AFTER answer text already started streaming —
207
+ * e.g. the agent-builder streams an instant acknowledgment and then does
208
+ * its real work. Tells the UI to keep a live status line below the
209
+ * already-rendered text instead of showing just a frozen cursor.
210
+ */
211
+ hasTrailingActivity?: boolean;
202
212
  streamProgress?: StreamProgress;
203
213
  steps?: StreamingStep[];
204
214
  isCancelled?: boolean;
@@ -556,6 +566,16 @@ type V2EventProcessorState = {
556
566
  currentExecutingStepId?: string;
557
567
  /** Server-computed wall-clock duration captured from RUN_COMPLETED's `totalTimeMs`. */
558
568
  totalElapsedMs?: number;
569
+ /**
570
+ * True once work events (tool calls, LLM-call boundaries, keep-alive
571
+ * heartbeats) arrive AFTER answer text has started streaming. The
572
+ * agent-builder streams an instant acknowledgment first and then does
573
+ * its real work — this tells the UI to keep a live status line below
574
+ * the already-rendered text. Plain answer streaming (formatter deltas
575
+ * with no trailing work) never sets it. Sticky until the run ends so
576
+ * the status line doesn't flicker during think-gaps between tools.
577
+ */
578
+ trailingActivity: boolean;
559
579
  };
560
580
  declare function createInitialV2State(): V2EventProcessorState;
561
581
  declare function processStreamEventV2(rawEvent: StreamEvent, state: V2EventProcessorState): V2EventProcessorState;
@@ -639,6 +659,12 @@ declare function cancelUserAction(config: ChatConfig, userActionId: string): Pro
639
659
  declare function resendUserAction(config: ChatConfig, userActionId: string): Promise<UserActionResponse>;
640
660
  declare function expireUserAction(config: ChatConfig, userActionId: string): Promise<UserActionResponse>;
641
661
 
662
+ /** Wall-clock deadline (epoch ms) for a prompt with a positive TTL. */
663
+ declare function resolveUserActionExpiresAt(req: UserActionRequest, existing?: ActiveUserAction): number | undefined;
664
+ /** Seconds remaining before expiry, or undefined when the prompt has no TTL. */
665
+ declare function getUserActionSecondsLeft(prompt: ActiveUserAction): number | undefined;
666
+ declare function isUserActionExpired(prompt: ActiveUserAction): boolean;
667
+
642
668
  declare function migrateActiveStream(oldUserId: string, newUserId: string): void;
643
669
 
644
- export { type APIConfig, type ActiveUserAction, type AgentStage, type AnalysisMode, type ChatCallbacks, type ChatConfig, type ChunkDisplay, type FieldWidget, type JsonSchemaField, type JsonSchemaOption, type MessageDisplay, type MessageFeedbackState, type MessageRole, type RequestedSchema, type SendMessageOptions, type SessionParams, type StreamEvent, type StreamOptions, type StreamProgress, type StreamingStep, type UseChatV2Return, type UseVoiceReturn, type UserActionKind, type UserActionRequest, UserActionStaleError, type UserActionState, type UserActionStatus, type UserActionSubAction, type UserNotification, type V2EventProcessorState, type VerificationType, type VoiceCallbacks, type VoiceConfig, type VoicePermissions, type VoiceResult, type VoiceState, buildContent, cancelUserAction, classifyField, classifyUserActionKind, coerceValue, createInitialV2State, defaultValueFor, expireUserAction, generateId, getOptions, isNestedOrUnsupported, isRequired, migrateActiveStream, processStreamEventV2, renderableFields, resendUserAction, streamWorkflowEvents, submitUserAction, useChatV2, useVoice, validateField, validateForm };
670
+ export { type APIConfig, type ActiveUserAction, type AgentStage, type AnalysisMode, type ChatCallbacks, type ChatConfig, type ChunkDisplay, type FieldWidget, type JsonSchemaField, type JsonSchemaOption, type MessageDisplay, type MessageFeedbackState, type MessageRole, type RequestedSchema, type SendMessageOptions, type SessionParams, type StreamEvent, type StreamOptions, type StreamProgress, type StreamingStep, type UseChatV2Return, type UseVoiceReturn, type UserActionKind, type UserActionRequest, UserActionStaleError, type UserActionState, type UserActionStatus, type UserActionSubAction, type UserNotification, type V2EventProcessorState, type VerificationType, type VoiceCallbacks, type VoiceConfig, type VoicePermissions, type VoiceResult, type VoiceState, buildContent, cancelUserAction, classifyField, classifyUserActionKind, coerceValue, createInitialV2State, defaultValueFor, expireUserAction, generateId, getOptions, getUserActionSecondsLeft, isNestedOrUnsupported, isRequired, isUserActionExpired, migrateActiveStream, processStreamEventV2, renderableFields, resendUserAction, resolveUserActionExpiresAt, streamWorkflowEvents, submitUserAction, useChatV2, useVoice, validateField, validateForm };
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ var chatStore = {
30
30
  };
31
31
 
32
32
  // src/utils/activeStreamStore.ts
33
+ var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
33
34
  var streams = /* @__PURE__ */ new Map();
34
35
  var activeStreamStore = {
35
36
  has(key) {
@@ -38,7 +39,11 @@ var activeStreamStore = {
38
39
  get(key) {
39
40
  const entry = streams.get(key);
40
41
  if (!entry) return null;
41
- return { messages: entry.messages, isWaiting: entry.isWaiting };
42
+ return {
43
+ messages: entry.messages,
44
+ isWaiting: entry.isWaiting,
45
+ userActionState: entry.userActionState
46
+ };
42
47
  },
43
48
  // Called before startStream — registers the controller and initial messages
44
49
  start(key, abortController, initialMessages) {
@@ -46,6 +51,7 @@ var activeStreamStore = {
46
51
  streams.set(key, {
47
52
  messages: initialMessages,
48
53
  isWaiting: true,
54
+ userActionState: EMPTY_USER_ACTION_STATE,
49
55
  abortController,
50
56
  listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
51
57
  });
@@ -56,20 +62,27 @@ var activeStreamStore = {
56
62
  if (!entry) return;
57
63
  const next = typeof updater === "function" ? updater(entry.messages) : updater;
58
64
  entry.messages = next;
59
- entry.listeners.forEach((l) => l(next, entry.isWaiting));
65
+ entry.listeners.forEach((l) => l(next, entry.isWaiting, entry.userActionState));
60
66
  },
61
67
  setWaiting(key, waiting) {
62
68
  const entry = streams.get(key);
63
69
  if (!entry) return;
64
70
  entry.isWaiting = waiting;
65
- entry.listeners.forEach((l) => l(entry.messages, waiting));
71
+ entry.listeners.forEach((l) => l(entry.messages, waiting, entry.userActionState));
72
+ },
73
+ applyUserActionState(key, updater) {
74
+ const entry = streams.get(key);
75
+ if (!entry) return;
76
+ const next = typeof updater === "function" ? updater(entry.userActionState) : updater;
77
+ entry.userActionState = next;
78
+ entry.listeners.forEach((l) => l(entry.messages, entry.isWaiting, next));
66
79
  },
67
80
  // Called when stream completes — persists to chatStore and cleans up
68
81
  complete(key) {
69
82
  const entry = streams.get(key);
70
83
  if (!entry) return;
71
84
  entry.isWaiting = false;
72
- entry.listeners.forEach((l) => l(entry.messages, false));
85
+ entry.listeners.forEach((l) => l(entry.messages, false, entry.userActionState));
73
86
  const toSave = entry.messages.filter((m) => !m.isStreaming);
74
87
  if (toSave.length > 0) chatStore.set(key, toSave);
75
88
  streams.delete(key);
@@ -263,6 +276,8 @@ function getEventMessage(event) {
263
276
  }
264
277
  case "RUN_IN_PROGRESS":
265
278
  return event.partialText || "Thinking...";
279
+ case "LLM_CALL_STARTED":
280
+ return event.description || "";
266
281
  case "RUN_COMPLETED":
267
282
  return "Agent run completed";
268
283
  case "RUN_FAILED":
@@ -400,7 +415,8 @@ function createInitialV2State() {
400
415
  finalData: void 0,
401
416
  steps: [],
402
417
  stepCounter: 0,
403
- currentExecutingStepId: void 0
418
+ currentExecutingStepId: void 0,
419
+ trailingActivity: false
404
420
  };
405
421
  }
406
422
  function upsertUserAction(state, req) {
@@ -422,6 +438,7 @@ function processStreamEventV2(rawEvent, state) {
422
438
  if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
423
439
  if (event.executionId) state.executionId = event.executionId;
424
440
  if (event.sessionId) state.sessionId = event.sessionId;
441
+ if (state.finalResponse) state.trailingActivity = true;
425
442
  const description = typeof event.description === "string" ? event.description : "";
426
443
  if (description) {
427
444
  for (let i = state.steps.length - 1; i >= 0; i--) {
@@ -456,7 +473,12 @@ function processStreamEventV2(rawEvent, state) {
456
473
  state.lastEventType = eventType;
457
474
  break;
458
475
  }
476
+ case "LLM_CALL_STARTED":
477
+ if (state.finalResponse) state.trailingActivity = true;
478
+ state.lastEventType = eventType;
479
+ break;
459
480
  case "INTENT_STARTED": {
481
+ if (state.finalResponse) state.trailingActivity = true;
460
482
  const stepId = `step-${state.stepCounter++}`;
461
483
  state.steps.push({
462
484
  id: stepId,
@@ -472,6 +494,7 @@ function processStreamEventV2(rawEvent, state) {
472
494
  break;
473
495
  }
474
496
  case "INTENT_COMPLETED": {
497
+ if (state.finalResponse) state.trailingActivity = true;
475
498
  const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
476
499
  if (intentStep) {
477
500
  intentStep.status = "completed";
@@ -503,6 +526,7 @@ function processStreamEventV2(rawEvent, state) {
503
526
  step.status = "completed";
504
527
  }
505
528
  });
529
+ state.trailingActivity = false;
506
530
  state.lastEventType = eventType;
507
531
  break;
508
532
  }
@@ -574,6 +598,7 @@ function processStreamEventV2(rawEvent, state) {
574
598
  case "WORKFLOW_ERROR":
575
599
  state.hasError = true;
576
600
  state.errorMessage = event.errorMessage || event.message || "Workflow error";
601
+ state.trailingActivity = false;
577
602
  state.lastEventType = eventType;
578
603
  break;
579
604
  // ---- K2 pipeline stage lifecycle events ----
@@ -868,7 +893,9 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
868
893
  const latestUsefulStep = [...state.steps].reverse().find(
869
894
  (s) => s.message && !isBlandStatus(s.message)
870
895
  );
871
- const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
896
+ const eventTypeUpper = typeof event.eventType === "string" ? event.eventType.toUpperCase() : "";
897
+ const liveEventLabel = eventTypeUpper === "KEEP_ALIVE" || eventTypeUpper === "LLM_CALL_STARTED" ? useful(getEventMessage(event)) : void 0;
898
+ const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? liveEventLabel ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
872
899
  // nothing useful seen yet). Render the bland label so the
873
900
  // bubble isn't blank.
874
901
  activeStep?.message ?? lastInProgressStep?.message ?? getEventMessage(event);
@@ -893,6 +920,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
893
920
  streamingContent: state.finalResponse,
894
921
  content: "",
895
922
  currentMessage,
923
+ hasTrailingActivity: state.trailingActivity,
896
924
  streamProgress: "processing",
897
925
  isError: false,
898
926
  steps: [...state.steps],
@@ -1094,13 +1122,38 @@ async function expireUserAction(config, userActionId) {
1094
1122
  return sendUserActionRequest(config, userActionId, "expired");
1095
1123
  }
1096
1124
 
1125
+ // src/utils/userActionExpiry.ts
1126
+ function resolveUserActionExpiresAt(req, existing) {
1127
+ if (typeof req.expirySeconds !== "number" || req.expirySeconds <= 0) {
1128
+ return void 0;
1129
+ }
1130
+ if (existing?.expiresAt && existing.userActionId === req.userActionId) {
1131
+ return existing.expiresAt;
1132
+ }
1133
+ return Date.now() + Math.floor(req.expirySeconds) * 1e3;
1134
+ }
1135
+ function getUserActionSecondsLeft(prompt) {
1136
+ if (typeof prompt.expiresAt === "number" && prompt.expiresAt > 0) {
1137
+ return Math.max(0, Math.ceil((prompt.expiresAt - Date.now()) / 1e3));
1138
+ }
1139
+ if (typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0) {
1140
+ return Math.floor(prompt.expirySeconds);
1141
+ }
1142
+ return void 0;
1143
+ }
1144
+ function isUserActionExpired(prompt) {
1145
+ const left = getUserActionSecondsLeft(prompt);
1146
+ return left !== void 0 && left <= 0;
1147
+ }
1148
+
1097
1149
  // src/hooks/useChatV2.ts
1098
- var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1150
+ var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
1099
1151
  function upsertPrompt(prompts, req) {
1100
- const active = { ...req, status: "pending" };
1101
1152
  const idx = prompts.findIndex(
1102
1153
  (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
1103
1154
  );
1155
+ const expiresAt = resolveUserActionExpiresAt(req, idx >= 0 ? prompts[idx] : void 0);
1156
+ const active = { ...req, status: "pending", expiresAt };
1104
1157
  if (idx >= 0) {
1105
1158
  const next = prompts.slice();
1106
1159
  next[idx] = active;
@@ -1171,7 +1224,28 @@ function useChatV2(config, callbacks = {}) {
1171
1224
  // eslint-disable-next-line react-hooks/exhaustive-deps
1172
1225
  []
1173
1226
  );
1174
- const [userActionState, setUserActionState] = react.useState(EMPTY_USER_ACTION_STATE);
1227
+ const [userActionState, setUserActionState] = react.useState(() => {
1228
+ if (!config.userId) return EMPTY_USER_ACTION_STATE2;
1229
+ return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
1230
+ });
1231
+ const storeAwareSetUserActionState = react.useCallback(
1232
+ (updater) => {
1233
+ const streamUserId = streamUserIdRef.current;
1234
+ const currentUserId = configRef.current.userId;
1235
+ const storeKey = streamUserId ?? currentUserId;
1236
+ if (storeKey && activeStreamStore.has(storeKey)) {
1237
+ activeStreamStore.applyUserActionState(
1238
+ storeKey,
1239
+ updater
1240
+ );
1241
+ }
1242
+ if (!streamUserId || streamUserId === currentUserId) {
1243
+ setUserActionState(updater);
1244
+ }
1245
+ },
1246
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1247
+ []
1248
+ );
1175
1249
  const wrappedCallbacks = react.useMemo(() => ({
1176
1250
  ...callbacksRef.current,
1177
1251
  onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
@@ -1181,14 +1255,14 @@ function useChatV2(config, callbacks = {}) {
1181
1255
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1182
1256
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1183
1257
  onUserActionRequired: (request) => {
1184
- setUserActionState((prev) => ({
1258
+ storeAwareSetUserActionState((prev) => ({
1185
1259
  ...prev,
1186
1260
  prompts: upsertPrompt(prev.prompts, request)
1187
1261
  }));
1188
1262
  callbacksRef.current.onUserActionRequired?.(request);
1189
1263
  },
1190
1264
  onUserNotification: (notification) => {
1191
- setUserActionState(
1265
+ storeAwareSetUserActionState(
1192
1266
  (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1193
1267
  );
1194
1268
  callbacksRef.current.onUserNotification?.(notification);
@@ -1278,7 +1352,7 @@ function useChatV2(config, callbacks = {}) {
1278
1352
  streamUserIdRef.current = void 0;
1279
1353
  cancelStreamManager();
1280
1354
  setIsWaitingForResponse(false);
1281
- setUserActionState((prev) => ({ ...prev, prompts: [] }));
1355
+ storeAwareSetUserActionState((prev) => ({ ...prev, prompts: [] }));
1282
1356
  setMessages(
1283
1357
  (prev) => prev.map((msg) => {
1284
1358
  if (msg.isStreaming) {
@@ -1293,7 +1367,7 @@ function useChatV2(config, callbacks = {}) {
1293
1367
  return msg;
1294
1368
  })
1295
1369
  );
1296
- }, [cancelStreamManager]);
1370
+ }, [cancelStreamManager, storeAwareSetUserActionState]);
1297
1371
  const resetSession = react.useCallback(() => {
1298
1372
  const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1299
1373
  if (streamUserId) {
@@ -1307,7 +1381,7 @@ function useChatV2(config, callbacks = {}) {
1307
1381
  sessionIdRef.current = void 0;
1308
1382
  abortControllerRef.current?.abort();
1309
1383
  setIsWaitingForResponse(false);
1310
- setUserActionState(EMPTY_USER_ACTION_STATE);
1384
+ storeAwareSetUserActionState(EMPTY_USER_ACTION_STATE2);
1311
1385
  }, []);
1312
1386
  const getSessionId = react.useCallback(() => {
1313
1387
  return sessionIdRef.current;
@@ -1317,21 +1391,21 @@ function useChatV2(config, callbacks = {}) {
1317
1391
  }, [messages]);
1318
1392
  const setPromptStatus = react.useCallback(
1319
1393
  (userActionId, status) => {
1320
- setUserActionState((prev) => ({
1394
+ storeAwareSetUserActionState((prev) => ({
1321
1395
  ...prev,
1322
1396
  prompts: prev.prompts.map(
1323
1397
  (p) => p.userActionId === userActionId ? { ...p, status } : p
1324
1398
  )
1325
1399
  }));
1326
1400
  },
1327
- []
1401
+ [storeAwareSetUserActionState]
1328
1402
  );
1329
1403
  const removePrompt = react.useCallback((userActionId) => {
1330
- setUserActionState((prev) => ({
1404
+ storeAwareSetUserActionState((prev) => ({
1331
1405
  ...prev,
1332
1406
  prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
1333
1407
  }));
1334
- }, []);
1408
+ }, [storeAwareSetUserActionState]);
1335
1409
  const submitUserAction2 = react.useCallback(
1336
1410
  async (userActionId, content) => {
1337
1411
  setPromptStatus(userActionId, "submitting");
@@ -1400,11 +1474,11 @@ function useChatV2(config, callbacks = {}) {
1400
1474
  [setPromptStatus]
1401
1475
  );
1402
1476
  const dismissNotification = react.useCallback((id) => {
1403
- setUserActionState((prev) => ({
1477
+ storeAwareSetUserActionState((prev) => ({
1404
1478
  ...prev,
1405
1479
  notifications: prev.notifications.filter((n) => n.id !== id)
1406
1480
  }));
1407
- }, []);
1481
+ }, [storeAwareSetUserActionState]);
1408
1482
  react.useEffect(() => {
1409
1483
  const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
1410
1484
  subscriptionPrevUserIdRef.current = config.userId;
@@ -1413,14 +1487,17 @@ function useChatV2(config, callbacks = {}) {
1413
1487
  if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
1414
1488
  streamUserIdRef.current = userId;
1415
1489
  }
1416
- const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1490
+ const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting, actions) => {
1417
1491
  setMessages(msgs);
1418
1492
  setIsWaitingForResponse(isWaiting);
1493
+ setUserActionState(actions);
1419
1494
  });
1420
1495
  const active = activeStreamStore.get(userId);
1421
1496
  if (active) {
1497
+ streamUserIdRef.current = userId;
1422
1498
  setMessages(active.messages);
1423
1499
  setIsWaitingForResponse(active.isWaiting);
1500
+ setUserActionState(active.userActionState);
1424
1501
  }
1425
1502
  return unsubscribe;
1426
1503
  }, [config.userId]);
@@ -1448,12 +1525,14 @@ function useChatV2(config, callbacks = {}) {
1448
1525
  setMessages([]);
1449
1526
  sessionIdRef.current = void 0;
1450
1527
  setIsWaitingForResponse(false);
1451
- setUserActionState(EMPTY_USER_ACTION_STATE);
1528
+ setUserActionState(EMPTY_USER_ACTION_STATE2);
1452
1529
  } else if (config.userId) {
1453
1530
  const active = activeStreamStore.get(config.userId);
1454
1531
  if (active) {
1532
+ streamUserIdRef.current = config.userId;
1455
1533
  setMessages(active.messages);
1456
1534
  setIsWaitingForResponse(active.isWaiting);
1535
+ setUserActionState(active.userActionState);
1457
1536
  sessionIdRef.current = getSessionIdFromMessages(active.messages) ?? config.initialSessionId;
1458
1537
  return;
1459
1538
  }
@@ -1832,12 +1911,15 @@ exports.defaultValueFor = defaultValueFor;
1832
1911
  exports.expireUserAction = expireUserAction;
1833
1912
  exports.generateId = generateId;
1834
1913
  exports.getOptions = getOptions;
1914
+ exports.getUserActionSecondsLeft = getUserActionSecondsLeft;
1835
1915
  exports.isNestedOrUnsupported = isNestedOrUnsupported;
1836
1916
  exports.isRequired = isRequired;
1917
+ exports.isUserActionExpired = isUserActionExpired;
1837
1918
  exports.migrateActiveStream = migrateActiveStream;
1838
1919
  exports.processStreamEventV2 = processStreamEventV2;
1839
1920
  exports.renderableFields = renderableFields;
1840
1921
  exports.resendUserAction = resendUserAction;
1922
+ exports.resolveUserActionExpiresAt = resolveUserActionExpiresAt;
1841
1923
  exports.streamWorkflowEvents = streamWorkflowEvents;
1842
1924
  exports.submitUserAction = submitUserAction;
1843
1925
  exports.useChatV2 = useChatV2;