@paymanai/payman-ask-sdk 4.0.24 → 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 +111 -175
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +111 -175
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +129 -188
- package/dist/index.native.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ var chatStore = {
|
|
|
60
60
|
memoryStore.delete(key);
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
+
var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
|
|
63
64
|
var streams = /* @__PURE__ */ new Map();
|
|
64
65
|
var activeStreamStore = {
|
|
65
66
|
has(key) {
|
|
@@ -68,7 +69,11 @@ var activeStreamStore = {
|
|
|
68
69
|
get(key) {
|
|
69
70
|
const entry = streams.get(key);
|
|
70
71
|
if (!entry) return null;
|
|
71
|
-
return {
|
|
72
|
+
return {
|
|
73
|
+
messages: entry.messages,
|
|
74
|
+
isWaiting: entry.isWaiting,
|
|
75
|
+
userActionState: entry.userActionState
|
|
76
|
+
};
|
|
72
77
|
},
|
|
73
78
|
// Called before startStream — registers the controller and initial messages
|
|
74
79
|
start(key, abortController, initialMessages) {
|
|
@@ -76,6 +81,7 @@ var activeStreamStore = {
|
|
|
76
81
|
streams.set(key, {
|
|
77
82
|
messages: initialMessages,
|
|
78
83
|
isWaiting: true,
|
|
84
|
+
userActionState: EMPTY_USER_ACTION_STATE,
|
|
79
85
|
abortController,
|
|
80
86
|
listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
|
|
81
87
|
});
|
|
@@ -86,20 +92,27 @@ var activeStreamStore = {
|
|
|
86
92
|
if (!entry) return;
|
|
87
93
|
const next = typeof updater === "function" ? updater(entry.messages) : updater;
|
|
88
94
|
entry.messages = next;
|
|
89
|
-
entry.listeners.forEach((l) => l(next, entry.isWaiting));
|
|
95
|
+
entry.listeners.forEach((l) => l(next, entry.isWaiting, entry.userActionState));
|
|
90
96
|
},
|
|
91
97
|
setWaiting(key, waiting) {
|
|
92
98
|
const entry = streams.get(key);
|
|
93
99
|
if (!entry) return;
|
|
94
100
|
entry.isWaiting = waiting;
|
|
95
|
-
entry.listeners.forEach((l) => l(entry.messages, waiting));
|
|
101
|
+
entry.listeners.forEach((l) => l(entry.messages, waiting, entry.userActionState));
|
|
102
|
+
},
|
|
103
|
+
applyUserActionState(key, updater) {
|
|
104
|
+
const entry = streams.get(key);
|
|
105
|
+
if (!entry) return;
|
|
106
|
+
const next = typeof updater === "function" ? updater(entry.userActionState) : updater;
|
|
107
|
+
entry.userActionState = next;
|
|
108
|
+
entry.listeners.forEach((l) => l(entry.messages, entry.isWaiting, next));
|
|
96
109
|
},
|
|
97
110
|
// Called when stream completes — persists to chatStore and cleans up
|
|
98
111
|
complete(key) {
|
|
99
112
|
const entry = streams.get(key);
|
|
100
113
|
if (!entry) return;
|
|
101
114
|
entry.isWaiting = false;
|
|
102
|
-
entry.listeners.forEach((l) => l(entry.messages, false));
|
|
115
|
+
entry.listeners.forEach((l) => l(entry.messages, false, entry.userActionState));
|
|
103
116
|
const toSave = entry.messages.filter((m) => !m.isStreaming);
|
|
104
117
|
if (toSave.length > 0) chatStore.set(key, toSave);
|
|
105
118
|
streams.delete(key);
|
|
@@ -736,8 +749,7 @@ function buildRequestBody(config, userMessage, sessionId, options) {
|
|
|
736
749
|
sessionAttributes,
|
|
737
750
|
analysisMode: options?.analysisMode,
|
|
738
751
|
locale: resolveLocale(config.locale),
|
|
739
|
-
timezone: resolveTimezone(config.timezone)
|
|
740
|
-
...options?.attachments?.length ? { attachments: options.attachments } : {}
|
|
752
|
+
timezone: resolveTimezone(config.timezone)
|
|
741
753
|
};
|
|
742
754
|
}
|
|
743
755
|
function resolveLocale(configured) {
|
|
@@ -782,7 +794,6 @@ function buildResolveImagesUrl(config) {
|
|
|
782
794
|
const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
|
|
783
795
|
return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
|
|
784
796
|
}
|
|
785
|
-
var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
|
|
786
797
|
function buildRequestHeaders(config) {
|
|
787
798
|
const headers = {
|
|
788
799
|
...config.api.headers
|
|
@@ -790,9 +801,6 @@ function buildRequestHeaders(config) {
|
|
|
790
801
|
if (config.api.authToken) {
|
|
791
802
|
headers.Authorization = `Bearer ${config.api.authToken}`;
|
|
792
803
|
}
|
|
793
|
-
if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
|
|
794
|
-
headers[NGROK_SKIP_BROWSER_WARNING] = "true";
|
|
795
|
-
}
|
|
796
804
|
return headers;
|
|
797
805
|
}
|
|
798
806
|
var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
|
|
@@ -1073,81 +1081,6 @@ function createCancelledMessageUpdate(steps, currentMessage) {
|
|
|
1073
1081
|
currentMessage: currentMessage || "Thinking..."
|
|
1074
1082
|
};
|
|
1075
1083
|
}
|
|
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
|
-
}
|
|
1151
1084
|
var UserActionStaleError = class extends Error {
|
|
1152
1085
|
constructor(userActionId, message = "User action is no longer actionable") {
|
|
1153
1086
|
super(message);
|
|
@@ -1184,12 +1117,34 @@ async function cancelUserAction(config, userActionId) {
|
|
|
1184
1117
|
async function resendUserAction(config, userActionId) {
|
|
1185
1118
|
return sendUserActionRequest(config, userActionId, "resend");
|
|
1186
1119
|
}
|
|
1187
|
-
|
|
1120
|
+
async function expireUserAction(config, userActionId) {
|
|
1121
|
+
return sendUserActionRequest(config, userActionId, "expired");
|
|
1122
|
+
}
|
|
1123
|
+
function resolveUserActionExpiresAt(req, existing) {
|
|
1124
|
+
if (typeof req.expirySeconds !== "number" || req.expirySeconds <= 0) {
|
|
1125
|
+
return void 0;
|
|
1126
|
+
}
|
|
1127
|
+
if (existing?.expiresAt && existing.userActionId === req.userActionId) {
|
|
1128
|
+
return existing.expiresAt;
|
|
1129
|
+
}
|
|
1130
|
+
return Date.now() + Math.floor(req.expirySeconds) * 1e3;
|
|
1131
|
+
}
|
|
1132
|
+
function getUserActionSecondsLeft(prompt) {
|
|
1133
|
+
if (typeof prompt.expiresAt === "number" && prompt.expiresAt > 0) {
|
|
1134
|
+
return Math.max(0, Math.ceil((prompt.expiresAt - Date.now()) / 1e3));
|
|
1135
|
+
}
|
|
1136
|
+
if (typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0) {
|
|
1137
|
+
return Math.floor(prompt.expirySeconds);
|
|
1138
|
+
}
|
|
1139
|
+
return void 0;
|
|
1140
|
+
}
|
|
1141
|
+
var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
|
|
1188
1142
|
function upsertPrompt(prompts, req) {
|
|
1189
|
-
const active = { ...req, status: "pending" };
|
|
1190
1143
|
const idx = prompts.findIndex(
|
|
1191
1144
|
(p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
|
|
1192
1145
|
);
|
|
1146
|
+
const expiresAt = resolveUserActionExpiresAt(req, idx >= 0 ? prompts[idx] : void 0);
|
|
1147
|
+
const active = { ...req, status: "pending", expiresAt };
|
|
1193
1148
|
if (idx >= 0) {
|
|
1194
1149
|
const next = prompts.slice();
|
|
1195
1150
|
next[idx] = active;
|
|
@@ -1212,32 +1167,12 @@ function getStoredOrInitialMessages(config) {
|
|
|
1212
1167
|
function getSessionIdFromMessages(messages) {
|
|
1213
1168
|
return messages.find((message) => message.sessionId)?.sessionId;
|
|
1214
1169
|
}
|
|
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
|
-
}
|
|
1234
1170
|
function useChatV2(config, callbacks = {}) {
|
|
1235
1171
|
const [messages, setMessages] = react.useState(() => getStoredOrInitialMessages(config));
|
|
1236
1172
|
const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(() => {
|
|
1237
1173
|
if (!config.userId) return false;
|
|
1238
1174
|
return activeStreamStore.get(config.userId)?.isWaiting ?? false;
|
|
1239
1175
|
});
|
|
1240
|
-
const [isUploadingAttachments, setIsUploadingAttachments] = react.useState(false);
|
|
1241
1176
|
const sessionIdRef = react.useRef(
|
|
1242
1177
|
getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
|
|
1243
1178
|
);
|
|
@@ -1280,7 +1215,28 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1280
1215
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1281
1216
|
[]
|
|
1282
1217
|
);
|
|
1283
|
-
const [userActionState, setUserActionState] = react.useState(
|
|
1218
|
+
const [userActionState, setUserActionState] = react.useState(() => {
|
|
1219
|
+
if (!config.userId) return EMPTY_USER_ACTION_STATE2;
|
|
1220
|
+
return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
|
|
1221
|
+
});
|
|
1222
|
+
const storeAwareSetUserActionState = react.useCallback(
|
|
1223
|
+
(updater) => {
|
|
1224
|
+
const streamUserId = streamUserIdRef.current;
|
|
1225
|
+
const currentUserId = configRef.current.userId;
|
|
1226
|
+
const storeKey = streamUserId ?? currentUserId;
|
|
1227
|
+
if (storeKey && activeStreamStore.has(storeKey)) {
|
|
1228
|
+
activeStreamStore.applyUserActionState(
|
|
1229
|
+
storeKey,
|
|
1230
|
+
updater
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1233
|
+
if (!streamUserId || streamUserId === currentUserId) {
|
|
1234
|
+
setUserActionState(updater);
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1238
|
+
[]
|
|
1239
|
+
);
|
|
1284
1240
|
const wrappedCallbacks = react.useMemo(() => ({
|
|
1285
1241
|
...callbacksRef.current,
|
|
1286
1242
|
onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
|
|
@@ -1290,14 +1246,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1290
1246
|
onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
|
|
1291
1247
|
onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
|
|
1292
1248
|
onUserActionRequired: (request) => {
|
|
1293
|
-
|
|
1249
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1294
1250
|
...prev,
|
|
1295
1251
|
prompts: upsertPrompt(prev.prompts, request)
|
|
1296
1252
|
}));
|
|
1297
1253
|
callbacksRef.current.onUserActionRequired?.(request);
|
|
1298
1254
|
},
|
|
1299
1255
|
onUserNotification: (notification) => {
|
|
1300
|
-
|
|
1256
|
+
storeAwareSetUserActionState(
|
|
1301
1257
|
(prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
|
|
1302
1258
|
);
|
|
1303
1259
|
callbacksRef.current.onUserNotification?.(notification);
|
|
@@ -1312,45 +1268,21 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1312
1268
|
);
|
|
1313
1269
|
const sendMessage = react.useCallback(
|
|
1314
1270
|
async (userMessage, options) => {
|
|
1315
|
-
|
|
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
|
-
}
|
|
1271
|
+
if (!userMessage.trim()) return;
|
|
1333
1272
|
if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
|
|
1334
1273
|
sessionIdRef.current = generateId();
|
|
1335
1274
|
callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
|
|
1336
1275
|
}
|
|
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;
|
|
1343
1276
|
const userMessageId = `user-${Date.now()}`;
|
|
1344
1277
|
const userMsg = {
|
|
1345
1278
|
id: userMessageId,
|
|
1346
1279
|
sessionId: sessionIdRef.current,
|
|
1347
1280
|
role: "user",
|
|
1348
|
-
content:
|
|
1349
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1350
|
-
attachments: messageAttachments
|
|
1281
|
+
content: userMessage,
|
|
1282
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1351
1283
|
};
|
|
1352
1284
|
setMessages((prev) => [...prev, userMsg]);
|
|
1353
|
-
callbacksRef.current.onMessageSent?.(
|
|
1285
|
+
callbacksRef.current.onMessageSent?.(userMessage);
|
|
1354
1286
|
setIsWaitingForResponse(true);
|
|
1355
1287
|
callbacksRef.current.onStreamStart?.();
|
|
1356
1288
|
const streamingId = `assistant-${Date.now()}`;
|
|
@@ -1377,14 +1309,11 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1377
1309
|
activeStreamStore.start(userId, abortController, initialMessages);
|
|
1378
1310
|
}
|
|
1379
1311
|
const newSessionId = await startStream(
|
|
1380
|
-
|
|
1312
|
+
userMessage,
|
|
1381
1313
|
streamingId,
|
|
1382
1314
|
sessionIdRef.current,
|
|
1383
1315
|
abortController,
|
|
1384
|
-
|
|
1385
|
-
analysisMode: options?.analysisMode,
|
|
1386
|
-
attachments: streamAttachments
|
|
1387
|
-
}
|
|
1316
|
+
options
|
|
1388
1317
|
);
|
|
1389
1318
|
const finalStreamUserId = streamUserIdRef.current ?? userId;
|
|
1390
1319
|
if (finalStreamUserId) {
|
|
@@ -1414,7 +1343,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1414
1343
|
streamUserIdRef.current = void 0;
|
|
1415
1344
|
cancelStreamManager();
|
|
1416
1345
|
setIsWaitingForResponse(false);
|
|
1417
|
-
|
|
1346
|
+
storeAwareSetUserActionState((prev) => ({ ...prev, prompts: [] }));
|
|
1418
1347
|
setMessages(
|
|
1419
1348
|
(prev) => prev.map((msg) => {
|
|
1420
1349
|
if (msg.isStreaming) {
|
|
@@ -1429,7 +1358,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1429
1358
|
return msg;
|
|
1430
1359
|
})
|
|
1431
1360
|
);
|
|
1432
|
-
}, [cancelStreamManager]);
|
|
1361
|
+
}, [cancelStreamManager, storeAwareSetUserActionState]);
|
|
1433
1362
|
const resetSession = react.useCallback(() => {
|
|
1434
1363
|
const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
|
|
1435
1364
|
if (streamUserId) {
|
|
@@ -1443,7 +1372,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1443
1372
|
sessionIdRef.current = void 0;
|
|
1444
1373
|
abortControllerRef.current?.abort();
|
|
1445
1374
|
setIsWaitingForResponse(false);
|
|
1446
|
-
|
|
1375
|
+
storeAwareSetUserActionState(EMPTY_USER_ACTION_STATE2);
|
|
1447
1376
|
}, []);
|
|
1448
1377
|
const getSessionId = react.useCallback(() => {
|
|
1449
1378
|
return sessionIdRef.current;
|
|
@@ -1453,21 +1382,21 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1453
1382
|
}, [messages]);
|
|
1454
1383
|
const setPromptStatus = react.useCallback(
|
|
1455
1384
|
(userActionId, status) => {
|
|
1456
|
-
|
|
1385
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1457
1386
|
...prev,
|
|
1458
1387
|
prompts: prev.prompts.map(
|
|
1459
1388
|
(p) => p.userActionId === userActionId ? { ...p, status } : p
|
|
1460
1389
|
)
|
|
1461
1390
|
}));
|
|
1462
1391
|
},
|
|
1463
|
-
[]
|
|
1392
|
+
[storeAwareSetUserActionState]
|
|
1464
1393
|
);
|
|
1465
1394
|
const removePrompt = react.useCallback((userActionId) => {
|
|
1466
|
-
|
|
1395
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1467
1396
|
...prev,
|
|
1468
1397
|
prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
|
|
1469
1398
|
}));
|
|
1470
|
-
}, []);
|
|
1399
|
+
}, [storeAwareSetUserActionState]);
|
|
1471
1400
|
const submitUserAction2 = react.useCallback(
|
|
1472
1401
|
async (userActionId, content) => {
|
|
1473
1402
|
setPromptStatus(userActionId, "submitting");
|
|
@@ -1522,12 +1451,25 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1522
1451
|
},
|
|
1523
1452
|
[setPromptStatus]
|
|
1524
1453
|
);
|
|
1454
|
+
const expireUserAction2 = react.useCallback(
|
|
1455
|
+
async (userActionId) => {
|
|
1456
|
+
setPromptStatus(userActionId, "expired");
|
|
1457
|
+
try {
|
|
1458
|
+
await expireUserAction(configRef.current, userActionId);
|
|
1459
|
+
} catch (error) {
|
|
1460
|
+
if (error instanceof UserActionStaleError) return;
|
|
1461
|
+
callbacksRef.current.onError?.(error);
|
|
1462
|
+
throw error;
|
|
1463
|
+
}
|
|
1464
|
+
},
|
|
1465
|
+
[setPromptStatus]
|
|
1466
|
+
);
|
|
1525
1467
|
const dismissNotification = react.useCallback((id) => {
|
|
1526
|
-
|
|
1468
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1527
1469
|
...prev,
|
|
1528
1470
|
notifications: prev.notifications.filter((n) => n.id !== id)
|
|
1529
1471
|
}));
|
|
1530
|
-
}, []);
|
|
1472
|
+
}, [storeAwareSetUserActionState]);
|
|
1531
1473
|
react.useEffect(() => {
|
|
1532
1474
|
const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
|
|
1533
1475
|
subscriptionPrevUserIdRef.current = config.userId;
|
|
@@ -1536,14 +1478,17 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1536
1478
|
if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
|
|
1537
1479
|
streamUserIdRef.current = userId;
|
|
1538
1480
|
}
|
|
1539
|
-
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
|
|
1481
|
+
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting, actions) => {
|
|
1540
1482
|
setMessages(msgs);
|
|
1541
1483
|
setIsWaitingForResponse(isWaiting);
|
|
1484
|
+
setUserActionState(actions);
|
|
1542
1485
|
});
|
|
1543
1486
|
const active = activeStreamStore.get(userId);
|
|
1544
1487
|
if (active) {
|
|
1488
|
+
streamUserIdRef.current = userId;
|
|
1545
1489
|
setMessages(active.messages);
|
|
1546
1490
|
setIsWaitingForResponse(active.isWaiting);
|
|
1491
|
+
setUserActionState(active.userActionState);
|
|
1547
1492
|
}
|
|
1548
1493
|
return unsubscribe;
|
|
1549
1494
|
}, [config.userId]);
|
|
@@ -1562,18 +1507,6 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1562
1507
|
setMessages(config.initialMessages);
|
|
1563
1508
|
sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
|
|
1564
1509
|
}, [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]);
|
|
1577
1510
|
react.useEffect(() => {
|
|
1578
1511
|
const prevUserId = prevUserIdRef.current;
|
|
1579
1512
|
prevUserIdRef.current = config.userId;
|
|
@@ -1583,12 +1516,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1583
1516
|
setMessages([]);
|
|
1584
1517
|
sessionIdRef.current = void 0;
|
|
1585
1518
|
setIsWaitingForResponse(false);
|
|
1586
|
-
setUserActionState(
|
|
1519
|
+
setUserActionState(EMPTY_USER_ACTION_STATE2);
|
|
1587
1520
|
} else if (config.userId) {
|
|
1588
1521
|
const active = activeStreamStore.get(config.userId);
|
|
1589
1522
|
if (active) {
|
|
1523
|
+
streamUserIdRef.current = config.userId;
|
|
1590
1524
|
setMessages(active.messages);
|
|
1591
1525
|
setIsWaitingForResponse(active.isWaiting);
|
|
1526
|
+
setUserActionState(active.userActionState);
|
|
1592
1527
|
sessionIdRef.current = getSessionIdFromMessages(active.messages) ?? config.initialSessionId;
|
|
1593
1528
|
return;
|
|
1594
1529
|
}
|
|
@@ -1608,12 +1543,12 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1608
1543
|
getSessionId,
|
|
1609
1544
|
getMessages,
|
|
1610
1545
|
isWaitingForResponse,
|
|
1611
|
-
isUploadingAttachments,
|
|
1612
1546
|
sessionId: sessionIdRef.current,
|
|
1613
1547
|
userActionState,
|
|
1614
1548
|
submitUserAction: submitUserAction2,
|
|
1615
1549
|
cancelUserAction: cancelUserAction2,
|
|
1616
1550
|
resendUserAction: resendUserAction2,
|
|
1551
|
+
expireUserAction: expireUserAction2,
|
|
1617
1552
|
dismissNotification
|
|
1618
1553
|
};
|
|
1619
1554
|
}
|
|
@@ -4761,14 +4696,15 @@ function NotificationInline({ notification, onDismiss }) {
|
|
|
4761
4696
|
] });
|
|
4762
4697
|
}
|
|
4763
4698
|
function useExpiryCountdown(prompt) {
|
|
4764
|
-
const
|
|
4765
|
-
const [secondsLeft, setSecondsLeft] = react.useState(
|
|
4699
|
+
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;
|
|
4700
|
+
const [secondsLeft, setSecondsLeft] = react.useState(
|
|
4701
|
+
() => deadlineMs !== void 0 ? Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3)) : void 0
|
|
4702
|
+
);
|
|
4766
4703
|
react.useEffect(() => {
|
|
4767
|
-
if (
|
|
4704
|
+
if (deadlineMs === void 0) {
|
|
4768
4705
|
setSecondsLeft(void 0);
|
|
4769
4706
|
return;
|
|
4770
4707
|
}
|
|
4771
|
-
const deadlineMs = Date.now() + initial * 1e3;
|
|
4772
4708
|
let intervalId;
|
|
4773
4709
|
const remaining = () => Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3));
|
|
4774
4710
|
const sync = () => {
|
|
@@ -4790,8 +4726,8 @@ function useExpiryCountdown(prompt) {
|
|
|
4790
4726
|
window.removeEventListener("focus", sync);
|
|
4791
4727
|
window.removeEventListener("pageshow", sync);
|
|
4792
4728
|
};
|
|
4793
|
-
}, [prompt.userActionId, prompt.subAction,
|
|
4794
|
-
const expired =
|
|
4729
|
+
}, [prompt.userActionId, prompt.subAction, deadlineMs]);
|
|
4730
|
+
const expired = deadlineMs !== void 0 && (getUserActionSecondsLeft(prompt) ?? secondsLeft ?? 0) <= 0;
|
|
4795
4731
|
return [secondsLeft, expired];
|
|
4796
4732
|
}
|
|
4797
4733
|
function UserActionInline({
|
|
@@ -6346,7 +6282,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6346
6282
|
const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
|
|
6347
6283
|
const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
|
|
6348
6284
|
const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
|
|
6349
|
-
const
|
|
6285
|
+
const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
|
|
6350
6286
|
const dismissNotification = chat.dismissNotification ?? NOOP;
|
|
6351
6287
|
const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
|
|
6352
6288
|
const {
|
|
@@ -6747,7 +6683,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6747
6683
|
onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
|
|
6748
6684
|
onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
|
|
6749
6685
|
onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
|
|
6750
|
-
onExpireUserAction: isUserActionSupported ?
|
|
6686
|
+
onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
|
|
6751
6687
|
onDismissNotification: dismissNotification,
|
|
6752
6688
|
onSubmitFeedback: handleSubmitFeedback
|
|
6753
6689
|
}
|