@paymanai/payman-ask-sdk 4.0.22 → 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
@@ -495,9 +495,9 @@ function processStreamEventV2(rawEvent, state) {
495
495
  state.hasError = true;
496
496
  state.errorMessage = "WORKFLOW_FAILED";
497
497
  }
498
- state.steps.forEach((step) => {
499
- if (step.status === "in_progress") {
500
- step.status = "completed";
498
+ state.steps.forEach((step2) => {
499
+ if (step2.status === "in_progress") {
500
+ step2.status = "completed";
501
501
  }
502
502
  });
503
503
  state.lastEventType = eventType;
@@ -709,7 +709,8 @@ function buildRequestBody(config, userMessage, sessionId, options) {
709
709
  sessionAttributes,
710
710
  analysisMode: options?.analysisMode,
711
711
  locale: resolveLocale(config.locale),
712
- timezone: resolveTimezone(config.timezone)
712
+ timezone: resolveTimezone(config.timezone),
713
+ ...options?.attachments?.length ? { attachments: options.attachments } : {}
713
714
  };
714
715
  }
715
716
  function resolveLocale(configured) {
@@ -754,6 +755,7 @@ function buildResolveImagesUrl(config) {
754
755
  const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
755
756
  return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
756
757
  }
758
+ var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
757
759
  function buildRequestHeaders(config) {
758
760
  const headers = {
759
761
  ...config.api.headers
@@ -761,6 +763,9 @@ function buildRequestHeaders(config) {
761
763
  if (config.api.authToken) {
762
764
  headers.Authorization = `Bearer ${config.api.authToken}`;
763
765
  }
766
+ if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
767
+ headers[NGROK_SKIP_BROWSER_WARNING] = "true";
768
+ }
764
769
  return headers;
765
770
  }
766
771
  var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
@@ -912,11 +917,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
912
917
  errorDetails: isAborted ? void 0 : error.message,
913
918
  content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
914
919
  currentMessage: isAborted ? "Thinking..." : void 0,
915
- steps: [...state.steps].map((step) => {
916
- if (step.status === "in_progress" && isAborted) {
917
- return { ...step, status: "pending" };
920
+ steps: [...state.steps].map((step2) => {
921
+ if (step2.status === "in_progress" && isAborted) {
922
+ return { ...step2, status: "pending" };
918
923
  }
919
- return step;
924
+ return step2;
920
925
  }),
921
926
  currentExecutingStepId: void 0
922
927
  } : msg
@@ -1002,11 +1007,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1002
1007
  isCancelled: isAborted,
1003
1008
  errorDetails: isAborted ? void 0 : error.message,
1004
1009
  content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1005
- steps: [...state.steps].map((step) => {
1006
- if (step.status === "in_progress" && isAborted) {
1007
- return { ...step, status: "pending" };
1010
+ steps: [...state.steps].map((step2) => {
1011
+ if (step2.status === "in_progress" && isAborted) {
1012
+ return { ...step2, status: "pending" };
1008
1013
  }
1009
- return step;
1014
+ return step2;
1010
1015
  }),
1011
1016
  currentExecutingStepId: void 0
1012
1017
  } : msg
@@ -1027,11 +1032,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1027
1032
  };
1028
1033
  }
1029
1034
  function createCancelledMessageUpdate(steps, currentMessage) {
1030
- const updatedSteps = steps.map((step) => {
1031
- if (step.status === "in_progress") {
1032
- return { ...step, status: "pending" };
1035
+ const updatedSteps = steps.map((step2) => {
1036
+ if (step2.status === "in_progress") {
1037
+ return { ...step2, status: "pending" };
1033
1038
  }
1034
- return step;
1039
+ return step2;
1035
1040
  });
1036
1041
  return {
1037
1042
  isStreaming: false,
@@ -1041,6 +1046,81 @@ function createCancelledMessageUpdate(steps, currentMessage) {
1041
1046
  currentMessage: currentMessage || "Thinking..."
1042
1047
  };
1043
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
+ }
1044
1124
  var UserActionStaleError = class extends Error {
1045
1125
  constructor(userActionId, message = "User action is no longer actionable") {
1046
1126
  super(message);
@@ -1077,9 +1157,6 @@ async function cancelUserAction(config, userActionId) {
1077
1157
  async function resendUserAction(config, userActionId) {
1078
1158
  return sendUserActionRequest(config, userActionId, "resend");
1079
1159
  }
1080
- async function expireUserAction(config, userActionId) {
1081
- return sendUserActionRequest(config, userActionId, "expired");
1082
- }
1083
1160
  var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1084
1161
  function upsertPrompt(prompts, req) {
1085
1162
  const active = { ...req, status: "pending" };
@@ -1108,12 +1185,32 @@ function getStoredOrInitialMessages(config) {
1108
1185
  function getSessionIdFromMessages(messages) {
1109
1186
  return messages.find((message) => message.sessionId)?.sessionId;
1110
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
+ }
1111
1207
  function useChatV2(config, callbacks = {}) {
1112
1208
  const [messages, setMessages] = useState(() => getStoredOrInitialMessages(config));
1113
1209
  const [isWaitingForResponse, setIsWaitingForResponse] = useState(() => {
1114
1210
  if (!config.userId) return false;
1115
1211
  return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1116
1212
  });
1213
+ const [isUploadingAttachments, setIsUploadingAttachments] = useState(false);
1117
1214
  const sessionIdRef = useRef(
1118
1215
  getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1119
1216
  );
@@ -1188,21 +1285,45 @@ function useChatV2(config, callbacks = {}) {
1188
1285
  );
1189
1286
  const sendMessage = useCallback(
1190
1287
  async (userMessage, options) => {
1191
- 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
+ }
1192
1306
  if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1193
1307
  sessionIdRef.current = generateId();
1194
1308
  callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1195
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;
1196
1316
  const userMessageId = `user-${Date.now()}`;
1197
1317
  const userMsg = {
1198
1318
  id: userMessageId,
1199
1319
  sessionId: sessionIdRef.current,
1200
1320
  role: "user",
1201
- content: userMessage,
1202
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1321
+ content: trimmedMessage,
1322
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1323
+ attachments: messageAttachments
1203
1324
  };
1204
1325
  setMessages((prev) => [...prev, userMsg]);
1205
- callbacksRef.current.onMessageSent?.(userMessage);
1326
+ callbacksRef.current.onMessageSent?.(trimmedMessage);
1206
1327
  setIsWaitingForResponse(true);
1207
1328
  callbacksRef.current.onStreamStart?.();
1208
1329
  const streamingId = `assistant-${Date.now()}`;
@@ -1229,11 +1350,14 @@ function useChatV2(config, callbacks = {}) {
1229
1350
  activeStreamStore.start(userId, abortController, initialMessages);
1230
1351
  }
1231
1352
  const newSessionId = await startStream(
1232
- userMessage,
1353
+ trimmedMessage,
1233
1354
  streamingId,
1234
1355
  sessionIdRef.current,
1235
1356
  abortController,
1236
- options
1357
+ {
1358
+ analysisMode: options?.analysisMode,
1359
+ attachments: streamAttachments
1360
+ }
1237
1361
  );
1238
1362
  const finalStreamUserId = streamUserIdRef.current ?? userId;
1239
1363
  if (finalStreamUserId) {
@@ -1371,19 +1495,6 @@ function useChatV2(config, callbacks = {}) {
1371
1495
  },
1372
1496
  [setPromptStatus]
1373
1497
  );
1374
- const expireUserAction2 = useCallback(
1375
- async (userActionId) => {
1376
- setPromptStatus(userActionId, "expired");
1377
- try {
1378
- await expireUserAction(configRef.current, userActionId);
1379
- } catch (error) {
1380
- if (error instanceof UserActionStaleError) return;
1381
- callbacksRef.current.onError?.(error);
1382
- throw error;
1383
- }
1384
- },
1385
- [setPromptStatus]
1386
- );
1387
1498
  const dismissNotification = useCallback((id) => {
1388
1499
  setUserActionState((prev) => ({
1389
1500
  ...prev,
@@ -1424,6 +1535,18 @@ function useChatV2(config, callbacks = {}) {
1424
1535
  setMessages(config.initialMessages);
1425
1536
  sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1426
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]);
1427
1550
  useEffect(() => {
1428
1551
  const prevUserId = prevUserIdRef.current;
1429
1552
  prevUserIdRef.current = config.userId;
@@ -1458,12 +1581,12 @@ function useChatV2(config, callbacks = {}) {
1458
1581
  getSessionId,
1459
1582
  getMessages,
1460
1583
  isWaitingForResponse,
1584
+ isUploadingAttachments,
1461
1585
  sessionId: sessionIdRef.current,
1462
1586
  userActionState,
1463
1587
  submitUserAction: submitUserAction2,
1464
1588
  cancelUserAction: cancelUserAction2,
1465
1589
  resendUserAction: resendUserAction2,
1466
- expireUserAction: expireUserAction2,
1467
1590
  dismissNotification
1468
1591
  };
1469
1592
  }
@@ -2268,7 +2391,7 @@ function createMarkdownComponents(options = {}) {
2268
2391
  },
2269
2392
  pre: ({ children }) => /* @__PURE__ */ jsx("pre", { className: "my-2 max-w-full overflow-x-auto rounded-lg", children }),
2270
2393
  ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "mb-3 ml-4 list-disc space-y-1 text-sm", children }),
2271
- 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 }),
2272
2395
  li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "text-sm leading-relaxed", children }),
2273
2396
  h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "mt-4 mb-2 text-lg font-semibold first:mt-0", children }),
2274
2397
  h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "mt-3 mb-2 text-base font-semibold first:mt-0", children }),
@@ -2337,7 +2460,7 @@ function AgentMessage({
2337
2460
  }
2338
2461
  wasStreamingRef.current = isStreaming;
2339
2462
  }, [isStreaming, hasSteps]);
2340
- 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;
2341
2464
  const currentMessage = message.currentMessage;
2342
2465
  const rawContent = message.streamingContent || message.content || "";
2343
2466
  const content = rawContent.replace(/\\n/g, "\n");
@@ -2365,25 +2488,25 @@ function AgentMessage({
2365
2488
  }
2366
2489
  return `${count} ${stepWord} completed`;
2367
2490
  };
2368
- const renderStepIcon = (step, isCurrentlyExecuting) => {
2491
+ const renderStepIcon = (step2, isCurrentlyExecuting) => {
2369
2492
  const wrapper = "h-4 w-4 flex items-center justify-center shrink-0";
2370
2493
  if (isCurrentlyExecuting)
2371
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" }) });
2372
- if (step.status === "pending" && isCancelled)
2495
+ if (step2.status === "pending" && isCancelled)
2373
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" }) });
2374
- if (step.status === "pending" || step.status === "in_progress")
2497
+ if (step2.status === "pending" || step2.status === "in_progress")
2375
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" }) });
2376
- if (step.status === "completed")
2499
+ if (step2.status === "completed")
2377
2500
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(
2378
2501
  Check,
2379
2502
  {
2380
2503
  className: cn(
2381
2504
  "h-3.5 w-3.5",
2382
- 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"
2383
2506
  )
2384
2507
  }
2385
2508
  ) });
2386
- if (step.status === "error")
2509
+ if (step2.status === "error")
2387
2510
  return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5 payman-agent-step-icon--error" }) });
2388
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" }) });
2389
2512
  };
@@ -2395,33 +2518,33 @@ function AgentMessage({
2395
2518
  exit: { height: 0, opacity: 0 },
2396
2519
  transition: { duration: 0.2, ease: "easeInOut" },
2397
2520
  className: "overflow-hidden",
2398
- children: /* @__PURE__ */ jsx("div", { className: "pt-1.5", children: message.steps.map((step) => {
2399
- const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
2400
- 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;
2401
2524
  return /* @__PURE__ */ jsxs("div", { className: "mb-1.5", children: [
2402
2525
  /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
2403
- /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
2526
+ /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step2, isCurrentlyExecuting) }),
2404
2527
  /* @__PURE__ */ jsx(
2405
2528
  "span",
2406
2529
  {
2407
2530
  className: cn(
2408
2531
  "text-xs leading-relaxed min-w-0 break-words",
2409
2532
  isCurrentlyExecuting && "shimmer-text font-medium",
2410
- !isCurrentlyExecuting && step.status === "error" && "payman-agent-step-text--error",
2411
- !isCurrentlyExecuting && step.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
2412
- !isCurrentlyExecuting && step.status === "completed" && "payman-agent-step-text--completed",
2413
- !isCurrentlyExecuting && step.status === "pending" && "payman-agent-step-text--pending",
2414
- !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"
2415
2538
  ),
2416
- children: step.message
2539
+ children: step2.message
2417
2540
  }
2418
2541
  )
2419
2542
  ] }),
2420
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: [
2421
2544
  /* @__PURE__ */ jsx(Clock, { className: "h-2.5 w-2.5 shrink-0 payman-agent-step-time-icon" }),
2422
- /* @__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) })
2423
2546
  ] }) })
2424
- ] }, step.id);
2547
+ ] }, step2.id);
2425
2548
  }) })
2426
2549
  }
2427
2550
  ) });
@@ -3384,7 +3507,7 @@ function buildComponents(onImageClick, isResolvingRef) {
3384
3507
  code: ({ children }) => /* @__PURE__ */ jsx("code", { children }),
3385
3508
  pre: ({ children }) => /* @__PURE__ */ jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsx("pre", { children }) }),
3386
3509
  ul: ({ children }) => /* @__PURE__ */ jsx("ul", { children }),
3387
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", { children }),
3510
+ ol: ({ children, start }) => /* @__PURE__ */ jsx("ol", { start, children }),
3388
3511
  li: ({ children }) => /* @__PURE__ */ jsx("li", { children }),
3389
3512
  h1: ({ children }) => /* @__PURE__ */ jsx("h1", { children }),
3390
3513
  h2: ({ children }) => /* @__PURE__ */ jsx("h2", { children }),
@@ -3685,6 +3808,87 @@ function FeedbackReasonModal({
3685
3808
  }
3686
3809
  ) : null });
3687
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
3688
3892
  var RESPONSE_SPEED = {
3689
3893
  normal: [2, 4],
3690
3894
  fast: 1,
@@ -3692,7 +3896,6 @@ var RESPONSE_SPEED = {
3692
3896
  newline: [4, 6],
3693
3897
  idle: 15
3694
3898
  };
3695
- var MARKDOWN_IMAGE_REGEX = /^!\[[^\]]*\]\([^)]*\)/;
3696
3899
  function charDelay(char, speed, multiplier) {
3697
3900
  const raw = (() => {
3698
3901
  if (char === "*") return speed.fast;
@@ -3751,9 +3954,9 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
3751
3954
  }
3752
3955
  if (displayedRef.current.length < targetRef.current.length) {
3753
3956
  const remaining = targetRef.current.slice(displayedRef.current.length);
3754
- const imgMatch = MARKDOWN_IMAGE_REGEX.exec(remaining);
3755
- if (imgMatch) {
3756
- displayedRef.current += imgMatch[0];
3957
+ const imageTokenLength = completeMarkdownImageTokenLength(remaining);
3958
+ if (imageTokenLength > 0) {
3959
+ displayedRef.current += remaining.slice(0, imageTokenLength);
3757
3960
  setDisplayedText(displayedRef.current);
3758
3961
  timerRef.current = setTimeout(tick, 0);
3759
3962
  return;
@@ -3782,13 +3985,6 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
3782
3985
  isTyping
3783
3986
  };
3784
3987
  }
3785
- function stripIncompleteImageToken(text) {
3786
- const lastBang = text.lastIndexOf("![");
3787
- if (lastBang === -1) return text;
3788
- const after = text.slice(lastBang);
3789
- if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
3790
- return text.slice(0, lastBang);
3791
- }
3792
3988
  function getFeedbackState(message) {
3793
3989
  const feedback = message.feedback;
3794
3990
  if (feedback === "up" || feedback === "down") return feedback;
@@ -5672,9 +5868,9 @@ var PRE_PIPELINE_STEPS = /* @__PURE__ */ new Set([
5672
5868
  "build_pipeline",
5673
5869
  "load_skill_index"
5674
5870
  ]);
5675
- function stepColor(step) {
5676
- if (step.status === "failed") return "#ef4444";
5677
- 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";
5678
5874
  return "#3b82f6";
5679
5875
  }
5680
5876
  function formatMs(ms) {
@@ -5851,10 +6047,10 @@ function TimelineBars({
5851
6047
  totalMs,
5852
6048
  unaccountedMs
5853
6049
  }) {
5854
- const bars = trace.pipelineSteps.map((step) => {
5855
- const duration = step.durationMs ?? 0;
6050
+ const bars = trace.pipelineSteps.map((step2) => {
6051
+ const duration = step2.durationMs ?? 0;
5856
6052
  const widthPct = totalMs > 0 ? duration / totalMs * 100 : 0;
5857
- const llmMs = step.llmCall?.durationMs ?? null;
6053
+ const llmMs = step2.llmCall?.durationMs ?? null;
5858
6054
  return /* @__PURE__ */ jsxs("div", { style: { marginBottom: 8 }, children: [
5859
6055
  /* @__PURE__ */ jsxs(
5860
6056
  "div",
@@ -5868,8 +6064,8 @@ function TimelineBars({
5868
6064
  },
5869
6065
  children: [
5870
6066
  /* @__PURE__ */ jsxs("span", { children: [
5871
- step.step,
5872
- 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)" })
5873
6069
  ] }),
5874
6070
  /* @__PURE__ */ jsxs("span", { style: { opacity: 0.75 }, children: [
5875
6071
  formatMs(duration),
@@ -5897,14 +6093,14 @@ function TimelineBars({
5897
6093
  style: {
5898
6094
  height: "100%",
5899
6095
  width: `${Math.max(widthPct, 0.3)}%`,
5900
- background: stepColor(step),
6096
+ background: stepColor(step2),
5901
6097
  transition: "width 0.2s ease"
5902
6098
  }
5903
6099
  }
5904
6100
  )
5905
6101
  }
5906
6102
  )
5907
- ] }, `${step.step}-${step.startTime}`);
6103
+ ] }, `${step2.step}-${step2.startTime}`);
5908
6104
  });
5909
6105
  return /* @__PURE__ */ jsxs("div", { children: [
5910
6106
  bars,
@@ -6123,7 +6319,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6123
6319
  const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
6124
6320
  const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
6125
6321
  const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6126
- const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
6322
+ const expireUserAction = chat.expireUserAction ?? NOOP_ASYNC;
6127
6323
  const dismissNotification = chat.dismissNotification ?? NOOP;
6128
6324
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
6129
6325
  const {
@@ -6524,7 +6720,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6524
6720
  onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6525
6721
  onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6526
6722
  onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6527
- onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
6723
+ onExpireUserAction: isUserActionSupported ? expireUserAction : void 0,
6528
6724
  onDismissNotification: dismissNotification,
6529
6725
  onSubmitFeedback: handleSubmitFeedback
6530
6726
  }