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