@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.js CHANGED
@@ -522,9 +522,9 @@ function processStreamEventV2(rawEvent, state) {
522
522
  state.hasError = true;
523
523
  state.errorMessage = "WORKFLOW_FAILED";
524
524
  }
525
- state.steps.forEach((step) => {
526
- if (step.status === "in_progress") {
527
- step.status = "completed";
525
+ state.steps.forEach((step2) => {
526
+ if (step2.status === "in_progress") {
527
+ step2.status = "completed";
528
528
  }
529
529
  });
530
530
  state.lastEventType = eventType;
@@ -736,7 +736,8 @@ function buildRequestBody(config, userMessage, sessionId, options) {
736
736
  sessionAttributes,
737
737
  analysisMode: options?.analysisMode,
738
738
  locale: resolveLocale(config.locale),
739
- timezone: resolveTimezone(config.timezone)
739
+ timezone: resolveTimezone(config.timezone),
740
+ ...options?.attachments?.length ? { attachments: options.attachments } : {}
740
741
  };
741
742
  }
742
743
  function resolveLocale(configured) {
@@ -781,6 +782,7 @@ function buildResolveImagesUrl(config) {
781
782
  const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
782
783
  return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
783
784
  }
785
+ var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
784
786
  function buildRequestHeaders(config) {
785
787
  const headers = {
786
788
  ...config.api.headers
@@ -788,6 +790,9 @@ function buildRequestHeaders(config) {
788
790
  if (config.api.authToken) {
789
791
  headers.Authorization = `Bearer ${config.api.authToken}`;
790
792
  }
793
+ if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
794
+ headers[NGROK_SKIP_BROWSER_WARNING] = "true";
795
+ }
791
796
  return headers;
792
797
  }
793
798
  var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
@@ -939,11 +944,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
939
944
  errorDetails: isAborted ? void 0 : error.message,
940
945
  content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
941
946
  currentMessage: isAborted ? "Thinking..." : void 0,
942
- steps: [...state.steps].map((step) => {
943
- if (step.status === "in_progress" && isAborted) {
944
- return { ...step, status: "pending" };
947
+ steps: [...state.steps].map((step2) => {
948
+ if (step2.status === "in_progress" && isAborted) {
949
+ return { ...step2, status: "pending" };
945
950
  }
946
- return step;
951
+ return step2;
947
952
  }),
948
953
  currentExecutingStepId: void 0
949
954
  } : msg
@@ -1029,11 +1034,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1029
1034
  isCancelled: isAborted,
1030
1035
  errorDetails: isAborted ? void 0 : error.message,
1031
1036
  content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
1032
- steps: [...state.steps].map((step) => {
1033
- if (step.status === "in_progress" && isAborted) {
1034
- return { ...step, status: "pending" };
1037
+ steps: [...state.steps].map((step2) => {
1038
+ if (step2.status === "in_progress" && isAborted) {
1039
+ return { ...step2, status: "pending" };
1035
1040
  }
1036
- return step;
1041
+ return step2;
1037
1042
  }),
1038
1043
  currentExecutingStepId: void 0
1039
1044
  } : msg
@@ -1054,11 +1059,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1054
1059
  };
1055
1060
  }
1056
1061
  function createCancelledMessageUpdate(steps, currentMessage) {
1057
- const updatedSteps = steps.map((step) => {
1058
- if (step.status === "in_progress") {
1059
- return { ...step, status: "pending" };
1062
+ const updatedSteps = steps.map((step2) => {
1063
+ if (step2.status === "in_progress") {
1064
+ return { ...step2, status: "pending" };
1060
1065
  }
1061
- return step;
1066
+ return step2;
1062
1067
  });
1063
1068
  return {
1064
1069
  isStreaming: false,
@@ -1068,6 +1073,81 @@ function createCancelledMessageUpdate(steps, currentMessage) {
1068
1073
  currentMessage: currentMessage || "Thinking..."
1069
1074
  };
1070
1075
  }
1076
+ var DEFAULT_SIGNED_URL_ENDPOINT = "/api/files/signed-url";
1077
+ function fileExtension(filename) {
1078
+ const ext = filename.split(".").pop()?.toLowerCase().replace(/^\./, "");
1079
+ return ext && ext.length > 0 ? ext : "bin";
1080
+ }
1081
+ function resolveMimeType(file) {
1082
+ if (file.type && file.type.trim().length > 0) return file.type;
1083
+ const ext = fileExtension(file.name);
1084
+ const byExt = {
1085
+ pdf: "application/pdf",
1086
+ png: "image/png",
1087
+ jpg: "image/jpeg",
1088
+ jpeg: "image/jpeg",
1089
+ gif: "image/gif",
1090
+ webp: "image/webp"
1091
+ };
1092
+ return byExt[ext] ?? "application/octet-stream";
1093
+ }
1094
+ function buildSignedUrlEndpoint(config, ext) {
1095
+ const endpoint = config.api.signedUrlEndpoint || DEFAULT_SIGNED_URL_ENDPOINT;
1096
+ const queryParams = new URLSearchParams({ extn: ext.replace(/^\./, "") });
1097
+ return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
1098
+ }
1099
+ async function requestSignedUrl(config, ext, signal) {
1100
+ const url = buildSignedUrlEndpoint(config, ext);
1101
+ const headers = buildRequestHeaders(config);
1102
+ const response = await fetch(url, {
1103
+ method: "GET",
1104
+ headers,
1105
+ signal
1106
+ });
1107
+ if (!response.ok) {
1108
+ const errorText = await response.text().catch(() => "");
1109
+ throw new Error(
1110
+ errorText ? `Failed to get upload URL (${response.status}): ${errorText}` : `Failed to get upload URL (${response.status})`
1111
+ );
1112
+ }
1113
+ const data = await response.json();
1114
+ if (!data.key || !data.url) {
1115
+ throw new Error("Signed URL response missing key or url");
1116
+ }
1117
+ return { key: data.key, url: data.url };
1118
+ }
1119
+ async function uploadToSignedUrl(signedUrl, file, mimeType, signal) {
1120
+ const response = await fetch(signedUrl, {
1121
+ method: "PUT",
1122
+ headers: {
1123
+ "Content-Type": mimeType,
1124
+ "x-ms-blob-type": "BlockBlob"
1125
+ },
1126
+ body: file,
1127
+ signal
1128
+ });
1129
+ if (!response.ok) {
1130
+ const errorText = await response.text().catch(() => "");
1131
+ throw new Error(
1132
+ errorText ? `Failed to upload file (${response.status}): ${errorText}` : `Failed to upload file (${response.status})`
1133
+ );
1134
+ }
1135
+ }
1136
+ async function uploadAttachment(config, file, signal) {
1137
+ const ext = fileExtension(file.name);
1138
+ const mimeType = resolveMimeType(file);
1139
+ const { key, url } = await requestSignedUrl(config, ext, signal);
1140
+ await uploadToSignedUrl(url, file, mimeType, signal);
1141
+ return {
1142
+ tempKey: key,
1143
+ filename: file.name,
1144
+ mimeType
1145
+ };
1146
+ }
1147
+ async function uploadAttachments(config, files, signal) {
1148
+ const uploads = files.map((file) => uploadAttachment(config, file, signal));
1149
+ return Promise.all(uploads);
1150
+ }
1071
1151
  var UserActionStaleError = class extends Error {
1072
1152
  constructor(userActionId, message = "User action is no longer actionable") {
1073
1153
  super(message);
@@ -1104,9 +1184,6 @@ async function cancelUserAction(config, userActionId) {
1104
1184
  async function resendUserAction(config, userActionId) {
1105
1185
  return sendUserActionRequest(config, userActionId, "resend");
1106
1186
  }
1107
- async function expireUserAction(config, userActionId) {
1108
- return sendUserActionRequest(config, userActionId, "expired");
1109
- }
1110
1187
  var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1111
1188
  function upsertPrompt(prompts, req) {
1112
1189
  const active = { ...req, status: "pending" };
@@ -1135,12 +1212,32 @@ function getStoredOrInitialMessages(config) {
1135
1212
  function getSessionIdFromMessages(messages) {
1136
1213
  return messages.find((message) => message.sessionId)?.sessionId;
1137
1214
  }
1215
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["jpg", "jpeg", "png", "gif", "webp", "avif"]);
1216
+ function attachmentKindFromFile(file) {
1217
+ const ext = file.name.split(".").pop()?.toLowerCase() ?? "";
1218
+ if (IMAGE_EXTENSIONS.has(ext) || file.type.startsWith("image/")) return "image";
1219
+ return "file";
1220
+ }
1221
+ function buildMessageAttachments(files) {
1222
+ return files.map((file, index) => {
1223
+ const kind = attachmentKindFromFile(file);
1224
+ return {
1225
+ id: `att-${Date.now()}-${index}`,
1226
+ filename: file.name,
1227
+ mimeType: file.type || "application/octet-stream",
1228
+ // Blob URL for all local files so PDFs/docs stay previewable after send.
1229
+ previewUrl: URL.createObjectURL(file),
1230
+ kind
1231
+ };
1232
+ });
1233
+ }
1138
1234
  function useChatV2(config, callbacks = {}) {
1139
1235
  const [messages, setMessages] = react.useState(() => getStoredOrInitialMessages(config));
1140
1236
  const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(() => {
1141
1237
  if (!config.userId) return false;
1142
1238
  return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1143
1239
  });
1240
+ const [isUploadingAttachments, setIsUploadingAttachments] = react.useState(false);
1144
1241
  const sessionIdRef = react.useRef(
1145
1242
  getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1146
1243
  );
@@ -1215,21 +1312,45 @@ function useChatV2(config, callbacks = {}) {
1215
1312
  );
1216
1313
  const sendMessage = react.useCallback(
1217
1314
  async (userMessage, options) => {
1218
- if (!userMessage.trim()) return;
1315
+ const trimmedMessage = userMessage.trim();
1316
+ const files = options?.files ?? [];
1317
+ const hasPreuploadedAttachments = (options?.attachments?.length ?? 0) > 0;
1318
+ if (!trimmedMessage && files.length === 0 && !hasPreuploadedAttachments) return;
1319
+ let streamAttachments = options?.attachments;
1320
+ if (!streamAttachments && files.length > 0) {
1321
+ setIsUploadingAttachments(true);
1322
+ try {
1323
+ streamAttachments = await uploadAttachments(configRef.current, files);
1324
+ } catch (error) {
1325
+ if (error.name !== "AbortError") {
1326
+ callbacksRef.current.onError?.(error);
1327
+ }
1328
+ throw error;
1329
+ } finally {
1330
+ setIsUploadingAttachments(false);
1331
+ }
1332
+ }
1219
1333
  if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1220
1334
  sessionIdRef.current = generateId();
1221
1335
  callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1222
1336
  }
1337
+ const messageAttachments = files.length > 0 ? buildMessageAttachments(files) : streamAttachments?.length ? streamAttachments.map((attachment, index) => ({
1338
+ id: `att-${Date.now()}-${index}`,
1339
+ filename: attachment.filename,
1340
+ mimeType: attachment.mimeType,
1341
+ kind: attachment.mimeType.startsWith("image/") ? "image" : "file"
1342
+ })) : void 0;
1223
1343
  const userMessageId = `user-${Date.now()}`;
1224
1344
  const userMsg = {
1225
1345
  id: userMessageId,
1226
1346
  sessionId: sessionIdRef.current,
1227
1347
  role: "user",
1228
- content: userMessage,
1229
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1348
+ content: trimmedMessage,
1349
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1350
+ attachments: messageAttachments
1230
1351
  };
1231
1352
  setMessages((prev) => [...prev, userMsg]);
1232
- callbacksRef.current.onMessageSent?.(userMessage);
1353
+ callbacksRef.current.onMessageSent?.(trimmedMessage);
1233
1354
  setIsWaitingForResponse(true);
1234
1355
  callbacksRef.current.onStreamStart?.();
1235
1356
  const streamingId = `assistant-${Date.now()}`;
@@ -1256,11 +1377,14 @@ function useChatV2(config, callbacks = {}) {
1256
1377
  activeStreamStore.start(userId, abortController, initialMessages);
1257
1378
  }
1258
1379
  const newSessionId = await startStream(
1259
- userMessage,
1380
+ trimmedMessage,
1260
1381
  streamingId,
1261
1382
  sessionIdRef.current,
1262
1383
  abortController,
1263
- options
1384
+ {
1385
+ analysisMode: options?.analysisMode,
1386
+ attachments: streamAttachments
1387
+ }
1264
1388
  );
1265
1389
  const finalStreamUserId = streamUserIdRef.current ?? userId;
1266
1390
  if (finalStreamUserId) {
@@ -1398,19 +1522,6 @@ function useChatV2(config, callbacks = {}) {
1398
1522
  },
1399
1523
  [setPromptStatus]
1400
1524
  );
1401
- const expireUserAction2 = react.useCallback(
1402
- async (userActionId) => {
1403
- setPromptStatus(userActionId, "expired");
1404
- try {
1405
- await expireUserAction(configRef.current, userActionId);
1406
- } catch (error) {
1407
- if (error instanceof UserActionStaleError) return;
1408
- callbacksRef.current.onError?.(error);
1409
- throw error;
1410
- }
1411
- },
1412
- [setPromptStatus]
1413
- );
1414
1525
  const dismissNotification = react.useCallback((id) => {
1415
1526
  setUserActionState((prev) => ({
1416
1527
  ...prev,
@@ -1451,6 +1562,18 @@ function useChatV2(config, callbacks = {}) {
1451
1562
  setMessages(config.initialMessages);
1452
1563
  sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1453
1564
  }, [config.initialMessages, config.initialSessionId, config.userId]);
1565
+ const hydratedSessionIdRef = react.useRef(void 0);
1566
+ react.useEffect(() => {
1567
+ if (config.userId) return;
1568
+ if (!config.initialMessages?.length) return;
1569
+ const sessionId = config.initialSessionId;
1570
+ if (hydratedSessionIdRef.current === sessionId && messagesRef.current.length > 0) {
1571
+ return;
1572
+ }
1573
+ hydratedSessionIdRef.current = sessionId;
1574
+ setMessages(config.initialMessages);
1575
+ sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? sessionId;
1576
+ }, [config.initialMessages, config.initialSessionId, config.userId]);
1454
1577
  react.useEffect(() => {
1455
1578
  const prevUserId = prevUserIdRef.current;
1456
1579
  prevUserIdRef.current = config.userId;
@@ -1485,12 +1608,12 @@ function useChatV2(config, callbacks = {}) {
1485
1608
  getSessionId,
1486
1609
  getMessages,
1487
1610
  isWaitingForResponse,
1611
+ isUploadingAttachments,
1488
1612
  sessionId: sessionIdRef.current,
1489
1613
  userActionState,
1490
1614
  submitUserAction: submitUserAction2,
1491
1615
  cancelUserAction: cancelUserAction2,
1492
1616
  resendUserAction: resendUserAction2,
1493
- expireUserAction: expireUserAction2,
1494
1617
  dismissNotification
1495
1618
  };
1496
1619
  }
@@ -2295,7 +2418,7 @@ function createMarkdownComponents(options = {}) {
2295
2418
  },
2296
2419
  pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "my-2 max-w-full overflow-x-auto rounded-lg", children }),
2297
2420
  ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mb-3 ml-4 list-disc space-y-1 text-sm", children }),
2298
- ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "mb-3 ml-4 list-decimal space-y-1 text-sm", children }),
2421
+ ol: ({ children, start }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "mb-3 ml-4 list-decimal space-y-1 text-sm", start, children }),
2299
2422
  li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "text-sm leading-relaxed", children }),
2300
2423
  h1: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-4 mb-2 text-lg font-semibold first:mt-0", children }),
2301
2424
  h2: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mt-3 mb-2 text-base font-semibold first:mt-0", children }),
@@ -2364,7 +2487,7 @@ function AgentMessage({
2364
2487
  }
2365
2488
  wasStreamingRef.current = isStreaming;
2366
2489
  }, [isStreaming, hasSteps]);
2367
- const totalElapsedMs = hasSteps ? message.steps.reduce((sum, step) => sum + (step.elapsedMs || 0), 0) : 0;
2490
+ const totalElapsedMs = hasSteps ? message.steps.reduce((sum, step2) => sum + (step2.elapsedMs || 0), 0) : 0;
2368
2491
  const currentMessage = message.currentMessage;
2369
2492
  const rawContent = message.streamingContent || message.content || "";
2370
2493
  const content = rawContent.replace(/\\n/g, "\n");
@@ -2392,25 +2515,25 @@ function AgentMessage({
2392
2515
  }
2393
2516
  return `${count} ${stepWord} completed`;
2394
2517
  };
2395
- const renderStepIcon = (step, isCurrentlyExecuting) => {
2518
+ const renderStepIcon = (step2, isCurrentlyExecuting) => {
2396
2519
  const wrapper = "h-4 w-4 flex items-center justify-center shrink-0";
2397
2520
  if (isCurrentlyExecuting)
2398
2521
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapper, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 payman-agent-step-icon--active animate-spin" }) });
2399
- if (step.status === "pending" && isCancelled)
2522
+ if (step2.status === "pending" && isCancelled)
2400
2523
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapper, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-1.5 rounded-full payman-agent-step-icon--pending" }) });
2401
- if (step.status === "pending" || step.status === "in_progress")
2524
+ if (step2.status === "pending" || step2.status === "in_progress")
2402
2525
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapper, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 payman-agent-step-icon--in-progress-dim animate-spin" }) });
2403
- if (step.status === "completed")
2526
+ if (step2.status === "completed")
2404
2527
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapper, children: /* @__PURE__ */ jsxRuntime.jsx(
2405
2528
  lucideReact.Check,
2406
2529
  {
2407
2530
  className: cn(
2408
2531
  "h-3.5 w-3.5",
2409
- step.eventType === "USER_ACTION_SUCCESS" ? "payman-agent-step-icon--success" : "payman-agent-step-icon--success-dim"
2532
+ step2.eventType === "USER_ACTION_SUCCESS" ? "payman-agent-step-icon--success" : "payman-agent-step-icon--success-dim"
2410
2533
  )
2411
2534
  }
2412
2535
  ) });
2413
- if (step.status === "error")
2536
+ if (step2.status === "error")
2414
2537
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapper, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5 payman-agent-step-icon--error" }) });
2415
2538
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: wrapper, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-1.5 rounded-full payman-agent-step-icon--pending-dim" }) });
2416
2539
  };
@@ -2422,33 +2545,33 @@ function AgentMessage({
2422
2545
  exit: { height: 0, opacity: 0 },
2423
2546
  transition: { duration: 0.2, ease: "easeInOut" },
2424
2547
  className: "overflow-hidden",
2425
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-1.5", children: message.steps.map((step) => {
2426
- const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
2427
- const hasTime = step.elapsedMs != null && step.elapsedMs > 0;
2548
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-1.5", children: message.steps.map((step2) => {
2549
+ const isCurrentlyExecuting = step2.id === currentExecutingStepId && step2.status === "in_progress" && isStreaming && !isCancelled;
2550
+ const hasTime = step2.elapsedMs != null && step2.elapsedMs > 0;
2428
2551
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1.5", children: [
2429
2552
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5 items-start", children: [
2430
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
2553
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-px", children: renderStepIcon(step2, isCurrentlyExecuting) }),
2431
2554
  /* @__PURE__ */ jsxRuntime.jsx(
2432
2555
  "span",
2433
2556
  {
2434
2557
  className: cn(
2435
2558
  "text-xs leading-relaxed min-w-0 break-words",
2436
2559
  isCurrentlyExecuting && "shimmer-text font-medium",
2437
- !isCurrentlyExecuting && step.status === "error" && "payman-agent-step-text--error",
2438
- !isCurrentlyExecuting && step.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
2439
- !isCurrentlyExecuting && step.status === "completed" && "payman-agent-step-text--completed",
2440
- !isCurrentlyExecuting && step.status === "pending" && "payman-agent-step-text--pending",
2441
- !isCurrentlyExecuting && step.status === "in_progress" && "payman-agent-step-text--in-progress"
2560
+ !isCurrentlyExecuting && step2.status === "error" && "payman-agent-step-text--error",
2561
+ !isCurrentlyExecuting && step2.eventType === "USER_ACTION_SUCCESS" && "payman-agent-step-text--success",
2562
+ !isCurrentlyExecuting && step2.status === "completed" && "payman-agent-step-text--completed",
2563
+ !isCurrentlyExecuting && step2.status === "pending" && "payman-agent-step-text--pending",
2564
+ !isCurrentlyExecuting && step2.status === "in_progress" && "payman-agent-step-text--in-progress"
2442
2565
  ),
2443
- children: step.message
2566
+ children: step2.message
2444
2567
  }
2445
2568
  )
2446
2569
  ] }),
2447
2570
  hasTime && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-[22px] mt-1", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md payman-agent-step-time leading-none", children: [
2448
2571
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-2.5 w-2.5 shrink-0 payman-agent-step-time-icon" }),
2449
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step.elapsedMs) })
2572
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono payman-agent-step-time-text", children: formatElapsedTime(step2.elapsedMs) })
2450
2573
  ] }) })
2451
- ] }, step.id);
2574
+ ] }, step2.id);
2452
2575
  }) })
