@paymanai/payman-ask-sdk 4.0.23 → 4.0.24

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';
@@ -496,9 +495,9 @@ function processStreamEventV2(rawEvent, state) {
496
495
  state.hasError = true;
497
496
  state.errorMessage = "WORKFLOW_FAILED";
498
497
  }
499
- state.steps.forEach((step) => {
500
- if (step.status === "in_progress") {
501
- step.status = "completed";
498
+ state.steps.forEach((step2) => {
499
+ if (step2.status === "in_progress") {
500
+ step2.status = "completed";
502
501
  }
503
502
  });
504
503
  state.lastEventType = eventType;
@@ -710,7 +709,8 @@ function buildRequestBody(config, userMessage, sessionId, options) {
710
709
  sessionAttributes,
711
710
  analysisMode: options?.analysisMode,
712
711
  locale: resolveLocale(config.locale),
713
- timezone: resolveTimezone(config.timezone)
712
+ timezone: resolveTimezone(config.timezone),
713
+ ...options?.attachments?.length ? { attachments: options.attachments } : {}
714
714
  };
715
715
  }
716
716
  function resolveLocale(configured) {
@@ -755,6 +755,7 @@ function buildResolveImagesUrl(config) {
755
755
  const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
756
756
  return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
757
757
  }
758
+ var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
758
759
  function buildRequestHeaders(config) {
759
760
  const headers = {
760
761
  ...config.api.headers
@@ -762,6 +763,9 @@ function buildRequestHeaders(config) {
762
763
  if (config.api.authToken) {
763
764
  headers.Authorization = `Bearer ${config.api.authToken}`;
764
765
  }
766
+ if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
767
+ headers[NGROK_SKIP_BROWSER_WARNING] = "true";
768
+ }
765
769
  return headers;
766
770
  }
767
771
  var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
@@ -913,11 +917,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
913
917
  errorDetails: isAborted ? void 0 : error.message,
914
918
  content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
915
919
  currentMessage: isAborted ? "Thinking..." : void 0,
916
- steps: [...state.steps].map((step) => {
917
- if (step.status === "in_progress" && isAborted) {
918
- return { ...step, status: "pending" };
920
+ steps: [...state.steps].map((step2) => {
921
+ if (step2.status === "in_progress" && isAborted) {
922
+ return { ...step2, status: "pending" };
919
923
  }
920
- return step;
924
+ return step2;
921
925
  }),
922
926
  currentExecutingStepId: void 0
923
927
  } : msg
@@ -1003,11 +1007,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1003
1007
  isCancelled: isAborted,
1004
1008
  errorDetails: isAborted ? void 0 : error.message,
1005
1009
  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" };
1010
+ steps: [...state.steps].map((step2) => {
1011
+ if (step2.status === "in_progress" && isAborted) {
1012
+ return { ...step2, status: "pending" };
1009
1013
  }
1010
- return step;
1014
+ return step2;
1011
1015
  }),
1012
1016
  currentExecutingStepId: void 0
1013
1017
  } : msg
@@ -1028,11 +1032,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1028
1032
  };
1029
1033
  }
1030
1034
  function createCancelledMessageUpdate(steps, currentMessage) {
1031
- const updatedSteps = steps.map((step) => {
1032
- if (step.status === "in_progress") {
1033
- return { ...step, status: "pending" };
1035
+ const updatedSteps = steps.map((step2) => {
1036
+ if (step2.status === "in_progress") {
1037
+ return { ...step2, status: "pending" };
1034
1038
  }
1035
- return step;
1039
+ return step2;
1036
1040
  });
1037
1041
  return {
1038
1042
  isStreaming: false,
@@ -1042,6 +1046,81 @@ function createCancelledMessageUpdate(steps, currentMessage) {
1042
1046
  currentMessage: currentMessage || "Thinking..."
1043
1047
  };
1044
1048
  }
1049
+ var DEFAULT_SIGNED_URL_ENDPOINT = "/api/files/signed-url";
1050
+ function fileExtension(filename) {
1051
+ const ext = filename.split(".").pop()?.toLowerCase().replace(/^\./, "");
1052
+ return ext && ext.length > 0 ? ext : "bin";
1053
+ }
1054
+ function resolveMimeType(file) {
1055
+ if (file.type && file.type.trim().length > 0) return file.type;
1056
+ const ext = fileExtension(file.name);
1057
+ const byExt = {
1058
+ pdf: "application/pdf",
1059
+ png: "image/png",
1060
+ jpg: "image/jpeg",
1061
+ jpeg: "image/jpeg",
1062
+ gif: "image/gif",
1063
+ webp: "image/webp"
1064
+ };
1065
+ return byExt[ext] ?? "application/octet-stream";
1066
+ }
1067
+ function buildSignedUrlEndpoint(config, ext) {
1068
+ const endpoint = config.api.signedUrlEndpoint || DEFAULT_SIGNED_URL_ENDPOINT;
1069
+ const queryParams = new URLSearchParams({ extn: ext.replace(/^\./, "") });
1070
+ return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
1071
+ }
1072
+ async function requestSignedUrl(config, ext, signal) {
1073
+ const url = buildSignedUrlEndpoint(config, ext);
1074
+ const headers = buildRequestHeaders(config);
1075
+ const response = await fetch(url, {
1076
+ method: "GET",
1077
+ headers,
1078
+ signal
1079
+ });
1080
+ if (!response.ok) {
1081
+ const errorText = await response.text().catch(() => "");
1082
+ throw new Error(
1083
+ errorText ? `Failed to get upload URL (${response.status}): ${errorText}` : `Failed to get upload URL (${response.status})`
1084
+ );
1085
+ }
1086
+ const data = await response.json();
1087
+ if (!data.key || !data.url) {
1088
+ throw new Error("Signed URL response missing key or url");
1089
+ }
1090
+ return { key: data.key, url: data.url };
1091
+ }
1092
+ async function uploadToSignedUrl(signedUrl, file, mimeType, signal) {
1093
+ const response = await fetch(signedUrl, {
1094
+ method: "PUT",
1095
+ headers: {
1096
+ "Content-Type": mimeType,
1097
+ "x-ms-blob-type": "BlockBlob"
1098
+ },
1099
+ body: file,
1100
+ signal
1101
+ });
1102
+ if (!response.ok) {
1103
+ const errorText = await response.text().catch(() => "");
1104
+ throw new Error(
1105
+ errorText ? `Failed to upload file (${response.status}): ${errorText}` : `Failed to upload file (${response.status})`
1106
+ );
1107
+ }
1108
+ }
1109
+ async function uploadAttachment(config, file, signal) {
1110
+ const ext = fileExtension(file.name);
1111
+ const mimeType = resolveMimeType(file);
1112
+ const { key, url } = await requestSignedUrl(config, ext, signal);
1113
+ await uploadToSignedUrl(url, file, mimeType, signal);
1114
+ return {
1115
+ tempKey: key,
1116
+ filename: file.name,
1117
+ mimeType
1118
+ };
1119
+ }
1120
+ async function uploadAttachments(config, files, signal) {
1121
+ const uploads = files.map((file) => uploadAttachment(config, file, signal));
1122
+ return Promise.all(uploads);
1123
+ }
1045
1124
  var UserActionStaleError = class extends Error {
1046
1125
  constructor(userActionId, message = "User action is no longer actionable") {
1047
1126
  super(message);
@@ -1078,9 +1157,6 @@ async function cancelUserAction(config, userActionId) {
1078
1157
  async function resendUserAction(config, userActionId) {
1079
1158
  return sendUserActionRequest(config, userActionId, "resend");
1080
1159
  }
1081
- async function expireUserAction(config, userActionId) {
1082
- return sendUserActionRequest(config, userActionId, "expired");
1083
- }
1084
1160
  var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1085
1161
  function upsertPrompt(prompts, req) {
1086
1162
  const active = { ...req, status: "pending" };
@@ -1109,12 +1185,32 @@ function getStoredOrInitialMessages(config) {
1109
1185
  function getSessionIdFromMessages(messages) {
1110
1186
  return messages.find((message) => message.sessionId)?.sessionId;
1111
1187
  }
1188
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["jpg", "jpeg", "png", "gif", "webp", "avif"]);
1189
+ function attachmentKindFromFile(file) {
1190
+ const ext = file.name.split(".").pop()?.toLowerCase() ?? "";
1191
+ if (IMAGE_EXTENSIONS.has(ext) || file.type.startsWith("image/")) return "image";
1192
+ return "file";
1193
+ }
1194
+ function buildMessageAttachments(files) {
1195
+ return files.map((file, index) => {
1196
+ const kind = attachmentKindFromFile(file);
1197
+ return {
1198
+ id: `att-${Date.now()}-${index}`,
1199
+ filename: file.name,
1200
+ mimeType: file.type || "application/octet-stream",
1201
+ // Blob URL for all local files so PDFs/docs stay previewable after send.
1202
+ previewUrl: URL.createObjectURL(file),
1203
+ kind
1204
+ };
1205
+ });
1206
+ }
1112
1207
  function useChatV2(config, callbacks = {}) {
1113
1208
  const [messages, setMessages] = useState(() => getStoredOrInitialMessages(config));
1114
1209
  const [isWaitingForResponse, setIsWaitingForResponse] = useState(() => {
1115
1210
  if (!config.userId) return false;
1116
1211
  return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1117
1212
  });
1213
+ const [isUploadingAttachments, setIsUploadingAttachments] = useState(false);
1118
1214
  const sessionIdRef = useRef(
1119
1215
  getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1120
1216
  );
@@ -1189,21 +1285,45 @@ function useChatV2(config, callbacks = {}) {
1189
1285
  );
1190
1286
  const sendMessage = useCallback(
1191
1287
  async (userMessage, options) => {
1192
- if (!userMessage.trim()) return;
1288
+ const trimmedMessage = userMessage.trim();
1289
+ const files = options?.files ?? [];
1290
+ const hasPreuploadedAttachments = (options?.attachments?.length ?? 0) > 0;
1291
+ if (!trimmedMessage && files.length === 0 && !hasPreuploadedAttachments) return;
1292
+ let streamAttachments = options?.attachments;
1293
+ if (!streamAttachments && files.length > 0) {
1294
+ setIsUploadingAttachments(true);
1295
+ try {
1296
+ streamAttachments = await uploadAttachments(configRef.current, files);
1297
+ } catch (error) {
1298
+ if (error.name !== "AbortError") {
1299
+ callbacksRef.current.onError?.(error);
1300
+ }
1301
+ throw error;
1302
+ } finally {
1303
+ setIsUploadingAttachments(false);
1304
+ }
1305
+ }
1193
1306
  if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1194
1307
  sessionIdRef.current = generateId();
1195
1308
  callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1196
1309
  }
1310
+ const messageAttachments = files.length > 0 ? buildMessageAttachments(files) : streamAttachments?.length ? streamAttachments.map((attachment, index) => ({
1311
+ id: `att-${Date.now()}-${index}`,
1312
+ filename: attachment.filename,
1313
+ mimeType: attachment.mimeType,
1314
+ kind: attachment.mimeType.startsWith("image/") ? "image" : "file"
1315
+ })) : void 0;
1197
1316
  const userMessageId = `user-${Date.now()}`;
1198
1317
  const userMsg = {
1199
1318
  id: userMessageId,
1200
1319
  sessionId: sessionIdRef.current,
1201
1320
  role: "user",
1202
- content: userMessage,
1203
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1321
+ content: trimmedMessage,
1322
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1323
+ attachments: messageAttachments
1204
1324
  };
1205
1325
  setMessages((prev) => [...prev, userMsg]);
1206
- callbacksRef.current.onMessageSent?.(userMessage);
1326
+ callbacksRef.current.onMessageSent?.(trimmedMessage);
1207
1327
  setIsWaitingForResponse(true);
1208
1328
  callbacksRef.current.onStreamStart?.();
1209
1329
  const streamingId = `assistant-${Date.now()}`;
@@ -1230,11 +1350,14 @@ function useChatV2(config, callbacks = {}) {
1230
1350
  activeStreamStore.start(userId, abortController, initialMessages);
1231
1351
  }
1232
1352
  const newSessionId = await startStream(
1233
- userMessage,
1353
+ trimmedMessage,
1234
1354
  streamingId,
1235
1355
  sessionIdRef.current,
1236
1356
  abortController,
1237
- options
1357
+ {
1358
+ analysisMode: options?.analysisMode,
1359
+ attachments: streamAttachments
1360
+ }
1238
1361
  );
1239
1362
  const finalStreamUserId = streamUserIdRef.current ?? userId;
1240
1363
  if (finalStreamUserId) {
@@ -1372,19 +1495,6 @@ function useChatV2(config, callbacks = {}) {
1372
1495
  },
1373
1496
  [setPromptStatus]
1374
1497
  );
1375
- const expireUserAction2 = useCallback(
1376
- async (userActionId) => {
1377
- setPromptStatus(userActionId, "expired");
1378
- try {
1379
- await expireUserAction(configRef.current, userActionId);
1380
- } catch (error) {
1381
- if (error instanceof UserActionStaleError) return;
1382
- callbacksRef.current.onError?.(error);
1383
- throw error;
1384
- }
1385
- },
1386
- [setPromptStatus]
1387
- );
1388
1498
  const dismissNotification = useCallback((id) => {
1389
1499
  setUserActionState((prev) => ({
1390
1500
  ...prev,
@@ -1425,6 +1535,18 @@ function useChatV2(config, callbacks = {}) {
1425
1535
  setMessages(config.initialMessages);
1426
1536
  sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1427
1537
  }, [config.initialMessages, config.initialSessionId, config.userId]);
1538
+ const hydratedSessionIdRef = useRef(void 0);
1539
+ useEffect(() => {
1540
+ if (config.userId) return;
1541
+ if (!config.initialMessages?.length) return;
1542
+ const sessionId = config.initialSessionId;
1543
+ if (hydratedSessionIdRef.current === sessionId && messagesRef.current.length > 0) {
1544
+ return;
1545
+ }
1546
+ hydratedSessionIdRef.current = sessionId;
1547
+ setMessages(config.initialMessages);
1548
+ sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? sessionId;
1549
+ }, [config.initialMessages, config.initialSessionId, config.userId]);
1428
1550
  useEffect(() => {
1429
1551
  const prevUserId = prevUserIdRef.current;
1430
1552
  prevUserIdRef.current = config.userId;
@@ -1459,12 +1581,12 @@ function useChatV2(config, callbacks = {}) {
1459
1581
  getSessionId,
1460
1582
  getMessages,
1461
1583
  isWaitingForResponse,
1584
+ isUploadingAttachments,
1462
1585
  sessionId: sessionIdRef.current,
1463
1586
  userActionState,
1464
1587
  submitUserAction: submitUserAction2,
1465
1588
  cancelUserAction: cancelUserAction2,
1466
1589
  resendUserAction: resendUserAction2,
1467
- expireUserAction: expireUserAction2,
1468
1590
  dismissNotification
1469
1591
  };
1470
1592
  }
@@ -2269,7 +2391,7 @@ function createMarkdownComponents(options = {}) {
2269
2391
  },
2270
2392
  pre: ({ children }) => /* @__PURE__ */ jsx("pre", { className: "my-2 max-w-full overflow-x-auto rounded-lg", children }),
2271
2393
  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 }),
2394
+ ol: ({ children, start }) => /* @__PURE__ */ jsx("ol", { className: "mb-3 ml-4 list-decimal space-y-1 text-sm", start, children }),
2273
2395
  li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "text-sm leading-relaxed", children }),
2274
2396
  h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "mt-4 mb-2 text-lg font-semibold first:mt-0", children }),
2275
2397
  h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "mt-3 mb-2 text-base font-semibold first:mt-0", children }),
@@ -2338,7 +2460,7 @@ function AgentMessage({
2338
2460
  }
2339
2461
  wasStreamingRef.current = isStreaming;
2340
2462
  }, [isStreaming, hasSteps]);
2341
- const totalElapsedMs = hasSteps ? message.steps.reduce((sum, step) => sum + (step.elapsedMs || 0), 0) : 0;
2463
+ const totalElapsedMs = hasSteps ? message.steps.reduce((sum, step2) => sum + (step2.elapsedMs || 0), 0) : 0;
2342
2464
  const currentMessage = message.currentMessage;
2343
2465
  const rawContent = message.streamingContent || message.content || "";
2344
2466
  const content = rawContent.replace(/\\n/g, "\n");
@@ -2366,25 +2488,25 @@ function AgentMessage({
2366
2488
  }
2367
2489
  return `${count} ${stepWord} completed`;
2368
2490
  };
2369
- const renderStepIcon = (step, isCurrentlyExecuting) => {
2491
+ const renderStepIcon = (step2, isCurrentlyExecuting) => {
2370
2492
  const wrapper = "h-4 w-4 flex items-center justify-center shrink-0";
2371
2493
  if (isCurrentlyExecuting)
2372
2494
  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)
2495
+ if (step2.status === "pending" && isCancelled)
2374
2496
  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")
2497
+ if (step2.status === "pending" || step2.status === "in_progress")
2376
2498
  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")
2499
+ if (step2.status === "completed")
2378
2500
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(
2379
2501
  Check,
2380
2502
  {
2381
2503
  className: cn(
2382
2504
  "h-3.5 w-3.5",
2383
- step.eventType === "USER_ACTION_SUCCESS" ? "payman-agent-step-icon--success" : "payman-agent-step-icon--success-dim"
2505
+ step2.eventType === "USER_ACTION_SUCCESS" ? "payman-agent-step-icon--success" : "payman-agent-step-icon--success-dim"
2384
2506
  )
2385
2507
  }
2386
2508
  ) });
2387
- if (step.status === "error")
2509
+ if (step2.status === "error")
2388
2510
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5 payman-agent-step-icon--error" }) });
2389
2511
  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
2512
  };
@@ -2396,33 +2518,33 @@ function AgentMessage({
2396
2518
  exit: { height: 0, opacity: 0 },
2397
2519
  transition: { duration: 0.2, ease: "easeInOut" },
2398
2520
  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;
2521
+ children: /* @__PURE__ */ jsx("div", { className: "pt-1.5", children: message.steps.map((step2) => {
2522
+ const isCurrentlyExecuting = step2.id === currentExecutingStepId && step2.status === "in_progress" && isStreaming && !isCancelled;
2523
+ const hasTime = step2.elapsedMs != null && step2.elapsedMs > 0;
2402
2524
  return /* @__PURE__ */ jsxs("div", { className: "mb-1.5", children: [
2403
2525
  /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
2404
- /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
2526
+ /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step2, isCurrentlyExecuting) }),
2405
2527
  /* @__PURE__ */ jsx(
2406
2528
  "span",
2407
2529
  {
2408
2530
  className: cn(
2409
2531
  "text-xs leading-relaxed min-w-0 break-words",
2410
2532
  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"
2533
+ !isCurrentlyExecuting && step2.status === "error" && "payman-agent-step-text--error",
2534
+ !isCurrentlyExecuting && step2.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
2535
+ !isCurrentlyExecuting && step2.status === "completed" && "payman-agent-step-text--completed",
2536
+ !isCurrentlyExecuting && step2.status === "pending" && "payman-agent-step-text--pending",
2537
+ !isCurrentlyExecuting && step2.status === "in_progress" && "payman-agent-step-text--in-progress"
2416
2538
  ),
2417
- children: step.message
2539
+ children: step2.message
2418
2540
  }
2419
2541
  )
2420
2542
  ] }),
2421
2543
  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
2544
  /* @__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) })
2545
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step2.elapsedMs) })
2424
2546
  ] }) })
2425
- ] }, step.id);
2547
+ ] }, step2.id);
2426
2548
  }) })
2427
2549
  }
2428
2550
  ) });
@@ -3379,92 +3501,13 @@ function MarkdownImageV2({
3379
3501
  }
3380
3502
  ) });
3381
3503
  }
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) {
3504
+ function buildComponents(onImageClick, isResolvingRef) {
3456
3505
  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
- },
3506
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { children }),
3464
3507
  code: ({ children }) => /* @__PURE__ */ jsx("code", { children }),
3465
3508
  pre: ({ children }) => /* @__PURE__ */ jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsx("pre", { children }) }),
3466
3509
  ul: ({ children }) => /* @__PURE__ */ jsx("ul", { children }),
3467
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", { children }),
3510
+ ol: ({ children, start }) => /* @__PURE__ */ jsx("ol", { start, children }),
3468
3511
  li: ({ children }) => /* @__PURE__ */ jsx("li", { children }),
3469
3512
  h1: ({ children }) => /* @__PURE__ */ jsx("h1", { children }),
3470
3513
  h2: ({ children }) => /* @__PURE__ */ jsx("h2", { children }),
@@ -3498,13 +3541,8 @@ function MarkdownRendererV2({
3498
3541
  }) {
3499
3542
  const isResolvingRef = useRef(isResolvingImages);
3500
3543
  isResolvingRef.current = isResolvingImages;
3501
- const ragParagraphsRef = useRef([]);
3502
- ragParagraphsRef.current = useMemo(
3503
- () => extractRagParagraphTexts(content),
3504
- [content]
3505
- );
3506
3544
  const components = useMemo(
3507
- () => buildComponents(onImageClick, isResolvingRef, ragParagraphsRef),
3545
+ () => buildComponents(onImageClick, isResolvingRef),
3508
3546
  [onImageClick]
3509
3547
  );
3510
3548
  return /* @__PURE__ */ jsx(
@@ -3770,6 +3808,87 @@ function FeedbackReasonModal({
3770
3808
  }
3771
3809
  ) : null });
3772
3810
  }
3811
+
3812
+ // src/utils/markdownImageToken.ts
3813
+ function step(text, i) {
3814
+ if (text[i] === "\\" && i + 1 < text.length) return i + 2;
3815
+ return i + 1;
3816
+ }
3817
+ function scanAltText(text, start) {
3818
+ let i = start;
3819
+ while (i < text.length) {
3820
+ if (text[i] === "]") return i;
3821
+ i = step(text, i);
3822
+ }
3823
+ return -1;
3824
+ }
3825
+ function scanOptionalTitle(text, start) {
3826
+ let i = start;
3827
+ while (i < text.length && /[\t\n ]/.test(text[i])) i++;
3828
+ if (i >= text.length) return i;
3829
+ const quote = text[i];
3830
+ if (quote !== '"' && quote !== "'") return i;
3831
+ i++;
3832
+ while (i < text.length) {
3833
+ if (text[i] === quote) return i + 1;
3834
+ i = step(text, i);
3835
+ }
3836
+ return -1;
3837
+ }
3838
+ function scanAngleBracketDestination(text, start) {
3839
+ let i = start + 1;
3840
+ while (i < text.length) {
3841
+ if (text[i] === ">") return i + 1;
3842
+ i = step(text, i);
3843
+ }
3844
+ return -1;
3845
+ }
3846
+ function scanRawDestination(text, start) {
3847
+ let i = start;
3848
+ let depth = 1;
3849
+ while (i < text.length) {
3850
+ const char = text[i];
3851
+ if (char === "\\") {
3852
+ if (i + 1 >= text.length) return -1;
3853
+ i += 2;
3854
+ continue;
3855
+ }
3856
+ if (char === "(") depth++;
3857
+ else if (char === ")") {
3858
+ depth--;
3859
+ if (depth === 0) return i + 1;
3860
+ }
3861
+ i++;
3862
+ }
3863
+ return -1;
3864
+ }
3865
+ function completeMarkdownImageTokenLength(text) {
3866
+ if (!text.startsWith("![")) return 0;
3867
+ const altEnd = scanAltText(text, 2);
3868
+ if (altEnd === -1 || altEnd + 1 >= text.length || text[altEnd + 1] !== "(") {
3869
+ return 0;
3870
+ }
3871
+ let i = altEnd + 2;
3872
+ if (i >= text.length) return 0;
3873
+ if (text[i] === "<") {
3874
+ i = scanAngleBracketDestination(text, i);
3875
+ if (i === -1) return 0;
3876
+ i = scanOptionalTitle(text, i);
3877
+ if (i === -1 || i >= text.length || text[i] !== ")") return 0;
3878
+ return i + 1;
3879
+ }
3880
+ i = scanRawDestination(text, i);
3881
+ return i === -1 ? 0 : i;
3882
+ }
3883
+ function stripIncompleteImageToken(text) {
3884
+ const lastBang = text.lastIndexOf("![");
3885
+ if (lastBang === -1) return text;
3886
+ const suffix = text.slice(lastBang);
3887
+ if (completeMarkdownImageTokenLength(suffix) > 0) return text;
3888
+ return text.slice(0, lastBang);
3889
+ }
3890
+
3891
+ // src/components/v2/useTypingEffect.ts
3773
3892
  var RESPONSE_SPEED = {
3774
3893
  normal: [2, 4],
3775
3894
  fast: 1,
@@ -3777,7 +3896,6 @@ var RESPONSE_SPEED = {
3777
3896
  newline: [4, 6],
3778
3897
  idle: 15
3779
3898
  };
3780
- var MARKDOWN_IMAGE_REGEX = /^!\[[^\]]*\]\([^)]*\)/;
3781
3899
  function charDelay(char, speed, multiplier) {
3782
3900
  const raw = (() => {
3783
3901
  if (char === "*") return speed.fast;
@@ -3836,9 +3954,9 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
3836
3954
  }
3837
3955
  if (displayedRef.current.length < targetRef.current.length) {
3838
3956
  const remaining = targetRef.current.slice(displayedRef.current.length);
3839
- const imgMatch = MARKDOWN_IMAGE_REGEX.exec(remaining);
3840
- if (imgMatch) {
3841
- displayedRef.current += imgMatch[0];
3957
+ const imageTokenLength = completeMarkdownImageTokenLength(remaining);
3958
+ if (imageTokenLength > 0) {
3959
+ displayedRef.current += remaining.slice(0, imageTokenLength);
3842
3960
  setDisplayedText(displayedRef.current);
3843
3961
  timerRef.current = setTimeout(tick, 0);
3844
3962
  return;
@@ -3867,13 +3985,6 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
3867
3985
  isTyping
3868
3986
  };
3869
3987
  }
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
3988
  function getFeedbackState(message) {
3878
3989
  const feedback = message.feedback;
3879
3990
  if (feedback === "up" || feedback === "down") return feedback;
@@ -5757,9 +5868,9 @@ var PRE_PIPELINE_STEPS = /* @__PURE__ */ new Set([
5757
5868
  "build_pipeline",
5758
5869
  "load_skill_index"
5759
5870
  ]);
5760
- function stepColor(step) {
5761
- if (step.status === "failed") return "#ef4444";
5762
- if (PRE_PIPELINE_STEPS.has(step.step)) return "#f59e0b";
5871
+ function stepColor(step2) {
5872
+ if (step2.status === "failed") return "#ef4444";
5873
+ if (PRE_PIPELINE_STEPS.has(step2.step)) return "#f59e0b";
5763
5874
  return "#3b82f6";
5764
5875
  }
5765
5876
  function formatMs(ms) {
@@ -5936,10 +6047,10 @@ function TimelineBars({
5936
6047
  totalMs,
5937
6048
  unaccountedMs
5938
6049
  }) {
5939
- const bars = trace.pipelineSteps.map((step) => {
5940
- const duration = step.durationMs ?? 0;
6050
+ const bars = trace.pipelineSteps.map((step2) => {
6051
+ const duration = step2.durationMs ?? 0;
5941
6052
  const widthPct = totalMs > 0 ? duration / totalMs * 100 : 0;
5942
- const llmMs = step.llmCall?.durationMs ?? null;
6053
+ const llmMs = step2.llmCall?.durationMs ?? null;
5943
6054
  return /* @__PURE__ */ jsxs("div", { style: { marginBottom: 8 }, children: [
5944
6055
  /* @__PURE__ */ jsxs(
5945
6056
  "div",
@@ -5953,8 +6064,8 @@ function TimelineBars({
5953
6064
  },
5954
6065
  children: [
5955
6066
  /* @__PURE__ */ jsxs("span", { children: [
5956
- step.step,
5957
- step.status === "failed" && /* @__PURE__ */ jsx("span", { style: { color: "#fca5a5", marginLeft: 6 }, children: "(failed)" })
6067
+ step2.step,
6068
+ step2.status === "failed" && /* @__PURE__ */ jsx("span", { style: { color: "#fca5a5", marginLeft: 6 }, children: "(failed)" })
5958
6069
  ] }),
5959
6070
  /* @__PURE__ */ jsxs("span", { style: { opacity: 0.75 }, children: [
5960
6071
  formatMs(duration),
@@ -5982,14 +6093,14 @@ function TimelineBars({
5982
6093
  style: {
5983
6094
  height: "100%",
5984
6095
  width: `${Math.max(widthPct, 0.3)}%`,
5985
- background: stepColor(step),
6096
+ background: stepColor(step2),
5986
6097
  transition: "width 0.2s ease"
5987
6098
  }
5988
6099
  }
5989
6100
  )
5990
6101
  }
5991
6102
  )
5992
- ] }, `${step.step}-${step.startTime}`);
6103
+ ] }, `${step2.step}-${step2.startTime}`);
5993
6104
  });
5994
6105
  return /* @__PURE__ */ jsxs("div", { children: [
5995
6106
  bars,
@@ -6208,7 +6319,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6208
6319
  const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
6209
6320
  const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
6210
6321
  const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6211
- const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
6322
+ const expireUserAction = chat.expireUserAction ?? NOOP_ASYNC;
6212
6323
  const dismissNotification = chat.dismissNotification ?? NOOP;
6213
6324
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
6214
6325
  const {
@@ -6609,7 +6720,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6609
6720
  onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6610
6721
  onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6611
6722
  onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6612
- onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
6723
+ onExpireUserAction: isUserActionSupported ? expireUserAction : void 0,
6613
6724
  onDismissNotification: dismissNotification,
6614
6725
  onSubmitFeedback: handleSubmitFeedback
6615
6726
  }