@paymanai/payman-typescript-ask-sdk 4.0.8 → 4.0.10

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.
@@ -4,7 +4,9 @@ var react = require('react');
4
4
  var reactNativeMmkv = require('react-native-mmkv');
5
5
  var expoSpeechRecognition = require('expo-speech-recognition');
6
6
 
7
- // src/hooks/useChatV2.ts
7
+ var __defProp = Object.defineProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
8
10
 
9
11
  // src/utils/generateId.ts
10
12
  function generateId() {
@@ -275,31 +277,27 @@ function normalizeEvent(event) {
275
277
  return event;
276
278
  }
277
279
  }
278
- function isUserActionPrompt(text) {
279
- return /\benter\b.+\b(code|otp)\b/i.test(text) || /\b(authorization|verification)\s+code\b/i.test(text) || /\bsent\s+to\s+your\b/i.test(text);
280
+ function isVerificationSchema(schema) {
281
+ const props = schema?.properties;
282
+ if (!props) return false;
283
+ const keys = Object.keys(props);
284
+ return keys.length === 1 && keys[0] === "verificationCode";
280
285
  }
281
- function getUserActionDisplayMessage(eventType, rawMessage) {
282
- const raw = rawMessage?.trim();
283
- const safeRaw = raw && !isUserActionPrompt(raw) ? raw : "";
284
- switch (eventType) {
285
- case "USER_ACTION_REQUIRED":
286
- return raw || "Waiting for verification...";
287
- case "USER_ACTION_SUCCESS":
288
- return safeRaw || "Verification approved";
289
- case "USER_ACTION_INVALID":
290
- return safeRaw || "Invalid code. Please try again.";
291
- case "USER_ACTION_REJECTED":
292
- return safeRaw || "Verification cancelled";
293
- case "USER_ACTION_EXPIRED":
294
- return safeRaw || "Verification expired";
295
- case "USER_ACTION_RESENT":
296
- return safeRaw || "Verification code resent";
297
- case "USER_ACTION_FAILED":
298
- return safeRaw || "Verification failed";
286
+ function classifyUserActionKind(action, schema) {
287
+ switch ((action || "").toLowerCase()) {
288
+ case "userverificationrequest":
289
+ return "verification";
290
+ case "usernotificationrequest":
291
+ return "notification";
292
+ case "userformrequest":
293
+ return isVerificationSchema(schema) ? "verification" : "form";
299
294
  default:
300
- return safeRaw || raw || eventType;
295
+ return isVerificationSchema(schema) ? "verification" : "form";
301
296
  }
302
297
  }
298
+ function userActionHeader(kind) {
299
+ return kind === "verification" ? "**Verification required**" : "**Action required**";
300
+ }
303
301
  function workingPhaseDetailForDisplay(raw) {
304
302
  const t = raw.trim();
305
303
  if (!t) return "";
@@ -359,18 +357,32 @@ function createInitialV2State() {
359
357
  executionId: void 0,
360
358
  hasError: false,
361
359
  errorMessage: "",
362
- userActionRequest: void 0,
363
- userActionPending: false,
364
- userActionResult: void 0,
360
+ userActions: [],
361
+ notifications: [],
362
+ lastUserAction: void 0,
363
+ lastNotification: void 0,
365
364
  finalData: void 0,
366
365
  steps: [],
367
366
  stepCounter: 0,
368
367
  currentExecutingStepId: void 0
369
368
  };
370
369
  }
370
+ function upsertUserAction(state, req) {
371
+ const active = { ...req, status: "pending" };
372
+ const matchIdx = state.userActions.findIndex(
373
+ (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
374
+ );
375
+ if (matchIdx >= 0) {
376
+ state.userActions[matchIdx] = active;
377
+ } else {
378
+ state.userActions.push(active);
379
+ }
380
+ }
371
381
  function processStreamEventV2(rawEvent, state) {
372
382
  const event = normalizeEvent(rawEvent);
373
383
  const eventType = event.eventType;
384
+ state.lastUserAction = void 0;
385
+ state.lastNotification = void 0;
374
386
  if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
375
387
  if (event.executionId) state.executionId = event.executionId;
376
388
  if (event.sessionId) state.sessionId = event.sessionId;
@@ -592,152 +604,72 @@ function processStreamEventV2(rawEvent, state) {
592
604
  break;
593
605
  }
594
606
  case "USER_ACTION_REQUIRED": {
595
- completeLastInProgressStep(state.steps);
596
- if (event.userActionRequest) {
597
- state.userActionRequest = {
598
- userActionId: event.userActionRequest.userActionId,
599
- userActionType: event.userActionRequest.userActionType,
600
- message: event.userActionRequest.message,
601
- requestedSchema: event.userActionRequest.requestedSchema,
602
- metadata: event.userActionRequest.metadata
607
+ const rawAction = typeof event.action === "string" ? event.action : void 0;
608
+ const schema = event.requestedSchema;
609
+ const kind = classifyUserActionKind(rawAction, schema);
610
+ const promptMessage = typeof event.message === "string" && event.message.trim() || message || "";
611
+ const userActionId = typeof event.userActionId === "string" ? event.userActionId : "";
612
+ if (kind === "notification") {
613
+ const notification = {
614
+ id: userActionId || `note-${state.stepCounter++}`,
615
+ message: promptMessage
603
616
  };
617
+ state.notifications.push(notification);
618
+ state.lastNotification = notification;
619
+ if (promptMessage) addThinkingDetail(state, promptMessage);
620
+ state.lastEventType = eventType;
621
+ break;
604
622
  }
605
- state.userActionPending = true;
606
- const req = event.userActionRequest;
607
- const displayMessage = getUserActionDisplayMessage(
608
- eventType,
609
- req?.message || message
610
- );
611
- if (req) {
612
- addThinkingLine(state, "**Verification Required**", displayMessage);
623
+ if (!userActionId) {
624
+ state.lastEventType = eventType;
625
+ break;
613
626
  }
614
- const stepId = `step-${state.stepCounter++}`;
615
- state.steps.push({
616
- id: stepId,
617
- eventType,
618
- message: displayMessage,
619
- status: "in_progress",
620
- timestamp: Date.now(),
621
- elapsedMs: event.elapsedMs
622
- });
623
- state.currentExecutingStepId = stepId;
624
- state.lastEventType = eventType;
625
- break;
626
- }
627
- case "USER_ACTION_SUCCESS": {
628
- const displayMessage = getUserActionDisplayMessage(eventType, event.message);
629
- appendThinkingText(state, "\n\u2713 " + displayMessage);
630
627
  completeLastInProgressStep(state.steps);
631
- state.userActionRequest = void 0;
632
- state.userActionPending = false;
633
- state.userActionResult = "approved";
628
+ const verificationType = event.verificationType === "ALPHANUMERIC_CODE" || event.verificationType === "NUMERIC_CODE" ? event.verificationType : void 0;
629
+ const request = {
630
+ userActionId,
631
+ kind,
632
+ rawAction,
633
+ subAction: typeof event.subAction === "string" ? event.subAction : void 0,
634
+ verificationType,
635
+ expirySeconds: typeof event.expirySeconds === "number" ? event.expirySeconds : void 0,
636
+ message: promptMessage || void 0,
637
+ requestedSchema: schema,
638
+ metadata: event.metadata,
639
+ toolCallId: typeof event.toolCallId === "string" ? event.toolCallId : void 0,
640
+ executionId: state.executionId,
641
+ sessionId: state.sessionId
642
+ };
643
+ upsertUserAction(state, request);
644
+ state.lastUserAction = request;
645
+ const header = userActionHeader(kind);
646
+ if (promptMessage) addThinkingLine(state, header, promptMessage);
647
+ else addThinkingHeader(state, header);
634
648
  const stepId = `step-${state.stepCounter++}`;
635
649
  state.steps.push({
636
650
  id: stepId,
637
651
  eventType,
638
- message: displayMessage,
639
- status: "completed",
640
- timestamp: Date.now(),
641
- elapsedMs: event.elapsedMs
642
- });
643
- state.lastEventType = eventType;
644
- break;
645
- }
646
- case "USER_ACTION_INVALID": {
647
- const displayMessage = getUserActionDisplayMessage(eventType, event.message);
648
- appendThinkingText(state, "\n\u2717 " + displayMessage);
649
- completeLastInProgressStep(state.steps);
650
- const errorStepId = `step-${state.stepCounter++}`;
651
- state.steps.push({
652
- id: errorStepId,
653
- eventType,
654
- message: displayMessage,
655
- status: "error",
656
- timestamp: Date.now(),
657
- elapsedMs: event.elapsedMs
658
- });
659
- const retryStepId = `step-${state.stepCounter++}`;
660
- state.steps.push({
661
- id: retryStepId,
662
- eventType: "USER_ACTION_REQUIRED",
663
- message: "Waiting for verification...",
652
+ message: promptMessage || (kind === "verification" ? "Waiting for verification..." : "Waiting for your input..."),
664
653
  status: "in_progress",
665
- timestamp: Date.now()
666
- });
667
- state.currentExecutingStepId = retryStepId;
668
- state.lastEventType = eventType;
669
- break;
670
- }
671
- case "USER_ACTION_REJECTED": {
672
- const displayMessage = getUserActionDisplayMessage(eventType, event.message);
673
- appendThinkingText(state, "\n" + displayMessage);
674
- completeLastInProgressStep(state.steps);
675
- state.userActionRequest = void 0;
676
- state.userActionPending = false;
677
- state.userActionResult = "rejected";
678
- const stepId = `step-${state.stepCounter++}`;
679
- state.steps.push({
680
- id: stepId,
681
- eventType,
682
- message: displayMessage,
683
- status: "completed",
684
- timestamp: Date.now(),
685
- elapsedMs: event.elapsedMs
686
- });
687
- state.lastEventType = eventType;
688
- break;
689
- }
690
- case "USER_ACTION_EXPIRED": {
691
- const displayMessage = getUserActionDisplayMessage(eventType, event.message);
692
- appendThinkingText(state, "\n\u2717 " + displayMessage);
693
- completeLastInProgressStep(state.steps);
694
- state.userActionRequest = void 0;
695
- state.userActionPending = false;
696
- const stepId = `step-${state.stepCounter++}`;
697
- state.steps.push({
698
- id: stepId,
699
- eventType,
700
- message: displayMessage,
701
- status: "error",
702
- timestamp: Date.now(),
703
- elapsedMs: event.elapsedMs
704
- });
705
- state.lastEventType = eventType;
706
- break;
707
- }
708
- case "USER_ACTION_RESENT": {
709
- const displayMessage = getUserActionDisplayMessage(eventType, event.message);
710
- appendThinkingText(state, "\n" + displayMessage);
711
- const stepId = `step-${state.stepCounter++}`;
712
- state.steps.push({
713
- id: stepId,
714
- eventType,
715
- message: displayMessage,
716
- status: "completed",
717
654
  timestamp: Date.now(),
718
655
  elapsedMs: event.elapsedMs
719
656
  });
657
+ state.currentExecutingStepId = stepId;
720
658
  state.lastEventType = eventType;
721
659
  break;
722
660
  }
723
- case "USER_ACTION_FAILED": {
724
- const displayMessage = getUserActionDisplayMessage(
725
- eventType,
726
- event.message || event.errorMessage
727
- );
728
- appendThinkingText(state, "\n\u2717 " + displayMessage);
729
- completeLastInProgressStep(state.steps);
730
- state.userActionRequest = void 0;
731
- state.userActionPending = false;
732
- const stepId = `step-${state.stepCounter++}`;
733
- state.steps.push({
734
- id: stepId,
735
- eventType,
736
- message: displayMessage,
737
- status: "error",
738
- timestamp: Date.now(),
739
- elapsedMs: event.elapsedMs
740
- });
661
+ case "USER_NOTIFICATION": {
662
+ const noteMessage = typeof event.message === "string" && event.message.trim() || message || "";
663
+ const userActionId = typeof event.userActionId === "string" ? event.userActionId : "";
664
+ if (noteMessage || userActionId) {
665
+ const notification = {
666
+ id: userActionId || `note-${state.stepCounter++}`,
667
+ message: noteMessage
668
+ };
669
+ state.notifications.push(notification);
670
+ state.lastNotification = notification;
671
+ if (noteMessage) addThinkingDetail(state, noteMessage);
672
+ }
741
673
  state.lastEventType = eventType;
742
674
  break;
743
675
  }
@@ -931,21 +863,9 @@ function buildFormattedThinking(steps, allThinkingText) {
931
863
  if (step.message) parts.push(step.message);
932
864
  break;
933
865
  case "USER_ACTION_REQUIRED":
934
- parts.push("**Verification Required**");
866
+ parts.push("**Action required**");
935
867
  if (step.message) parts.push(step.message);
936
868
  break;
937
- case "USER_ACTION_SUCCESS":
938
- parts.push(`\u2713 ${step.message || "Verification successful"}`);
939
- break;
940
- case "USER_ACTION_REJECTED":
941
- parts.push(`\u2717 ${step.message || "Verification rejected"}`);
942
- break;
943
- case "USER_ACTION_EXPIRED":
944
- parts.push(`\u2717 ${step.message || "Verification expired"}`);
945
- break;
946
- case "USER_ACTION_FAILED":
947
- parts.push(`\u2717 ${step.message || "Verification failed"}`);
948
- break;
949
869
  }
950
870
  }
951
871
  return parts.length > 0 ? parts.join("\n") : allThinkingText;
@@ -1041,6 +961,14 @@ function buildRequestHeaders(config) {
1041
961
  }
1042
962
 
1043
963
  // src/utils/userActionClient.ts
964
+ var UserActionStaleError = class extends Error {
965
+ constructor(userActionId, message = "User action is no longer actionable") {
966
+ super(message);
967
+ __publicField(this, "userActionId");
968
+ this.name = "UserActionStaleError";
969
+ this.userActionId = userActionId;
970
+ }
971
+ };
1044
972
  async function sendUserActionRequest(config, userActionId, action, data) {
1045
973
  const url = buildUserActionUrl(config, userActionId, action);
1046
974
  const baseHeaders = buildRequestHeaders(config);
@@ -1051,14 +979,17 @@ async function sendUserActionRequest(config, userActionId, action, data) {
1051
979
  headers,
1052
980
  body: hasBody ? JSON.stringify(data) : void 0
1053
981
  });
982
+ if (response.status === 404) {
983
+ throw new UserActionStaleError(userActionId);
984
+ }
1054
985
  if (!response.ok) {
1055
986
  const errorText = await response.text();
1056
987
  throw new Error(`HTTP ${response.status}: ${errorText}`);
1057
988
  }
1058
989
  return await response.json();
1059
990
  }
1060
- async function submitUserAction(config, userActionId, data) {
1061
- return sendUserActionRequest(config, userActionId, "submit", data);
991
+ async function submitUserAction(config, userActionId, content) {
992
+ return sendUserActionRequest(config, userActionId, "submit", content ?? {});
1062
993
  }
1063
994
  async function cancelUserAction(config, userActionId) {
1064
995
  return sendUserActionRequest(config, userActionId, "cancel");
@@ -1140,6 +1071,13 @@ var activeStreamStore = {
1140
1071
  streams.get(key)?.listeners.delete(listener);
1141
1072
  };
1142
1073
  },
1074
+ // Rename an entry — used when the server assigns a new session ID mid-stream
1075
+ rename(oldKey, newKey) {
1076
+ const entry = streams.get(oldKey);
1077
+ if (!entry) return;
1078
+ streams.set(newKey, entry);
1079
+ streams.delete(oldKey);
1080
+ },
1143
1081
  // Explicit user cancel — aborts the controller and removes the entry
1144
1082
  abort(key) {
1145
1083
  const entry = streams.get(key);
@@ -1236,15 +1174,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1236
1174
  onEvent: (event) => {
1237
1175
  if (abortController.signal.aborted) return;
1238
1176
  processStreamEventV2(event, state);
1239
- const eventType = event.eventType;
1240
- if (eventType === "USER_ACTION_REQUIRED" && state.userActionRequest) {
1241
- callbacksRef.current.onUserActionRequired?.(state.userActionRequest);
1242
- } else if (typeof eventType === "string" && eventType.startsWith("USER_ACTION_") && eventType !== "USER_ACTION_REQUIRED") {
1243
- const msg = getUserActionDisplayMessage(
1244
- eventType,
1245
- event.message?.trim() || event.errorMessage?.trim() || getEventMessage(event)
1246
- );
1247
- callbacksRef.current.onUserActionEvent?.(eventType, msg);
1177
+ if (state.lastUserAction) {
1178
+ callbacksRef.current.onUserActionRequired?.(state.lastUserAction);
1179
+ }
1180
+ if (state.lastNotification) {
1181
+ callbacksRef.current.onUserNotification?.(state.lastNotification);
1248
1182
  }
1249
1183
  const activeStep = state.steps.find((s) => s.id === state.currentExecutingStepId);
1250
1184
  const lastInProgressStep = [...state.steps].reverse().find((s) => s.status === "in_progress");
@@ -1285,7 +1219,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1285
1219
  currentExecutingStepId: state.currentExecutingStepId,
1286
1220
  executionId: state.executionId,
1287
1221
  sessionId: state.sessionId,
1288
- userActionResult: state.userActionResult,
1289
1222
  isCancelled: false
1290
1223
  });
1291
1224
  }
@@ -1296,14 +1229,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1296
1229
  if (error.name !== "AbortError") {
1297
1230
  callbacksRef.current.onError?.(error);
1298
1231
  }
1299
- if (state.userActionPending) {
1300
- state.userActionPending = false;
1301
- state.userActionRequest = void 0;
1302
- callbacksRef.current.onUserActionEvent?.(
1303
- "USER_ACTION_FAILED",
1304
- "Connection lost. Please try again."
1305
- );
1306
- }
1307
1232
  const isAborted = error.name === "AbortError";
1308
1233
  setMessages(
1309
1234
  (prev) => prev.map(
@@ -1332,14 +1257,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1332
1257
  setIsWaitingForResponse(false);
1333
1258
  callbacksRef.current.onStatusMessage?.(null);
1334
1259
  callbacksRef.current.onStepsUpdate?.([]);
1335
- if (state.userActionPending) {
1336
- state.userActionPending = false;
1337
- state.userActionRequest = void 0;
1338
- callbacksRef.current.onUserActionEvent?.(
1339
- "USER_ACTION_FAILED",
1340
- "Verification could not be completed."
1341
- );
1342
- }
1343
1260
  if (state.sessionId && state.sessionId !== sessionId) {
1344
1261
  callbacksRef.current.onSessionIdChange?.(state.sessionId);
1345
1262
  }
@@ -1363,7 +1280,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1363
1280
  steps: state.hasError ? [] : [...state.steps],
1364
1281
  isCancelled: false,
1365
1282
  currentExecutingStepId: void 0,
1366
- userActionResult: state.userActionResult,
1367
1283
  formattedThinkingText: state.hasError ? void 0 : state.formattedThinkingText || void 0,
1368
1284
  isResolvingImages: needsImageResolve,
1369
1285
  totalElapsedMs: state.hasError ? void 0 : state.totalElapsedMs
@@ -1406,14 +1322,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1406
1322
  if (error.name !== "AbortError") {
1407
1323
  callbacksRef.current.onError?.(error);
1408
1324
  }
1409
- if (state.userActionPending) {
1410
- state.userActionPending = false;
1411
- state.userActionRequest = void 0;
1412
- callbacksRef.current.onUserActionEvent?.(
1413
- "USER_ACTION_FAILED",
1414
- "Connection lost. Please try again."
1415
- );
1416
- }
1417
1325
  const isAborted = error.name === "AbortError";
1418
1326
  setMessages(
1419
1327
  (prev) => prev.map(
@@ -1452,8 +1360,23 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1452
1360
  }
1453
1361
 
1454
1362
  // src/hooks/useChatV2.ts
1363
+ var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1364
+ function upsertPrompt(prompts, req) {
1365
+ const active = { ...req, status: "pending" };
1366
+ const idx = prompts.findIndex(
1367
+ (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
1368
+ );
1369
+ if (idx >= 0) {
1370
+ const next = prompts.slice();
1371
+ next[idx] = active;
1372
+ return next;
1373
+ }
1374
+ return [...prompts, active];
1375
+ }
1455
1376
  function getStoredOrInitialMessages(config) {
1456
1377
  if (!config.userId) return config.initialMessages ?? [];
1378
+ const activeStream = activeStreamStore.get(config.userId);
1379
+ if (activeStream) return activeStream.messages;
1457
1380
  const stored = chatStore.get(config.userId);
1458
1381
  if (stored.length > 0) return stored;
1459
1382
  if (config.initialMessages?.length) {
@@ -1467,11 +1390,16 @@ function getSessionIdFromMessages(messages) {
1467
1390
  }
1468
1391
  function useChatV2(config, callbacks = {}) {
1469
1392
  const [messages, setMessages] = react.useState(() => getStoredOrInitialMessages(config));
1470
- const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
1393
+ const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(() => {
1394
+ if (!config.userId) return false;
1395
+ return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1396
+ });
1471
1397
  const sessionIdRef = react.useRef(
1472
1398
  getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1473
1399
  );
1474
1400
  const prevUserIdRef = react.useRef(config.userId);
1401
+ const streamUserIdRef = react.useRef(void 0);
1402
+ const subscriptionPrevUserIdRef = react.useRef(config.userId);
1475
1403
  const callbacksRef = react.useRef(callbacks);
1476
1404
  callbacksRef.current = callbacks;
1477
1405
  const configRef = react.useRef(config);
@@ -1480,33 +1408,35 @@ function useChatV2(config, callbacks = {}) {
1480
1408
  messagesRef.current = messages;
1481
1409
  const storeAwareSetMessages = react.useCallback(
1482
1410
  (updater) => {
1483
- const { userId } = configRef.current;
1484
- if (userId && activeStreamStore.has(userId)) {
1485
- activeStreamStore.applyMessages(userId, updater);
1411
+ const streamUserId = streamUserIdRef.current;
1412
+ const currentUserId = configRef.current.userId;
1413
+ const storeKey = streamUserId ?? currentUserId;
1414
+ if (storeKey && activeStreamStore.has(storeKey)) {
1415
+ activeStreamStore.applyMessages(storeKey, updater);
1416
+ }
1417
+ if (!streamUserId || streamUserId === currentUserId) {
1418
+ setMessages(updater);
1486
1419
  }
1487
- setMessages(updater);
1488
1420
  },
1489
1421
  // eslint-disable-next-line react-hooks/exhaustive-deps
1490
1422
  []
1491
1423
  );
1492
1424
  const storeAwareSetIsWaiting = react.useCallback(
1493
1425
  (waiting) => {
1494
- const { userId } = configRef.current;
1495
- if (userId && activeStreamStore.has(userId)) {
1496
- activeStreamStore.setWaiting(userId, waiting);
1426
+ const streamUserId = streamUserIdRef.current;
1427
+ const currentUserId = configRef.current.userId;
1428
+ const storeKey = streamUserId ?? currentUserId;
1429
+ if (storeKey && activeStreamStore.has(storeKey)) {
1430
+ activeStreamStore.setWaiting(storeKey, waiting);
1431
+ }
1432
+ if (!streamUserId || streamUserId === currentUserId) {
1433
+ setIsWaitingForResponse(waiting);
1497
1434
  }
1498
- setIsWaitingForResponse(waiting);
1499
1435
  },
1500
1436
  // eslint-disable-next-line react-hooks/exhaustive-deps
1501
1437
  []
1502
1438
  );
1503
- const [userActionState, setUserActionState] = react.useState({
1504
- request: null,
1505
- result: null,
1506
- clearOtpTrigger: 0
1507
- });
1508
- const userActionStateRef = react.useRef(userActionState);
1509
- userActionStateRef.current = userActionState;
1439
+ const [userActionState, setUserActionState] = react.useState(EMPTY_USER_ACTION_STATE);
1510
1440
  const wrappedCallbacks = react.useMemo(() => ({
1511
1441
  ...callbacksRef.current,
1512
1442
  onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
@@ -1516,26 +1446,17 @@ function useChatV2(config, callbacks = {}) {
1516
1446
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1517
1447
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1518
1448
  onUserActionRequired: (request) => {
1519
- setUserActionState((prev) => ({ ...prev, request, result: null }));
1449
+ setUserActionState((prev) => ({
1450
+ ...prev,
1451
+ prompts: upsertPrompt(prev.prompts, request)
1452
+ }));
1520
1453
  callbacksRef.current.onUserActionRequired?.(request);
1521
1454
  },
1522
- onUserActionEvent: (eventType, message) => {
1523
- switch (eventType) {
1524
- case "USER_ACTION_SUCCESS":
1525
- setUserActionState((prev) => ({ ...prev, request: null, result: "approved" }));
1526
- break;
1527
- case "USER_ACTION_REJECTED":
1528
- setUserActionState((prev) => ({ ...prev, request: null, result: "rejected" }));
1529
- break;
1530
- case "USER_ACTION_EXPIRED":
1531
- case "USER_ACTION_FAILED":
1532
- setUserActionState((prev) => ({ ...prev, request: null }));
1533
- break;
1534
- case "USER_ACTION_INVALID":
1535
- setUserActionState((prev) => ({ ...prev, clearOtpTrigger: prev.clearOtpTrigger + 1 }));
1536
- break;
1537
- }
1538
- callbacksRef.current.onUserActionEvent?.(eventType, message);
1455
+ onUserNotification: (notification) => {
1456
+ setUserActionState(
1457
+ (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1458
+ );
1459
+ callbacksRef.current.onUserNotification?.(notification);
1539
1460
  }
1540
1461
  // eslint-disable-next-line react-hooks/exhaustive-deps
1541
1462
  }), []);
@@ -1583,6 +1504,7 @@ function useChatV2(config, callbacks = {}) {
1583
1504
  const abortController = new AbortController();
1584
1505
  const { userId } = configRef.current;
1585
1506
  if (userId) {
1507
+ streamUserIdRef.current = userId;
1586
1508
  const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
1587
1509
  activeStreamStore.start(userId, abortController, initialMessages);
1588
1510
  }
@@ -1593,9 +1515,11 @@ function useChatV2(config, callbacks = {}) {
1593
1515
  abortController,
1594
1516
  options
1595
1517
  );
1596
- if (userId) {
1597
- activeStreamStore.complete(userId);
1518
+ const finalStreamUserId = streamUserIdRef.current ?? userId;
1519
+ if (finalStreamUserId) {
1520
+ activeStreamStore.complete(finalStreamUserId);
1598
1521
  }
1522
+ streamUserIdRef.current = void 0;
1599
1523
  if (!abortController.signal.aborted && newSessionId && newSessionId !== sessionIdRef.current) {
1600
1524
  sessionIdRef.current = newSessionId;
1601
1525
  }
@@ -1612,12 +1536,14 @@ function useChatV2(config, callbacks = {}) {
1612
1536
  setMessages((prev) => [...msgs, ...prev]);
1613
1537
  }, []);
1614
1538
  const cancelStream = react.useCallback(() => {
1615
- if (configRef.current.userId) {
1616
- activeStreamStore.abort(configRef.current.userId);
1539
+ const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1540
+ if (streamUserId) {
1541
+ activeStreamStore.abort(streamUserId);
1617
1542
  }
1543
+ streamUserIdRef.current = void 0;
1618
1544
  cancelStreamManager();
1619
1545
  setIsWaitingForResponse(false);
1620
- setUserActionState((prev) => ({ ...prev, request: null, result: null }));
1546
+ setUserActionState((prev) => ({ ...prev, prompts: [] }));
1621
1547
  setMessages(
1622
1548
  (prev) => prev.map((msg) => {
1623
1549
  if (msg.isStreaming) {
@@ -1634,15 +1560,19 @@ function useChatV2(config, callbacks = {}) {
1634
1560
  );
1635
1561
  }, [cancelStreamManager]);
1636
1562
  const resetSession = react.useCallback(() => {
1563
+ const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1564
+ if (streamUserId) {
1565
+ activeStreamStore.abort(streamUserId);
1566
+ }
1637
1567
  if (configRef.current.userId) {
1638
- activeStreamStore.abort(configRef.current.userId);
1639
1568
  chatStore.delete(configRef.current.userId);
1640
1569
  }
1570
+ streamUserIdRef.current = void 0;
1641
1571
  setMessages([]);
1642
1572
  sessionIdRef.current = void 0;
1643
1573
  abortControllerRef.current?.abort();
1644
1574
  setIsWaitingForResponse(false);
1645
- setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
1575
+ setUserActionState(EMPTY_USER_ACTION_STATE);
1646
1576
  }, []);
1647
1577
  const getSessionId = react.useCallback(() => {
1648
1578
  return sessionIdRef.current;
@@ -1650,59 +1580,91 @@ function useChatV2(config, callbacks = {}) {
1650
1580
  const getMessages = react.useCallback(() => {
1651
1581
  return messages;
1652
1582
  }, [messages]);
1653
- const approveUserAction = react.useCallback(
1654
- async (otp) => {
1655
- const request = userActionStateRef.current.request;
1656
- if (!request) return;
1583
+ const setPromptStatus = react.useCallback(
1584
+ (userActionId, status) => {
1585
+ setUserActionState((prev) => ({
1586
+ ...prev,
1587
+ prompts: prev.prompts.map(
1588
+ (p) => p.userActionId === userActionId ? { ...p, status } : p
1589
+ )
1590
+ }));
1591
+ },
1592
+ []
1593
+ );
1594
+ const removePrompt = react.useCallback((userActionId) => {
1595
+ setUserActionState((prev) => ({
1596
+ ...prev,
1597
+ prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
1598
+ }));
1599
+ }, []);
1600
+ const submitUserAction2 = react.useCallback(
1601
+ async (userActionId, content) => {
1602
+ setPromptStatus(userActionId, "submitting");
1657
1603
  try {
1658
- await submitUserAction(configRef.current, request.userActionId, { otp });
1604
+ await submitUserAction(configRef.current, userActionId, content);
1605
+ removePrompt(userActionId);
1659
1606
  } catch (error) {
1660
- setUserActionState((prev) => ({
1661
- ...prev,
1662
- clearOtpTrigger: prev.clearOtpTrigger + 1
1663
- }));
1607
+ if (error instanceof UserActionStaleError) {
1608
+ setPromptStatus(userActionId, "stale");
1609
+ return;
1610
+ }
1611
+ setPromptStatus(userActionId, "pending");
1664
1612
  callbacksRef.current.onError?.(error);
1665
1613
  throw error;
1666
1614
  }
1667
1615
  },
1668
- []
1616
+ [removePrompt, setPromptStatus]
1669
1617
  );
1670
- const rejectUserAction = react.useCallback(async () => {
1671
- const request = userActionStateRef.current.request;
1672
- if (!request) return;
1673
- try {
1674
- setMessages((prev) => {
1675
- let lastStreamingIdx = -1;
1676
- for (let i = prev.length - 1; i >= 0; i--) {
1677
- if (prev[i].role === "assistant" && prev[i].isStreaming) {
1678
- lastStreamingIdx = i;
1679
- break;
1680
- }
1618
+ const cancelUserAction2 = react.useCallback(
1619
+ async (userActionId) => {
1620
+ setPromptStatus(userActionId, "submitting");
1621
+ try {
1622
+ await cancelUserAction(configRef.current, userActionId);
1623
+ removePrompt(userActionId);
1624
+ } catch (error) {
1625
+ if (error instanceof UserActionStaleError) {
1626
+ removePrompt(userActionId);
1627
+ return;
1681
1628
  }
1682
- if (lastStreamingIdx === -1) return prev;
1683
- return prev.map(
1684
- (msg, i) => i === lastStreamingIdx ? { ...msg, currentMessage: "Rejecting..." } : msg
1685
- );
1686
- });
1687
- await cancelUserAction(configRef.current, request.userActionId);
1688
- } catch (error) {
1689
- callbacksRef.current.onError?.(error);
1690
- throw error;
1691
- }
1692
- }, []);
1693
- const resendOtp = react.useCallback(async () => {
1694
- const request = userActionStateRef.current.request;
1695
- if (!request) return;
1696
- try {
1697
- await resendUserAction(configRef.current, request.userActionId);
1698
- } catch (error) {
1699
- callbacksRef.current.onError?.(error);
1700
- throw error;
1701
- }
1629
+ setPromptStatus(userActionId, "pending");
1630
+ callbacksRef.current.onError?.(error);
1631
+ throw error;
1632
+ }
1633
+ },
1634
+ [removePrompt, setPromptStatus]
1635
+ );
1636
+ const resendUserAction2 = react.useCallback(
1637
+ async (userActionId) => {
1638
+ setPromptStatus(userActionId, "submitting");
1639
+ try {
1640
+ await resendUserAction(configRef.current, userActionId);
1641
+ setPromptStatus(userActionId, "pending");
1642
+ } catch (error) {
1643
+ if (error instanceof UserActionStaleError) {
1644
+ setPromptStatus(userActionId, "stale");
1645
+ return;
1646
+ }
1647
+ setPromptStatus(userActionId, "pending");
1648
+ callbacksRef.current.onError?.(error);
1649
+ throw error;
1650
+ }
1651
+ },
1652
+ [setPromptStatus]
1653
+ );
1654
+ const dismissNotification = react.useCallback((id) => {
1655
+ setUserActionState((prev) => ({
1656
+ ...prev,
1657
+ notifications: prev.notifications.filter((n) => n.id !== id)
1658
+ }));
1702
1659
  }, []);
1703
1660
  react.useEffect(() => {
1661
+ const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
1662
+ subscriptionPrevUserIdRef.current = config.userId;
1704
1663
  const { userId } = config;
1705
1664
  if (!userId) return;
1665
+ if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
1666
+ streamUserIdRef.current = userId;
1667
+ }
1706
1668
  const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1707
1669
  setMessages(msgs);
1708
1670
  setIsWaitingForResponse(isWaiting);
@@ -1716,6 +1678,7 @@ function useChatV2(config, callbacks = {}) {
1716
1678
  }, [config.userId]);
1717
1679
  react.useEffect(() => {
1718
1680
  if (!config.userId) return;
1681
+ if (prevUserIdRef.current !== config.userId) return;
1719
1682
  const toSave = messages.filter((m) => !m.isStreaming);
1720
1683
  if (toSave.length > 0) {
1721
1684
  chatStore.set(config.userId, toSave);
@@ -1737,7 +1700,7 @@ function useChatV2(config, callbacks = {}) {
1737
1700
  setMessages([]);
1738
1701
  sessionIdRef.current = void 0;
1739
1702
  setIsWaitingForResponse(false);
1740
- setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
1703
+ setUserActionState(EMPTY_USER_ACTION_STATE);
1741
1704
  } else if (config.userId) {
1742
1705
  const active = activeStreamStore.get(config.userId);
1743
1706
  if (active) {
@@ -1764,9 +1727,10 @@ function useChatV2(config, callbacks = {}) {
1764
1727
  isWaitingForResponse,
1765
1728
  sessionId: sessionIdRef.current,
1766
1729
  userActionState,
1767
- approveUserAction,
1768
- rejectUserAction,
1769
- resendOtp
1730
+ submitUserAction: submitUserAction2,
1731
+ cancelUserAction: cancelUserAction2,
1732
+ resendUserAction: resendUserAction2,
1733
+ dismissNotification
1770
1734
  };
1771
1735
  }
1772
1736
  function useVoice() {
@@ -1885,15 +1849,151 @@ function useVoice() {
1885
1849
  };
1886
1850
  }
1887
1851
 
1852
+ // src/utils/jsonSchemaForm.ts
1853
+ function classifyField(field) {
1854
+ if (!field) return "text";
1855
+ if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
1856
+ switch (field.type) {
1857
+ case "boolean":
1858
+ return "boolean";
1859
+ case "integer":
1860
+ return "integer";
1861
+ case "number":
1862
+ return "decimal";
1863
+ case "string":
1864
+ return "text";
1865
+ default:
1866
+ return "text";
1867
+ }
1868
+ }
1869
+ function isNestedOrUnsupported(field) {
1870
+ if (!field) return false;
1871
+ if (field.type === "object" || field.type === "array") return true;
1872
+ if ("properties" in field && field.properties != null) return true;
1873
+ if ("items" in field && field.items != null) return true;
1874
+ return false;
1875
+ }
1876
+ function getOptions(field) {
1877
+ if (!field || !Array.isArray(field.oneOf)) return [];
1878
+ return field.oneOf.filter(
1879
+ (o) => !!o && typeof o === "object" && typeof o.const === "string"
1880
+ );
1881
+ }
1882
+ function isRequired(schema, key) {
1883
+ return Array.isArray(schema?.required) && schema.required.includes(key);
1884
+ }
1885
+ function coerceValue(field, raw) {
1886
+ const widget = classifyField(field);
1887
+ if (widget === "boolean") {
1888
+ if (typeof raw === "boolean") return raw;
1889
+ if (raw === "true") return true;
1890
+ if (raw === "false") return false;
1891
+ return Boolean(raw);
1892
+ }
1893
+ if (widget === "integer" || widget === "decimal") {
1894
+ if (raw === "" || raw == null) return void 0;
1895
+ const num = typeof raw === "number" ? raw : Number(String(raw).trim());
1896
+ if (Number.isNaN(num)) return void 0;
1897
+ return widget === "integer" ? Math.trunc(num) : num;
1898
+ }
1899
+ if (raw == null) return void 0;
1900
+ const str = String(raw);
1901
+ return str === "" ? void 0 : str;
1902
+ }
1903
+ function defaultValueFor(field) {
1904
+ if (!field || field.default === void 0) {
1905
+ return classifyField(field) === "boolean" ? false : "";
1906
+ }
1907
+ return field.default;
1908
+ }
1909
+ function validateField(field, value, required) {
1910
+ const widget = classifyField(field);
1911
+ const label = field?.title || "This field";
1912
+ const isEmpty = value === void 0 || value === null || typeof value === "string" && value.trim() === "";
1913
+ if (isEmpty) {
1914
+ if (required && widget !== "boolean") return `${label} is required.`;
1915
+ return null;
1916
+ }
1917
+ if (widget === "integer" || widget === "decimal") {
1918
+ const num = typeof value === "number" ? value : Number(value);
1919
+ if (Number.isNaN(num)) return `${label} must be a number.`;
1920
+ if (widget === "integer" && !Number.isInteger(num)) {
1921
+ return `${label} must be a whole number.`;
1922
+ }
1923
+ if (typeof field?.minimum === "number" && num < field.minimum) {
1924
+ return `${label} must be at least ${field.minimum}.`;
1925
+ }
1926
+ if (typeof field?.maximum === "number" && num > field.maximum) {
1927
+ return `${label} must be at most ${field.maximum}.`;
1928
+ }
1929
+ return null;
1930
+ }
1931
+ if (widget === "select") {
1932
+ const allowed = getOptions(field).map((o) => o.const);
1933
+ if (allowed.length > 0 && !allowed.includes(String(value))) {
1934
+ return `${label} has an invalid selection.`;
1935
+ }
1936
+ return null;
1937
+ }
1938
+ const str = String(value);
1939
+ if (typeof field?.minLength === "number" && str.length < field.minLength) {
1940
+ return `${label} must be at least ${field.minLength} characters.`;
1941
+ }
1942
+ if (typeof field?.maxLength === "number" && str.length > field.maxLength) {
1943
+ return `${label} must be at most ${field.maxLength} characters.`;
1944
+ }
1945
+ return null;
1946
+ }
1947
+ function renderableFields(schema) {
1948
+ const props = schema?.properties;
1949
+ if (!props) return [];
1950
+ return Object.entries(props).filter(([, field]) => !isNestedOrUnsupported(field));
1951
+ }
1952
+ function validateForm(schema, values) {
1953
+ const errors = {};
1954
+ for (const [key, field] of renderableFields(schema)) {
1955
+ const coerced = coerceValue(field, values[key]);
1956
+ const err = validateField(field, coerced, isRequired(schema, key));
1957
+ if (err) errors[key] = err;
1958
+ }
1959
+ return errors;
1960
+ }
1961
+ function buildContent(schema, values) {
1962
+ const content = {};
1963
+ for (const [key, field] of renderableFields(schema)) {
1964
+ const coerced = coerceValue(field, values[key]);
1965
+ if (coerced !== void 0) content[key] = coerced;
1966
+ }
1967
+ return content;
1968
+ }
1969
+
1970
+ // src/index.ts
1971
+ function migrateActiveStream(oldUserId, newUserId) {
1972
+ activeStreamStore.rename(oldUserId, newUserId);
1973
+ }
1974
+
1975
+ exports.UserActionStaleError = UserActionStaleError;
1976
+ exports.buildContent = buildContent;
1888
1977
  exports.buildFormattedThinking = buildFormattedThinking;
1889
1978
  exports.cancelUserAction = cancelUserAction;
1979
+ exports.classifyField = classifyField;
1980
+ exports.classifyUserActionKind = classifyUserActionKind;
1981
+ exports.coerceValue = coerceValue;
1890
1982
  exports.createInitialV2State = createInitialV2State;
1983
+ exports.defaultValueFor = defaultValueFor;
1891
1984
  exports.generateId = generateId;
1985
+ exports.getOptions = getOptions;
1986
+ exports.isNestedOrUnsupported = isNestedOrUnsupported;
1987
+ exports.isRequired = isRequired;
1988
+ exports.migrateActiveStream = migrateActiveStream;
1892
1989
  exports.processStreamEventV2 = processStreamEventV2;
1990
+ exports.renderableFields = renderableFields;
1893
1991
  exports.resendUserAction = resendUserAction;
1894
1992
  exports.streamWorkflowEvents = streamWorkflowEvents;
1895
1993
  exports.submitUserAction = submitUserAction;
1896
1994
  exports.useChatV2 = useChatV2;
1897
1995
  exports.useVoice = useVoice;
1996
+ exports.validateField = validateField;
1997
+ exports.validateForm = validateForm;
1898
1998
  //# sourceMappingURL=index.native.js.map
1899
1999
  //# sourceMappingURL=index.native.js.map