2453
2576
  }
2454
2577
  ) });
@@ -3411,7 +3534,7 @@ function buildComponents(onImageClick, isResolvingRef) {
3411
3534
  code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
3412
3535
  pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { children }) }),
3413
3536
  ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { children }),
3414
- ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { children }),
3537
+ ol: ({ children, start }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { start, children }),
3415
3538
  li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { children }),
3416
3539
  h1: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h1", { children }),
3417
3540
  h2: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("h2", { children }),
@@ -3712,6 +3835,87 @@ function FeedbackReasonModal({
3712
3835
  }
3713
3836
  ) : null });
3714
3837
  }
3838
+
3839
+ // src/utils/markdownImageToken.ts
3840
+ function step(text, i) {
3841
+ if (text[i] === "\\" && i + 1 < text.length) return i + 2;
3842
+ return i + 1;
3843
+ }
3844
+ function scanAltText(text, start) {
3845
+ let i = start;
3846
+ while (i < text.length) {
3847
+ if (text[i] === "]") return i;
3848
+ i = step(text, i);
3849
+ }
3850
+ return -1;
3851
+ }
3852
+ function scanOptionalTitle(text, start) {
3853
+ let i = start;
3854
+ while (i < text.length && /[\t\n ]/.test(text[i])) i++;
3855
+ if (i >= text.length) return i;
3856
+ const quote = text[i];
3857
+ if (quote !== '"' && quote !== "'") return i;
3858
+ i++;
3859
+ while (i < text.length) {
3860
+ if (text[i] === quote) return i + 1;
3861
+ i = step(text, i);
3862
+ }
3863
+ return -1;
3864
+ }
3865
+ function scanAngleBracketDestination(text, start) {
3866
+ let i = start + 1;
3867
+ while (i < text.length) {
3868
+ if (text[i] === ">") return i + 1;
3869
+ i = step(text, i);
3870
+ }
3871
+ return -1;
3872
+ }
3873
+ function scanRawDestination(text, start) {
3874
+ let i = start;
3875
+ let depth = 1;
3876
+ while (i < text.length) {
3877
+ const char = text[i];
3878
+ if (char === "\\") {
3879
+ if (i + 1 >= text.length) return -1;
3880
+ i += 2;
3881
+ continue;
3882
+ }
3883
+ if (char === "(") depth++;
3884
+ else if (char === ")") {
3885
+ depth--;
3886
+ if (depth === 0) return i + 1;
3887
+ }
3888
+ i++;
3889
+ }
3890
+ return -1;
3891
+ }
3892
+ function completeMarkdownImageTokenLength(text) {
3893
+ if (!text.startsWith("![")) return 0;
3894
+ const altEnd = scanAltText(text, 2);
3895
+ if (altEnd === -1 || altEnd + 1 >= text.length || text[altEnd + 1] !== "(") {
3896
+ return 0;
3897
+ }
3898
+ let i = altEnd + 2;
3899
+ if (i >= text.length) return 0;
3900
+ if (text[i] === "<") {
3901
+ i = scanAngleBracketDestination(text, i);
3902
+ if (i === -1) return 0;
3903
+ i = scanOptionalTitle(text, i);
3904
+ if (i === -1 || i >= text.length || text[i] !== ")") return 0;
3905
+ return i + 1;
3906
+ }
3907
+ i = scanRawDestination(text, i);
3908
+ return i === -1 ? 0 : i;
3909
+ }
3910
+ function stripIncompleteImageToken(text) {
3911
+ const lastBang = text.lastIndexOf("![");
3912
+ if (lastBang === -1) return text;
3913
+ const suffix = text.slice(lastBang);
3914
+ if (completeMarkdownImageTokenLength(suffix) > 0) return text;
3915
+ return text.slice(0, lastBang);
3916
+ }
3917
+
3918
+ // src/components/v2/useTypingEffect.ts
3715
3919
  var RESPONSE_SPEED = {
3716
3920
  normal: [2, 4],
3717
3921
  fast: 1,
@@ -3719,7 +3923,6 @@ var RESPONSE_SPEED = {
3719
3923
  newline: [4, 6],
3720
3924
  idle: 15
3721
3925
  };
3722
- var MARKDOWN_IMAGE_REGEX = /^!\[[^\]]*\]\([^)]*\)/;
3723
3926
  function charDelay(char, speed, multiplier) {
3724
3927
  const raw = (() => {
3725
3928
  if (char === "*") return speed.fast;
@@ -3778,9 +3981,9 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
3778
3981
  }
3779
3982
  if (displayedRef.current.length < targetRef.current.length) {
3780
3983
  const remaining = targetRef.current.slice(displayedRef.current.length);
3781
- const imgMatch = MARKDOWN_IMAGE_REGEX.exec(remaining);
3782
- if (imgMatch) {
3783
- displayedRef.current += imgMatch[0];
3984
+ const imageTokenLength = completeMarkdownImageTokenLength(remaining);
3985
+ if (imageTokenLength > 0) {
3986
+ displayedRef.current += remaining.slice(0, imageTokenLength);
3784
3987
  setDisplayedText(displayedRef.current);
3785
3988
  timerRef.current = setTimeout(tick, 0);
3786
3989
  return;
@@ -3809,13 +4012,6 @@ function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDis
3809
4012
  isTyping
3810
4013
  };
3811
4014
  }
3812
- function stripIncompleteImageToken(text) {
3813
- const lastBang = text.lastIndexOf("![");
3814
- if (lastBang === -1) return text;
3815
- const after = text.slice(lastBang);
3816
- if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
3817
- return text.slice(0, lastBang);
3818
- }
3819
4015
  function getFeedbackState(message) {
3820
4016
  const feedback = message.feedback;
3821
4017
  if (feedback === "up" || feedback === "down") return feedback;
@@ -5699,9 +5895,9 @@ var PRE_PIPELINE_STEPS = /* @__PURE__ */ new Set([
5699
5895
  "build_pipeline",
5700
5896
  "load_skill_index"
5701
5897
  ]);
5702
- function stepColor(step) {
5703
- if (step.status === "failed") return "#ef4444";
5704
- if (PRE_PIPELINE_STEPS.has(step.step)) return "#f59e0b";
5898
+ function stepColor(step2) {
5899
+ if (step2.status === "failed") return "#ef4444";
5900
+ if (PRE_PIPELINE_STEPS.has(step2.step)) return "#f59e0b";
5705
5901
  return "#3b82f6";
5706
5902
  }
5707
5903
  function formatMs(ms) {
@@ -5878,10 +6074,10 @@ function TimelineBars({
5878
6074
  totalMs,
5879
6075
  unaccountedMs
5880
6076
  }) {
5881
- const bars = trace.pipelineSteps.map((step) => {
5882
- const duration = step.durationMs ?? 0;
6077
+ const bars = trace.pipelineSteps.map((step2) => {
6078
+ const duration = step2.durationMs ?? 0;
5883
6079
  const widthPct = totalMs > 0 ? duration / totalMs * 100 : 0;
5884
- const llmMs = step.llmCall?.durationMs ?? null;
6080
+ const llmMs = step2.llmCall?.durationMs ?? null;
5885
6081
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 8 }, children: [
5886
6082
  /* @__PURE__ */ jsxRuntime.jsxs(
5887
6083
  "div",
@@ -5895,8 +6091,8 @@ function TimelineBars({
5895
6091
  },
5896
6092
  children: [
5897
6093
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5898
- step.step,
5899
- step.status === "failed" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#fca5a5", marginLeft: 6 }, children: "(failed)" })
6094
+ step2.step,
6095
+ step2.status === "failed" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#fca5a5", marginLeft: 6 }, children: "(failed)" })
5900
6096
  ] }),
5901
6097
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { opacity: 0.75 }, children: [
5902
6098
  formatMs(duration),
@@ -5924,14 +6120,14 @@ function TimelineBars({
5924
6120
  style: {
5925
6121
  height: "100%",
5926
6122
  width: `${Math.max(widthPct, 0.3)}%`,
5927
- background: stepColor(step),
6123
+ background: stepColor(step2),
5928
6124
  transition: "width 0.2s ease"
5929
6125
  }
5930
6126
  }
5931
6127
  )
5932
6128
  }
5933
6129
  )
5934
- ] }, `${step.step}-${step.startTime}`);
6130
+ ] }, `${step2.step}-${step2.startTime}`);
5935
6131
  });
5936
6132
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5937
6133
  bars,
@@ -6150,7 +6346,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6150
6346
  const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
6151
6347
  const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
6152
6348
  const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6153
- const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
6349
+ const expireUserAction = chat.expireUserAction ?? NOOP_ASYNC;
6154
6350
  const dismissNotification = chat.dismissNotification ?? NOOP;
6155
6351
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
6156
6352
  const {
@@ -6551,7 +6747,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
6551
6747
  onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6552
6748
  onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6553
6749
  onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6554
- onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
6750
+ onExpireUserAction: isUserActionSupported ? expireUserAction : void 0,
6555
6751
  onDismissNotification: dismissNotification,
6556
6752
  onSubmitFeedback: handleSubmitFeedback
6557
6753
  }