@paymanai/payman-ask-sdk 4.0.25 → 4.0.27

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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var react = require('react');
3
+ var React = require('react');
4
4
  var framerMotion = require('framer-motion');
5
5
  var clsx = require('clsx');
6
6
  var tailwindMerge = require('tailwind-merge');
@@ -33,6 +33,7 @@ function _interopNamespace(e) {
33
33
  return Object.freeze(n);
34
34
  }
35
35
 
36
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
36
37
  var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
37
38
  var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
38
39
  var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
@@ -300,6 +301,8 @@ function getEventMessage(event) {
300
301
  }
301
302
  case "RUN_IN_PROGRESS":
302
303
  return event.partialText || "Thinking...";
304
+ case "LLM_CALL_STARTED":
305
+ return event.description || "";
303
306
  case "RUN_COMPLETED":
304
307
  return "Agent run completed";
305
308
  case "RUN_FAILED":
@@ -437,7 +440,8 @@ function createInitialV2State() {
437
440
  finalData: void 0,
438
441
  steps: [],
439
442
  stepCounter: 0,
440
- currentExecutingStepId: void 0
443
+ currentExecutingStepId: void 0,
444
+ trailingActivity: false
441
445
  };
442
446
  }
443
447
  function upsertUserAction(state, req) {
@@ -459,6 +463,7 @@ function processStreamEventV2(rawEvent, state) {
459
463
  if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
460
464
  if (event.executionId) state.executionId = event.executionId;
461
465
  if (event.sessionId) state.sessionId = event.sessionId;
466
+ if (state.finalResponse) state.trailingActivity = true;
462
467
  const description = typeof event.description === "string" ? event.description : "";
463
468
  if (description) {
464
469
  for (let i = state.steps.length - 1; i >= 0; i--) {
@@ -493,7 +498,12 @@ function processStreamEventV2(rawEvent, state) {
493
498
  state.lastEventType = eventType;
494
499
  break;
495
500
  }
501
+ case "LLM_CALL_STARTED":
502
+ if (state.finalResponse) state.trailingActivity = true;
503
+ state.lastEventType = eventType;
504
+ break;
496
505
  case "INTENT_STARTED": {
506
+ if (state.finalResponse) state.trailingActivity = true;
497
507
  const stepId = `step-${state.stepCounter++}`;
498
508
  state.steps.push({
499
509
  id: stepId,
@@ -509,6 +519,7 @@ function processStreamEventV2(rawEvent, state) {
509
519
  break;
510
520
  }
511
521
  case "INTENT_COMPLETED": {
522
+ if (state.finalResponse) state.trailingActivity = true;
512
523
  const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
513
524
  if (intentStep) {
514
525
  intentStep.status = "completed";
@@ -540,6 +551,7 @@ function processStreamEventV2(rawEvent, state) {
540
551
  step2.status = "completed";
541
552
  }
542
553
  });
554
+ state.trailingActivity = false;
543
555
  state.lastEventType = eventType;
544
556
  break;
545
557
  }
@@ -611,6 +623,7 @@ function processStreamEventV2(rawEvent, state) {
611
623
  case "WORKFLOW_ERROR":
612
624
  state.hasError = true;
613
625
  state.errorMessage = event.errorMessage || event.message || "Workflow error";
626
+ state.trailingActivity = false;
614
627
  state.lastEventType = eventType;
615
628
  break;
616
629
  // ---- K2 pipeline stage lifecycle events ----
@@ -853,12 +866,12 @@ async function resolveRagImageUrls(config, content, signal) {
853
866
  }
854
867
  var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
855
868
  function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForResponse) {
856
- const abortControllerRef = react.useRef(null);
857
- const configRef = react.useRef(config);
869
+ const abortControllerRef = React.useRef(null);
870
+ const configRef = React.useRef(config);
858
871
  configRef.current = config;
859
- const callbacksRef = react.useRef(callbacks);
872
+ const callbacksRef = React.useRef(callbacks);
860
873
  callbacksRef.current = callbacks;
861
- const startStream = react.useCallback(
874
+ const startStream = React.useCallback(
862
875
  async (userMessage, streamingId, sessionId, externalAbortController, options) => {
863
876
  abortControllerRef.current?.abort();
864
877
  const abortController = externalAbortController ?? new AbortController();
@@ -899,7 +912,9 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
899
912
  const latestUsefulStep = [...state.steps].reverse().find(
900
913
  (s) => s.message && !isBlandStatus(s.message)
901
914
  );
902
- const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
915
+ const eventTypeUpper = typeof event.eventType === "string" ? event.eventType.toUpperCase() : "";
916
+ const liveEventLabel = eventTypeUpper === "KEEP_ALIVE" || eventTypeUpper === "LLM_CALL_STARTED" ? useful(getEventMessage(event)) : void 0;
917
+ const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? liveEventLabel ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
903
918
  // nothing useful seen yet). Render the bland label so the
904
919
  // bubble isn't blank.
905
920
  activeStep?.message ?? lastInProgressStep?.message ?? getEventMessage(event);
@@ -924,6 +939,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
924
939
  streamingContent: state.finalResponse,
925
940
  content: "",
926
941
  currentMessage,
942
+ hasTrailingActivity: state.trailingActivity,
927
943
  streamProgress: "processing",
928
944
  isError: false,
929
945
  steps: [...state.steps],
@@ -1057,7 +1073,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1057
1073
  },
1058
1074
  [setMessages, setIsWaitingForResponse]
1059
1075
  );
1060
- const cancelStream = react.useCallback(() => {
1076
+ const cancelStream = React.useCallback(() => {
1061
1077
  abortControllerRef.current?.abort();
1062
1078
  }, []);
1063
1079
  return {
@@ -1139,6 +1155,9 @@ function getUserActionSecondsLeft(prompt) {
1139
1155
  return void 0;
1140
1156
  }
1141
1157
  var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
1158
+ function promptSlotKey(p) {
1159
+ return p.toolCallId || p.userActionId;
1160
+ }
1142
1161
  function upsertPrompt(prompts, req) {
1143
1162
  const idx = prompts.findIndex(
1144
1163
  (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
@@ -1168,24 +1187,25 @@ function getSessionIdFromMessages(messages) {
1168
1187
  return messages.find((message) => message.sessionId)?.sessionId;
1169
1188
  }
1170
1189
  function useChatV2(config, callbacks = {}) {
1171
- const [messages, setMessages] = react.useState(() => getStoredOrInitialMessages(config));
1172
- const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(() => {
1190
+ const [messages, setMessages] = React.useState(() => getStoredOrInitialMessages(config));
1191
+ const [isWaitingForResponse, setIsWaitingForResponse] = React.useState(() => {
1173
1192
  if (!config.userId) return false;
1174
1193
  return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1175
1194
  });
1176
- const sessionIdRef = react.useRef(
1195
+ const sessionIdRef = React.useRef(
1177
1196
  getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1178
1197
  );
1179
- const prevUserIdRef = react.useRef(config.userId);
1180
- const streamUserIdRef = react.useRef(void 0);
1181
- const subscriptionPrevUserIdRef = react.useRef(config.userId);
1182
- const callbacksRef = react.useRef(callbacks);
1198
+ const prevUserIdRef = React.useRef(config.userId);
1199
+ const streamUserIdRef = React.useRef(void 0);
1200
+ const subscriptionPrevUserIdRef = React.useRef(config.userId);
1201
+ const callbacksRef = React.useRef(callbacks);
1183
1202
  callbacksRef.current = callbacks;
1184
- const configRef = react.useRef(config);
1203
+ const configRef = React.useRef(config);
1185
1204
  configRef.current = config;
1186
- const messagesRef = react.useRef(messages);
1205
+ const messagesRef = React.useRef(messages);
1187
1206
  messagesRef.current = messages;
1188
- const storeAwareSetMessages = react.useCallback(
1207
+ const pendingSubmissionsRef = React.useRef(/* @__PURE__ */ new Map());
1208
+ const storeAwareSetMessages = React.useCallback(
1189
1209
  (updater) => {
1190
1210
  const streamUserId = streamUserIdRef.current;
1191
1211
  const currentUserId = configRef.current.userId;
@@ -1200,7 +1220,7 @@ function useChatV2(config, callbacks = {}) {
1200
1220
  // eslint-disable-next-line react-hooks/exhaustive-deps
1201
1221
  []
1202
1222
  );
1203
- const storeAwareSetIsWaiting = react.useCallback(
1223
+ const storeAwareSetIsWaiting = React.useCallback(
1204
1224
  (waiting) => {
1205
1225
  const streamUserId = streamUserIdRef.current;
1206
1226
  const currentUserId = configRef.current.userId;
@@ -1215,11 +1235,13 @@ function useChatV2(config, callbacks = {}) {
1215
1235
  // eslint-disable-next-line react-hooks/exhaustive-deps
1216
1236
  []
1217
1237
  );
1218
- const [userActionState, setUserActionState] = react.useState(() => {
1238
+ const [userActionState, setUserActionState] = React.useState(() => {
1219
1239
  if (!config.userId) return EMPTY_USER_ACTION_STATE2;
1220
1240
  return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
1221
1241
  });
1222
- const storeAwareSetUserActionState = react.useCallback(
1242
+ const userActionStateRef = React.useRef(userActionState);
1243
+ userActionStateRef.current = userActionState;
1244
+ const storeAwareSetUserActionState = React.useCallback(
1223
1245
  (updater) => {
1224
1246
  const streamUserId = streamUserIdRef.current;
1225
1247
  const currentUserId = configRef.current.userId;
@@ -1237,7 +1259,7 @@ function useChatV2(config, callbacks = {}) {
1237
1259
  // eslint-disable-next-line react-hooks/exhaustive-deps
1238
1260
  []
1239
1261
  );
1240
- const wrappedCallbacks = react.useMemo(() => ({
1262
+ const wrappedCallbacks = React.useMemo(() => ({
1241
1263
  ...callbacksRef.current,
1242
1264
  onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
1243
1265
  onStreamStart: () => callbacksRef.current.onStreamStart?.(),
@@ -1246,6 +1268,12 @@ function useChatV2(config, callbacks = {}) {
1246
1268
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1247
1269
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1248
1270
  onUserActionRequired: (request) => {
1271
+ const requestSlotKey = promptSlotKey(request);
1272
+ for (const [pendingId, slotKey] of pendingSubmissionsRef.current) {
1273
+ if (slotKey === requestSlotKey) {
1274
+ pendingSubmissionsRef.current.delete(pendingId);
1275
+ }
1276
+ }
1249
1277
  storeAwareSetUserActionState((prev) => ({
1250
1278
  ...prev,
1251
1279
  prompts: upsertPrompt(prev.prompts, request)
@@ -1257,6 +1285,28 @@ function useChatV2(config, callbacks = {}) {
1257
1285
  (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1258
1286
  );
1259
1287
  callbacksRef.current.onUserNotification?.(notification);
1288
+ },
1289
+ // A submitted-but-unconfirmed prompt (see `submitUserAction`) is only
1290
+ // safe to drop once the stream has demonstrably moved on. `onEvent`
1291
+ // in useStreamManagerV2 calls `onStepsUpdate` for *every* processed
1292
+ // stream event, unconditionally — so the first time this fires
1293
+ // after a submission, the event in hand is either the rejection
1294
+ // retry (a fresh `USER_ACTION_REQUIRED`, which the handler above
1295
+ // already deleted this pending entry for, earlier in the very same
1296
+ // event) or genuine forward progress (RUN_IN_PROGRESS, a tool call,
1297
+ // content deltas, RUN_COMPLETED, ...). Anything still pending by
1298
+ // the time we get here therefore means the run resumed normally —
1299
+ // clear it.
1300
+ onStepsUpdate: (steps) => {
1301
+ if (pendingSubmissionsRef.current.size > 0) {
1302
+ const resolved = [...pendingSubmissionsRef.current.keys()];
1303
+ pendingSubmissionsRef.current.clear();
1304
+ storeAwareSetUserActionState((prev) => ({
1305
+ ...prev,
1306
+ prompts: prev.prompts.filter((p) => !resolved.includes(p.userActionId))
1307
+ }));
1308
+ }
1309
+ callbacksRef.current.onStepsUpdate?.(steps);
1260
1310
  }
1261
1311
  // eslint-disable-next-line react-hooks/exhaustive-deps
1262
1312
  }), []);
@@ -1266,7 +1316,7 @@ function useChatV2(config, callbacks = {}) {
1266
1316
  storeAwareSetMessages,
1267
1317
  storeAwareSetIsWaiting
1268
1318
  );
1269
- const sendMessage = react.useCallback(
1319
+ const sendMessage = React.useCallback(
1270
1320
  async (userMessage, options) => {
1271
1321
  if (!userMessage.trim()) return;
1272
1322
  if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
@@ -1326,16 +1376,16 @@ function useChatV2(config, callbacks = {}) {
1326
1376
  },
1327
1377
  [startStream]
1328
1378
  );
1329
- const clearMessages = react.useCallback(() => {
1379
+ const clearMessages = React.useCallback(() => {
1330
1380
  if (configRef.current.userId) {
1331
1381
  chatStore.delete(configRef.current.userId);
1332
1382
  }
1333
1383
  setMessages([]);
1334
1384
  }, []);
1335
- const prependMessages = react.useCallback((msgs) => {
1385
+ const prependMessages = React.useCallback((msgs) => {
1336
1386
  setMessages((prev) => [...msgs, ...prev]);
1337
1387
  }, []);
1338
- const cancelStream = react.useCallback(() => {
1388
+ const cancelStream = React.useCallback(() => {
1339
1389
  const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1340
1390
  if (streamUserId) {
1341
1391
  activeStreamStore.abort(streamUserId);
@@ -1359,7 +1409,7 @@ function useChatV2(config, callbacks = {}) {
1359
1409
  })
1360
1410
  );
1361
1411
  }, [cancelStreamManager, storeAwareSetUserActionState]);
1362
- const resetSession = react.useCallback(() => {
1412
+ const resetSession = React.useCallback(() => {
1363
1413
  const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1364
1414
  if (streamUserId) {
1365
1415
  activeStreamStore.abort(streamUserId);
@@ -1374,13 +1424,13 @@ function useChatV2(config, callbacks = {}) {
1374
1424
  setIsWaitingForResponse(false);
1375
1425
  storeAwareSetUserActionState(EMPTY_USER_ACTION_STATE2);
1376
1426
  }, []);
1377
- const getSessionId = react.useCallback(() => {
1427
+ const getSessionId = React.useCallback(() => {
1378
1428
  return sessionIdRef.current;
1379
1429
  }, []);
1380
- const getMessages = react.useCallback(() => {
1430
+ const getMessages = React.useCallback(() => {
1381
1431
  return messages;
1382
1432
  }, [messages]);
1383
- const setPromptStatus = react.useCallback(
1433
+ const setPromptStatus = React.useCallback(
1384
1434
  (userActionId, status) => {
1385
1435
  storeAwareSetUserActionState((prev) => ({
1386
1436
  ...prev,
@@ -1391,18 +1441,22 @@ function useChatV2(config, callbacks = {}) {
1391
1441
  },
1392
1442
  [storeAwareSetUserActionState]
1393
1443
  );
1394
- const removePrompt = react.useCallback((userActionId) => {
1444
+ const removePrompt = React.useCallback((userActionId) => {
1445
+ pendingSubmissionsRef.current.delete(userActionId);
1395
1446
  storeAwareSetUserActionState((prev) => ({
1396
1447
  ...prev,
1397
1448
  prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
1398
1449
  }));
1399
1450
  }, [storeAwareSetUserActionState]);
1400
- const submitUserAction2 = react.useCallback(
1451
+ const submitUserAction2 = React.useCallback(
1401
1452
  async (userActionId, content) => {
1402
1453
  setPromptStatus(userActionId, "submitting");
1403
1454
  try {
1404
1455
  await submitUserAction(configRef.current, userActionId, content);
1405
- removePrompt(userActionId);
1456
+ const prompt = userActionStateRef.current.prompts.find(
1457
+ (p) => p.userActionId === userActionId
1458
+ );
1459
+ pendingSubmissionsRef.current.set(userActionId, prompt ? promptSlotKey(prompt) : userActionId);
1406
1460
  } catch (error) {
1407
1461
  if (error instanceof UserActionStaleError) {
1408
1462
  setPromptStatus(userActionId, "stale");
@@ -1413,9 +1467,9 @@ function useChatV2(config, callbacks = {}) {
1413
1467
  throw error;
1414
1468
  }
1415
1469
  },
1416
- [removePrompt, setPromptStatus]
1470
+ [setPromptStatus]
1417
1471
  );
1418
- const cancelUserAction2 = react.useCallback(
1472
+ const cancelUserAction2 = React.useCallback(
1419
1473
  async (userActionId) => {
1420
1474
  setPromptStatus(userActionId, "submitting");
1421
1475
  try {
@@ -1433,7 +1487,7 @@ function useChatV2(config, callbacks = {}) {
1433
1487
  },
1434
1488
  [removePrompt, setPromptStatus]
1435
1489
  );
1436
- const resendUserAction2 = react.useCallback(
1490
+ const resendUserAction2 = React.useCallback(
1437
1491
  async (userActionId) => {
1438
1492
  setPromptStatus(userActionId, "submitting");
1439
1493
  try {
@@ -1451,7 +1505,7 @@ function useChatV2(config, callbacks = {}) {
1451
1505
  },
1452
1506
  [setPromptStatus]
1453
1507
  );
1454
- const expireUserAction2 = react.useCallback(
1508
+ const expireUserAction2 = React.useCallback(
1455
1509
  async (userActionId) => {
1456
1510
  setPromptStatus(userActionId, "expired");
1457
1511
  try {
@@ -1464,13 +1518,13 @@ function useChatV2(config, callbacks = {}) {
1464
1518
  },
1465
1519
  [setPromptStatus]
1466
1520
  );
1467
- const dismissNotification = react.useCallback((id) => {
1521
+ const dismissNotification = React.useCallback((id) => {
1468
1522
  storeAwareSetUserActionState((prev) => ({
1469
1523
  ...prev,
1470
1524
  notifications: prev.notifications.filter((n) => n.id !== id)
1471
1525
  }));
1472
1526
  }, [storeAwareSetUserActionState]);
1473
- react.useEffect(() => {
1527
+ React.useEffect(() => {
1474
1528
  const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
1475
1529
  subscriptionPrevUserIdRef.current = config.userId;
1476
1530
  const { userId } = config;
@@ -1492,7 +1546,7 @@ function useChatV2(config, callbacks = {}) {
1492
1546
  }
1493
1547
  return unsubscribe;
1494
1548
  }, [config.userId]);
1495
- react.useEffect(() => {
1549
+ React.useEffect(() => {
1496
1550
  if (!config.userId) return;
1497
1551
  if (prevUserIdRef.current !== config.userId) return;
1498
1552
  const toSave = messages.filter((m) => !m.isStreaming);
@@ -1500,14 +1554,14 @@ function useChatV2(config, callbacks = {}) {
1500
1554
  chatStore.set(config.userId, toSave);
1501
1555
  }
1502
1556
  }, [messages, config.userId]);
1503
- react.useEffect(() => {
1557
+ React.useEffect(() => {
1504
1558
  if (!config.userId || activeStreamStore.has(config.userId)) return;
1505
1559
  if (!config.initialMessages?.length || messagesRef.current.length > 0) return;
1506
1560
  chatStore.set(config.userId, config.initialMessages);
1507
1561
  setMessages(config.initialMessages);
1508
1562
  sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1509
1563
  }, [config.initialMessages, config.initialSessionId, config.userId]);
1510
- react.useEffect(() => {
1564
+ React.useEffect(() => {
1511
1565
  const prevUserId = prevUserIdRef.current;
1512
1566
  prevUserIdRef.current = config.userId;
1513
1567
  if (prevUserId === config.userId) return;
@@ -1557,12 +1611,12 @@ function getSpeechRecognition() {
1557
1611
  return window.SpeechRecognition || window.webkitSpeechRecognition || null;
1558
1612
  }
1559
1613
  function useVoice(config = {}, callbacks = {}) {
1560
- const [voiceState, setVoiceState] = react.useState("idle");
1561
- const [transcribedText, setTranscribedText] = react.useState("");
1562
- const [isAvailable, setIsAvailable] = react.useState(false);
1563
- const [isRecording, setIsRecording] = react.useState(false);
1564
- const recognitionRef = react.useRef(null);
1565
- const autoStopTimerRef = react.useRef(null);
1614
+ const [voiceState, setVoiceState] = React.useState("idle");
1615
+ const [transcribedText, setTranscribedText] = React.useState("");
1616
+ const [isAvailable, setIsAvailable] = React.useState(false);
1617
+ const [isRecording, setIsRecording] = React.useState(false);
1618
+ const recognitionRef = React.useRef(null);
1619
+ const autoStopTimerRef = React.useRef(null);
1566
1620
  const {
1567
1621
  lang = "en-US",
1568
1622
  interimResults = true,
@@ -1571,14 +1625,14 @@ function useVoice(config = {}, callbacks = {}) {
1571
1625
  autoStopAfterSilence
1572
1626
  } = config;
1573
1627
  const { onStart, onEnd, onResult, onError, onStateChange } = callbacks;
1574
- react.useEffect(() => {
1628
+ React.useEffect(() => {
1575
1629
  const SpeechRecognitionAPI = getSpeechRecognition();
1576
1630
  setIsAvailable(SpeechRecognitionAPI !== null);
1577
1631
  }, []);
1578
- react.useEffect(() => {
1632
+ React.useEffect(() => {
1579
1633
  onStateChange?.(voiceState);
1580
1634
  }, [voiceState, onStateChange]);
1581
- const requestPermissions = react.useCallback(async () => {
1635
+ const requestPermissions = React.useCallback(async () => {
1582
1636
  try {
1583
1637
  const result = await navigator.mediaDevices.getUserMedia({
1584
1638
  audio: true
@@ -1595,7 +1649,7 @@ function useVoice(config = {}, callbacks = {}) {
1595
1649
  };
1596
1650
  }
1597
1651
  }, []);
1598
- const getPermissions = react.useCallback(async () => {
1652
+ const getPermissions = React.useCallback(async () => {
1599
1653
  if (typeof navigator === "undefined" || !navigator.permissions) {
1600
1654
  return {
1601
1655
  granted: false,
@@ -1617,13 +1671,13 @@ function useVoice(config = {}, callbacks = {}) {
1617
1671
  };
1618
1672
  }
1619
1673
  }, []);
1620
- const clearAutoStopTimer = react.useCallback(() => {
1674
+ const clearAutoStopTimer = React.useCallback(() => {
1621
1675
  if (autoStopTimerRef.current) {
1622
1676
  clearTimeout(autoStopTimerRef.current);
1623
1677
  autoStopTimerRef.current = null;
1624
1678
  }
1625
1679
  }, []);
1626
- const resetAutoStopTimer = react.useCallback(() => {
1680
+ const resetAutoStopTimer = React.useCallback(() => {
1627
1681
  clearAutoStopTimer();
1628
1682
  if (autoStopAfterSilence && autoStopAfterSilence > 0) {
1629
1683
  autoStopTimerRef.current = setTimeout(() => {
@@ -1633,7 +1687,7 @@ function useVoice(config = {}, callbacks = {}) {
1633
1687
  }, autoStopAfterSilence);
1634
1688
  }
1635
1689
  }, [autoStopAfterSilence, clearAutoStopTimer, isRecording]);
1636
- const stopRecording = react.useCallback(() => {
1690
+ const stopRecording = React.useCallback(() => {
1637
1691
  if (recognitionRef.current) {
1638
1692
  try {
1639
1693
  recognitionRef.current.stop();
@@ -1645,7 +1699,7 @@ function useVoice(config = {}, callbacks = {}) {
1645
1699
  setIsRecording(false);
1646
1700
  setVoiceState("idle");
1647
1701
  }, [clearAutoStopTimer]);
1648
- const startRecording = react.useCallback(async () => {
1702
+ const startRecording = React.useCallback(async () => {
1649
1703
  const SpeechRecognitionAPI = getSpeechRecognition();
1650
1704
  if (!SpeechRecognitionAPI) {
1651
1705
  onError?.("Speech recognition not supported in this browser");
@@ -1735,15 +1789,15 @@ function useVoice(config = {}, callbacks = {}) {
1735
1789
  resetAutoStopTimer,
1736
1790
  clearAutoStopTimer
1737
1791
  ]);
1738
- const clearTranscript = react.useCallback(() => {
1792
+ const clearTranscript = React.useCallback(() => {
1739
1793
  setTranscribedText("");
1740
1794
  }, []);
1741
- const reset = react.useCallback(() => {
1795
+ const reset = React.useCallback(() => {
1742
1796
  stopRecording();
1743
1797
  setTranscribedText("");
1744
1798
  setVoiceState("idle");
1745
1799
  }, [stopRecording]);
1746
- react.useEffect(() => {
1800
+ React.useEffect(() => {
1747
1801
  return () => {
1748
1802
  if (recognitionRef.current) {
1749
1803
  try {
@@ -1883,9 +1937,9 @@ function buildContent(schema, values) {
1883
1937
  }
1884
1938
  return content;
1885
1939
  }
1886
- var PaymanChatContext = react.createContext(void 0);
1940
+ var PaymanChatContext = React.createContext(void 0);
1887
1941
  function usePaymanChat() {
1888
- const context = react.useContext(PaymanChatContext);
1942
+ const context = React.useContext(PaymanChatContext);
1889
1943
  if (!context) {
1890
1944
  throw new Error("usePaymanChat must be used within a PaymanChat component");
1891
1945
  }
@@ -2082,6 +2136,9 @@ function getConflictErrorMessage(errorDetails) {
2082
2136
  }
2083
2137
  function initSentryIfNeeded(dsn) {
2084
2138
  if (!dsn) return;
2139
+ if (typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")) {
2140
+ return;
2141
+ }
2085
2142
  if (Sentry__namespace.getClient()) return;
2086
2143
  Sentry__namespace.init({
2087
2144
  dsn,
@@ -2143,8 +2200,8 @@ function ImageLightbox({
2143
2200
  alt = "",
2144
2201
  onClose
2145
2202
  }) {
2146
- const [isMounted, setIsMounted] = react.useState(false);
2147
- const [isImageLoaded, setIsImageLoaded] = react.useState(false);
2203
+ const [isMounted, setIsMounted] = React.useState(false);
2204
+ const [isImageLoaded, setIsImageLoaded] = React.useState(false);
2148
2205
  const overlayStyle = {
2149
2206
  position: "fixed",
2150
2207
  inset: 0,
@@ -2165,14 +2222,14 @@ function ImageLightbox({
2165
2222
  justifyContent: "center",
2166
2223
  overflow: "hidden"
2167
2224
  };
2168
- react.useEffect(() => {
2225
+ React.useEffect(() => {
2169
2226
  setIsMounted(true);
2170
2227
  return () => setIsMounted(false);
2171
2228
  }, []);
2172
- react.useEffect(() => {
2229
+ React.useEffect(() => {
2173
2230
  setIsImageLoaded(false);
2174
2231
  }, [src]);
2175
- react.useEffect(() => {
2232
+ React.useEffect(() => {
2176
2233
  if (typeof document === "undefined") return;
2177
2234
  const previousOverflow = document.body.style.overflow;
2178
2235
  document.body.style.overflow = "hidden";
@@ -2180,7 +2237,7 @@ function ImageLightbox({
2180
2237
  document.body.style.overflow = previousOverflow;
2181
2238
  };
2182
2239
  }, []);
2183
- react.useEffect(() => {
2240
+ React.useEffect(() => {
2184
2241
  if (typeof document === "undefined") return;
2185
2242
  const handleKeyDown = (event) => {
2186
2243
  if (event.key === "Escape") {
@@ -2276,15 +2333,15 @@ function MarkdownImage({
2276
2333
  maxHeight: "18rem",
2277
2334
  objectFit: "contain"
2278
2335
  };
2279
- const [isLoaded, setIsLoaded] = react.useState(false);
2280
- const [hasError, setHasError] = react.useState(false);
2281
- const [isLightboxOpen, setIsLightboxOpen] = react.useState(false);
2282
- const isUnresolvedRagImage = react.useMemo(
2336
+ const [isLoaded, setIsLoaded] = React.useState(false);
2337
+ const [hasError, setHasError] = React.useState(false);
2338
+ const [isLightboxOpen, setIsLightboxOpen] = React.useState(false);
2339
+ const isUnresolvedRagImage = React.useMemo(
2283
2340
  () => src ? isUnresolvedRagImageSource(src) : false,
2284
2341
  [src]
2285
2342
  );
2286
2343
  const isResolvingRagImage = isResolving && isUnresolvedRagImage;
2287
- react.useEffect(() => {
2344
+ React.useEffect(() => {
2288
2345
  setIsLoaded(false);
2289
2346
  setHasError(false);
2290
2347
  setIsLightboxOpen(false);
@@ -2411,9 +2468,9 @@ function AgentMessage({
2411
2468
  const hasTraceData = !!(message.tracingData || message.executionId) && !isStreaming;
2412
2469
  const isCancelled = message.isCancelled ?? false;
2413
2470
  const currentExecutingStepId = message.currentExecutingStepId;
2414
- const [isStepsExpanded, setIsStepsExpanded] = react.useState(false);
2415
- const wasStreamingRef = react.useRef(isStreaming);
2416
- react.useEffect(() => {
2471
+ const [isStepsExpanded, setIsStepsExpanded] = React.useState(false);
2472
+ const wasStreamingRef = React.useRef(isStreaming);
2473
+ React.useEffect(() => {
2417
2474
  if (isStreaming && hasSteps) {
2418
2475
  setIsStepsExpanded(true);
2419
2476
  }
@@ -2430,13 +2487,13 @@ function AgentMessage({
2430
2487
  const completedWithNoContent = !isStreaming && !isCancelled && content.length === 0 && (message.streamProgress === "completed" || message.streamProgress === "error");
2431
2488
  const conflictErrorMessage = getConflictErrorMessage(message.errorDetails);
2432
2489
  const isError = !!conflictErrorMessage || (isFriendlyWorkflowError(message.errorDetails) || looksLikeRawError(content)) && !hasMeaningfulContent || completedWithNoContent;
2433
- const currentStep = react.useMemo(
2490
+ const currentStep = React.useMemo(
2434
2491
  () => message.steps?.find(
2435
2492
  (s) => s.id === currentExecutingStepId && s.status === "in_progress"
2436
2493
  ),
2437
2494
  [message.steps, currentExecutingStepId]
2438
2495
  );
2439
- const markdownRenderers = react.useMemo(
2496
+ const markdownRenderers = React.useMemo(
2440
2497
  () => createMarkdownComponents({
2441
2498
  isResolvingImages: message.isResolvingImages
2442
2499
  }),
@@ -2510,8 +2567,8 @@ function AgentMessage({
2510
2567
  }) })
2511
2568
  }
2512
2569
  ) });
2513
- const stepsToggleRef = react.useRef(null);
2514
- const handleStepsToggle = react.useCallback(() => {
2570
+ const stepsToggleRef = React.useRef(null);
2571
+ const handleStepsToggle = React.useCallback(() => {
2515
2572
  setIsStepsExpanded((prev) => {
2516
2573
  const next = !prev;
2517
2574
  if (next) {
@@ -2839,23 +2896,23 @@ function MessageList({
2839
2896
  isLoadingMoreMessages = false,
2840
2897
  hasMoreMessages = false
2841
2898
  }) {
2842
- const scrollRef = react.useRef(null);
2843
- const isNearBottomRef = react.useRef(true);
2844
- const [showScrollBtn, setShowScrollBtn] = react.useState(false);
2845
- const prevMessageCountRef = react.useRef(messages.length);
2846
- const scrollHeightBeforePrependRef = react.useRef(null);
2847
- const firstMessageIdRef = react.useRef(messages[0]?.id);
2848
- const getDistanceFromBottom = react.useCallback(() => {
2899
+ const scrollRef = React.useRef(null);
2900
+ const isNearBottomRef = React.useRef(true);
2901
+ const [showScrollBtn, setShowScrollBtn] = React.useState(false);
2902
+ const prevMessageCountRef = React.useRef(messages.length);
2903
+ const scrollHeightBeforePrependRef = React.useRef(null);
2904
+ const firstMessageIdRef = React.useRef(messages[0]?.id);
2905
+ const getDistanceFromBottom = React.useCallback(() => {
2849
2906
  const el = scrollRef.current;
2850
2907
  if (!el) return 0;
2851
2908
  return el.scrollHeight - el.scrollTop - el.clientHeight;
2852
2909
  }, []);
2853
- const scrollToBottom = react.useCallback((behavior = "smooth") => {
2910
+ const scrollToBottom = React.useCallback((behavior = "smooth") => {
2854
2911
  const el = scrollRef.current;
2855
2912
  if (!el) return;
2856
2913
  el.scrollTo({ top: el.scrollHeight, behavior });
2857
2914
  }, []);
2858
- const handleScroll = react.useCallback(() => {
2915
+ const handleScroll = React.useCallback(() => {
2859
2916
  const el = scrollRef.current;
2860
2917
  if (!el) return;
2861
2918
  const distance = getDistanceFromBottom();
@@ -2867,7 +2924,7 @@ function MessageList({
2867
2924
  onLoadMoreMessages();
2868
2925
  }
2869
2926
  }, [getDistanceFromBottom, hasMoreMessages, isLoadingMoreMessages, onLoadMoreMessages]);
2870
- react.useLayoutEffect(() => {
2927
+ React.useLayoutEffect(() => {
2871
2928
  const el = scrollRef.current;
2872
2929
  const prevHeight = scrollHeightBeforePrependRef.current;
2873
2930
  const newFirstId = messages[0]?.id;
@@ -2877,25 +2934,25 @@ function MessageList({
2877
2934
  }
2878
2935
  firstMessageIdRef.current = newFirstId;
2879
2936
  }, [messages]);
2880
- react.useEffect(() => {
2937
+ React.useEffect(() => {
2881
2938
  const prevCount = prevMessageCountRef.current;
2882
2939
  prevMessageCountRef.current = messages.length;
2883
2940
  if (messages.length > prevCount && isNearBottomRef.current) {
2884
2941
  requestAnimationFrame(() => scrollToBottom());
2885
2942
  }
2886
2943
  }, [messages.length, scrollToBottom]);
2887
- react.useEffect(() => {
2944
+ React.useEffect(() => {
2888
2945
  const lastMsg = messages[messages.length - 1];
2889
2946
  if (!lastMsg?.isStreaming) return;
2890
2947
  if (!isNearBottomRef.current) return;
2891
2948
  requestAnimationFrame(() => scrollToBottom());
2892
2949
  });
2893
- react.useEffect(() => {
2950
+ React.useEffect(() => {
2894
2951
  if (messages.length > 0) {
2895
2952
  setTimeout(() => scrollToBottom("instant"), 50);
2896
2953
  }
2897
2954
  }, []);
2898
- react.useEffect(() => {
2955
+ React.useEffect(() => {
2899
2956
  const handleStepsToggle = () => {
2900
2957
  requestAnimationFrame(() => {
2901
2958
  requestAnimationFrame(() => {
@@ -2912,7 +2969,7 @@ function MessageList({
2912
2969
  return () => el.removeEventListener("payman-steps-toggle", handleStepsToggle);
2913
2970
  }
2914
2971
  }, [getDistanceFromBottom, scrollToBottom]);
2915
- const handleScrollToBottom = react.useCallback(() => {
2972
+ const handleScrollToBottom = React.useCallback(() => {
2916
2973
  scrollToBottom();
2917
2974
  }, [scrollToBottom]);
2918
2975
  if (isLoading) {
@@ -3045,7 +3102,7 @@ function ResetSessionConfirmModal({
3045
3102
  onClose,
3046
3103
  onConfirm
3047
3104
  }) {
3048
- const handleKeyDown = react.useCallback(
3105
+ const handleKeyDown = React.useCallback(
3049
3106
  (event) => {
3050
3107
  if (event.key === "Escape") {
3051
3108
  onClose();
@@ -3053,7 +3110,7 @@ function ResetSessionConfirmModal({
3053
3110
  },
3054
3111
  [onClose]
3055
3112
  );
3056
- react.useEffect(() => {
3113
+ React.useEffect(() => {
3057
3114
  if (!isOpen || typeof document === "undefined") return;
3058
3115
  document.addEventListener("keydown", handleKeyDown);
3059
3116
  const previousOverflow = document.body.style.overflow;
@@ -3159,14 +3216,14 @@ function UserMessageV2({
3159
3216
  retryDisabled = false,
3160
3217
  actions
3161
3218
  }) {
3162
- const [copied, setCopied] = react.useState(false);
3163
- const [toast, setToast] = react.useState(null);
3164
- const copyResetTimerRef = react.useRef(null);
3165
- const toastTimerRef = react.useRef(null);
3219
+ const [copied, setCopied] = React.useState(false);
3220
+ const [toast, setToast] = React.useState(null);
3221
+ const copyResetTimerRef = React.useRef(null);
3222
+ const toastTimerRef = React.useRef(null);
3166
3223
  const showCopyAction = actions?.copy ?? true;
3167
3224
  const showEditAction = actions?.edit ?? false;
3168
3225
  const showRetryAction = actions?.retry ?? false;
3169
- react.useEffect(() => {
3226
+ React.useEffect(() => {
3170
3227
  return () => {
3171
3228
  if (copyResetTimerRef.current) clearTimeout(copyResetTimerRef.current);
3172
3229
  if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
@@ -3322,18 +3379,18 @@ function MarkdownImageV2({
3322
3379
  onImageClick
3323
3380
  }) {
3324
3381
  const cachedAlready = src ? loadedImageCache.has(src) : false;
3325
- const [isVisible, setIsVisible] = react.useState(cachedAlready);
3326
- const [isLoaded, setIsLoaded] = react.useState(cachedAlready);
3327
- const [hasError, setHasError] = react.useState(false);
3328
- const [retryCount, setRetryCount] = react.useState(0);
3329
- const sentinelRef = react.useRef(null);
3330
- const prevLoadedRef = react.useRef(cachedAlready);
3331
- const isUnresolvedRag = react.useMemo(
3382
+ const [isVisible, setIsVisible] = React.useState(cachedAlready);
3383
+ const [isLoaded, setIsLoaded] = React.useState(cachedAlready);
3384
+ const [hasError, setHasError] = React.useState(false);
3385
+ const [retryCount, setRetryCount] = React.useState(0);
3386
+ const sentinelRef = React.useRef(null);
3387
+ const prevLoadedRef = React.useRef(cachedAlready);
3388
+ const isUnresolvedRag = React.useMemo(
3332
3389
  () => src ? isUnresolvedRagImageSource2(src) : false,
3333
3390
  [src]
3334
3391
  );
3335
3392
  const isResolvingRag = Boolean(isResolving && isUnresolvedRag);
3336
- react.useEffect(() => {
3393
+ React.useEffect(() => {
3337
3394
  if (src && loadedImageCache.has(src)) {
3338
3395
  setIsLoaded(true);
3339
3396
  setIsVisible(true);
@@ -3342,7 +3399,7 @@ function MarkdownImageV2({
3342
3399
  setHasError(false);
3343
3400
  setRetryCount(0);
3344
3401
  }, [src]);
3345
- react.useEffect(() => {
3402
+ React.useEffect(() => {
3346
3403
  const el = sentinelRef.current;
3347
3404
  if (!el || !src) return;
3348
3405
  const rect = el.getBoundingClientRect();
@@ -3463,9 +3520,88 @@ function MarkdownImageV2({
3463
3520
  }
3464
3521
  ) });
3465
3522
  }
3466
- function buildComponents(onImageClick, isResolvingRef) {
3523
+ var RAG_SOURCE_CLASS = "payman-v2-rag-source";
3524
+ var RAG_FOLLOW_UP_CLASS = "payman-v2-rag-follow-up";
3525
+ var SOURCE_PATTERN = /^Sources?:\s*\S/i;
3526
+ var MAX_FOLLOW_UP_LENGTH = 320;
3527
+ var HR_PATTERN = /^-{3,}$|^_{3,}$|^\*{3,}$/;
3528
+ function normalizeRagParagraphText(text) {
3529
+ return text.replace(/\s+/g, " ").trim();
3530
+ }
3531
+ function isRagSourceParagraph(text) {
3532
+ return SOURCE_PATTERN.test(normalizeRagParagraphText(text));
3533
+ }
3534
+ function isRagFollowUpCandidate(text) {
3535
+ const normalized = normalizeRagParagraphText(text);
3536
+ return normalized.length > 0 && normalized.length <= MAX_FOLLOW_UP_LENGTH && normalized.endsWith("?");
3537
+ }
3538
+ function extractRagParagraphTexts(content) {
3539
+ const paragraphs = [];
3540
+ const currentParagraph = [];
3541
+ const flushParagraph = () => {
3542
+ const paragraph = normalizeRagParagraphText(currentParagraph.join(" "));
3543
+ currentParagraph.length = 0;
3544
+ if (paragraph) paragraphs.push(paragraph);
3545
+ };
3546
+ content.replace(/\\n/g, "\n").split(/\r?\n/).forEach((line) => {
3547
+ const trimmedLine = line.trim();
3548
+ if (!trimmedLine) {
3549
+ flushParagraph();
3550
+ return;
3551
+ }
3552
+ if (HR_PATTERN.test(trimmedLine)) {
3553
+ flushParagraph();
3554
+ return;
3555
+ }
3556
+ currentParagraph.push(trimmedLine);
3557
+ });
3558
+ flushParagraph();
3559
+ return paragraphs;
3560
+ }
3561
+ function getRagAnswerParagraphRoles(paragraphs) {
3562
+ const normalized = paragraphs.map(normalizeRagParagraphText);
3563
+ const roles = normalized.map(() => null);
3564
+ const sourceIndexes = normalized.map((paragraph, index) => isRagSourceParagraph(paragraph) ? index : -1).filter((index) => index >= 0);
3565
+ for (const sourceIndex of sourceIndexes) {
3566
+ roles[sourceIndex] = "source";
3567
+ for (const candidateIndex of [sourceIndex - 1, sourceIndex + 1]) {
3568
+ if (candidateIndex >= 0 && candidateIndex < normalized.length && roles[candidateIndex] == null && isRagFollowUpCandidate(normalized[candidateIndex] ?? "")) {
3569
+ roles[candidateIndex] = "follow-up";
3570
+ }
3571
+ }
3572
+ }
3573
+ return roles;
3574
+ }
3575
+ function getRagAnswerParagraphRole(paragraph, paragraphs) {
3576
+ const normalizedParagraph = normalizeRagParagraphText(paragraph);
3577
+ if (isRagSourceParagraph(normalizedParagraph)) return "source";
3578
+ const roles = getRagAnswerParagraphRoles(paragraphs);
3579
+ return paragraphs.map(normalizeRagParagraphText).some((candidate, index) => {
3580
+ return candidate === normalizedParagraph && roles[index] === "follow-up";
3581
+ }) ? "follow-up" : null;
3582
+ }
3583
+ function ragAnswerRoleClassName(role) {
3584
+ if (role === "source") return RAG_SOURCE_CLASS;
3585
+ if (role === "follow-up") return RAG_FOLLOW_UP_CLASS;
3586
+ return void 0;
3587
+ }
3588
+ function reactNodeText(node) {
3589
+ if (typeof node === "string" || typeof node === "number") return String(node);
3590
+ if (Array.isArray(node)) return node.map(reactNodeText).join("");
3591
+ if (React__namespace.isValidElement(node)) {
3592
+ return reactNodeText(node.props.children);
3593
+ }
3594
+ return "";
3595
+ }
3596
+ function buildComponents(onImageClick, isResolvingRef, ragParagraphsRef) {
3467
3597
  return {
3468
- p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { children }),
3598
+ p: ({ children }) => {
3599
+ const role = getRagAnswerParagraphRole(
3600
+ reactNodeText(children),
3601
+ ragParagraphsRef?.current ?? []
3602
+ );
3603
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: ragAnswerRoleClassName(role), children });
3604
+ },
3469
3605
  code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
3470
3606
  pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { children }) }),
3471
3607
  ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { children }),
@@ -3501,10 +3637,15 @@ function MarkdownRendererV2({
3501
3637
  isResolvingImages,
3502
3638
  onImageClick
3503
3639
  }) {
3504
- const isResolvingRef = react.useRef(isResolvingImages);
3640
+ const isResolvingRef = React.useRef(isResolvingImages);
3505
3641
  isResolvingRef.current = isResolvingImages;
3506
- const components = react.useMemo(
3507
- () => buildComponents(onImageClick, isResolvingRef),
3642
+ const ragParagraphsRef = React.useRef([]);
3643
+ ragParagraphsRef.current = React.useMemo(
3644
+ () => extractRagParagraphTexts(content),
3645
+ [content]
3646
+ );
3647
+ const components = React.useMemo(
3648
+ () => buildComponents(onImageClick, isResolvingRef, ragParagraphsRef),
3508
3649
  [onImageClick]
3509
3650
  );
3510
3651
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3561,14 +3702,14 @@ function FeedbackReasonModal({
3561
3702
  onClose,
3562
3703
  onSubmit
3563
3704
  }) {
3564
- const [reason, setReason] = react.useState(
3705
+ const [reason, setReason] = React.useState(
3565
3706
  NEGATIVE_FEEDBACK_REASONS[0].value
3566
3707
  );
3567
- const [details, setDetails] = react.useState("");
3568
- const [submitting, setSubmitting] = react.useState(false);
3569
- const [error, setError] = react.useState(null);
3570
- const [reasonOpen, setReasonOpen] = react.useState(false);
3571
- react.useEffect(() => {
3708
+ const [details, setDetails] = React.useState("");
3709
+ const [submitting, setSubmitting] = React.useState(false);
3710
+ const [error, setError] = React.useState(null);
3711
+ const [reasonOpen, setReasonOpen] = React.useState(false);
3712
+ React.useEffect(() => {
3572
3713
  if (open) {
3573
3714
  setReason(NEGATIVE_FEEDBACK_REASONS[0].value);
3574
3715
  setDetails("");
@@ -3577,7 +3718,7 @@ function FeedbackReasonModal({
3577
3718
  setReasonOpen(false);
3578
3719
  }
3579
3720
  }, [open]);
3580
- const handleKeyDown = react.useCallback(
3721
+ const handleKeyDown = React.useCallback(
3581
3722
  (event) => {
3582
3723
  if (event.key !== "Escape") return;
3583
3724
  if (reasonOpen) {
@@ -3588,7 +3729,7 @@ function FeedbackReasonModal({
3588
3729
  },
3589
3730
  [onClose, reasonOpen]
3590
3731
  );
3591
- react.useEffect(() => {
3732
+ React.useEffect(() => {
3592
3733
  if (!open || typeof document === "undefined") return;
3593
3734
  document.addEventListener("keydown", handleKeyDown);
3594
3735
  const previousOverflow = document.body.style.overflow;
@@ -3872,17 +4013,17 @@ function charDelay(char, speed, multiplier) {
3872
4013
  function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDisplayedText, speedMultiplier = 1) {
3873
4014
  const instant = speedMultiplier === 0;
3874
4015
  const multiplier = instant ? 1 : Math.max(speedMultiplier, 0.1);
3875
- const [displayedText, setDisplayedText] = react.useState("");
3876
- const displayedRef = react.useRef("");
3877
- const targetRef = react.useRef(targetText);
3878
- const enabledRef = react.useRef(enabled);
3879
- const initialDisplayedRef = react.useRef(initialDisplayedText);
3880
- const timerRef = react.useRef(null);
3881
- const runningRef = react.useRef(false);
4016
+ const [displayedText, setDisplayedText] = React.useState("");
4017
+ const displayedRef = React.useRef("");
4018
+ const targetRef = React.useRef(targetText);
4019
+ const enabledRef = React.useRef(enabled);
4020
+ const initialDisplayedRef = React.useRef(initialDisplayedText);
4021
+ const timerRef = React.useRef(null);
4022
+ const runningRef = React.useRef(false);
3882
4023
  targetRef.current = targetText;
3883
4024
  enabledRef.current = enabled;
3884
4025
  initialDisplayedRef.current = initialDisplayedText;
3885
- react.useEffect(() => {
4026
+ React.useEffect(() => {
3886
4027
  if (!enabled || instant) {
3887
4028
  if (timerRef.current) {
3888
4029
  clearTimeout(timerRef.current);
@@ -3965,26 +4106,26 @@ function AssistantMessageV2({
3965
4106
  actions,
3966
4107
  typingSpeed = 4
3967
4108
  }) {
3968
- const [copied, setCopied] = react.useState(false);
3969
- const [activeFeedback, setActiveFeedback] = react.useState(
4109
+ const [copied, setCopied] = React.useState(false);
4110
+ const [activeFeedback, setActiveFeedback] = React.useState(
3970
4111
  () => getFeedbackState(message)
3971
4112
  );
3972
- const [reasonModalOpen, setReasonModalOpen] = react.useState(false);
4113
+ const [reasonModalOpen, setReasonModalOpen] = React.useState(false);
3973
4114
  const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
3974
- const [toast, setToast] = react.useState(null);
3975
- const copyResetTimerRef = react.useRef(null);
3976
- const toastTimerRef = react.useRef(null);
4115
+ const [toast, setToast] = React.useState(null);
4116
+ const copyResetTimerRef = React.useRef(null);
4117
+ const toastTimerRef = React.useRef(null);
3977
4118
  const showCopyAction = actions?.copy ?? true;
3978
4119
  const showTraceAction = (actions?.trace ?? true) && !!onExecutionTraceClick;
3979
4120
  const showThumbsUp = actions?.thumbsUp ?? true;
3980
4121
  const showThumbsDown = actions?.thumbsDown ?? true;
3981
4122
  const hydratedFeedback = message.feedback;
3982
- const hasEverStreamed = react.useRef(!!message.isStreaming);
4123
+ const hasEverStreamed = React.useRef(!!message.isStreaming);
3983
4124
  if (message.isStreaming) hasEverStreamed.current = true;
3984
- react.useEffect(() => {
4125
+ React.useEffect(() => {
3985
4126
  setActiveFeedback(getFeedbackState(message));
3986
4127
  }, [hydratedFeedback, message.id]);
3987
- react.useEffect(() => {
4128
+ React.useEffect(() => {
3988
4129
  return () => {
3989
4130
  if (copyResetTimerRef.current) clearTimeout(copyResetTimerRef.current);
3990
4131
  if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
@@ -3997,6 +4138,7 @@ function AssistantMessageV2({
3997
4138
  return message.isStreaming ? stripIncompleteImageToken(normalized) : normalized;
3998
4139
  })();
3999
4140
  const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
4141
+ const showTrailingProgress = !!message.isStreaming && !!rawResponseContent && !message.isError && !message.isCancelled && !!message.hasTrailingActivity;
4000
4142
  const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
4001
4143
  const { displayedText: displayContent, isTyping: isResponseTyping } = useTypingEffect(
4002
4144
  rawResponseContent,
@@ -4005,6 +4147,8 @@ function AssistantMessageV2({
4005
4147
  void 0,
4006
4148
  typingSpeed
4007
4149
  );
4150
+ const displayContentPending = Boolean(rawResponseContent) && !displayContent;
4151
+ const showThinkingBlock = isThinkingStreaming || displayContentPending;
4008
4152
  const stickyLabel = (() => {
4009
4153
  const steps = message.steps;
4010
4154
  if (!steps || steps.length === 0) return void 0;
@@ -4139,7 +4283,7 @@ function AssistantMessageV2({
4139
4283
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4140
4284
  toastPortal,
4141
4285
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg payman-v2-fade-in", children: [
4142
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isThinkingStreaming && /* @__PURE__ */ jsxRuntime.jsx(
4286
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showThinkingBlock && /* @__PURE__ */ jsxRuntime.jsx(
4143
4287
  framerMotion.motion.div,
4144
4288
  {
4145
4289
  initial: { opacity: 0, height: 0 },
@@ -4155,7 +4299,7 @@ function AssistantMessageV2({
4155
4299
  {
4156
4300
  isStreaming: isThinkingStreaming,
4157
4301
  stickyLabel,
4158
- currentStepLabel
4302
+ currentStepLabel: currentStepLabel ?? message.currentMessage
4159
4303
  }
4160
4304
  )
4161
4305
  },
@@ -4169,7 +4313,32 @@ function AssistantMessageV2({
4169
4313
  isResolvingImages: message.isResolvingImages,
4170
4314
  onImageClick
4171
4315
  }
4316
+ ) : displayContentPending ? (
4317
+ // The intent/status line above is still bridging this
4318
+ // gap (see `showThinkingBlock`) — nothing to add here.
4319
+ null
4172
4320
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
4321
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showTrailingProgress && /* @__PURE__ */ jsxRuntime.jsx(
4322
+ framerMotion.motion.div,
4323
+ {
4324
+ initial: { opacity: 0, height: 0 },
4325
+ animate: { opacity: 1, height: "auto" },
4326
+ exit: { opacity: 0, height: 0 },
4327
+ transition: {
4328
+ duration: 0.32,
4329
+ ease: [0.2, 0, 0, 1]
4330
+ },
4331
+ style: { overflow: "hidden" },
4332
+ children: /* @__PURE__ */ jsxRuntime.jsx(
4333
+ ThinkingBlockV2,
4334
+ {
4335
+ isStreaming: true,
4336
+ currentStepLabel: message.currentMessage ?? currentStepLabel
4337
+ }
4338
+ )
4339
+ },
4340
+ "trailing-progress"
4341
+ ) }),
4173
4342
  isCancelled && message.isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
4174
4343
  /* @__PURE__ */ jsxRuntime.jsx(
4175
4344
  lucideReact.WifiOff,
@@ -4268,6 +4437,112 @@ function AssistantMessageV2({
4268
4437
  )
4269
4438
  ] });
4270
4439
  }
4440
+ var CHAR_VARIANTS = {
4441
+ fade: {
4442
+ initial: { opacity: 0, scale: 0.7 },
4443
+ animate: { opacity: 1, scale: 1 },
4444
+ exit: { opacity: 0, scale: 0.7 },
4445
+ transition: { duration: 0.14, ease: "easeOut" },
4446
+ overflow: "hidden"
4447
+ },
4448
+ blur: {
4449
+ initial: { opacity: 0, filter: "blur(8px)", y: -8 },
4450
+ animate: { opacity: 1, filter: "blur(0px)", y: 0 },
4451
+ exit: { opacity: 0, filter: "blur(8px)", y: 8 },
4452
+ transition: { duration: 0.18, ease: "easeOut" },
4453
+ overflow: "visible"
4454
+ }
4455
+ };
4456
+ function CharSlot({
4457
+ char,
4458
+ charKey,
4459
+ effect
4460
+ }) {
4461
+ if (!/\d/.test(char)) return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-block" }, children: char });
4462
+ const v = CHAR_VARIANTS[effect];
4463
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "relative", display: "inline-block", overflow: v.overflow }, children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "popLayout", initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
4464
+ framerMotion.motion.span,
4465
+ {
4466
+ initial: v.initial,
4467
+ animate: v.animate,
4468
+ exit: v.exit,
4469
+ transition: v.transition,
4470
+ style: { display: "inline-block" },
4471
+ children: char
4472
+ },
4473
+ charKey
4474
+ ) }) });
4475
+ }
4476
+ function CountUp({
4477
+ to,
4478
+ from = 0,
4479
+ duration = 2,
4480
+ delay = 0,
4481
+ digitEffect = "none",
4482
+ className,
4483
+ startWhen = true,
4484
+ separator = ""
4485
+ }) {
4486
+ const ref = React.useRef(null);
4487
+ const motionValue = framerMotion.useMotionValue(from);
4488
+ const damping = 20 + 40 * (1 / duration);
4489
+ const stiffness = 100 * (1 / duration);
4490
+ const springValue = framerMotion.useSpring(motionValue, { damping, stiffness });
4491
+ const isInView = framerMotion.useInView(ref, { once: true, margin: "0px" });
4492
+ const decimals = React.useMemo(() => {
4493
+ const d = (n) => {
4494
+ const s = n.toString();
4495
+ return s.includes(".") ? s.split(".")[1].length : 0;
4496
+ };
4497
+ return Math.max(d(from), d(to));
4498
+ }, [from, to]);
4499
+ const formatValue = React.useCallback(
4500
+ (latest) => {
4501
+ const formatted = Intl.NumberFormat("en-US", {
4502
+ useGrouping: !!separator,
4503
+ minimumFractionDigits: decimals,
4504
+ maximumFractionDigits: decimals
4505
+ }).format(latest);
4506
+ return separator ? formatted.replace(/,/g, separator) : formatted;
4507
+ },
4508
+ [decimals, separator]
4509
+ );
4510
+ const [chars, setChars] = React.useState(formatValue(from).split(""));
4511
+ React.useEffect(() => {
4512
+ if (isInView && startWhen) {
4513
+ const t = setTimeout(() => motionValue.set(to), delay * 1e3);
4514
+ return () => clearTimeout(t);
4515
+ }
4516
+ }, [isInView, startWhen, motionValue, to, delay]);
4517
+ React.useEffect(() => {
4518
+ const unsub = springValue.on("change", (latest) => {
4519
+ if (digitEffect === "none") {
4520
+ if (ref.current) ref.current.textContent = formatValue(latest);
4521
+ } else {
4522
+ setChars(formatValue(latest).split(""));
4523
+ }
4524
+ });
4525
+ return () => unsub();
4526
+ }, [springValue, formatValue, digitEffect]);
4527
+ if (digitEffect === "none") {
4528
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { ref, className: cn(className), children: formatValue(from) });
4529
+ }
4530
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { ref, className: cn("inline-flex items-center", className), children: chars.map((char, i) => /* @__PURE__ */ jsxRuntime.jsx(CharSlot, { char, charKey: `${i}-${char}`, effect: digitEffect }, i)) });
4531
+ }
4532
+ function AnimatedDuration({ seconds, className }) {
4533
+ const total = Math.max(0, seconds);
4534
+ const m = Math.floor(total / 60);
4535
+ const s = total % 60;
4536
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("tabular-nums", className), children: [
4537
+ m > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4538
+ /* @__PURE__ */ jsxRuntime.jsx(CountUp, { to: m, duration: 0.9, digitEffect: "blur", className: "tabular-nums" }),
4539
+ "m",
4540
+ " "
4541
+ ] }),
4542
+ /* @__PURE__ */ jsxRuntime.jsx(CountUp, { to: s, duration: 0.9, digitEffect: "blur", className: "tabular-nums" }),
4543
+ "s"
4544
+ ] });
4545
+ }
4271
4546
  var DEFAULT_MAX_LENGTH = 6;
4272
4547
  var MAX_SUPPORTED_LENGTH = 12;
4273
4548
  var AUTO_FOCUS_DELAY_MS = 250;
@@ -4278,10 +4553,10 @@ function OtpInputV2({
4278
4553
  disabled = false,
4279
4554
  error = false
4280
4555
  }) {
4281
- const inputRefs = react.useRef([]);
4556
+ const inputRefs = React.useRef([]);
4282
4557
  const safeMaxLength = Number.isInteger(maxLength) && maxLength > 0 ? Math.min(maxLength, MAX_SUPPORTED_LENGTH) : DEFAULT_MAX_LENGTH;
4283
4558
  const digits = value.split("").concat(Array(safeMaxLength).fill("")).slice(0, safeMaxLength);
4284
- react.useEffect(() => {
4559
+ React.useEffect(() => {
4285
4560
  if (disabled) return;
4286
4561
  const timer = window.setTimeout(() => {
4287
4562
  inputRefs.current[0]?.focus();
@@ -4380,31 +4655,30 @@ function VerificationInline({
4380
4655
  onResend
4381
4656
  }) {
4382
4657
  const isNumeric = prompt.verificationType !== "ALPHANUMERIC_CODE";
4383
- const codeLen = react.useMemo(() => codeLengthFromSchema(prompt), [prompt]);
4384
- const [code, setCode] = react.useState("");
4385
- const [errored, setErrored] = react.useState(false);
4386
- const [resendSec, setResendSec] = react.useState(0);
4387
- const [hiddenAfterResend, setHiddenAfterResend] = react.useState(false);
4388
- const lastSubmittedRef = react.useRef(null);
4389
- const resendTimerRef = react.useRef(void 0);
4658
+ const codeLen = React.useMemo(() => codeLengthFromSchema(prompt), [prompt]);
4659
+ const [code, setCode] = React.useState("");
4660
+ const [errored, setErrored] = React.useState(false);
4661
+ const [resendSec, setResendSec] = React.useState(0);
4662
+ const [hiddenAfterResend, setHiddenAfterResend] = React.useState(false);
4663
+ const lastSubmittedRef = React.useRef(null);
4664
+ const resendTimerRef = React.useRef(void 0);
4390
4665
  const status = prompt.status;
4391
4666
  const busy = status === "submitting";
4392
4667
  const stale = status === "stale";
4393
4668
  const locked = busy || stale || expired;
4394
- react.useEffect(() => {
4669
+ React.useEffect(() => {
4395
4670
  if (prompt.subAction === "SubmissionInvalid") {
4396
4671
  setErrored(true);
4397
- setCode("");
4398
4672
  lastSubmittedRef.current = null;
4399
4673
  }
4400
4674
  }, [prompt.subAction, prompt.userActionId]);
4401
- react.useEffect(() => {
4675
+ React.useEffect(() => {
4402
4676
  setHiddenAfterResend(false);
4403
4677
  }, [prompt.expirySeconds, prompt.message, prompt.subAction, prompt.userActionId]);
4404
- react.useEffect(() => {
4678
+ React.useEffect(() => {
4405
4679
  if (code.length < codeLen) lastSubmittedRef.current = null;
4406
4680
  }, [code, codeLen]);
4407
- const doSubmit = react.useCallback(
4681
+ const doSubmit = React.useCallback(
4408
4682
  (value) => {
4409
4683
  if (locked || !value) return;
4410
4684
  if (lastSubmittedRef.current === value) return;
@@ -4416,20 +4690,20 @@ function VerificationInline({
4416
4690
  },
4417
4691
  [locked, onSubmit, prompt.userActionId]
4418
4692
  );
4419
- react.useEffect(() => {
4693
+ React.useEffect(() => {
4420
4694
  if (!isNumeric || locked) return;
4421
4695
  if (code.length === codeLen && /^\d+$/.test(code)) {
4422
4696
  doSubmit(code);
4423
4697
  }
4424
4698
  }, [code, codeLen, doSubmit, isNumeric, locked]);
4425
- react.useEffect(() => {
4699
+ React.useEffect(() => {
4426
4700
  return () => {
4427
4701
  if (typeof resendTimerRef.current === "number") {
4428
4702
  window.clearInterval(resendTimerRef.current);
4429
4703
  }
4430
4704
  };
4431
4705
  }, []);
4432
- const startResendCooldown = react.useCallback(() => {
4706
+ const startResendCooldown = React.useCallback(() => {
4433
4707
  setResendSec(RESEND_COOLDOWN_S);
4434
4708
  if (typeof resendTimerRef.current === "number") {
4435
4709
  window.clearInterval(resendTimerRef.current);
@@ -4447,7 +4721,7 @@ function VerificationInline({
4447
4721
  });
4448
4722
  }, 1e3);
4449
4723
  }, []);
4450
- const handleResend = react.useCallback(async () => {
4724
+ const handleResend = React.useCallback(async () => {
4451
4725
  if (locked || resendSec > 0) return;
4452
4726
  setErrored(false);
4453
4727
  setCode("");
@@ -4460,19 +4734,22 @@ function VerificationInline({
4460
4734
  setHiddenAfterResend(false);
4461
4735
  }
4462
4736
  }, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
4463
- const handleCancel = react.useCallback(() => {
4737
+ const handleCancel = React.useCallback(() => {
4464
4738
  if (busy) return;
4465
4739
  void onCancel(prompt.userActionId);
4466
4740
  }, [busy, onCancel, prompt.userActionId]);
4467
- const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4741
+ const promptMessage = prompt.message?.trim();
4742
+ const description = promptMessage || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4743
+ const messageFormat = prompt.metadata?.["payman/messageFormat"];
4744
+ const renderMarkdown = !!promptMessage && messageFormat === "markdown";
4468
4745
  if (hiddenAfterResend) return null;
4469
4746
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
4470
4747
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
4471
4748
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
4472
4749
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Verification required" }),
4473
- typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4750
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : /* @__PURE__ */ jsxRuntime.jsx(AnimatedDuration, { seconds: secondsLeft }) })
4474
4751
  ] }),
4475
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: description }),
4752
+ renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRendererV2, { content: description }) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: description }),
4476
4753
  stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4477
4754
  isNumeric ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-field", children: /* @__PURE__ */ jsxRuntime.jsx(
4478
4755
  OtpInputV2,
@@ -4542,6 +4819,228 @@ function VerificationInline({
4542
4819
  ] })
4543
4820
  ] });
4544
4821
  }
4822
+ function AmountFieldV2({
4823
+ id,
4824
+ value,
4825
+ onChange,
4826
+ disabled,
4827
+ className,
4828
+ onEnter
4829
+ }) {
4830
+ const inputRef = React.useRef(null);
4831
+ const digits = digitsFromValue(value);
4832
+ const display = formatDigits(digits);
4833
+ React.useLayoutEffect(() => {
4834
+ const el = inputRef.current;
4835
+ if (el && document.activeElement === el) {
4836
+ el.setSelectionRange(el.value.length, el.value.length);
4837
+ }
4838
+ }, [display]);
4839
+ return /* @__PURE__ */ jsxRuntime.jsx(
4840
+ "input",
4841
+ {
4842
+ ref: inputRef,
4843
+ id,
4844
+ type: "text",
4845
+ inputMode: "decimal",
4846
+ autoComplete: "off",
4847
+ className,
4848
+ value: display,
4849
+ placeholder: "0.00",
4850
+ disabled,
4851
+ onChange: (e) => {
4852
+ onChange(valueFromDigits(digitsFromDisplay(e.target.value)));
4853
+ },
4854
+ onKeyDown: (e) => {
4855
+ if (e.key === "Enter") {
4856
+ e.preventDefault();
4857
+ onEnter?.();
4858
+ }
4859
+ },
4860
+ onFocus: (e) => {
4861
+ const el = e.currentTarget;
4862
+ requestAnimationFrame(() => el.setSelectionRange(el.value.length, el.value.length));
4863
+ }
4864
+ }
4865
+ );
4866
+ }
4867
+ function digitsFromDisplay(raw) {
4868
+ return raw.replace(/[^0-9]/g, "").replace(/^0+(?=\d)/, "").slice(-9);
4869
+ }
4870
+ function digitsFromValue(value) {
4871
+ const num = Number(value);
4872
+ if (!value || !Number.isFinite(num) || num <= 0) return "";
4873
+ return String(Math.round(num * 100));
4874
+ }
4875
+ function formatDigits(digits) {
4876
+ if (!digits) return "";
4877
+ const padded = digits.padStart(3, "0");
4878
+ const cents = padded.slice(-2);
4879
+ const whole = padded.slice(0, -2).replace(/^0+(?=\d)/, "") || "0";
4880
+ const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
4881
+ return `${grouped}.${cents}`;
4882
+ }
4883
+ function valueFromDigits(digits) {
4884
+ if (!digits) return "";
4885
+ return (Number(digits) / 100).toString();
4886
+ }
4887
+ function findRootClassName(el) {
4888
+ const root = el?.closest(".payman-v2-root");
4889
+ return root instanceof HTMLElement ? root.className : "";
4890
+ }
4891
+ var MENU_MAX_HEIGHT = 240;
4892
+ var OPTION_HEIGHT = 34;
4893
+ var VIEWPORT_MARGIN = 8;
4894
+ function SelectFieldV2({
4895
+ id,
4896
+ value,
4897
+ options,
4898
+ onChange,
4899
+ disabled,
4900
+ error,
4901
+ placeholder = "Select\u2026"
4902
+ }) {
4903
+ const triggerRef = React.useRef(null);
4904
+ const listRef = React.useRef(null);
4905
+ const [open, setOpen] = React.useState(false);
4906
+ const [rect, setRect] = React.useState(null);
4907
+ const [activeIndex, setActiveIndex] = React.useState(-1);
4908
+ const [rootClassName, setRootClassName] = React.useState("");
4909
+ const selected = options.find((o) => o.const === value);
4910
+ React.useLayoutEffect(() => {
4911
+ if (!open) return;
4912
+ const place = () => {
4913
+ const el = triggerRef.current;
4914
+ if (!el) return;
4915
+ const r = el.getBoundingClientRect();
4916
+ const viewportH = window.innerHeight;
4917
+ const estimatedMenuH = Math.min(
4918
+ options.length * OPTION_HEIGHT + VIEWPORT_MARGIN,
4919
+ MENU_MAX_HEIGHT
4920
+ );
4921
+ const spaceBelow = viewportH - r.bottom;
4922
+ const spaceAbove = r.top;
4923
+ const openUp = spaceBelow < estimatedMenuH && spaceAbove > spaceBelow;
4924
+ setRect({ top: r.bottom, bottom: viewportH - r.top, left: r.left, width: r.width, openUp });
4925
+ setRootClassName(findRootClassName(el));
4926
+ };
4927
+ place();
4928
+ window.addEventListener("resize", place);
4929
+ window.addEventListener("scroll", place, true);
4930
+ return () => {
4931
+ window.removeEventListener("resize", place);
4932
+ window.removeEventListener("scroll", place, true);
4933
+ };
4934
+ }, [open, options.length]);
4935
+ React.useEffect(() => {
4936
+ if (!open) return;
4937
+ setActiveIndex(Math.max(0, options.findIndex((o) => o.const === value)));
4938
+ listRef.current?.focus();
4939
+ }, [open]);
4940
+ React.useEffect(() => {
4941
+ if (!open) return;
4942
+ const onDocDown = (e) => {
4943
+ const target = e.target;
4944
+ if (triggerRef.current?.contains(target) || listRef.current?.contains(target)) return;
4945
+ setOpen(false);
4946
+ };
4947
+ document.addEventListener("mousedown", onDocDown);
4948
+ return () => document.removeEventListener("mousedown", onDocDown);
4949
+ }, [open]);
4950
+ const commit = (opt) => {
4951
+ onChange(opt.const);
4952
+ setOpen(false);
4953
+ triggerRef.current?.focus();
4954
+ };
4955
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4956
+ /* @__PURE__ */ jsxRuntime.jsxs(
4957
+ "button",
4958
+ {
4959
+ ref: triggerRef,
4960
+ id,
4961
+ type: "button",
4962
+ disabled,
4963
+ className: cn("payman-v2-ua-select-trigger", error && "payman-v2-ua-input-error"),
4964
+ "aria-haspopup": "listbox",
4965
+ "aria-expanded": open,
4966
+ onClick: () => setOpen((v) => !v),
4967
+ onKeyDown: (e) => {
4968
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
4969
+ e.preventDefault();
4970
+ setOpen(true);
4971
+ }
4972
+ },
4973
+ children: [
4974
+ /* @__PURE__ */ jsxRuntime.jsx(
4975
+ "span",
4976
+ {
4977
+ className: cn(
4978
+ "payman-v2-ua-select-value",
4979
+ !selected && "payman-v2-ua-select-placeholder"
4980
+ ),
4981
+ children: selected ? selected.title || selected.const : placeholder
4982
+ }
4983
+ ),
4984
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 14, className: "payman-v2-ua-select-chevron", "aria-hidden": true })
4985
+ ]
4986
+ }
4987
+ ),
4988
+ open && rect && typeof document !== "undefined" && reactDom.createPortal(
4989
+ /* @__PURE__ */ jsxRuntime.jsx(
4990
+ "div",
4991
+ {
4992
+ ref: listRef,
4993
+ role: "listbox",
4994
+ className: cn(rootClassName, "payman-v2-ua-select-menu"),
4995
+ tabIndex: -1,
4996
+ style: {
4997
+ position: "fixed",
4998
+ left: rect.left,
4999
+ width: rect.width,
5000
+ height: "fit-content",
5001
+ maxHeight: MENU_MAX_HEIGHT,
5002
+ ...rect.openUp ? { bottom: rect.bottom } : { top: rect.top }
5003
+ },
5004
+ onKeyDown: (e) => {
5005
+ if (e.key === "ArrowDown") {
5006
+ e.preventDefault();
5007
+ setActiveIndex((i) => Math.min(options.length - 1, i + 1));
5008
+ } else if (e.key === "ArrowUp") {
5009
+ e.preventDefault();
5010
+ setActiveIndex((i) => Math.max(0, i - 1));
5011
+ } else if (e.key === "Enter" || e.key === " ") {
5012
+ e.preventDefault();
5013
+ if (activeIndex >= 0) commit(options[activeIndex]);
5014
+ } else if (e.key === "Escape" || e.key === "Tab") {
5015
+ setOpen(false);
5016
+ triggerRef.current?.focus();
5017
+ }
5018
+ },
5019
+ children: options.map((opt, i) => /* @__PURE__ */ jsxRuntime.jsxs(
5020
+ "div",
5021
+ {
5022
+ role: "option",
5023
+ "aria-selected": opt.const === value,
5024
+ className: cn(
5025
+ "payman-v2-ua-select-option",
5026
+ i === activeIndex && "payman-v2-ua-select-option-active",
5027
+ opt.const === value && "payman-v2-ua-select-option-selected"
5028
+ ),
5029
+ onMouseEnter: () => setActiveIndex(i),
5030
+ onClick: () => commit(opt),
5031
+ children: [
5032
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: opt.title || opt.const }),
5033
+ opt.const === value && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 13, "aria-hidden": true })
5034
+ ]
5035
+ },
5036
+ opt.const
5037
+ ))
5038
+ }
5039
+ ),
5040
+ document.body
5041
+ )
5042
+ ] });
5043
+ }
4545
5044
  function SchemaFormInline({
4546
5045
  prompt,
4547
5046
  secondsLeft,
@@ -4550,13 +5049,13 @@ function SchemaFormInline({
4550
5049
  onCancel
4551
5050
  }) {
4552
5051
  const schema = prompt.requestedSchema;
4553
- const fields = react.useMemo(() => renderableFields(schema), [schema]);
4554
- const [values, setValues] = react.useState(() => {
5052
+ const fields = React.useMemo(() => renderableFields(schema), [schema]);
5053
+ const [values, setValues] = React.useState(() => {
4555
5054
  const init2 = {};
4556
5055
  for (const [key, field] of fields) init2[key] = defaultValueFor(field);
4557
5056
  return init2;
4558
5057
  });
4559
- const [errors, setErrors] = react.useState({});
5058
+ const [errors, setErrors] = React.useState({});
4560
5059
  const status = prompt.status;
4561
5060
  const busy = status === "submitting";
4562
5061
  const stale = status === "stale";
@@ -4587,7 +5086,7 @@ function SchemaFormInline({
4587
5086
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
4588
5087
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "payman-v2-ua-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4589
5088
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4590
- typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
5089
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : /* @__PURE__ */ jsxRuntime.jsx(AnimatedDuration, { seconds: secondsLeft }) })
4591
5090
  ] }),
4592
5091
  prompt.message?.trim() && (renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRendererV2, { content: prompt.message }) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: prompt.message })),
4593
5092
  stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
@@ -4619,26 +5118,33 @@ function SchemaFormInline({
4619
5118
  required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-req", children: "*" })
4620
5119
  ] }),
4621
5120
  field.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-hint", children: field.description }),
4622
- widget === "select" ? /* @__PURE__ */ jsxRuntime.jsxs(
4623
- "select",
5121
+ widget === "select" ? /* @__PURE__ */ jsxRuntime.jsx(
5122
+ SelectFieldV2,
4624
5123
  {
4625
5124
  id: fieldId,
4626
- className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4627
5125
  value: String(values[key] ?? ""),
5126
+ options: getOptions(field),
4628
5127
  disabled: locked,
4629
- onChange: (e) => setValue(key, e.target.value),
4630
- children: [
4631
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select\u2026" }),
4632
- getOptions(field).map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.const, children: opt.title || opt.const }, opt.const))
4633
- ]
5128
+ error: Boolean(err),
5129
+ onChange: (v) => setValue(key, v)
5130
+ }
5131
+ ) : widget === "decimal" ? /* @__PURE__ */ jsxRuntime.jsx(
5132
+ AmountFieldV2,
5133
+ {
5134
+ id: fieldId,
5135
+ value: String(values[key] ?? ""),
5136
+ disabled: locked,
5137
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
5138
+ onChange: (v) => setValue(key, v),
5139
+ onEnter: handleSubmit
4634
5140
  }
4635
5141
  ) : /* @__PURE__ */ jsxRuntime.jsx(
4636
5142
  "input",
4637
5143
  {
4638
5144
  id: fieldId,
4639
- type: widget === "integer" || widget === "decimal" ? "number" : "text",
4640
- inputMode: widget === "integer" ? "numeric" : widget === "decimal" ? "decimal" : void 0,
4641
- step: widget === "decimal" ? "any" : widget === "integer" ? "1" : void 0,
5145
+ type: widget === "integer" ? "number" : "text",
5146
+ inputMode: widget === "integer" ? "numeric" : void 0,
5147
+ step: widget === "integer" ? "1" : void 0,
4642
5148
  className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4643
5149
  value: String(values[key] ?? ""),
4644
5150
  disabled: locked,
@@ -4697,10 +5203,10 @@ function NotificationInline({ notification, onDismiss }) {
4697
5203
  }
4698
5204
  function useExpiryCountdown(prompt) {
4699
5205
  const deadlineMs = typeof prompt.expiresAt === "number" && prompt.expiresAt > 0 ? prompt.expiresAt : typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0 ? Date.now() + Math.floor(prompt.expirySeconds) * 1e3 : void 0;
4700
- const [secondsLeft, setSecondsLeft] = react.useState(
5206
+ const [secondsLeft, setSecondsLeft] = React.useState(
4701
5207
  () => deadlineMs !== void 0 ? Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3)) : void 0
4702
5208
  );
4703
- react.useEffect(() => {
5209
+ React.useEffect(() => {
4704
5210
  if (deadlineMs === void 0) {
4705
5211
  setSecondsLeft(void 0);
4706
5212
  return;
@@ -4735,12 +5241,16 @@ function UserActionInline({
4735
5241
  onSubmit,
4736
5242
  onCancel,
4737
5243
  onResend,
4738
- onExpired
5244
+ onExpired,
5245
+ suppressExpiredNote
4739
5246
  }) {
4740
5247
  const [secondsLeft, expired] = useExpiryCountdown(prompt);
4741
- react.useEffect(() => {
5248
+ React.useEffect(() => {
4742
5249
  if (expired && prompt.kind !== "notification") onExpired?.();
4743
5250
  }, [expired, onExpired, prompt.kind]);
5251
+ if (expired && prompt.kind !== "notification" && suppressExpiredNote) {
5252
+ return null;
5253
+ }
4744
5254
  let body;
4745
5255
  if (expired && prompt.kind !== "notification") {
4746
5256
  const note = {
@@ -4801,7 +5311,10 @@ function getPromptViewKey(prompt) {
4801
5311
  prompt.message ?? ""
4802
5312
  ].join(PROMPT_KEY_SEPARATOR);
4803
5313
  }
4804
- var MessageListV2 = react.forwardRef(
5314
+ function getPromptMountKey(prompt) {
5315
+ return [getPromptSlotKey(prompt), prompt.userActionId].join(PROMPT_KEY_SEPARATOR);
5316
+ }
5317
+ var MessageListV2 = React.forwardRef(
4805
5318
  function MessageListV22({
4806
5319
  messages,
4807
5320
  isStreaming = false,
@@ -4812,6 +5325,7 @@ var MessageListV2 = react.forwardRef(
4812
5325
  messageActions,
4813
5326
  retryDisabled = false,
4814
5327
  userActionPrompts,
5328
+ dockedUserActionId,
4815
5329
  notifications,
4816
5330
  onSubmitUserAction,
4817
5331
  onCancelUserAction,
@@ -4821,25 +5335,25 @@ var MessageListV2 = react.forwardRef(
4821
5335
  onSubmitFeedback,
4822
5336
  typingSpeed = 4
4823
5337
  }, ref) {
4824
- const noop = react.useCallback(async () => {
5338
+ const noop = React.useCallback(async () => {
4825
5339
  }, []);
4826
- const scrollRef = react.useRef(null);
4827
- const scrollInnerRef = react.useRef(null);
4828
- const isNearBottomRef = react.useRef(true);
4829
- const [showScrollBtn, setShowScrollBtn] = react.useState(false);
4830
- const [expiredPromptViewState, setExpiredPromptViewState] = react.useState({});
4831
- const expiredUserActionIdsRef = react.useRef(/* @__PURE__ */ new Set());
4832
- const prevCountRef = react.useRef(messages.length);
4833
- const pauseStickUntilUserMessageRef = react.useRef(false);
4834
- const followingBottomRef = react.useRef(true);
4835
- const lastPinAtRef = react.useRef(0);
4836
- const prevScrollTopRef = react.useRef(0);
4837
- const getDistanceFromBottom = react.useCallback(() => {
5340
+ const scrollRef = React.useRef(null);
5341
+ const scrollInnerRef = React.useRef(null);
5342
+ const isNearBottomRef = React.useRef(true);
5343
+ const [showScrollBtn, setShowScrollBtn] = React.useState(false);
5344
+ const [expiredPromptViewState, setExpiredPromptViewState] = React.useState({});
5345
+ const expiredUserActionIdsRef = React.useRef(/* @__PURE__ */ new Set());
5346
+ const prevCountRef = React.useRef(messages.length);
5347
+ const pauseStickUntilUserMessageRef = React.useRef(false);
5348
+ const followingBottomRef = React.useRef(true);
5349
+ const lastPinAtRef = React.useRef(0);
5350
+ const prevScrollTopRef = React.useRef(0);
5351
+ const getDistanceFromBottom = React.useCallback(() => {
4838
5352
  const el = scrollRef.current;
4839
5353
  if (!el) return 0;
4840
5354
  return el.scrollHeight - el.scrollTop - el.clientHeight;
4841
5355
  }, []);
4842
- const messageActivityFingerprint = react.useMemo(() => {
5356
+ const messageActivityFingerprint = React.useMemo(() => {
4843
5357
  const last = messages[messages.length - 1];
4844
5358
  const promptFingerprint = (userActionPrompts ?? []).map((prompt) => `${getPromptViewKey(prompt)}:${prompt.status}`).join(PROMPT_KEY_SEPARATOR);
4845
5359
  const notificationFingerprint = (notifications ?? []).map((notification) => notification.id).join(PROMPT_KEY_SEPARATOR);
@@ -4865,7 +5379,7 @@ var MessageListV2 = react.forwardRef(
4865
5379
  notificationFingerprint
4866
5380
  ].join(PROMPT_KEY_SEPARATOR);
4867
5381
  }, [isStreaming, messages, notifications, userActionPrompts]);
4868
- const handleUserActionExpired = react.useCallback(
5382
+ const handleUserActionExpired = React.useCallback(
4869
5383
  (promptKey, userActionId) => {
4870
5384
  setExpiredPromptViewState((prev) => {
4871
5385
  if (prev[promptKey]) return prev;
@@ -4882,7 +5396,7 @@ var MessageListV2 = react.forwardRef(
4882
5396
  },
4883
5397
  [messageActivityFingerprint, onExpireUserAction]
4884
5398
  );
4885
- react.useEffect(() => {
5399
+ React.useEffect(() => {
4886
5400
  setExpiredPromptViewState((prev) => {
4887
5401
  let changed = false;
4888
5402
  const next = {};
@@ -4897,7 +5411,7 @@ var MessageListV2 = react.forwardRef(
4897
5411
  return changed ? next : prev;
4898
5412
  });
4899
5413
  }, [messageActivityFingerprint]);
4900
- react.useEffect(() => {
5414
+ React.useEffect(() => {
4901
5415
  const livePromptKeys = new Set((userActionPrompts ?? []).map(getPromptViewKey));
4902
5416
  const liveUserActionIds = new Set((userActionPrompts ?? []).map((p) => p.userActionId));
4903
5417
  for (const userActionId of expiredUserActionIdsRef.current) {
@@ -4918,21 +5432,22 @@ var MessageListV2 = react.forwardRef(
4918
5432
  return changed ? next : prev;
4919
5433
  });
4920
5434
  }, [userActionPrompts]);
4921
- const visibleUserActionPrompts = react.useMemo(
5435
+ const visibleUserActionPrompts = React.useMemo(
4922
5436
  () => userActionPrompts?.filter((prompt) => {
5437
+ if (prompt.userActionId === dockedUserActionId) return false;
4923
5438
  const promptKey = getPromptViewKey(prompt);
4924
5439
  return !expiredPromptViewState[promptKey]?.hidden;
4925
5440
  }),
4926
- [expiredPromptViewState, userActionPrompts]
5441
+ [dockedUserActionId, expiredPromptViewState, userActionPrompts]
4927
5442
  );
4928
- const pinToBottom = react.useCallback((behavior = "instant") => {
5443
+ const pinToBottom = React.useCallback((behavior = "instant") => {
4929
5444
  const el = scrollRef.current;
4930
5445
  if (!el) return;
4931
5446
  lastPinAtRef.current = performance.now();
4932
5447
  el.scrollTo({ top: el.scrollHeight, behavior });
4933
5448
  prevScrollTopRef.current = el.scrollHeight;
4934
5449
  }, []);
4935
- const scrollToBottom = react.useCallback(
5450
+ const scrollToBottom = React.useCallback(
4936
5451
  (behavior = "smooth") => {
4937
5452
  const el = scrollRef.current;
4938
5453
  if (!el) return;
@@ -4942,7 +5457,7 @@ var MessageListV2 = react.forwardRef(
4942
5457
  },
4943
5458
  [pinToBottom]
4944
5459
  );
4945
- react.useImperativeHandle(
5460
+ React.useImperativeHandle(
4946
5461
  ref,
4947
5462
  () => ({
4948
5463
  scrollToBottom: (behavior = "smooth") => {
@@ -4955,7 +5470,7 @@ var MessageListV2 = react.forwardRef(
4955
5470
  }),
4956
5471
  [scrollToBottom]
4957
5472
  );
4958
- const handleScroll = react.useCallback(() => {
5473
+ const handleScroll = React.useCallback(() => {
4959
5474
  const el = scrollRef.current;
4960
5475
  if (!el) return;
4961
5476
  const currentScrollTop = el.scrollTop;
@@ -4976,7 +5491,7 @@ var MessageListV2 = react.forwardRef(
4976
5491
  pauseStickUntilUserMessageRef.current = false;
4977
5492
  }
4978
5493
  }, [getDistanceFromBottom]);
4979
- react.useEffect(() => {
5494
+ React.useEffect(() => {
4980
5495
  const prevCount = prevCountRef.current;
4981
5496
  prevCountRef.current = messages.length;
4982
5497
  if (messages.length > prevCount) {
@@ -4990,7 +5505,7 @@ var MessageListV2 = react.forwardRef(
4990
5505
  }
4991
5506
  }
4992
5507
  }, [messages.length, scrollToBottom]);
4993
- react.useEffect(() => {
5508
+ React.useEffect(() => {
4994
5509
  const inner = scrollInnerRef.current;
4995
5510
  if (!inner) return;
4996
5511
  const pinIfFollowing = () => {
@@ -5015,7 +5530,7 @@ var MessageListV2 = react.forwardRef(
5015
5530
  mo.disconnect();
5016
5531
  };
5017
5532
  }, [pinToBottom]);
5018
- react.useEffect(() => {
5533
+ React.useEffect(() => {
5019
5534
  if (messages.length > 0) {
5020
5535
  setTimeout(() => scrollToBottom("instant"), 50);
5021
5536
  }
@@ -5072,7 +5587,7 @@ var MessageListV2 = react.forwardRef(
5072
5587
  onResend: onResendUserAction ?? noop,
5073
5588
  onExpired: () => handleUserActionExpired(promptKey, prompt.userActionId)
5074
5589
  },
5075
- promptKey
5590
+ getPromptMountKey(prompt)
5076
5591
  );
5077
5592
  })
5078
5593
  ]
@@ -5104,7 +5619,7 @@ var MessageListV2 = react.forwardRef(
5104
5619
  ] });
5105
5620
  }
5106
5621
  );
5107
- var ChatInputV2 = react.forwardRef(
5622
+ var ChatInputV2 = React.forwardRef(
5108
5623
  function ChatInputV22({
5109
5624
  onSend,
5110
5625
  disabled = false,
@@ -5128,20 +5643,20 @@ var ChatInputV2 = react.forwardRef(
5128
5643
  onAnalysisModeChange,
5129
5644
  slashCommands = []
5130
5645
  }, ref) {
5131
- const [value, setValue] = react.useState("");
5132
- const [isFocused, setIsFocused] = react.useState(false);
5133
- const [showActions, setShowActions] = react.useState(false);
5134
- const [showVoiceTooltip, setShowVoiceTooltip] = react.useState(false);
5135
- const [selectedCommandIndex, setSelectedCommandIndex] = react.useState(0);
5136
- const [inlineHint, setInlineHint] = react.useState(null);
5137
- const [commandMenuDismissed, setCommandMenuDismissed] = react.useState(false);
5138
- const textareaRef = react.useRef(null);
5139
- const actionsRef = react.useRef(null);
5140
- const preRecordTextRef = react.useRef("");
5141
- const voiceTooltipTimerRef = react.useRef(
5646
+ const [value, setValue] = React.useState("");
5647
+ const [isFocused, setIsFocused] = React.useState(false);
5648
+ const [showActions, setShowActions] = React.useState(false);
5649
+ const [showVoiceTooltip, setShowVoiceTooltip] = React.useState(false);
5650
+ const [selectedCommandIndex, setSelectedCommandIndex] = React.useState(0);
5651
+ const [inlineHint, setInlineHint] = React.useState(null);
5652
+ const [commandMenuDismissed, setCommandMenuDismissed] = React.useState(false);
5653
+ const textareaRef = React.useRef(null);
5654
+ const actionsRef = React.useRef(null);
5655
+ const preRecordTextRef = React.useRef("");
5656
+ const voiceTooltipTimerRef = React.useRef(
5142
5657
  null
5143
5658
  );
5144
- const voiceDraftSyncActiveRef = react.useRef(false);
5659
+ const voiceDraftSyncActiveRef = React.useRef(false);
5145
5660
  const isInputLocked = disabled || isRecording;
5146
5661
  const hasAttachmentOptions = showUploadImageButton || showAttachFileButton;
5147
5662
  const showAttachmentMenuButton = showAttachmentButton && hasAttachmentOptions;
@@ -5152,14 +5667,14 @@ var ChatInputV2 = react.forwardRef(
5152
5667
  (command) => command.name.toLowerCase().startsWith(commandQuery)
5153
5668
  );
5154
5669
  const showCommandSuggestions = !commandMenuDismissed && !isRecording && !disabled && commandSuggestions.length > 0;
5155
- react.useEffect(() => {
5670
+ React.useEffect(() => {
5156
5671
  if (textareaRef.current) {
5157
5672
  textareaRef.current.style.height = "auto";
5158
5673
  const scrollHeight = textareaRef.current.scrollHeight;
5159
5674
  textareaRef.current.style.height = `${Math.min(scrollHeight, 200)}px`;
5160
5675
  }
5161
5676
  }, [value]);
5162
- react.useEffect(() => {
5677
+ React.useEffect(() => {
5163
5678
  if (disabled) {
5164
5679
  setIsFocused(false);
5165
5680
  setShowActions(false);
@@ -5169,7 +5684,7 @@ var ChatInputV2 = react.forwardRef(
5169
5684
  const frameId = requestAnimationFrame(() => textareaRef.current?.focus());
5170
5685
  return () => cancelAnimationFrame(frameId);
5171
5686
  }, [disabled]);
5172
- react.useImperativeHandle(
5687
+ React.useImperativeHandle(
5173
5688
  ref,
5174
5689
  () => ({
5175
5690
  setDraft: (message) => {
@@ -5186,7 +5701,7 @@ var ChatInputV2 = react.forwardRef(
5186
5701
  }),
5187
5702
  [disabled]
5188
5703
  );
5189
- react.useEffect(() => {
5704
+ React.useEffect(() => {
5190
5705
  if (!showActions) return;
5191
5706
  const handleClickOutside = (e) => {
5192
5707
  if (actionsRef.current && !actionsRef.current.contains(e.target)) {
@@ -5196,29 +5711,29 @@ var ChatInputV2 = react.forwardRef(
5196
5711
  document.addEventListener("mousedown", handleClickOutside);
5197
5712
  return () => document.removeEventListener("mousedown", handleClickOutside);
5198
5713
  }, [showActions]);
5199
- react.useEffect(() => {
5714
+ React.useEffect(() => {
5200
5715
  if (!showAttachmentMenuButton) {
5201
5716
  setShowActions(false);
5202
5717
  }
5203
5718
  }, [showAttachmentMenuButton]);
5204
- react.useEffect(() => {
5719
+ React.useEffect(() => {
5205
5720
  setSelectedCommandIndex(0);
5206
5721
  setCommandMenuDismissed(false);
5207
5722
  }, [commandQuery]);
5208
- react.useEffect(() => {
5723
+ React.useEffect(() => {
5209
5724
  return () => {
5210
5725
  if (voiceTooltipTimerRef.current) {
5211
5726
  clearTimeout(voiceTooltipTimerRef.current);
5212
5727
  }
5213
5728
  };
5214
5729
  }, []);
5215
- react.useEffect(() => {
5730
+ React.useEffect(() => {
5216
5731
  if (!voiceDraftSyncActiveRef.current) return;
5217
5732
  const base = preRecordTextRef.current;
5218
5733
  const separator = base && !base.endsWith(" ") && transcribedText ? " " : "";
5219
5734
  setValue(`${base}${separator}${transcribedText}`);
5220
5735
  }, [isRecording, transcribedText]);
5221
- const handleSend = react.useCallback(() => {
5736
+ const handleSend = React.useCallback(() => {
5222
5737
  if (!value.trim() || disabled) return;
5223
5738
  const commandHint = getSlashCommandValidationHint(value);
5224
5739
  if (commandHint) {
@@ -5238,7 +5753,7 @@ var ChatInputV2 = react.forwardRef(
5238
5753
  }
5239
5754
  });
5240
5755
  }, [value, disabled, onClearEditing, onSend]);
5241
- const selectCommand = react.useCallback(
5756
+ const selectCommand = React.useCallback(
5242
5757
  (command) => {
5243
5758
  const insertText = command.insertText ?? `${command.name} `;
5244
5759
  setValue(insertText);
@@ -5294,14 +5809,14 @@ var ChatInputV2 = react.forwardRef(
5294
5809
  onAttachFileClick?.();
5295
5810
  setShowActions(false);
5296
5811
  };
5297
- const hideVoiceTooltip = react.useCallback(() => {
5812
+ const hideVoiceTooltip = React.useCallback(() => {
5298
5813
  if (voiceTooltipTimerRef.current) {
5299
5814
  clearTimeout(voiceTooltipTimerRef.current);
5300
5815
  voiceTooltipTimerRef.current = null;
5301
5816
  }
5302
5817
  setShowVoiceTooltip(false);
5303
5818
  }, []);
5304
- const startVoiceTooltipTimer = react.useCallback(() => {
5819
+ const startVoiceTooltipTimer = React.useCallback(() => {
5305
5820
  if (isVoiceButtonDisabled || isRecording) return;
5306
5821
  if (voiceTooltipTimerRef.current) {
5307
5822
  clearTimeout(voiceTooltipTimerRef.current);
@@ -5618,14 +6133,459 @@ var ChatInputV2 = react.forwardRef(
5618
6133
  ] });
5619
6134
  }
5620
6135
  );
5621
-
5622
- // src/assets/thinking-atom.lottie
5623
- var thinking_atom_default = "data:application/zip;base64,UEsDBBQAAAAIAMWrkFzFrMWzUgAAAF4AAAANAAAAbWFuaWZlc3QuanNvbqtWKkstKs7Mz1OyUjJS0lFKT81LLUosyS8C8h1S8kty8ktKMlP14SzdrGIHQz0zPZDaxLzM3MQSoN5iJavoaqXMFKAe38TMPIXgZKApSrWxtQBQSwMEFAAAAAgAxauQXLRghFpXBAAA1RwAABEAAABhL01haW4gU2NlbmUuanNvbu1YWW/jNhD+KwafJVWkqMtPbdEDfdiiQIq+GMZCcei1GvmApLRdGP7v/YYiddqbGNumR4wcomZGHM7B76N0ZLstm7N3Wb6b3a3UTjGHPTw8sLnvsA2bCx/X3811q+qMzY/sAx74stjXda7WeaGqL1alyup9OeNeEnmcnRxWZB9VWbH54sjqj2weOI2bH5+KYibhoirZnONSa0f7AzykGOQYkGP4X2dFpbq13ONx0mTVu6x6bNXZXosf4erI9OLwjwRw7Tv4WWLSP+ALa7I2Rs0RkvkzRhEZQdWz8iHCms48Z54RMEAs2gABkcGRYVFHBqXv8Sh2GBKgRzDNW00SIClag1GzugVNShk5OUd9zxMj4cI/2UhoSdUwVBJpn53EGPMTOd0hh8hAUwuodC1UoVZ1ud+9UjWog+jvb6hIv9D9egxT8aK8kfte5tD5kiTYCfpaqvUPyABb7beH9/57zlUi4zBwE+FnrsyUdDOfC/c+iNVqzVOVKoH06vwLLDsr1Y6q+VZLgX7+dxQDW29SDGmKsVNPr1iLSyB1Bkui0CDGAEdkEmmpeXghQwe/Td0slnRzyDQ5NwdH8L05htXHLGFqIWk0fQ+YPhc9P3+3VpvsoDrSYR9KFNHWpKvWdgfdV998/e3sF2w70Nb35f7pAFNdfn0z49QpNLHDVnqh0FL9IUXU1kG1eZmDO1rYzB06+imrN30/hNG9JmkiPLIVm9flE2ZFuRZoFzf0hIyXzkJfqXcgtLJGCSHyi1SR/URF9pNJYP8b2adeaEwwoqc6AQ2XS+obXTi7bdi6eGmWs8MmXyEN3+UFPaOzQGOdBcQ52BeeDGXqgB3jRNIlirBe7Vs2TYNkXeiGsFtcTS0w3XLPAp8x0MQ86uluEVPse66t9R4ZdbVd88Vw4tNpqTcZIZekEUrFQi/2fAS3hqPIwBMXSIqBp6yqVN1shjHH5NejpvUyOtdZzNyX93lNVfwPn+vGrHjDpiuxSXAvFIHDhSdFRNghvcSPnMQLhKRbo3dbg0bvNgYWsZ4xcz7txuIYrGOfOzz0/JiTUZB4acwdVwiNa43abfVGrbVnMA79fCXG3WGvPdLLlM5nc6czWiBpKFXxq07ptqBOudg7hDI49vRUOAJYuJggZiQDrqFSxA6CMgj2/8PCNqDty6oCjqlViaL8XOb0iC4JjWfU51Xb6CiLunz8CiPop0cnnIb6Ryd7YGpf4bqTUmhPSpSSYXxuSsBtk3ztGkQwPAJS7QevkWdOa2SLNLSn378smfqMTZF0yRzV8Ez4SToNf4i5drmWByfHdk1B5PxGQW+Xgix5jMlhTB5jchlS0CeNOiK74GREQROOGVLQhKFuFHSjoH+CgkSELwM3CrqCgjDJGQoKbhT0pikIH0oE+CAMgOmpF3HAof580grxHaWRdt9opjr9wWUyj6UWHnvSD4yZ1N9pBiIXshuF3Cjktd9izmHo61CIxeSAPpWd/gRQSwECFAAUAAAACADFq5BcxazFs1IAAABeAAAADQAAAAAAAAAAAAAAAAAAAAAAbWFuaWZlc3QuanNvblBLAQIUABQAAAAIAMWrkFy0YIRaVwQAANUcAAARAAAAAAAAAAAAAAAAAH0AAABhL01haW4gU2NlbmUuanNvblBLBQYAAAAAAgACAHoAAAADBQAAAAA=";
5624
- var DEFAULT_SIZE = 36;
6136
+ function dockKey(prompt) {
6137
+ return `${prompt.toolCallId || prompt.userActionId}:${prompt.userActionId}`;
6138
+ }
6139
+ function UserActionDock({
6140
+ prompt,
6141
+ onSubmit,
6142
+ onCancel,
6143
+ onResend,
6144
+ onExpired,
6145
+ children
6146
+ }) {
6147
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-dock", children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.motion.div, { layout: "size", transition: { duration: 0.3, ease: [0.2, 0, 0, 1] }, children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", initial: false, children: prompt ? /* @__PURE__ */ jsxRuntime.jsx(
6148
+ framerMotion.motion.div,
6149
+ {
6150
+ initial: { opacity: 0 },
6151
+ animate: { opacity: 1 },
6152
+ exit: { opacity: 0 },
6153
+ transition: { duration: 0.15 },
6154
+ className: "payman-v2-ua-dock-panel",
6155
+ children: /* @__PURE__ */ jsxRuntime.jsx(
6156
+ UserActionInline,
6157
+ {
6158
+ prompt,
6159
+ onSubmit,
6160
+ onCancel,
6161
+ onResend,
6162
+ onExpired: () => onExpired?.(prompt.userActionId),
6163
+ suppressExpiredNote: true
6164
+ }
6165
+ )
6166
+ },
6167
+ dockKey(prompt)
6168
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
6169
+ framerMotion.motion.div,
6170
+ {
6171
+ initial: { opacity: 0 },
6172
+ animate: { opacity: 1 },
6173
+ exit: { opacity: 0 },
6174
+ transition: { duration: 0.15 },
6175
+ children
6176
+ },
6177
+ "composer"
6178
+ ) }) }) });
6179
+ }
6180
+ function clamp(value) {
6181
+ return Math.max(0, Math.min(1, value));
6182
+ }
6183
+ function ensureFrameSize(frame, rows, cols) {
6184
+ const result = [];
6185
+ for (let r = 0; r < rows; r++) {
6186
+ const row = frame[r] || [];
6187
+ result.push([]);
6188
+ for (let c = 0; c < cols; c++) {
6189
+ result[r][c] = row[c] ?? 0;
6190
+ }
6191
+ }
6192
+ return result;
6193
+ }
6194
+ function useAnimation(frames, options) {
6195
+ const [frameIndex, setFrameIndex] = React.useState(0);
6196
+ const [isPlaying, setIsPlaying] = React.useState(options.autoplay);
6197
+ const frameIdRef = React.useRef(void 0);
6198
+ const lastTimeRef = React.useRef(0);
6199
+ const accumulatorRef = React.useRef(0);
6200
+ React.useEffect(() => {
6201
+ if (!frames || frames.length === 0 || !isPlaying) {
6202
+ return;
6203
+ }
6204
+ const frameInterval = 1e3 / options.fps;
6205
+ const animate = (currentTime) => {
6206
+ if (lastTimeRef.current === 0) {
6207
+ lastTimeRef.current = currentTime;
6208
+ }
6209
+ const deltaTime = currentTime - lastTimeRef.current;
6210
+ lastTimeRef.current = currentTime;
6211
+ accumulatorRef.current += deltaTime;
6212
+ if (accumulatorRef.current >= frameInterval) {
6213
+ accumulatorRef.current -= frameInterval;
6214
+ setFrameIndex((prev) => {
6215
+ const next = prev + 1;
6216
+ if (next >= frames.length) {
6217
+ if (options.loop) {
6218
+ options.onFrame?.(0);
6219
+ return 0;
6220
+ } else {
6221
+ setIsPlaying(false);
6222
+ return prev;
6223
+ }
6224
+ }
6225
+ options.onFrame?.(next);
6226
+ return next;
6227
+ });
6228
+ }
6229
+ frameIdRef.current = requestAnimationFrame(animate);
6230
+ };
6231
+ frameIdRef.current = requestAnimationFrame(animate);
6232
+ return () => {
6233
+ if (frameIdRef.current) {
6234
+ cancelAnimationFrame(frameIdRef.current);
6235
+ }
6236
+ };
6237
+ }, [frames, isPlaying, options.fps, options.loop, options.onFrame]);
6238
+ React.useEffect(() => {
6239
+ setFrameIndex(0);
6240
+ setIsPlaying(options.autoplay);
6241
+ lastTimeRef.current = 0;
6242
+ accumulatorRef.current = 0;
6243
+ }, [frames, options.autoplay]);
6244
+ return { frameIndex, isPlaying };
6245
+ }
6246
+ function emptyFrame(rows, cols) {
6247
+ return Array.from({ length: rows }, () => Array(cols).fill(0));
6248
+ }
6249
+ function setPixel(frame, row, col, value) {
6250
+ if (row >= 0 && row < frame.length && col >= 0 && col < frame[0].length) {
6251
+ frame[row][col] = value;
6252
+ }
6253
+ }
6254
+ var loader = (() => {
6255
+ const frames = [];
6256
+ const size = 7;
6257
+ const center = 3;
6258
+ const radius = 2.5;
6259
+ for (let frame = 0; frame < 12; frame++) {
6260
+ const f = emptyFrame(size, size);
6261
+ for (let i = 0; i < 8; i++) {
6262
+ const angle = frame / 12 * Math.PI * 2 + i / 8 * Math.PI * 2;
6263
+ const x = Math.round(center + Math.cos(angle) * radius);
6264
+ const y = Math.round(center + Math.sin(angle) * radius);
6265
+ const brightness = 1 - i / 10;
6266
+ setPixel(f, y, x, Math.max(0.2, brightness));
6267
+ }
6268
+ frames.push(f);
6269
+ }
6270
+ return frames;
6271
+ })();
6272
+ var pulse = (() => {
6273
+ const frames = [];
6274
+ const size = 7;
6275
+ const center = 3;
6276
+ for (let frame = 0; frame < 16; frame++) {
6277
+ const f = emptyFrame(size, size);
6278
+ const phase = frame / 16 * Math.PI * 2;
6279
+ const intensity = (Math.sin(phase) + 1) / 2;
6280
+ setPixel(f, center, center, 1);
6281
+ const radius = Math.floor((1 - intensity) * 3) + 1;
6282
+ for (let dy = -radius; dy <= radius; dy++) {
6283
+ for (let dx = -radius; dx <= radius; dx++) {
6284
+ const dist = Math.sqrt(dx * dx + dy * dy);
6285
+ if (Math.abs(dist - radius) < 0.7) {
6286
+ setPixel(f, center + dy, center + dx, intensity * 0.6);
6287
+ }
6288
+ }
6289
+ }
6290
+ frames.push(f);
6291
+ }
6292
+ return frames;
6293
+ })();
6294
+ function vu(columns, levels) {
6295
+ const rows = 7;
6296
+ const frame = emptyFrame(rows, columns);
6297
+ for (let col = 0; col < Math.min(columns, levels.length); col++) {
6298
+ const level = Math.max(0, Math.min(1, levels[col]));
6299
+ const height = Math.floor(level * rows);
6300
+ for (let row = 0; row < rows; row++) {
6301
+ const rowFromBottom = rows - 1 - row;
6302
+ if (rowFromBottom < height) {
6303
+ let brightness = 1;
6304
+ if (row < rows * 0.3) {
6305
+ brightness = 1;
6306
+ } else if (row < rows * 0.6) {
6307
+ brightness = 0.8;
6308
+ } else {
6309
+ brightness = 0.6;
6310
+ }
6311
+ frame[row][col] = brightness;
6312
+ }
6313
+ }
6314
+ }
6315
+ return frame;
6316
+ }
6317
+ var wave = (() => {
6318
+ const frames = [];
6319
+ const rows = 7;
6320
+ const cols = 7;
6321
+ for (let frame = 0; frame < 24; frame++) {
6322
+ const f = emptyFrame(rows, cols);
6323
+ const phase = frame / 24 * Math.PI * 2;
6324
+ for (let col = 0; col < cols; col++) {
6325
+ const colPhase = col / cols * Math.PI * 2;
6326
+ const height = Math.sin(phase + colPhase) * 2.5 + 3.5;
6327
+ const row = Math.floor(height);
6328
+ if (row >= 0 && row < rows) {
6329
+ setPixel(f, row, col, 1);
6330
+ const frac = height - row;
6331
+ if (row > 0) setPixel(f, row - 1, col, 1 - frac);
6332
+ if (row < rows - 1) setPixel(f, row + 1, col, frac);
6333
+ }
6334
+ }
6335
+ frames.push(f);
6336
+ }
6337
+ return frames;
6338
+ })();
6339
+ var snake = (() => {
6340
+ const frames = [];
6341
+ const rows = 7;
6342
+ const cols = 7;
6343
+ const path = [];
6344
+ let x = 0;
6345
+ let y = 0;
6346
+ let dx = 1;
6347
+ let dy = 0;
6348
+ const visited = /* @__PURE__ */ new Set();
6349
+ while (path.length < rows * cols) {
6350
+ path.push([y, x]);
6351
+ visited.add(`${y},${x}`);
6352
+ const nextX = x + dx;
6353
+ const nextY = y + dy;
6354
+ if (nextX >= 0 && nextX < cols && nextY >= 0 && nextY < rows && !visited.has(`${nextY},${nextX}`)) {
6355
+ x = nextX;
6356
+ y = nextY;
6357
+ } else {
6358
+ const newDx = -dy;
6359
+ const newDy = dx;
6360
+ dx = newDx;
6361
+ dy = newDy;
6362
+ const nextX2 = x + dx;
6363
+ const nextY2 = y + dy;
6364
+ if (nextX2 >= 0 && nextX2 < cols && nextY2 >= 0 && nextY2 < rows && !visited.has(`${nextY2},${nextX2}`)) {
6365
+ x = nextX2;
6366
+ y = nextY2;
6367
+ } else {
6368
+ break;
6369
+ }
6370
+ }
6371
+ }
6372
+ const snakeLength = 5;
6373
+ for (let frame = 0; frame < path.length; frame++) {
6374
+ const f = emptyFrame(rows, cols);
6375
+ for (let i = 0; i < snakeLength; i++) {
6376
+ const idx = frame - i;
6377
+ if (idx >= 0 && idx < path.length) {
6378
+ const [y2, x2] = path[idx];
6379
+ const brightness = 1 - i / snakeLength;
6380
+ setPixel(f, y2, x2, brightness);
6381
+ }
6382
+ }
6383
+ frames.push(f);
6384
+ }
6385
+ return frames;
6386
+ })();
6387
+ var Matrix = React__namespace.forwardRef(
6388
+ ({
6389
+ rows,
6390
+ cols,
6391
+ pattern,
6392
+ frames,
6393
+ fps = 12,
6394
+ autoplay = true,
6395
+ loop = true,
6396
+ size = 10,
6397
+ gap = 2,
6398
+ palette = {
6399
+ on: "currentColor",
6400
+ off: "var(--muted-foreground)"
6401
+ },
6402
+ brightness = 1,
6403
+ ariaLabel,
6404
+ onFrame,
6405
+ mode = "default",
6406
+ levels,
6407
+ className,
6408
+ ...props
6409
+ }, ref) => {
6410
+ const { frameIndex } = useAnimation(frames, {
6411
+ fps,
6412
+ autoplay: autoplay && !pattern,
6413
+ loop,
6414
+ onFrame
6415
+ });
6416
+ const currentFrame = React.useMemo(() => {
6417
+ if (mode === "vu" && levels && levels.length > 0) {
6418
+ return ensureFrameSize(vu(cols, levels), rows, cols);
6419
+ }
6420
+ if (pattern) {
6421
+ return ensureFrameSize(pattern, rows, cols);
6422
+ }
6423
+ if (frames && frames.length > 0) {
6424
+ return ensureFrameSize(frames[frameIndex] || frames[0], rows, cols);
6425
+ }
6426
+ return ensureFrameSize([], rows, cols);
6427
+ }, [pattern, frames, frameIndex, rows, cols, mode, levels]);
6428
+ const cellPositions = React.useMemo(() => {
6429
+ const positions = [];
6430
+ for (let row = 0; row < rows; row++) {
6431
+ positions[row] = [];
6432
+ for (let col = 0; col < cols; col++) {
6433
+ positions[row][col] = {
6434
+ x: col * (size + gap),
6435
+ y: row * (size + gap)
6436
+ };
6437
+ }
6438
+ }
6439
+ return positions;
6440
+ }, [rows, cols, size, gap]);
6441
+ const svgDimensions = React.useMemo(() => {
6442
+ return {
6443
+ width: cols * (size + gap) - gap,
6444
+ height: rows * (size + gap) - gap
6445
+ };
6446
+ }, [rows, cols, size, gap]);
6447
+ const isAnimating = !pattern && frames && frames.length > 0;
6448
+ return /* @__PURE__ */ jsxRuntime.jsx(
6449
+ "div",
6450
+ {
6451
+ ref,
6452
+ role: "img",
6453
+ "aria-label": ariaLabel ?? "matrix display",
6454
+ "aria-live": isAnimating ? "polite" : void 0,
6455
+ className: cn("relative inline-block", className),
6456
+ style: {
6457
+ "--matrix-on": palette.on,
6458
+ "--matrix-off": palette.off,
6459
+ "--matrix-gap": `${gap}px`,
6460
+ "--matrix-size": `${size}px`
6461
+ },
6462
+ ...props,
6463
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
6464
+ "svg",
6465
+ {
6466
+ width: svgDimensions.width,
6467
+ height: svgDimensions.height,
6468
+ viewBox: `0 0 ${svgDimensions.width} ${svgDimensions.height}`,
6469
+ xmlns: "http://www.w3.org/2000/svg",
6470
+ className: "block",
6471
+ style: { overflow: "visible" },
6472
+ children: [
6473
+ /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
6474
+ /* @__PURE__ */ jsxRuntime.jsxs("radialGradient", { id: "matrix-pixel-on", cx: "50%", cy: "50%", r: "50%", children: [
6475
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "var(--matrix-on)", stopOpacity: "1" }),
6476
+ /* @__PURE__ */ jsxRuntime.jsx(
6477
+ "stop",
6478
+ {
6479
+ offset: "70%",
6480
+ stopColor: "var(--matrix-on)",
6481
+ stopOpacity: "0.85"
6482
+ }
6483
+ ),
6484
+ /* @__PURE__ */ jsxRuntime.jsx(
6485
+ "stop",
6486
+ {
6487
+ offset: "100%",
6488
+ stopColor: "var(--matrix-on)",
6489
+ stopOpacity: "0.6"
6490
+ }
6491
+ )
6492
+ ] }),
6493
+ /* @__PURE__ */ jsxRuntime.jsxs("radialGradient", { id: "matrix-pixel-off", cx: "50%", cy: "50%", r: "50%", children: [
6494
+ /* @__PURE__ */ jsxRuntime.jsx(
6495
+ "stop",
6496
+ {
6497
+ offset: "0%",
6498
+ stopColor: "var(--muted-foreground)",
6499
+ stopOpacity: "1"
6500
+ }
6501
+ ),
6502
+ /* @__PURE__ */ jsxRuntime.jsx(
6503
+ "stop",
6504
+ {
6505
+ offset: "100%",
6506
+ stopColor: "var(--muted-foreground)",
6507
+ stopOpacity: "0.7"
6508
+ }
6509
+ )
6510
+ ] }),
6511
+ /* @__PURE__ */ jsxRuntime.jsxs(
6512
+ "filter",
6513
+ {
6514
+ id: "matrix-glow",
6515
+ x: "-50%",
6516
+ y: "-50%",
6517
+ width: "200%",
6518
+ height: "200%",
6519
+ children: [
6520
+ /* @__PURE__ */ jsxRuntime.jsx("feGaussianBlur", { stdDeviation: "2", result: "blur" }),
6521
+ /* @__PURE__ */ jsxRuntime.jsx("feComposite", { in: "SourceGraphic", in2: "blur", operator: "over" })
6522
+ ]
6523
+ }
6524
+ )
6525
+ ] }),
6526
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
6527
+ .matrix-pixel {
6528
+ transition: opacity 300ms ease-out, transform 150ms ease-out;
6529
+ transform-origin: center;
6530
+ transform-box: fill-box;
6531
+ }
6532
+ .matrix-pixel-active {
6533
+ filter: url(#matrix-glow);
6534
+ }
6535
+ ` }),
6536
+ currentFrame.map(
6537
+ (row, rowIndex) => row.map((value, colIndex) => {
6538
+ const pos = cellPositions[rowIndex]?.[colIndex];
6539
+ if (!pos) return null;
6540
+ const opacity = clamp(brightness * value);
6541
+ const isActive = opacity > 0.5;
6542
+ const isOn = opacity > 0.05;
6543
+ const fill = isOn ? "url(#matrix-pixel-on)" : "url(#matrix-pixel-off)";
6544
+ const scale = isActive ? 1.1 : 1;
6545
+ const radius = size / 2 * 0.9;
6546
+ return /* @__PURE__ */ jsxRuntime.jsx(
6547
+ "circle",
6548
+ {
6549
+ className: cn(
6550
+ "matrix-pixel",
6551
+ isActive && "matrix-pixel-active",
6552
+ !isOn && "opacity-20 dark:opacity-[0.1]"
6553
+ ),
6554
+ cx: pos.x + size / 2,
6555
+ cy: pos.y + size / 2,
6556
+ r: radius,
6557
+ fill,
6558
+ opacity: isOn ? opacity : 0.1,
6559
+ style: {
6560
+ transform: `scale(${scale})`
6561
+ }
6562
+ },
6563
+ `${rowIndex}-${colIndex}`
6564
+ );
6565
+ })
6566
+ )
6567
+ ]
6568
+ }
6569
+ )
6570
+ }
6571
+ );
6572
+ }
6573
+ );
6574
+ Matrix.displayName = "Matrix";
6575
+ var DEFAULT_SIZE = 22;
6576
+ var MATRIX_GRID = 7;
6577
+ var MATRIX_ON = "var(--payman-v2-matrix-color)";
6578
+ var MATRIX_PRESETS = [
6579
+ { frames: loader, fps: 8 },
6580
+ { frames: wave, fps: 14 },
6581
+ { frames: snake, fps: 16 },
6582
+ { frames: pulse, fps: 12 }
6583
+ ];
6584
+ var PRESET_ROTATE_MS = 1e4;
5625
6585
  function useElapsedSeconds(isStreaming) {
5626
- const [elapsed, setElapsed] = react.useState(0);
5627
- const startRef = react.useRef(null);
5628
- react.useEffect(() => {
6586
+ const [elapsed, setElapsed] = React.useState(0);
6587
+ const startRef = React.useRef(null);
6588
+ React.useEffect(() => {
5629
6589
  if (!isStreaming) {
5630
6590
  startRef.current = null;
5631
6591
  setElapsed(0);
@@ -5642,12 +6602,37 @@ function useElapsedSeconds(isStreaming) {
5642
6602
  }, [isStreaming]);
5643
6603
  return elapsed;
5644
6604
  }
6605
+ function useMatrixPresetIndex(isStreaming) {
6606
+ const [index, setIndex] = React.useState(0);
6607
+ React.useEffect(() => {
6608
+ if (!isStreaming) return;
6609
+ setIndex(0);
6610
+ const id = setInterval(() => {
6611
+ setIndex((prev) => (prev + 1) % MATRIX_PRESETS.length);
6612
+ }, PRESET_ROTATE_MS);
6613
+ return () => clearInterval(id);
6614
+ }, [isStreaming]);
6615
+ return index;
6616
+ }
6617
+ function formatElapsed(totalSeconds) {
6618
+ if (totalSeconds < 60) return `${totalSeconds}s`;
6619
+ const minutes = Math.floor(totalSeconds / 60);
6620
+ const seconds = totalSeconds % 60;
6621
+ return `${minutes}m ${seconds}s`;
6622
+ }
5645
6623
  function StreamingIndicatorV2({
5646
6624
  isStreaming,
5647
6625
  loadingAnimation
5648
6626
  }) {
5649
6627
  const size = loadingAnimation?.size ?? DEFAULT_SIZE;
5650
6628
  const elapsed = useElapsedSeconds(isStreaming);
6629
+ const presetIndex = useMatrixPresetIndex(isStreaming);
6630
+ const preset = MATRIX_PRESETS[presetIndex];
6631
+ const gap = 1;
6632
+ const cell = React.useMemo(
6633
+ () => Math.max(2, Math.round((size - (MATRIX_GRID - 1) * gap) / MATRIX_GRID)),
6634
+ [size]
6635
+ );
5651
6636
  return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isStreaming && /* @__PURE__ */ jsxRuntime.jsxs(
5652
6637
  framerMotion.motion.div,
5653
6638
  {
@@ -5663,43 +6648,84 @@ function StreamingIndicatorV2({
5663
6648
  {
5664
6649
  className: "payman-v2-streaming-indicator-glyph",
5665
6650
  style: { width: size, height: size },
5666
- children: /* @__PURE__ */ jsxRuntime.jsx(
5667
- dotlottieReact.DotLottieReact,
5668
- {
5669
- src: loadingAnimation?.src ?? thinking_atom_default,
5670
- loop: true,
5671
- autoplay: true,
5672
- style: { width: "100%", height: "100%" }
5673
- }
6651
+ children: loadingAnimation?.src ? (
6652
+ // Consumer override: render their Lottie animation.
6653
+ /* @__PURE__ */ jsxRuntime.jsx(
6654
+ dotlottieReact.DotLottieReact,
6655
+ {
6656
+ src: loadingAnimation.src,
6657
+ loop: true,
6658
+ autoplay: true,
6659
+ style: { width: "100%", height: "100%" }
6660
+ }
6661
+ )
6662
+ ) : (
6663
+ // Default: dot-matrix glyph, cycling presets +
6664
+ // Payman brand tint. Each preset lives in its own
6665
+ // keyed motion layer so swapping presets crossfades
6666
+ // (old scales/fades out while the new scales/fades
6667
+ // in) instead of hard-cutting. Keying the layer on
6668
+ // `presetIndex` also remounts the Matrix, restarting
6669
+ // its frame counter cleanly at frame 0.
6670
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
6671
+ framerMotion.motion.div,
6672
+ {
6673
+ className: "payman-v2-streaming-indicator-matrix",
6674
+ initial: { opacity: 0, scale: 0.8 },
6675
+ animate: { opacity: 1, scale: 1 },
6676
+ exit: { opacity: 0, scale: 0.8 },
6677
+ transition: { duration: 0.45, ease: [0.2, 0, 0, 1] },
6678
+ children: /* @__PURE__ */ jsxRuntime.jsx(
6679
+ Matrix,
6680
+ {
6681
+ rows: MATRIX_GRID,
6682
+ cols: MATRIX_GRID,
6683
+ frames: preset.frames,
6684
+ fps: preset.fps,
6685
+ size: cell,
6686
+ gap,
6687
+ palette: {
6688
+ on: MATRIX_ON,
6689
+ // Unlit dots use the v2 theme's own muted-text
6690
+ // token (light/dark-aware), not the bare
6691
+ // `--muted-foreground` the vendored Matrix
6692
+ // component falls back to internally — that
6693
+ // variable isn't defined under `.payman-v2-root`
6694
+ // and would otherwise resolve to black.
6695
+ off: "var(--payman-v2-matrix-dot-off)"
6696
+ },
6697
+ ariaLabel: "Working\u2026"
6698
+ }
6699
+ )
6700
+ },
6701
+ presetIndex
6702
+ ) })
5674
6703
  )
5675
6704
  }
5676
6705
  ),
5677
- elapsed > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-streaming-indicator-elapsed", children: [
5678
- elapsed,
5679
- "s"
5680
- ] })
6706
+ elapsed > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-streaming-indicator-elapsed", children: formatElapsed(elapsed) })
5681
6707
  ]
5682
6708
  },
5683
6709
  "streaming-indicator"
5684
6710
  ) });
5685
6711
  }
5686
6712
  function ImageLightboxV2({ src, alt, onClose }) {
5687
- const [isMounted, setIsMounted] = react.useState(false);
5688
- const [isImageLoaded, setIsImageLoaded] = react.useState(false);
5689
- react.useEffect(() => {
6713
+ const [isMounted, setIsMounted] = React.useState(false);
6714
+ const [isImageLoaded, setIsImageLoaded] = React.useState(false);
6715
+ React.useEffect(() => {
5690
6716
  setIsMounted(true);
5691
6717
  return () => setIsMounted(false);
5692
6718
  }, []);
5693
- react.useEffect(() => {
6719
+ React.useEffect(() => {
5694
6720
  setIsImageLoaded(false);
5695
6721
  }, [src]);
5696
- const handleKeyDown = react.useCallback(
6722
+ const handleKeyDown = React.useCallback(
5697
6723
  (e) => {
5698
6724
  if (e.key === "Escape") onClose();
5699
6725
  },
5700
6726
  [onClose]
5701
6727
  );
5702
- react.useEffect(() => {
6728
+ React.useEffect(() => {
5703
6729
  if (!src || typeof document === "undefined") return;
5704
6730
  document.addEventListener("keydown", handleKeyDown);
5705
6731
  const previousOverflow = document.body.style.overflow;
@@ -5848,10 +6874,10 @@ function TraceTimelineModal({
5848
6874
  apiBaseUrl,
5849
6875
  apiHeaders
5850
6876
  }) {
5851
- const [trace, setTrace] = react.useState(null);
5852
- const [loading, setLoading] = react.useState(false);
5853
- const [error, setError] = react.useState(null);
5854
- react.useEffect(() => {
6877
+ const [trace, setTrace] = React.useState(null);
6878
+ const [loading, setLoading] = React.useState(false);
6879
+ const [error, setError] = React.useState(null);
6880
+ React.useEffect(() => {
5855
6881
  if (!open || !executionId) return;
5856
6882
  const ctrl = new AbortController();
5857
6883
  setLoading(true);
@@ -5873,7 +6899,7 @@ function TraceTimelineModal({
5873
6899
  }).finally(() => setLoading(false));
5874
6900
  return () => ctrl.abort();
5875
6901
  }, [open, executionId, apiBaseUrl, apiHeaders]);
5876
- const totalMs = react.useMemo(() => {
6902
+ const totalMs = React.useMemo(() => {
5877
6903
  if (!trace) return 0;
5878
6904
  const meta = trace.runMetadata?.totalTimeMs;
5879
6905
  if (typeof meta === "number" && meta > 0) return meta;
@@ -5882,7 +6908,7 @@ function TraceTimelineModal({
5882
6908
  if (starts.length === 0 || ends.length === 0) return 0;
5883
6909
  return Math.max(...ends) - Math.min(...starts);
5884
6910
  }, [trace]);
5885
- const accountedMs = react.useMemo(
6911
+ const accountedMs = React.useMemo(
5886
6912
  () => (trace?.pipelineSteps ?? []).reduce(
5887
6913
  (sum, s) => sum + (s.durationMs ?? 0),
5888
6914
  0
@@ -6143,12 +7169,12 @@ var NOOP_ASYNC = async () => {
6143
7169
  var NOOP = () => {
6144
7170
  };
6145
7171
  function useSentryChatCallbacks(callbacks, config) {
6146
- const sentryCtxRef = react.useRef({});
6147
- const callbacksRef = react.useRef(callbacks);
7172
+ const sentryCtxRef = React.useRef({});
7173
+ const callbacksRef = React.useRef(callbacks);
6148
7174
  callbacksRef.current = callbacks;
6149
7175
  const initialSessionId = config.initialSessionId;
6150
7176
  const apiHeaders = config.api.headers;
6151
- react.useEffect(() => {
7177
+ React.useEffect(() => {
6152
7178
  sentryCtxRef.current.agentId = config.agentId;
6153
7179
  sentryCtxRef.current.sessionOwnerId = config.sessionParams?.id;
6154
7180
  if (initialSessionId) {
@@ -6164,13 +7190,13 @@ function useSentryChatCallbacks(callbacks, config) {
6164
7190
  initialSessionId,
6165
7191
  apiHeaders
6166
7192
  ]);
6167
- react.useEffect(() => {
7193
+ React.useEffect(() => {
6168
7194
  const endpoint = config.api.streamEndpoint || "/api/playground/ask/stream";
6169
7195
  return subscribeToCfRay(endpoint, (cfRay) => {
6170
7196
  sentryCtxRef.current.cfRay = cfRay;
6171
7197
  });
6172
7198
  }, [config.api.streamEndpoint]);
6173
- return react.useMemo(
7199
+ return React.useMemo(
6174
7200
  () => ({
6175
7201
  onMessageSent: (msg) => callbacksRef.current?.onMessageSent?.(msg),
6176
7202
  onStreamStart: () => callbacksRef.current?.onStreamStart?.(),
@@ -6219,7 +7245,7 @@ function useSentryChatCallbacks(callbacks, config) {
6219
7245
  []
6220
7246
  );
6221
7247
  }
6222
- var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7248
+ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6223
7249
  config,
6224
7250
  callbacks = {},
6225
7251
  className,
@@ -6230,18 +7256,18 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6230
7256
  hasMoreMessages = false,
6231
7257
  chat
6232
7258
  }, ref) {
6233
- const [inputValue, setInputValue] = react.useState("");
6234
- const prevInputValueRef = react.useRef(inputValue);
6235
- const [hasEverSentMessage, setHasEverSentMessage] = react.useState(false);
6236
- const [lightboxSrc, setLightboxSrc] = react.useState(null);
6237
- const [lightboxAlt, setLightboxAlt] = react.useState("");
6238
- const [editingMessageId, setEditingMessageId] = react.useState(null);
6239
- const [isResetSessionConfirmOpen, setIsResetSessionConfirmOpen] = react.useState(false);
6240
- const [analysisMode, setAnalysisMode] = react.useState("fast");
6241
- const chatInputV2Ref = react.useRef(null);
6242
- const messageListV2Ref = react.useRef(null);
6243
- const resetToEmptyStateRef = react.useRef(false);
6244
- react.useEffect(() => {
7259
+ const [inputValue, setInputValue] = React.useState("");
7260
+ const prevInputValueRef = React.useRef(inputValue);
7261
+ const [hasEverSentMessage, setHasEverSentMessage] = React.useState(false);
7262
+ const [lightboxSrc, setLightboxSrc] = React.useState(null);
7263
+ const [lightboxAlt, setLightboxAlt] = React.useState("");
7264
+ const [editingMessageId, setEditingMessageId] = React.useState(null);
7265
+ const [isResetSessionConfirmOpen, setIsResetSessionConfirmOpen] = React.useState(false);
7266
+ const [analysisMode, setAnalysisMode] = React.useState("fast");
7267
+ const chatInputV2Ref = React.useRef(null);
7268
+ const messageListV2Ref = React.useRef(null);
7269
+ const resetToEmptyStateRef = React.useRef(false);
7270
+ React.useEffect(() => {
6245
7271
  if (config.sentryDsn) {
6246
7272
  initSentryIfNeeded(config.sentryDsn);
6247
7273
  }
@@ -6257,7 +7283,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6257
7283
  getSessionId,
6258
7284
  getMessages
6259
7285
  } = chat;
6260
- react.useEffect(() => {
7286
+ React.useEffect(() => {
6261
7287
  if (resetToEmptyStateRef.current) {
6262
7288
  if (messages.length === 0) {
6263
7289
  setHasEverSentMessage(false);
@@ -6269,7 +7295,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6269
7295
  setHasEverSentMessage(true);
6270
7296
  }
6271
7297
  }, [messages.length, hasEverSentMessage]);
6272
- react.useEffect(() => {
7298
+ React.useEffect(() => {
6273
7299
  if (!editingMessageId) return;
6274
7300
  const editingMessageStillExists = messages.some(
6275
7301
  (message) => message.id === editingMessageId && message.role === "user"
@@ -6285,6 +7311,15 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6285
7311
  const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
6286
7312
  const dismissNotification = chat.dismissNotification ?? NOOP;
6287
7313
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
7314
+ const expiredUserActionIdsRef = React.useRef(/* @__PURE__ */ new Set());
7315
+ const expireUserActionOnce = React.useCallback(
7316
+ (userActionId) => {
7317
+ if (expiredUserActionIdsRef.current.has(userActionId)) return;
7318
+ expiredUserActionIdsRef.current.add(userActionId);
7319
+ void expireUserAction2(userActionId);
7320
+ },
7321
+ [expireUserAction2]
7322
+ );
6288
7323
  const {
6289
7324
  transcribedText,
6290
7325
  isAvailable: voiceAvailable,
@@ -6308,7 +7343,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6308
7343
  }
6309
7344
  }
6310
7345
  );
6311
- const contextValue = react.useMemo(
7346
+ const contextValue = React.useMemo(
6312
7347
  () => ({
6313
7348
  resetSession,
6314
7349
  clearMessages,
@@ -6335,8 +7370,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6335
7370
  onAttachFileClick,
6336
7371
  onMessageFeedback
6337
7372
  } = callbacks;
6338
- const [debugTraceExecutionId, setDebugTraceExecutionId] = react.useState(null);
6339
- const handleSubmitFeedback = react.useCallback(
7373
+ const [debugTraceExecutionId, setDebugTraceExecutionId] = React.useState(null);
7374
+ const handleSubmitFeedback = React.useCallback(
6340
7375
  async ({ messageId, executionId, feedback, details }) => {
6341
7376
  if (!executionId) {
6342
7377
  throw new Error("Cannot submit feedback before the response completes");
@@ -6367,14 +7402,14 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6367
7402
  onMessageFeedback
6368
7403
  ]
6369
7404
  );
6370
- const onExecutionTraceClick = react.useMemo(() => {
7405
+ const onExecutionTraceClick = React.useMemo(() => {
6371
7406
  if (!config.debug) return rawOnExecutionTraceClick;
6372
7407
  return (data) => {
6373
7408
  rawOnExecutionTraceClick?.(data);
6374
7409
  if (data.executionId) setDebugTraceExecutionId(data.executionId);
6375
7410
  };
6376
7411
  }, [config.debug, rawOnExecutionTraceClick]);
6377
- const performResetSession = react.useCallback(() => {
7412
+ const performResetSession = React.useCallback(() => {
6378
7413
  resetToEmptyStateRef.current = true;
6379
7414
  setEditingMessageId(null);
6380
7415
  setIsResetSessionConfirmOpen(false);
@@ -6395,13 +7430,13 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6395
7430
  resetSession,
6396
7431
  stopRecording
6397
7432
  ]);
6398
- const requestResetSession = react.useCallback(() => {
7433
+ const requestResetSession = React.useCallback(() => {
6399
7434
  setIsResetSessionConfirmOpen(true);
6400
7435
  }, []);
6401
- const closeResetSessionConfirm = react.useCallback(() => {
7436
+ const closeResetSessionConfirm = React.useCallback(() => {
6402
7437
  setIsResetSessionConfirmOpen(false);
6403
7438
  }, []);
6404
- react.useImperativeHandle(ref, () => ({
7439
+ React.useImperativeHandle(ref, () => ({
6405
7440
  resetSession: performResetSession,
6406
7441
  clearMessages,
6407
7442
  cancelStream,
@@ -6440,7 +7475,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6440
7475
  slashCommands: slashCommandsConfig,
6441
7476
  commandPermissions
6442
7477
  } = config;
6443
- const messageActions = react.useMemo(
7478
+ const messageActions = React.useMemo(
6444
7479
  () => ({
6445
7480
  userMessageActions: {
6446
7481
  copy: messageActionsConfig?.userMessageActions?.copy ?? true,
@@ -6456,18 +7491,18 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6456
7491
  }),
6457
7492
  [messageActionsConfig]
6458
7493
  );
6459
- const slashCommands = react.useMemo(
7494
+ const slashCommands = React.useMemo(
6460
7495
  () => enableSlashCommands ? filterSlashCommands(
6461
7496
  slashCommandsConfig ?? DEFAULT_SLASH_COMMANDS,
6462
7497
  commandPermissions
6463
7498
  ) : [],
6464
7499
  [commandPermissions, enableSlashCommands, slashCommandsConfig]
6465
7500
  );
6466
- const isSessionParamsConfigured = react.useMemo(() => {
7501
+ const isSessionParamsConfigured = React.useMemo(() => {
6467
7502
  if (!sessionParams) return false;
6468
7503
  return !!(sessionParams.id?.trim() && sessionParams.name?.trim());
6469
7504
  }, [sessionParams?.id, sessionParams?.name]);
6470
- react.useEffect(() => {
7505
+ React.useEffect(() => {
6471
7506
  const wasEmpty = prevInputValueRef.current.trim() === "";
6472
7507
  const isEmpty2 = inputValue.trim() === "";
6473
7508
  prevInputValueRef.current = inputValue;
@@ -6527,6 +7562,9 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6527
7562
  };
6528
7563
  const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
6529
7564
  const notifications = userActionState.notifications;
7565
+ const dockedPrompt = userActionPrompts?.find(
7566
+ (prompt) => prompt.kind !== "notification" && (prompt.status === "pending" || prompt.status === "submitting" || prompt.status === "stale")
7567
+ );
6530
7568
  const handleV2Send = (text) => {
6531
7569
  if (isRecording) stopRecording();
6532
7570
  if (text.trim() && !disableInput && isSessionParamsConfigured) {
@@ -6534,7 +7572,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6534
7572
  void sendMessage(text.trim(), { analysisMode: effectiveAnalysisMode });
6535
7573
  }
6536
7574
  };
6537
- const handleVoicePress = react.useCallback(async () => {
7575
+ const handleVoicePress = React.useCallback(async () => {
6538
7576
  if (!voiceAvailable) return;
6539
7577
  if (isRecording) {
6540
7578
  stopRecording();
@@ -6543,7 +7581,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6543
7581
  clearTranscript();
6544
7582
  await startRecording();
6545
7583
  }, [clearTranscript, isRecording, startRecording, stopRecording, voiceAvailable]);
6546
- const handleEditMessageDraft = react.useCallback((messageId) => {
7584
+ const handleEditMessageDraft = React.useCallback((messageId) => {
6547
7585
  const targetMessage = messages.find((message) => message.id === messageId);
6548
7586
  if (!targetMessage?.content.trim()) return;
6549
7587
  setEditingMessageId(messageId);
@@ -6552,7 +7590,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6552
7590
  messageListV2Ref.current?.scrollToBottom("smooth");
6553
7591
  });
6554
7592
  }, [messages]);
6555
- const handleRetryUserMessage = react.useCallback((messageId) => {
7593
+ const handleRetryUserMessage = React.useCallback((messageId) => {
6556
7594
  if (isWaitingForResponse) return;
6557
7595
  const targetMessage = messages.find(
6558
7596
  (message) => message.id === messageId && message.role === "user"
@@ -6569,7 +7607,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6569
7607
  });
6570
7608
  });
6571
7609
  }, [isWaitingForResponse, messages, sendMessage, effectiveAnalysisMode]);
6572
- const handleClearEditing = react.useCallback(() => {
7610
+ const handleClearEditing = React.useCallback(() => {
6573
7611
  setEditingMessageId(null);
6574
7612
  }, []);
6575
7613
  return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -6679,11 +7717,12 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6679
7717
  retryDisabled: isWaitingForResponse,
6680
7718
  typingSpeed: config.typingSpeed ?? 4,
6681
7719
  userActionPrompts,
7720
+ dockedUserActionId: dockedPrompt?.userActionId,
6682
7721
  notifications,
6683
7722
  onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6684
7723
  onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6685
7724
  onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6686
- onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
7725
+ onExpireUserAction: isUserActionSupported ? async (id) => expireUserActionOnce(id) : void 0,
6687
7726
  onDismissNotification: dismissNotification,
6688
7727
  onSubmitFeedback: handleSubmitFeedback
6689
7728
  }
@@ -6696,33 +7735,43 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6696
7735
  }
6697
7736
  ),
6698
7737
  hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
6699
- ChatInputV2,
7738
+ UserActionDock,
6700
7739
  {
6701
- ref: chatInputV2Ref,
6702
- onSend: handleV2Send,
6703
- onCancel: cancelStream,
6704
- disabled: isV2InputDisabled,
6705
- isStreaming: isWaitingForResponse,
6706
- placeholder: isRecording ? "Listening..." : placeholder,
6707
- enableVoice: config.enableVoice === true,
6708
- transcribedText: config.enableVoice === true ? transcribedText : "",
6709
- voiceAvailable: config.enableVoice === true && voiceAvailable,
6710
- isRecording,
6711
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6712
- onCancelRecording: handleCancelRecording,
6713
- onConfirmRecording: handleConfirmRecording,
6714
- showResetSession,
6715
- onResetSession: requestResetSession,
6716
- showAttachmentButton,
6717
- showUploadImageButton,
6718
- showAttachFileButton,
6719
- onUploadImageClick,
6720
- onAttachFileClick,
6721
- editingMessageId,
6722
- onClearEditing: handleClearEditing,
6723
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6724
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6725
- slashCommands
7740
+ prompt: isUserActionSupported ? dockedPrompt : void 0,
7741
+ onSubmit: submitUserAction2,
7742
+ onCancel: cancelUserAction2,
7743
+ onResend: resendUserAction2,
7744
+ onExpired: expireUserActionOnce,
7745
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7746
+ ChatInputV2,
7747
+ {
7748
+ ref: chatInputV2Ref,
7749
+ onSend: handleV2Send,
7750
+ onCancel: cancelStream,
7751
+ disabled: isV2InputDisabled,
7752
+ isStreaming: isWaitingForResponse,
7753
+ placeholder: isRecording ? "Listening..." : placeholder,
7754
+ enableVoice: config.enableVoice === true,
7755
+ transcribedText: config.enableVoice === true ? transcribedText : "",
7756
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
7757
+ isRecording,
7758
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
7759
+ onCancelRecording: handleCancelRecording,
7760
+ onConfirmRecording: handleConfirmRecording,
7761
+ showResetSession,
7762
+ onResetSession: requestResetSession,
7763
+ showAttachmentButton,
7764
+ showUploadImageButton,
7765
+ showAttachFileButton,
7766
+ onUploadImageClick,
7767
+ onAttachFileClick,
7768
+ editingMessageId,
7769
+ onClearEditing: handleClearEditing,
7770
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
7771
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
7772
+ slashCommands
7773
+ }
7774
+ )
6726
7775
  }
6727
7776
  )
6728
7777
  ]
@@ -6759,7 +7808,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6759
7808
  }
6760
7809
  ) });
6761
7810
  });
6762
- var PaymanChat = react.forwardRef(
7811
+ var PaymanChat = React.forwardRef(
6763
7812
  function PaymanChat2(props, ref) {
6764
7813
  const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
6765
7814
  const chat = useChatV2(props.config, mergedCallbacks);