@paymanai/payman-ask-sdk 4.0.23 → 4.0.25

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.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import * as React from 'react';
2
1
  import { createContext, forwardRef, useCallback, useRef, useState, useMemo, useEffect, useImperativeHandle, useLayoutEffect, useContext } from 'react';
3
2
  import { AnimatePresence, motion } from 'framer-motion';
4
3
  import { clsx } from 'clsx';
@@ -34,6 +33,7 @@ var chatStore = {
34
33
  memoryStore.delete(key);
35
34
  }
36
35
  };
36
+ var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
37
37
  var streams = /* @__PURE__ */ new Map();
38
38
  var activeStreamStore = {
39
39
  has(key) {
@@ -42,7 +42,11 @@ var activeStreamStore = {
42
42
  get(key) {
43
43
  const entry = streams.get(key);
44
44
  if (!entry) return null;
45
- return { messages: entry.messages, isWaiting: entry.isWaiting };
45
+ return {
46
+ messages: entry.messages,
47
+ isWaiting: entry.isWaiting,
48
+ userActionState: entry.userActionState
49
+ };
46
50
  },
47
51
  // Called before startStream — registers the controller and initial messages
48
52
  start(key, abortController, initialMessages) {
@@ -50,6 +54,7 @@ var activeStreamStore = {
50
54
  streams.set(key, {
51
55
  messages: initialMessages,
52
56
  isWaiting: true,
57
+ userActionState: EMPTY_USER_ACTION_STATE,
53
58
  abortController,
54
59
  listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
55
60
  });
@@ -60,20 +65,27 @@ var activeStreamStore = {
60
65
  if (!entry) return;
61
66
  const next = typeof updater === "function" ? updater(entry.messages) : updater;
62
67
  entry.messages = next;
63
- entry.listeners.forEach((l) => l(next, entry.isWaiting));
68
+ entry.listeners.forEach((l) => l(next, entry.isWaiting, entry.userActionState));
64
69
  },
65
70
  setWaiting(key, waiting) {
66
71
  const entry = streams.get(key);
67
72
  if (!entry) return;
68
73
  entry.isWaiting = waiting;
69
- entry.listeners.forEach((l) => l(entry.messages, waiting));
74
+ entry.listeners.forEach((l) => l(entry.messages, waiting, entry.userActionState));
75
+ },
76
+ applyUserActionState(key, updater) {
77
+ const entry = streams.get(key);
78
+ if (!entry) return;
79
+ const next = typeof updater === "function" ? updater(entry.userActionState) : updater;
80
+ entry.userActionState = next;
81
+ entry.listeners.forEach((l) => l(entry.messages, entry.isWaiting, next));
70
82
  },
71
83
  // Called when stream completes — persists to chatStore and cleans up
72
84
  complete(key) {
73
85
  const entry = streams.get(key);
74
86
  if (!entry) return;
75
87
  entry.isWaiting = false;
76
- entry.listeners.forEach((l) => l(entry.messages, false));
88
+ entry.listeners.forEach((l) => l(entry.messages, false, entry.userActionState));
77
89
  const toSave = entry.messages.filter((m) => !m.isStreaming);
78
90
  if (toSave.length > 0) chatStore.set(key, toSave);
79
91
  streams.delete(key);
@@ -496,9 +508,9 @@ function processStreamEventV2(rawEvent, state) {
496
508
  state.hasError = true;
497
509
  state.errorMessage = "WORKFLOW_FAILED";
498
510
  }
499
- state.steps.forEach((step) => {
500
- if (step.status === "in_progress") {
501
- step.status = "completed";
511
+ state.steps.forEach((step2) => {
512
+ if (step2.status === "in_progress") {
513
+ step2.status = "completed";
502
514
  }
503
515
  });
504
516
  state.lastEventType = eventType;
@@ -913,11 +925,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
913
925
  errorDetails: isAborted ? void 0 : error.message,
914
926
  content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
915
927
  currentMessage: isAborted ? "Thinking..." : void 0,
916
- steps: [...state.steps].map((step) => {
917
- if (step.status === "in_progress" && isAborted) {
918
- return { ...step, status: "pending" };
928
+ steps: [...state.steps].map((step2) => {
929
+ if (step2.status === "in_progress" && isAborted) {
930
+ return { ...step2, status: "pending" };
919
931
  }
920
- return step;
932
+ return step2;
921
933
  }),
922
934
  currentExecutingStepId: void 0
923
935
  } : msg
@@ -1003,11 +1015,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1003
1015
  isCancelled: isAborted,
1004
1016
  errorDetails: isAborted ? void 0 : error.message,
1005
1017
  content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1006
- steps: [...state.steps].map((step) => {
1007
- if (step.status === "in_progress" && isAborted) {
1008
- return { ...step, status: "pending" };
1018
+ steps: [...state.steps].map((step2) => {
1019
+ if (step2.status === "in_progress" && isAborted) {
1020
+ return { ...step2, status: "pending" };
1009
1021
  }
1010
- return step;
1022
+ return step2;
1011
1023
  }),
1012
1024
  currentExecutingStepId: void 0
1013
1025
  } : msg
@@ -1028,11 +1040,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1028
1040
  };
1029
1041
  }
1030
1042
  function createCancelledMessageUpdate(steps, currentMessage) {
1031
- const updatedSteps = steps.map((step) => {
1032
- if (step.status === "in_progress") {
1033
- return { ...step, status: "pending" };
1043
+ const updatedSteps = steps.map((step2) => {
1044
+ if (step2.status === "in_progress") {
1045
+ return { ...step2, status: "pending" };
1034
1046
  }
1035
- return step;
1047
+ return step2;
1036
1048
  });
1037
1049
  return {
1038
1050
  isStreaming: false,
@@ -1081,12 +1093,31 @@ async function resendUserAction(config, userActionId) {
1081
1093
  async function expireUserAction(config, userActionId) {
1082
1094
  return sendUserActionRequest(config, userActionId, "expired");
1083
1095
  }
1084
- var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1096
+ function resolveUserActionExpiresAt(req, existing) {
1097
+ if (typeof req.expirySeconds !== "number" || req.expirySeconds <= 0) {
1098
+ return void 0;
1099
+ }
1100
+ if (existing?.expiresAt && existing.userActionId === req.userActionId) {
1101
+ return existing.expiresAt;
1102
+ }
1103
+ return Date.now() + Math.floor(req.expirySeconds) * 1e3;
1104
+ }
1105
+ function getUserActionSecondsLeft(prompt) {
1106
+ if (typeof prompt.expiresAt === "number" && prompt.expiresAt > 0) {
1107
+ return Math.max(0, Math.ceil((prompt.expiresAt - Date.now()) / 1e3));
1108
+ }
1109
+ if (typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0) {
1110
+ return Math.floor(prompt.expirySeconds);
1111
+ }
1112
+ return void 0;
1113
+ }
1114
+ var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
1085
1115
  function upsertPrompt(prompts, req) {
1086
- const active = { ...req, status: "pending" };
1087
1116
  const idx = prompts.findIndex(
1088
1117
  (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
1089
1118
  );
1119
+ const expiresAt = resolveUserActionExpiresAt(req, idx >= 0 ? prompts[idx] : void 0);
1120
+ const active = { ...req, status: "pending", expiresAt };
1090
1121
  if (idx >= 0) {
1091
1122
  const next = prompts.slice();
1092
1123
  next[idx] = active;
@@ -1157,7 +1188,28 @@ function useChatV2(config, callbacks = {}) {
1157
1188
  // eslint-disable-next-line react-hooks/exhaustive-deps
1158
1189
  []
1159
1190
  );
1160
- const [userActionState, setUserActionState] = useState(EMPTY_USER_ACTION_STATE);
1191
+ const [userActionState, setUserActionState] = useState(() => {
1192
+ if (!config.userId) return EMPTY_USER_ACTION_STATE2;
1193
+ return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
1194
+ });
1195
+ const storeAwareSetUserActionState = useCallback(
1196
+ (updater) => {
1197
+ const streamUserId = streamUserIdRef.current;
1198
+ const currentUserId = configRef.current.userId;
1199
+ const storeKey = streamUserId ?? currentUserId;
1200
+ if (storeKey && activeStreamStore.has(storeKey)) {
1201
+ activeStreamStore.applyUserActionState(
1202
+ storeKey,
1203
+ updater
1204
+ );
1205
+ }
1206
+ if (!streamUserId || streamUserId === currentUserId) {
1207
+ setUserActionState(updater);
1208
+ }
1209
+ },
1210
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1211
+ []
1212
+ );
1161
1213
  const wrappedCallbacks = useMemo(() => ({
1162
1214
  ...callbacksRef.current,
1163
1215
  onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
@@ -1167,14 +1219,14 @@ function useChatV2(config, callbacks = {}) {
1167
1219
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1168
1220
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1169
1221
  onUserActionRequired: (request) => {
1170
- setUserActionState((prev) => ({
1222
+ storeAwareSetUserActionState((prev) => ({
1171
1223
  ...prev,
1172
1224
  prompts: upsertPrompt(prev.prompts, request)
1173
1225
  }));
1174
1226
  callbacksRef.current.onUserActionRequired?.(request);
1175
1227
  },
1176
1228
  onUserNotification: (notification) => {
1177
- setUserActionState(
1229
+ storeAwareSetUserActionState(
1178
1230
  (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1179
1231
  );
1180
1232
  callbacksRef.current.onUserNotification?.(notification);
@@ -1264,7 +1316,7 @@ function useChatV2(config, callbacks = {}) {
1264
1316
  streamUserIdRef.current = void 0;
1265
1317
  cancelStreamManager();
1266
1318
  setIsWaitingForResponse(false);
1267
- setUserActionState((prev) => ({ ...prev, prompts: [] }));
1319
+ storeAwareSetUserActionState((prev) => ({ ...prev, prompts: [] }));
1268
1320
  setMessages(
1269
1321
  (prev) => prev.map((msg) => {
1270
1322
  if (msg.isStreaming) {
@@ -1279,7 +1331,7 @@ function useChatV2(config, callbacks = {}) {
1279
1331
  return msg;
1280
1332
  })
1281
1333
  );
1282
- }, [cancelStreamManager]);
1334
+ }, [cancelStreamManager, storeAwareSetUserActionState]);
1283
1335
  const resetSession = useCallback(() => {
1284
1336
  const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
1285
1337
  if (streamUserId) {
@@ -1293,7 +1345,7 @@ function useChatV2(config, callbacks = {}) {
1293
1345
  sessionIdRef.current = void 0;
1294
1346
  abortControllerRef.current?.abort();
1295
1347
  setIsWaitingForResponse(false);
1296
- setUserActionState(EMPTY_USER_ACTION_STATE);
1348
+ storeAwareSetUserActionState(EMPTY_USER_ACTION_STATE2);
1297
1349
  }, []);
1298
1350
  const getSessionId = useCallback(() => {
1299
1351
  return sessionIdRef.current;
@@ -1303,21 +1355,21 @@ function useChatV2(config, callbacks = {}) {
1303
1355
  }, [messages]);
1304
1356
  const setPromptStatus = useCallback(
1305
1357
  (userActionId, status) => {
1306
- setUserActionState((prev) => ({
1358
+ storeAwareSetUserActionState((prev) => ({
1307
1359
  ...prev,
1308
1360
  prompts: prev.prompts.map(
1309
1361
  (p) => p.userActionId === userActionId ? { ...p, status } : p
1310
1362
  )
1311
1363
  }));
1312
1364
  },
1313
- []
1365
+ [storeAwareSetUserActionState]
1314
1366
  );
1315
1367
  const removePrompt = useCallback((userActionId) => {
1316
- setUserActionState((prev) => ({
1368
+ storeAwareSetUserActionState((prev) => ({
1317
1369
  ...prev,
1318
1370
  prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
1319
1371
  }));
1320
- }, []);
1372
+ }, [storeAwareSetUserActionState]);
1321
1373
  const submitUserAction2 = useCallback(
1322
1374
  async (userActionId, content) => {
1323
1375
  setPromptStatus(userActionId, "submitting");
@@ -1386,11 +1438,11 @@ function useChatV2(config, callbacks = {}) {
1386
1438
  [setPromptStatus]
1387
1439
  );
1388
1440
  const dismissNotification = useCallback((id) => {
1389
- setUserActionState((prev) => ({
1441
+ storeAwareSetUserActionState((prev) => ({
1390
1442
  ...prev,
1391
1443
  notifications: prev.notifications.filter((n) => n.id !== id)
1392
1444
  }));
1393
- }, []);
1445
+ }, [storeAwareSetUserActionState]);
1394
1446
  useEffect(() => {
1395
1447
  const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
1396
1448
  subscriptionPrevUserIdRef.current = config.userId;
@@ -1399,14 +1451,17 @@ function useChatV2(config, callbacks = {}) {
1399
1451
  if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
1400
1452
  streamUserIdRef.current = userId;
1401
1453
  }
1402
- const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1454
+ const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting, actions) => {
1403
1455
  setMessages(msgs);
1404
1456
  setIsWaitingForResponse(isWaiting);
1457
+ setUserActionState(actions);
1405
1458
  });
1406
1459
  const active = activeStreamStore.get(userId);
1407
1460
  if (active) {
1461
+ streamUserIdRef.current = userId;
1408
1462
  setMessages(active.messages);
1409
1463
  setIsWaitingForResponse(active.isWaiting);
1464
+ setUserActionState(active.userActionState);
1410
1465
  }
1411
1466
  return unsubscribe;
1412
1467
  }, [config.userId]);
@@ -1434,12 +1489,14 @@ function useChatV2(config, callbacks = {}) {
1434
1489
  setMessages([]);
1435
1490
  sessionIdRef.current = void 0;
1436
1491
  setIsWaitingForResponse(false);
1437
- setUserActionState(EMPTY_USER_ACTION_STATE);
1492
+ setUserActionState(EMPTY_USER_ACTION_STATE2);
1438
1493
  } else if (config.userId) {
1439
1494
  const active = activeStreamStore.get(config.userId);
1440
1495
  if (active) {
1496
+ streamUserIdRef.current = config.userId;
1441
1497
  setMessages(active.messages);
1442
1498
  setIsWaitingForResponse(active.isWaiting);
1499
+ setUserActionState(active.userActionState);
1443
1500
  sessionIdRef.current = getSessionIdFromMessages(active.messages) ?? config.initialSessionId;
1444
1501
  return;
1445
1502
  }
@@ -2269,7 +2326,7 @@ function createMarkdownComponents(options = {}) {
2269
2326
  },
2270
2327
  pre: ({ children }) => /* @__PURE__ */ jsx("pre", { className: "my-2 max-w-full overflow-x-auto rounded-lg", children }),
2271
2328
  ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "mb-3 ml-4 list-disc space-y-1 text-sm", children }),
2272
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "mb-3 ml-4 list-decimal space-y-1 text-sm", children }),
2329
+ ol: ({ children, start }) => /* @__PURE__ */ jsx("ol", { className: "mb-3 ml-4 list-decimal space-y-1 text-sm", start, children }),
2273
2330
  li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "text-sm leading-relaxed", children }),
2274
2331
  h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "mt-4 mb-2 text-lg font-semibold first:mt-0", children }),
2275
2332
  h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "mt-3 mb-2 text-base font-semibold first:mt-0", children }),
@@ -2338,7 +2395,7 @@ function AgentMessage({
2338
2395
  }
2339
2396
  wasStreamingRef.current = isStreaming;
2340
2397
  }, [isStreaming, hasSteps]);
2341
- const totalElapsedMs = hasSteps ? message.steps.reduce((sum, step) => sum + (step.elapsedMs || 0), 0) : 0;
2398
+ const totalElapsedMs = hasSteps ? message.steps.reduce((sum, step2) => sum + (step2.elapsedMs || 0), 0) : 0;
2342
2399
  const currentMessage = message.currentMessage;
2343
2400
  const rawContent = message.streamingContent || message.content || "";
2344
2401
  const content = rawContent.replace(/\\n/g, "\n");
@@ -2366,25 +2423,25 @@ function AgentMessage({
2366
2423
  }
2367
2424
  return `${count} ${stepWord} completed`;
2368
2425
  };
2369
- const renderStepIcon = (step, isCurrentlyExecuting) => {
2426
+ const renderStepIcon = (step2, isCurrentlyExecuting) => {
2370
2427
  const wrapper = "h-4 w-4 flex items-center justify-center shrink-0";
2371
2428
  if (isCurrentlyExecuting)
2372
2429
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 payman-agent-step-icon--active animate-spin" }) });
2373
- if (step.status === "pending" && isCancelled)
2430
+ if (step2.status === "pending" && isCancelled)
2374
2431
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full payman-agent-step-icon--pending" }) });
2375
- if (step.status === "pending" || step.status === "in_progress")
2432
+ if (step2.status === "pending" || step2.status === "in_progress")
2376
2433
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 payman-agent-step-icon--in-progress-dim animate-spin" }) });
2377
- if (step.status === "completed")
2434
+ if (step2.status === "completed")
2378
2435
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(
2379
2436
  Check,
2380
2437
  {
2381
2438
  className: cn(
2382
2439
  "h-3.5 w-3.5",
2383
- step.eventType === "USER_ACTION_SUCCESS" ? "payman-agent-step-icon--success" : "payman-agent-step-icon--success-dim"
2440
+ step2.eventType === "USER_ACTION_SUCCESS" ? "payman-agent-step-icon--success" : "payman-agent-step-icon--success-dim"
2384
2441
  )
2385
2442
  }
2386
2443
  ) });
2387
- if (step.status === "error")
2444
+ if (step2.status === "error")
2388
2445
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5 payman-agent-step-icon--error" }) });
2389
2446
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full payman-agent-step-icon--pending-dim" }) });
2390
2447
  };
@@ -2396,33 +2453,33 @@ function AgentMessage({
2396
2453
  exit: { height: 0, opacity: 0 },
2397
2454
  transition: { duration: 0.2, ease: "easeInOut" },
2398
2455
  className: "overflow-hidden",
2399
- children: /* @__PURE__ */ jsx("div", { className: "pt-1.5", children: message.steps.map((step) => {
2400
- const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
2401
- const hasTime = step.elapsedMs != null && step.elapsedMs > 0;
2456
+ children: /* @__PURE__ */ jsx("div", { className: "pt-1.5", children: message.steps.map((step2) => {
2457
+ const isCurrentlyExecuting = step2.id === currentExecutingStepId && step2.status === "in_progress" && isStreaming && !isCancelled;
2458
+ const hasTime = step2.elapsedMs != null && step2.elapsedMs > 0;
2402
2459
  return /* @__PURE__ */ jsxs("div", { className: "mb-1.5", children: [
2403
2460
  /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
2404
- /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
2461
+ /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step2, isCurrentlyExecuting) }),
2405
2462
  /* @__PURE__ */ jsx(
2406
2463
  "span",
2407
2464
  {
2408
2465
  className: cn(
2409
2466
  "text-xs leading-relaxed min-w-0 break-words",
2410
2467
  isCurrentlyExecuting && "shimmer-text font-medium",
2411
- !isCurrentlyExecuting && step.status === "error" && "payman-agent-step-text--error",
2412
- !isCurrentlyExecuting && step.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
2413
- !isCurrentlyExecuting && step.status === "completed" && "payman-agent-step-text--completed",
2414
- !isCurrentlyExecuting && step.status === "pending" && "payman-agent-step-text--pending",
2415
- !isCurrentlyExecuting && step.status === "in_progress" && "payman-agent-step-text--in-progress"
2468
+ !isCurrentlyExecuting && step2.status === "error" && "payman-agent-step-text--error",
2469
+ !isCurrentlyExecuting && step2.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
2470
+ !isCurrentlyExecuting && step2.status === "completed" && "payman-agent-step-text--completed",
2471
+ !isCurrentlyExecuting && step2.status === "pending" && "payman-agent-step-text--pending",
2472
+ !isCurrentlyExecuting && step2.status === "in_progress" && "payman-agent-step-text--in-progress"
2416
2473
  ),
2417
- children: step.message
2474
+ children: step2.message
2418
2475
  }
2419
2476
  )
2420
2477
  ] }),
2421
2478
  hasTime && /* @__PURE__ */ jsx("div", { className: "pl-[22px] mt-1", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md payman-agent-step-time leading-none", children: [
2422
2479
  /* @__PURE__ */ jsx(Clock, { className: "h-2.5 w-2.5 shrink-0 payman-agent-step-time-icon" }),
2423
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step.elapsedMs) })
2480
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step2.elapsedMs) })
2424
2481
  ] }) })
2425
- ] }, step.id);
2482
+ ] }, step2.id);
2426
2483
  }) })
2427
2484
  }
2428
2485
  ) });
@@ -3379,92 +3436,13 @@ function MarkdownImageV2({
3379
3436
  }
3380
3437
  ) });
3381
3438
  }
3382
- var RAG_SOURCE_CLASS = "payman-v2-rag-source";
3383
- var RAG_FOLLOW_UP_CLASS = "payman-v2-rag-follow-up";
3384
- var SOURCE_PATTERN = /^Sources?:\s*\S/i;
3385
- var MAX_FOLLOW_UP_LENGTH = 320;
3386
- var HR_PATTERN = /^-{3,}$|^_{3,}$|^\*{3,}$/;
3387
- function normalizeRagParagraphText(text) {
3388
- return text.replace(/\s+/g, " ").trim();
3389
- }
3390
- function isRagSourceParagraph(text) {
3391
- return SOURCE_PATTERN.test(normalizeRagParagraphText(text));
3392
- }
3393
- function isRagFollowUpCandidate(text) {
3394
- const normalized = normalizeRagParagraphText(text);
3395
- return normalized.length > 0 && normalized.length <= MAX_FOLLOW_UP_LENGTH && normalized.endsWith("?");
3396
- }
3397
- function extractRagParagraphTexts(content) {
3398
- const paragraphs = [];
3399
- const currentParagraph = [];
3400
- const flushParagraph = () => {
3401
- const paragraph = normalizeRagParagraphText(currentParagraph.join(" "));
3402
- currentParagraph.length = 0;
3403
- if (paragraph) paragraphs.push(paragraph);
3404
- };
3405
- content.replace(/\\n/g, "\n").split(/\r?\n/).forEach((line) => {
3406
- const trimmedLine = line.trim();
3407
- if (!trimmedLine) {
3408
- flushParagraph();
3409
- return;
3410
- }
3411
- if (HR_PATTERN.test(trimmedLine)) {
3412
- flushParagraph();
3413
- return;
3414
- }
3415
- currentParagraph.push(trimmedLine);
3416
- });
3417
- flushParagraph();
3418
- return paragraphs;
3419
- }
3420
- function getRagAnswerParagraphRoles(paragraphs) {
3421
- const normalized = paragraphs.map(normalizeRagParagraphText);
3422
- const roles = normalized.map(() => null);
3423
- const sourceIndexes = normalized.map((paragraph, index) => isRagSourceParagraph(paragraph) ? index : -1).filter((index) => index >= 0);
3424
- for (const sourceIndex of sourceIndexes) {
3425
- roles[sourceIndex] = "source";
3426
- for (const candidateIndex of [sourceIndex - 1, sourceIndex + 1]) {
3427
- if (candidateIndex >= 0 && candidateIndex < normalized.length && roles[candidateIndex] == null && isRagFollowUpCandidate(normalized[candidateIndex] ?? "")) {
3428
- roles[candidateIndex] = "follow-up";
3429
- }
3430
- }
3431
- }
3432
- return roles;
3433
- }
3434
- function getRagAnswerParagraphRole(paragraph, paragraphs) {
3435
- const normalizedParagraph = normalizeRagParagraphText(paragraph);
3436
- if (isRagSourceParagraph(normalizedParagraph)) return "source";
3437
- const roles = getRagAnswerParagraphRoles(paragraphs);
3438
- return paragraphs.map(normalizeRagParagraphText).some((candidate, index) => {
3439
- return candidate === normalizedParagraph && roles[index] === "follow-up";
3440
- }) ? "follow-up" : null;
3441
- }
3442
- function ragAnswerRoleClassName(role) {
3443
- if (role === "source") return RAG_SOURCE_CLASS;
3444
- if (role === "follow-up") return RAG_FOLLOW_UP_CLASS;
3445
- return void 0;
3446
- }
3447
- function reactNodeText(node) {
3448
- if (typeof node === "string" || typeof node === "number") return String(node);
3449
- if (Array.isArray(node)) return node.map(reactNodeText).join("");
3450
- if (React.isValidElement(node)) {
3451
- return reactNodeText(node.props.children);
3452
- }
3453
- return "";
3454
- }
3455
- function buildComponents(onImageClick, isResolvingRef, ragParagraphsRef) {
3439
+ function buildComponents(onImageClick, isResolvingRef) {
3456
3440
  return {
3457
- p: ({ children }) => {
3458
- const role = getRagAnswerParagraphRole(
3459
- reactNodeText(children),
3460
- ragParagraphsRef?.current ?? []
3461
- );
3462
- return /* @__PURE__ */ jsx("p", { className: ragAnswerRoleClassName(role), children });
3463
- },
3441
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { children }),
3464
3442
  code: ({ children }) => /* @__PURE__ */ jsx("code", { children }),
3465
3443
  pre: ({ children }) => /* @__PURE__ */ jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsx("pre", { children }) }),
3466
3444
  ul: ({ children }) => /* @__PURE__ */ jsx("ul", { children }),
3467
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", { children }),
3445
+ ol: ({ children, start }) => /* @__PURE__ */ jsx("ol", { start, children }),
3468
3446
  li: ({ children }) => /* @__PURE__ */ jsx("li", { children }),
3469
3447
  h1: ({ children }) => /* @__PURE__ */ jsx("h1", { children }),
3470
3448
  h2: ({ children }) => /* @__PURE__ */ jsx("h2", { children }),
@@ -3498,13 +3476,8 @@ function MarkdownRendererV2({
3498
3476
  }) {
3499
3477
  const isResolvingRef = useRef(isResolvingImages);
3500
3478
  isResolvingRef.current = isResolvingImages;
3501
- const ragParagraphsRef = useRef([]);
3502
- ragParagraphsRef.current = useMemo(
3503
- () => extractRagParagraphTexts(content),
3504
- [content]
3505
- );
3506
3479
  const components = useMemo(
3507
- () => buildComponents(onImageClick, isResolvingRef, ragParagraphsRef),
3480
+ () => buildComponents(onImageClick, isResolvingRef),
3508
3481
  [onImageClick]
3509
3482
  );
3510
3483
  return /* @__PURE__ */ jsx(
@@ -3770,6 +3743,87 @@ function FeedbackReasonModal({
3770
3743
  }
3771
3744
  ) : null });
3772
3745
  }
3746
+
3747
+ // src/utils/markdownImageToken.ts
3748
+ function step(text, i) {
3749
+ if (text[i] === "\\" && i + 1 < text.length) return i + 2;
3750
+ return i + 1;
3751
+ }
3752
+ function scanAltText(text, start) {
3753
+ let i = start;
3754
+ while (i < text.length) {
3755
+ if (text[i] === "]") return i;
3756
+ i = step(text, i);
3757
+ }
3758
+ return -1;
3759
+ }
3760
+ function scanOptionalTitle(text, start) {
3761
+ let i = start;
3762
+ while (i < text.length && /[\t\n ]/.test(text[i])) i++;
3763
+ if (i >= text.length) return i;
3764
+ const quote = text[i];
3765
+ if (quote !== '"' && quote !== "'") return i;
3766
+ i++;
3767
+ while (i < text.length) {
3768
+ if (text[i] === quote) return i + 1;
3769
+ i = step(text, i);
3770
+ }
3771
+ return -1;
3772
+ }
3773
+ function scanAngleBracketDestination(text, start) {
3774
+ let i = start + 1;
3775
+ while (i < text.length) {
3776
+ if (text[i] === ">") return i + 1;
3777
+ i = step(text, i);
3778
+ }
3779
+ return -1;
3780
+ }
3781
+ function scanRawDestination(text, start) {
3782
+ let i = start;
3783
+ let depth = 1;
3784
+ while (i < text.length) {
3785
+ const char = text[i];
3786
+ if (char === "\\") {
3787
+ if (i + 1 >= text.length) return -1;
3788
+ i += 2;
3789
+ continue;
3790
+ }
3791
+ if (char === "(") depth++;
3792
+ else if (char === ")") {
3793
+ depth--;
3794
+ if (depth === 0) return i + 1;
3795
+ }
3796
+ i++;
3797
+ }
3798
+ return -1;
3799
+ }
3800
+ function completeMarkdownImageTokenLength(text) {
3801
+ if (!text.startsWith("![")) return 0;
3802
+ const altEnd = scanAltText(text, 2);
3803
+ if (altEnd === -1 || altEnd + 1 >= text.length || text[altEnd + 1] !== "(") {
3804
+ return 0;
3805
+ }
3806
+ let i = altEnd + 2;
3807
+ if (i >= text.length) return 0;
3808
+ if (text[i] === "<") {
3809
+ i = scanAngleBracketDestination(text, i);
3810
+ if (i === -1) return 0;
3811
+ i = scanOptionalTitle(text, i);
3812
+ if (i === -1 || i >= text.length || text[i] !== ")") return 0;
3813
+ return i + 1;
3814
+ }
3815
+ i = scanRawDestination(text, i);
3816
+ return i === -1 ? 0 : i;
3817
+ }
3818
+ function stripIncompleteImageToken(text) {
3819
+ const lastBang = text.lastIndexOf("![");
3820
+ if (lastBang === -1) return text;
3821
+ const suffix = text.slice(lastBang);
3822
+ if (completeMarkdownImageTokenLength(suffix) > 0) return text;
3823
+ return text.slice(0, lastBang);
3824
+ }
3825
+
3826
+ // src/components/v2/useTypingEffect.ts
3773
3827
  var RESPONSE_SPEED = {
3774
3828
  normal: [2, 4],
3775
3829
  fast: 1,
@@ -3777,7 +3831,6 @@ var RESPONSE_SPEED = {
3777
3831
  newline: [4, 6],
3778
3832
  idle: 15
3779
3833
  };
3780
- var MARKDOWN_IMAGE_REGEX = /^!\[[^\]]*\]\([^)]*\)/;
3781
3834
  function charDelay(char, speed, multiplier) {
3782
3835
  const raw = (() => {
3783
3836
  if (char === "*") return speed.fast;
@@ -3836,9 +3889,9 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
3836
3889
  }
3837
3890
  if (displayedRef.current.length < targetRef.current.length) {
3838
3891
  const remaining = targetRef.current.slice(displayedRef.current.length);
3839
- const imgMatch = MARKDOWN_IMAGE_REGEX.exec(remaining);
3840
- if (imgMatch) {
3841
- displayedRef.current += imgMatch[0];
3892
+ const imageTokenLength = completeMarkdownImageTokenLength(remaining);
3893
+ if (imageTokenLength > 0) {
3894
+ displayedRef.current += remaining.slice(0, imageTokenLength);
3842
3895
  setDisplayedText(displayedRef.current);
3843
3896
  timerRef.current = setTimeout(tick, 0);
3844
3897
  return;
@@ -3867,13 +3920,6 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
3867
3920
  isTyping
3868
3921
  };
3869
3922
  }
3870
- function stripIncompleteImageToken(text) {
3871
- const lastBang = text.lastIndexOf("![");
3872
- if (lastBang === -1) return text;
3873
- const after = text.slice(lastBang);
3874
- if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
3875
- return text.slice(0, lastBang);
3876
- }
3877
3923
  function getFeedbackState(message) {
3878
3924
  const feedback = message.feedback;
3879
3925
  if (feedback === "up" || feedback === "down") return feedback;
@@ -4623,14 +4669,15 @@ function NotificationInline({ notification, onDismiss }) {
4623
4669
  ] });
4624
4670
  }
4625
4671
  function useExpiryCountdown(prompt) {
4626
- const initial = typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0 ? Math.floor(prompt.expirySeconds) : void 0;
4627
- const [secondsLeft, setSecondsLeft] = useState(initial);
4672
+ 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;
4673
+ const [secondsLeft, setSecondsLeft] = useState(
4674
+ () => deadlineMs !== void 0 ? Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3)) : void 0
4675
+ );
4628
4676
  useEffect(() => {
4629
- if (initial === void 0) {
4677
+ if (deadlineMs === void 0) {
4630
4678
  setSecondsLeft(void 0);
4631
4679
  return;
4632
4680
  }
4633
- const deadlineMs = Date.now() + initial * 1e3;
4634
4681
  let intervalId;
4635
4682
  const remaining = () => Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3));
4636
4683
  const sync = () => {
@@ -4652,8 +4699,8 @@ function useExpiryCountdown(prompt) {
4652
4699
  window.removeEventListener("focus", sync);
4653
4700
  window.removeEventListener("pageshow", sync);
4654
4701
  };
4655
- }, [prompt.userActionId, prompt.subAction, initial]);
4656
- const expired = initial !== void 0 && secondsLeft === 0;
4702
+ }, [prompt.userActionId, prompt.subAction, deadlineMs]);
4703
+ const expired = deadlineMs !== void 0 && (getUserActionSecondsLeft(prompt) ?? secondsLeft ?? 0) <= 0;
4657
4704
  return [secondsLeft, expired];
4658
4705
  }
4659
4706
  function UserActionInline({
@@ -5757,9 +5804,9 @@ var PRE_PIPELINE_STEPS = /* @__PURE__ */ new Set([
5757
5804
  "build_pipeline",
5758
5805
  "load_skill_index"
5759
5806
  ]);
5760
- function stepColor(step) {
5761
- if (step.status === "failed") return "#ef4444";
5762
- if (PRE_PIPELINE_STEPS.has(step.step)) return "#f59e0b";
5807
+ function stepColor(step2) {
5808
+ if (step2.status === "failed") return "#ef4444";
5809
+ if (PRE_PIPELINE_STEPS.has(step2.step)) return "#f59e0b";
5763
5810
  return "#3b82f6";
5764
5811
  }
5765
5812
  function formatMs(ms) {
@@ -5936,10 +5983,10 @@ function TimelineBars({
5936
5983
  totalMs,
5937
5984
  unaccountedMs
5938
5985
  }) {
5939
- const bars = trace.pipelineSteps.map((step) => {
5940
- const duration = step.durationMs ?? 0;
5986
+ const bars = trace.pipelineSteps.map((step2) => {
5987
+ const duration = step2.durationMs ?? 0;
5941
5988
  const widthPct = totalMs > 0 ? duration / totalMs * 100 : 0;
5942
- const llmMs = step.llmCall?.durationMs ?? null;
5989
+ const llmMs = step2.llmCall?.durationMs ?? null;
5943
5990
  return /* @__PURE__ */ jsxs("div", { style: { marginBottom: 8 }, children: [
5944
5991
  /* @__PURE__ */ jsxs(
5945
5992
  "div",
@@ -5953,8 +6000,8 @@ function TimelineBars({
5953
6000
  },
5954
6001
  children: [
5955
6002
  /* @__PURE__ */ jsxs("span", { children: [
5956
- step.step,
5957
- step.status === "failed" && /* @__PURE__ */ jsx("span", { style: { color: "#fca5a5", marginLeft: 6 }, children: "(failed)" })
6003
+ step2.step,
6004
+ step2.status === "failed" && /* @__PURE__ */ jsx("span", { style: { color: "#fca5a5", marginLeft: 6 }, children: "(failed)" })
5958
6005
  ] }),
5959
6006
  /* @__PURE__ */ jsxs("span", { style: { opacity: 0.75 }, children: [
5960
6007
  formatMs(duration),
@@ -5982,14 +6029,14 @@ function TimelineBars({
5982
6029
  style: {
5983
6030
  height: "100%",
5984
6031
  width: `${Math.max(widthPct, 0.3)}%`,
5985
- background: stepColor(step),
6032
+ background: stepColor(step2),
5986
6033
  transition: "width 0.2s ease"
5987
6034
  }
5988
6035
  }
5989
6036
  )
5990
6037
  }
5991
6038
  )
5992
- ] }, `${step.step}-${step.startTime}`);
6039
+ ] }, `${step2.step}-${step2.startTime}`);
5993
6040
  });
5994
6041
  return /* @__PURE__ */ jsxs("div", { children: [
5995
6042
  bars,