@paymanai/payman-ask-sdk 4.0.24 → 4.0.26
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 +503 -439
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +247 -183
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +154 -192
- package/dist/index.native.js.map +1 -1
- package/dist/styles.css +30 -0
- package/dist/styles.css.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React = require('react');
|
|
4
4
|
var framerMotion = require('framer-motion');
|
|
5
5
|
var clsx = require('clsx');
|
|
6
6
|
var tailwindMerge = require('tailwind-merge');
|
|
@@ -33,6 +33,7 @@ function _interopNamespace(e) {
|
|
|
33
33
|
return Object.freeze(n);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
36
37
|
var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
|
|
37
38
|
var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
38
39
|
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
@@ -60,6 +61,7 @@ var chatStore = {
|
|
|
60
61
|
memoryStore.delete(key);
|
|
61
62
|
}
|
|
62
63
|
};
|
|
64
|
+
var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
|
|
63
65
|
var streams = /* @__PURE__ */ new Map();
|
|
64
66
|
var activeStreamStore = {
|
|
65
67
|
has(key) {
|
|
@@ -68,7 +70,11 @@ var activeStreamStore = {
|
|
|
68
70
|
get(key) {
|
|
69
71
|
const entry = streams.get(key);
|
|
70
72
|
if (!entry) return null;
|
|
71
|
-
return {
|
|
73
|
+
return {
|
|
74
|
+
messages: entry.messages,
|
|
75
|
+
isWaiting: entry.isWaiting,
|
|
76
|
+
userActionState: entry.userActionState
|
|
77
|
+
};
|
|
72
78
|
},
|
|
73
79
|
// Called before startStream — registers the controller and initial messages
|
|
74
80
|
start(key, abortController, initialMessages) {
|
|
@@ -76,6 +82,7 @@ var activeStreamStore = {
|
|
|
76
82
|
streams.set(key, {
|
|
77
83
|
messages: initialMessages,
|
|
78
84
|
isWaiting: true,
|
|
85
|
+
userActionState: EMPTY_USER_ACTION_STATE,
|
|
79
86
|
abortController,
|
|
80
87
|
listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
|
|
81
88
|
});
|
|
@@ -86,20 +93,27 @@ var activeStreamStore = {
|
|
|
86
93
|
if (!entry) return;
|
|
87
94
|
const next = typeof updater === "function" ? updater(entry.messages) : updater;
|
|
88
95
|
entry.messages = next;
|
|
89
|
-
entry.listeners.forEach((l) => l(next, entry.isWaiting));
|
|
96
|
+
entry.listeners.forEach((l) => l(next, entry.isWaiting, entry.userActionState));
|
|
90
97
|
},
|
|
91
98
|
setWaiting(key, waiting) {
|
|
92
99
|
const entry = streams.get(key);
|
|
93
100
|
if (!entry) return;
|
|
94
101
|
entry.isWaiting = waiting;
|
|
95
|
-
entry.listeners.forEach((l) => l(entry.messages, waiting));
|
|
102
|
+
entry.listeners.forEach((l) => l(entry.messages, waiting, entry.userActionState));
|
|
103
|
+
},
|
|
104
|
+
applyUserActionState(key, updater) {
|
|
105
|
+
const entry = streams.get(key);
|
|
106
|
+
if (!entry) return;
|
|
107
|
+
const next = typeof updater === "function" ? updater(entry.userActionState) : updater;
|
|
108
|
+
entry.userActionState = next;
|
|
109
|
+
entry.listeners.forEach((l) => l(entry.messages, entry.isWaiting, next));
|
|
96
110
|
},
|
|
97
111
|
// Called when stream completes — persists to chatStore and cleans up
|
|
98
112
|
complete(key) {
|
|
99
113
|
const entry = streams.get(key);
|
|
100
114
|
if (!entry) return;
|
|
101
115
|
entry.isWaiting = false;
|
|
102
|
-
entry.listeners.forEach((l) => l(entry.messages, false));
|
|
116
|
+
entry.listeners.forEach((l) => l(entry.messages, false, entry.userActionState));
|
|
103
117
|
const toSave = entry.messages.filter((m) => !m.isStreaming);
|
|
104
118
|
if (toSave.length > 0) chatStore.set(key, toSave);
|
|
105
119
|
streams.delete(key);
|
|
@@ -287,6 +301,8 @@ function getEventMessage(event) {
|
|
|
287
301
|
}
|
|
288
302
|
case "RUN_IN_PROGRESS":
|
|
289
303
|
return event.partialText || "Thinking...";
|
|
304
|
+
case "LLM_CALL_STARTED":
|
|
305
|
+
return event.description || "";
|
|
290
306
|
case "RUN_COMPLETED":
|
|
291
307
|
return "Agent run completed";
|
|
292
308
|
case "RUN_FAILED":
|
|
@@ -424,7 +440,8 @@ function createInitialV2State() {
|
|
|
424
440
|
finalData: void 0,
|
|
425
441
|
steps: [],
|
|
426
442
|
stepCounter: 0,
|
|
427
|
-
currentExecutingStepId: void 0
|
|
443
|
+
currentExecutingStepId: void 0,
|
|
444
|
+
trailingActivity: false
|
|
428
445
|
};
|
|
429
446
|
}
|
|
430
447
|
function upsertUserAction(state, req) {
|
|
@@ -446,6 +463,7 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
446
463
|
if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
|
|
447
464
|
if (event.executionId) state.executionId = event.executionId;
|
|
448
465
|
if (event.sessionId) state.sessionId = event.sessionId;
|
|
466
|
+
if (state.finalResponse) state.trailingActivity = true;
|
|
449
467
|
const description = typeof event.description === "string" ? event.description : "";
|
|
450
468
|
if (description) {
|
|
451
469
|
for (let i = state.steps.length - 1; i >= 0; i--) {
|
|
@@ -480,7 +498,12 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
480
498
|
state.lastEventType = eventType;
|
|
481
499
|
break;
|
|
482
500
|
}
|
|
501
|
+
case "LLM_CALL_STARTED":
|
|
502
|
+
if (state.finalResponse) state.trailingActivity = true;
|
|
503
|
+
state.lastEventType = eventType;
|
|
504
|
+
break;
|
|
483
505
|
case "INTENT_STARTED": {
|
|
506
|
+
if (state.finalResponse) state.trailingActivity = true;
|
|
484
507
|
const stepId = `step-${state.stepCounter++}`;
|
|
485
508
|
state.steps.push({
|
|
486
509
|
id: stepId,
|
|
@@ -496,6 +519,7 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
496
519
|
break;
|
|
497
520
|
}
|
|
498
521
|
case "INTENT_COMPLETED": {
|
|
522
|
+
if (state.finalResponse) state.trailingActivity = true;
|
|
499
523
|
const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
|
|
500
524
|
if (intentStep) {
|
|
501
525
|
intentStep.status = "completed";
|
|
@@ -527,6 +551,7 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
527
551
|
step2.status = "completed";
|
|
528
552
|
}
|
|
529
553
|
});
|
|
554
|
+
state.trailingActivity = false;
|
|
530
555
|
state.lastEventType = eventType;
|
|
531
556
|
break;
|
|
532
557
|
}
|
|
@@ -598,6 +623,7 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
598
623
|
case "WORKFLOW_ERROR":
|
|
599
624
|
state.hasError = true;
|
|
600
625
|
state.errorMessage = event.errorMessage || event.message || "Workflow error";
|
|
626
|
+
state.trailingActivity = false;
|
|
601
627
|
state.lastEventType = eventType;
|
|
602
628
|
break;
|
|
603
629
|
// ---- K2 pipeline stage lifecycle events ----
|
|
@@ -736,8 +762,7 @@ function buildRequestBody(config, userMessage, sessionId, options) {
|
|
|
736
762
|
sessionAttributes,
|
|
737
763
|
analysisMode: options?.analysisMode,
|
|
738
764
|
locale: resolveLocale(config.locale),
|
|
739
|
-
timezone: resolveTimezone(config.timezone)
|
|
740
|
-
...options?.attachments?.length ? { attachments: options.attachments } : {}
|
|
765
|
+
timezone: resolveTimezone(config.timezone)
|
|
741
766
|
};
|
|
742
767
|
}
|
|
743
768
|
function resolveLocale(configured) {
|
|
@@ -782,7 +807,6 @@ function buildResolveImagesUrl(config) {
|
|
|
782
807
|
const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
|
|
783
808
|
return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
|
|
784
809
|
}
|
|
785
|
-
var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
|
|
786
810
|
function buildRequestHeaders(config) {
|
|
787
811
|
const headers = {
|
|
788
812
|
...config.api.headers
|
|
@@ -790,9 +814,6 @@ function buildRequestHeaders(config) {
|
|
|
790
814
|
if (config.api.authToken) {
|
|
791
815
|
headers.Authorization = `Bearer ${config.api.authToken}`;
|
|
792
816
|
}
|
|
793
|
-
if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
|
|
794
|
-
headers[NGROK_SKIP_BROWSER_WARNING] = "true";
|
|
795
|
-
}
|
|
796
817
|
return headers;
|
|
797
818
|
}
|
|
798
819
|
var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
|
|
@@ -845,12 +866,12 @@ async function resolveRagImageUrls(config, content, signal) {
|
|
|
845
866
|
}
|
|
846
867
|
var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
|
|
847
868
|
function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForResponse) {
|
|
848
|
-
const abortControllerRef =
|
|
849
|
-
const configRef =
|
|
869
|
+
const abortControllerRef = React.useRef(null);
|
|
870
|
+
const configRef = React.useRef(config);
|
|
850
871
|
configRef.current = config;
|
|
851
|
-
const callbacksRef =
|
|
872
|
+
const callbacksRef = React.useRef(callbacks);
|
|
852
873
|
callbacksRef.current = callbacks;
|
|
853
|
-
const startStream =
|
|
874
|
+
const startStream = React.useCallback(
|
|
854
875
|
async (userMessage, streamingId, sessionId, externalAbortController, options) => {
|
|
855
876
|
abortControllerRef.current?.abort();
|
|
856
877
|
const abortController = externalAbortController ?? new AbortController();
|
|
@@ -891,7 +912,9 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
891
912
|
const latestUsefulStep = [...state.steps].reverse().find(
|
|
892
913
|
(s) => s.message && !isBlandStatus(s.message)
|
|
893
914
|
);
|
|
894
|
-
const
|
|
915
|
+
const eventTypeUpper = typeof event.eventType === "string" ? event.eventType.toUpperCase() : "";
|
|
916
|
+
const liveEventLabel = eventTypeUpper === "KEEP_ALIVE" || eventTypeUpper === "LLM_CALL_STARTED" ? useful(getEventMessage(event)) : void 0;
|
|
917
|
+
const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? liveEventLabel ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
|
|
895
918
|
// nothing useful seen yet). Render the bland label so the
|
|
896
919
|
// bubble isn't blank.
|
|
897
920
|
activeStep?.message ?? lastInProgressStep?.message ?? getEventMessage(event);
|
|
@@ -916,6 +939,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
916
939
|
streamingContent: state.finalResponse,
|
|
917
940
|
content: "",
|
|
918
941
|
currentMessage,
|
|
942
|
+
hasTrailingActivity: state.trailingActivity,
|
|
919
943
|
streamProgress: "processing",
|
|
920
944
|
isError: false,
|
|
921
945
|
steps: [...state.steps],
|
|
@@ -1049,7 +1073,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1049
1073
|
},
|
|
1050
1074
|
[setMessages, setIsWaitingForResponse]
|
|
1051
1075
|
);
|
|
1052
|
-
const cancelStream =
|
|
1076
|
+
const cancelStream = React.useCallback(() => {
|
|
1053
1077
|
abortControllerRef.current?.abort();
|
|
1054
1078
|
}, []);
|
|
1055
1079
|
return {
|
|
@@ -1073,81 +1097,6 @@ function createCancelledMessageUpdate(steps, currentMessage) {
|
|
|
1073
1097
|
currentMessage: currentMessage || "Thinking..."
|
|
1074
1098
|
};
|
|
1075
1099
|
}
|
|
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
1100
|
var UserActionStaleError = class extends Error {
|
|
1152
1101
|
constructor(userActionId, message = "User action is no longer actionable") {
|
|
1153
1102
|
super(message);
|
|
@@ -1184,12 +1133,34 @@ async function cancelUserAction(config, userActionId) {
|
|
|
1184
1133
|
async function resendUserAction(config, userActionId) {
|
|
1185
1134
|
return sendUserActionRequest(config, userActionId, "resend");
|
|
1186
1135
|
}
|
|
1187
|
-
|
|
1136
|
+
async function expireUserAction(config, userActionId) {
|
|
1137
|
+
return sendUserActionRequest(config, userActionId, "expired");
|
|
1138
|
+
}
|
|
1139
|
+
function resolveUserActionExpiresAt(req, existing) {
|
|
1140
|
+
if (typeof req.expirySeconds !== "number" || req.expirySeconds <= 0) {
|
|
1141
|
+
return void 0;
|
|
1142
|
+
}
|
|
1143
|
+
if (existing?.expiresAt && existing.userActionId === req.userActionId) {
|
|
1144
|
+
return existing.expiresAt;
|
|
1145
|
+
}
|
|
1146
|
+
return Date.now() + Math.floor(req.expirySeconds) * 1e3;
|
|
1147
|
+
}
|
|
1148
|
+
function getUserActionSecondsLeft(prompt) {
|
|
1149
|
+
if (typeof prompt.expiresAt === "number" && prompt.expiresAt > 0) {
|
|
1150
|
+
return Math.max(0, Math.ceil((prompt.expiresAt - Date.now()) / 1e3));
|
|
1151
|
+
}
|
|
1152
|
+
if (typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0) {
|
|
1153
|
+
return Math.floor(prompt.expirySeconds);
|
|
1154
|
+
}
|
|
1155
|
+
return void 0;
|
|
1156
|
+
}
|
|
1157
|
+
var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
|
|
1188
1158
|
function upsertPrompt(prompts, req) {
|
|
1189
|
-
const active = { ...req, status: "pending" };
|
|
1190
1159
|
const idx = prompts.findIndex(
|
|
1191
1160
|
(p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
|
|
1192
1161
|
);
|
|
1162
|
+
const expiresAt = resolveUserActionExpiresAt(req, idx >= 0 ? prompts[idx] : void 0);
|
|
1163
|
+
const active = { ...req, status: "pending", expiresAt };
|
|
1193
1164
|
if (idx >= 0) {
|
|
1194
1165
|
const next = prompts.slice();
|
|
1195
1166
|
next[idx] = active;
|
|
@@ -1212,45 +1183,25 @@ function getStoredOrInitialMessages(config) {
|
|
|
1212
1183
|
function getSessionIdFromMessages(messages) {
|
|
1213
1184
|
return messages.find((message) => message.sessionId)?.sessionId;
|
|
1214
1185
|
}
|
|
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
1186
|
function useChatV2(config, callbacks = {}) {
|
|
1235
|
-
const [messages, setMessages] =
|
|
1236
|
-
const [isWaitingForResponse, setIsWaitingForResponse] =
|
|
1187
|
+
const [messages, setMessages] = React.useState(() => getStoredOrInitialMessages(config));
|
|
1188
|
+
const [isWaitingForResponse, setIsWaitingForResponse] = React.useState(() => {
|
|
1237
1189
|
if (!config.userId) return false;
|
|
1238
1190
|
return activeStreamStore.get(config.userId)?.isWaiting ?? false;
|
|
1239
1191
|
});
|
|
1240
|
-
const
|
|
1241
|
-
const sessionIdRef = react.useRef(
|
|
1192
|
+
const sessionIdRef = React.useRef(
|
|
1242
1193
|
getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
|
|
1243
1194
|
);
|
|
1244
|
-
const prevUserIdRef =
|
|
1245
|
-
const streamUserIdRef =
|
|
1246
|
-
const subscriptionPrevUserIdRef =
|
|
1247
|
-
const callbacksRef =
|
|
1195
|
+
const prevUserIdRef = React.useRef(config.userId);
|
|
1196
|
+
const streamUserIdRef = React.useRef(void 0);
|
|
1197
|
+
const subscriptionPrevUserIdRef = React.useRef(config.userId);
|
|
1198
|
+
const callbacksRef = React.useRef(callbacks);
|
|
1248
1199
|
callbacksRef.current = callbacks;
|
|
1249
|
-
const configRef =
|
|
1200
|
+
const configRef = React.useRef(config);
|
|
1250
1201
|
configRef.current = config;
|
|
1251
|
-
const messagesRef =
|
|
1202
|
+
const messagesRef = React.useRef(messages);
|
|
1252
1203
|
messagesRef.current = messages;
|
|
1253
|
-
const storeAwareSetMessages =
|
|
1204
|
+
const storeAwareSetMessages = React.useCallback(
|
|
1254
1205
|
(updater) => {
|
|
1255
1206
|
const streamUserId = streamUserIdRef.current;
|
|
1256
1207
|
const currentUserId = configRef.current.userId;
|
|
@@ -1265,7 +1216,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1265
1216
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1266
1217
|
[]
|
|
1267
1218
|
);
|
|
1268
|
-
const storeAwareSetIsWaiting =
|
|
1219
|
+
const storeAwareSetIsWaiting = React.useCallback(
|
|
1269
1220
|
(waiting) => {
|
|
1270
1221
|
const streamUserId = streamUserIdRef.current;
|
|
1271
1222
|
const currentUserId = configRef.current.userId;
|
|
@@ -1280,8 +1231,29 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1280
1231
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1281
1232
|
[]
|
|
1282
1233
|
);
|
|
1283
|
-
const [userActionState, setUserActionState] =
|
|
1284
|
-
|
|
1234
|
+
const [userActionState, setUserActionState] = React.useState(() => {
|
|
1235
|
+
if (!config.userId) return EMPTY_USER_ACTION_STATE2;
|
|
1236
|
+
return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
|
|
1237
|
+
});
|
|
1238
|
+
const storeAwareSetUserActionState = React.useCallback(
|
|
1239
|
+
(updater) => {
|
|
1240
|
+
const streamUserId = streamUserIdRef.current;
|
|
1241
|
+
const currentUserId = configRef.current.userId;
|
|
1242
|
+
const storeKey = streamUserId ?? currentUserId;
|
|
1243
|
+
if (storeKey && activeStreamStore.has(storeKey)) {
|
|
1244
|
+
activeStreamStore.applyUserActionState(
|
|
1245
|
+
storeKey,
|
|
1246
|
+
updater
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
if (!streamUserId || streamUserId === currentUserId) {
|
|
1250
|
+
setUserActionState(updater);
|
|
1251
|
+
}
|
|
1252
|
+
},
|
|
1253
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1254
|
+
[]
|
|
1255
|
+
);
|
|
1256
|
+
const wrappedCallbacks = React.useMemo(() => ({
|
|
1285
1257
|
...callbacksRef.current,
|
|
1286
1258
|
onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
|
|
1287
1259
|
onStreamStart: () => callbacksRef.current.onStreamStart?.(),
|
|
@@ -1290,14 +1262,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1290
1262
|
onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
|
|
1291
1263
|
onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
|
|
1292
1264
|
onUserActionRequired: (request) => {
|
|
1293
|
-
|
|
1265
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1294
1266
|
...prev,
|
|
1295
1267
|
prompts: upsertPrompt(prev.prompts, request)
|
|
1296
1268
|
}));
|
|
1297
1269
|
callbacksRef.current.onUserActionRequired?.(request);
|
|
1298
1270
|
},
|
|
1299
1271
|
onUserNotification: (notification) => {
|
|
1300
|
-
|
|
1272
|
+
storeAwareSetUserActionState(
|
|
1301
1273
|
(prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
|
|
1302
1274
|
);
|
|
1303
1275
|
callbacksRef.current.onUserNotification?.(notification);
|
|
@@ -1310,47 +1282,23 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1310
1282
|
storeAwareSetMessages,
|
|
1311
1283
|
storeAwareSetIsWaiting
|
|
1312
1284
|
);
|
|
1313
|
-
const sendMessage =
|
|
1285
|
+
const sendMessage = React.useCallback(
|
|
1314
1286
|
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
|
-
}
|
|
1287
|
+
if (!userMessage.trim()) return;
|
|
1333
1288
|
if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
|
|
1334
1289
|
sessionIdRef.current = generateId();
|
|
1335
1290
|
callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
|
|
1336
1291
|
}
|
|
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
1292
|
const userMessageId = `user-${Date.now()}`;
|
|
1344
1293
|
const userMsg = {
|
|
1345
1294
|
id: userMessageId,
|
|
1346
1295
|
sessionId: sessionIdRef.current,
|
|
1347
1296
|
role: "user",
|
|
1348
|
-
content:
|
|
1349
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1350
|
-
attachments: messageAttachments
|
|
1297
|
+
content: userMessage,
|
|
1298
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1351
1299
|
};
|
|
1352
1300
|
setMessages((prev) => [...prev, userMsg]);
|
|
1353
|
-
callbacksRef.current.onMessageSent?.(
|
|
1301
|
+
callbacksRef.current.onMessageSent?.(userMessage);
|
|
1354
1302
|
setIsWaitingForResponse(true);
|
|
1355
1303
|
callbacksRef.current.onStreamStart?.();
|
|
1356
1304
|
const streamingId = `assistant-${Date.now()}`;
|
|
@@ -1377,14 +1325,11 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1377
1325
|
activeStreamStore.start(userId, abortController, initialMessages);
|
|
1378
1326
|
}
|
|
1379
1327
|
const newSessionId = await startStream(
|
|
1380
|
-
|
|
1328
|
+
userMessage,
|
|
1381
1329
|
streamingId,
|
|
1382
1330
|
sessionIdRef.current,
|
|
1383
1331
|
abortController,
|
|
1384
|
-
|
|
1385
|
-
analysisMode: options?.analysisMode,
|
|
1386
|
-
attachments: streamAttachments
|
|
1387
|
-
}
|
|
1332
|
+
options
|
|
1388
1333
|
);
|
|
1389
1334
|
const finalStreamUserId = streamUserIdRef.current ?? userId;
|
|
1390
1335
|
if (finalStreamUserId) {
|
|
@@ -1397,16 +1342,16 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1397
1342
|
},
|
|
1398
1343
|
[startStream]
|
|
1399
1344
|
);
|
|
1400
|
-
const clearMessages =
|
|
1345
|
+
const clearMessages = React.useCallback(() => {
|
|
1401
1346
|
if (configRef.current.userId) {
|
|
1402
1347
|
chatStore.delete(configRef.current.userId);
|
|
1403
1348
|
}
|
|
1404
1349
|
setMessages([]);
|
|
1405
1350
|
}, []);
|
|
1406
|
-
const prependMessages =
|
|
1351
|
+
const prependMessages = React.useCallback((msgs) => {
|
|
1407
1352
|
setMessages((prev) => [...msgs, ...prev]);
|
|
1408
1353
|
}, []);
|
|
1409
|
-
const cancelStream =
|
|
1354
|
+
const cancelStream = React.useCallback(() => {
|
|
1410
1355
|
const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
|
|
1411
1356
|
if (streamUserId) {
|
|
1412
1357
|
activeStreamStore.abort(streamUserId);
|
|
@@ -1414,7 +1359,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1414
1359
|
streamUserIdRef.current = void 0;
|
|
1415
1360
|
cancelStreamManager();
|
|
1416
1361
|
setIsWaitingForResponse(false);
|
|
1417
|
-
|
|
1362
|
+
storeAwareSetUserActionState((prev) => ({ ...prev, prompts: [] }));
|
|
1418
1363
|
setMessages(
|
|
1419
1364
|
(prev) => prev.map((msg) => {
|
|
1420
1365
|
if (msg.isStreaming) {
|
|
@@ -1429,8 +1374,8 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1429
1374
|
return msg;
|
|
1430
1375
|
})
|
|
1431
1376
|
);
|
|
1432
|
-
}, [cancelStreamManager]);
|
|
1433
|
-
const resetSession =
|
|
1377
|
+
}, [cancelStreamManager, storeAwareSetUserActionState]);
|
|
1378
|
+
const resetSession = React.useCallback(() => {
|
|
1434
1379
|
const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
|
|
1435
1380
|
if (streamUserId) {
|
|
1436
1381
|
activeStreamStore.abort(streamUserId);
|
|
@@ -1443,32 +1388,32 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1443
1388
|
sessionIdRef.current = void 0;
|
|
1444
1389
|
abortControllerRef.current?.abort();
|
|
1445
1390
|
setIsWaitingForResponse(false);
|
|
1446
|
-
|
|
1391
|
+
storeAwareSetUserActionState(EMPTY_USER_ACTION_STATE2);
|
|
1447
1392
|
}, []);
|
|
1448
|
-
const getSessionId =
|
|
1393
|
+
const getSessionId = React.useCallback(() => {
|
|
1449
1394
|
return sessionIdRef.current;
|
|
1450
1395
|
}, []);
|
|
1451
|
-
const getMessages =
|
|
1396
|
+
const getMessages = React.useCallback(() => {
|
|
1452
1397
|
return messages;
|
|
1453
1398
|
}, [messages]);
|
|
1454
|
-
const setPromptStatus =
|
|
1399
|
+
const setPromptStatus = React.useCallback(
|
|
1455
1400
|
(userActionId, status) => {
|
|
1456
|
-
|
|
1401
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1457
1402
|
...prev,
|
|
1458
1403
|
prompts: prev.prompts.map(
|
|
1459
1404
|
(p) => p.userActionId === userActionId ? { ...p, status } : p
|
|
1460
1405
|
)
|
|
1461
1406
|
}));
|
|
1462
1407
|
},
|
|
1463
|
-
[]
|
|
1408
|
+
[storeAwareSetUserActionState]
|
|
1464
1409
|
);
|
|
1465
|
-
const removePrompt =
|
|
1466
|
-
|
|
1410
|
+
const removePrompt = React.useCallback((userActionId) => {
|
|
1411
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1467
1412
|
...prev,
|
|
1468
1413
|
prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
|
|
1469
1414
|
}));
|
|
1470
|
-
}, []);
|
|
1471
|
-
const submitUserAction2 =
|
|
1415
|
+
}, [storeAwareSetUserActionState]);
|
|
1416
|
+
const submitUserAction2 = React.useCallback(
|
|
1472
1417
|
async (userActionId, content) => {
|
|
1473
1418
|
setPromptStatus(userActionId, "submitting");
|
|
1474
1419
|
try {
|
|
@@ -1486,7 +1431,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1486
1431
|
},
|
|
1487
1432
|
[removePrompt, setPromptStatus]
|
|
1488
1433
|
);
|
|
1489
|
-
const cancelUserAction2 =
|
|
1434
|
+
const cancelUserAction2 = React.useCallback(
|
|
1490
1435
|
async (userActionId) => {
|
|
1491
1436
|
setPromptStatus(userActionId, "submitting");
|
|
1492
1437
|
try {
|
|
@@ -1504,7 +1449,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1504
1449
|
},
|
|
1505
1450
|
[removePrompt, setPromptStatus]
|
|
1506
1451
|
);
|
|
1507
|
-
const resendUserAction2 =
|
|
1452
|
+
const resendUserAction2 = React.useCallback(
|
|
1508
1453
|
async (userActionId) => {
|
|
1509
1454
|
setPromptStatus(userActionId, "submitting");
|
|
1510
1455
|
try {
|
|
@@ -1522,13 +1467,26 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1522
1467
|
},
|
|
1523
1468
|
[setPromptStatus]
|
|
1524
1469
|
);
|
|
1525
|
-
const
|
|
1526
|
-
|
|
1470
|
+
const expireUserAction2 = React.useCallback(
|
|
1471
|
+
async (userActionId) => {
|
|
1472
|
+
setPromptStatus(userActionId, "expired");
|
|
1473
|
+
try {
|
|
1474
|
+
await expireUserAction(configRef.current, userActionId);
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
if (error instanceof UserActionStaleError) return;
|
|
1477
|
+
callbacksRef.current.onError?.(error);
|
|
1478
|
+
throw error;
|
|
1479
|
+
}
|
|
1480
|
+
},
|
|
1481
|
+
[setPromptStatus]
|
|
1482
|
+
);
|
|
1483
|
+
const dismissNotification = React.useCallback((id) => {
|
|
1484
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1527
1485
|
...prev,
|
|
1528
1486
|
notifications: prev.notifications.filter((n) => n.id !== id)
|
|
1529
1487
|
}));
|
|
1530
|
-
}, []);
|
|
1531
|
-
|
|
1488
|
+
}, [storeAwareSetUserActionState]);
|
|
1489
|
+
React.useEffect(() => {
|
|
1532
1490
|
const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
|
|
1533
1491
|
subscriptionPrevUserIdRef.current = config.userId;
|
|
1534
1492
|
const { userId } = config;
|
|
@@ -1536,18 +1494,21 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1536
1494
|
if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
|
|
1537
1495
|
streamUserIdRef.current = userId;
|
|
1538
1496
|
}
|
|
1539
|
-
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
|
|
1497
|
+
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting, actions) => {
|
|
1540
1498
|
setMessages(msgs);
|
|
1541
1499
|
setIsWaitingForResponse(isWaiting);
|
|
1500
|
+
setUserActionState(actions);
|
|
1542
1501
|
});
|
|
1543
1502
|
const active = activeStreamStore.get(userId);
|
|
1544
1503
|
if (active) {
|
|
1504
|
+
streamUserIdRef.current = userId;
|
|
1545
1505
|
setMessages(active.messages);
|
|
1546
1506
|
setIsWaitingForResponse(active.isWaiting);
|
|
1507
|
+
setUserActionState(active.userActionState);
|
|
1547
1508
|
}
|
|
1548
1509
|
return unsubscribe;
|
|
1549
1510
|
}, [config.userId]);
|
|
1550
|
-
|
|
1511
|
+
React.useEffect(() => {
|
|
1551
1512
|
if (!config.userId) return;
|
|
1552
1513
|
if (prevUserIdRef.current !== config.userId) return;
|
|
1553
1514
|
const toSave = messages.filter((m) => !m.isStreaming);
|
|
@@ -1555,26 +1516,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1555
1516
|
chatStore.set(config.userId, toSave);
|
|
1556
1517
|
}
|
|
1557
1518
|
}, [messages, config.userId]);
|
|
1558
|
-
|
|
1519
|
+
React.useEffect(() => {
|
|
1559
1520
|
if (!config.userId || activeStreamStore.has(config.userId)) return;
|
|
1560
1521
|
if (!config.initialMessages?.length || messagesRef.current.length > 0) return;
|
|
1561
1522
|
chatStore.set(config.userId, config.initialMessages);
|
|
1562
1523
|
setMessages(config.initialMessages);
|
|
1563
1524
|
sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
|
|
1564
1525
|
}, [config.initialMessages, config.initialSessionId, config.userId]);
|
|
1565
|
-
|
|
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
|
-
react.useEffect(() => {
|
|
1526
|
+
React.useEffect(() => {
|
|
1578
1527
|
const prevUserId = prevUserIdRef.current;
|
|
1579
1528
|
prevUserIdRef.current = config.userId;
|
|
1580
1529
|
if (prevUserId === config.userId) return;
|
|
@@ -1583,12 +1532,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1583
1532
|
setMessages([]);
|
|
1584
1533
|
sessionIdRef.current = void 0;
|
|
1585
1534
|
setIsWaitingForResponse(false);
|
|
1586
|
-
setUserActionState(
|
|
1535
|
+
setUserActionState(EMPTY_USER_ACTION_STATE2);
|
|
1587
1536
|
} else if (config.userId) {
|
|
1588
1537
|
const active = activeStreamStore.get(config.userId);
|
|
1589
1538
|
if (active) {
|
|
1539
|
+
streamUserIdRef.current = config.userId;
|
|
1590
1540
|
setMessages(active.messages);
|
|
1591
1541
|
setIsWaitingForResponse(active.isWaiting);
|
|
1542
|
+
setUserActionState(active.userActionState);
|
|
1592
1543
|
sessionIdRef.current = getSessionIdFromMessages(active.messages) ?? config.initialSessionId;
|
|
1593
1544
|
return;
|
|
1594
1545
|
}
|
|
@@ -1608,12 +1559,12 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1608
1559
|
getSessionId,
|
|
1609
1560
|
getMessages,
|
|
1610
1561
|
isWaitingForResponse,
|
|
1611
|
-
isUploadingAttachments,
|
|
1612
1562
|
sessionId: sessionIdRef.current,
|
|
1613
1563
|
userActionState,
|
|
1614
1564
|
submitUserAction: submitUserAction2,
|
|
1615
1565
|
cancelUserAction: cancelUserAction2,
|
|
1616
1566
|
resendUserAction: resendUserAction2,
|
|
1567
|
+
expireUserAction: expireUserAction2,
|
|
1617
1568
|
dismissNotification
|
|
1618
1569
|
};
|
|
1619
1570
|
}
|
|
@@ -1622,12 +1573,12 @@ function getSpeechRecognition() {
|
|
|
1622
1573
|
return window.SpeechRecognition || window.webkitSpeechRecognition || null;
|
|
1623
1574
|
}
|
|
1624
1575
|
function useVoice(config = {}, callbacks = {}) {
|
|
1625
|
-
const [voiceState, setVoiceState] =
|
|
1626
|
-
const [transcribedText, setTranscribedText] =
|
|
1627
|
-
const [isAvailable, setIsAvailable] =
|
|
1628
|
-
const [isRecording, setIsRecording] =
|
|
1629
|
-
const recognitionRef =
|
|
1630
|
-
const autoStopTimerRef =
|
|
1576
|
+
const [voiceState, setVoiceState] = React.useState("idle");
|
|
1577
|
+
const [transcribedText, setTranscribedText] = React.useState("");
|
|
1578
|
+
const [isAvailable, setIsAvailable] = React.useState(false);
|
|
1579
|
+
const [isRecording, setIsRecording] = React.useState(false);
|
|
1580
|
+
const recognitionRef = React.useRef(null);
|
|
1581
|
+
const autoStopTimerRef = React.useRef(null);
|
|
1631
1582
|
const {
|
|
1632
1583
|
lang = "en-US",
|
|
1633
1584
|
interimResults = true,
|
|
@@ -1636,14 +1587,14 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1636
1587
|
autoStopAfterSilence
|
|
1637
1588
|
} = config;
|
|
1638
1589
|
const { onStart, onEnd, onResult, onError, onStateChange } = callbacks;
|
|
1639
|
-
|
|
1590
|
+
React.useEffect(() => {
|
|
1640
1591
|
const SpeechRecognitionAPI = getSpeechRecognition();
|
|
1641
1592
|
setIsAvailable(SpeechRecognitionAPI !== null);
|
|
1642
1593
|
}, []);
|
|
1643
|
-
|
|
1594
|
+
React.useEffect(() => {
|
|
1644
1595
|
onStateChange?.(voiceState);
|
|
1645
1596
|
}, [voiceState, onStateChange]);
|
|
1646
|
-
const requestPermissions =
|
|
1597
|
+
const requestPermissions = React.useCallback(async () => {
|
|
1647
1598
|
try {
|
|
1648
1599
|
const result = await navigator.mediaDevices.getUserMedia({
|
|
1649
1600
|
audio: true
|
|
@@ -1660,7 +1611,7 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1660
1611
|
};
|
|
1661
1612
|
}
|
|
1662
1613
|
}, []);
|
|
1663
|
-
const getPermissions =
|
|
1614
|
+
const getPermissions = React.useCallback(async () => {
|
|
1664
1615
|
if (typeof navigator === "undefined" || !navigator.permissions) {
|
|
1665
1616
|
return {
|
|
1666
1617
|
granted: false,
|
|
@@ -1682,13 +1633,13 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1682
1633
|
};
|
|
1683
1634
|
}
|
|
1684
1635
|
}, []);
|
|
1685
|
-
const clearAutoStopTimer =
|
|
1636
|
+
const clearAutoStopTimer = React.useCallback(() => {
|
|
1686
1637
|
if (autoStopTimerRef.current) {
|
|
1687
1638
|
clearTimeout(autoStopTimerRef.current);
|
|
1688
1639
|
autoStopTimerRef.current = null;
|
|
1689
1640
|
}
|
|
1690
1641
|
}, []);
|
|
1691
|
-
const resetAutoStopTimer =
|
|
1642
|
+
const resetAutoStopTimer = React.useCallback(() => {
|
|
1692
1643
|
clearAutoStopTimer();
|
|
1693
1644
|
if (autoStopAfterSilence && autoStopAfterSilence > 0) {
|
|
1694
1645
|
autoStopTimerRef.current = setTimeout(() => {
|
|
@@ -1698,7 +1649,7 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1698
1649
|
}, autoStopAfterSilence);
|
|
1699
1650
|
}
|
|
1700
1651
|
}, [autoStopAfterSilence, clearAutoStopTimer, isRecording]);
|
|
1701
|
-
const stopRecording =
|
|
1652
|
+
const stopRecording = React.useCallback(() => {
|
|
1702
1653
|
if (recognitionRef.current) {
|
|
1703
1654
|
try {
|
|
1704
1655
|
recognitionRef.current.stop();
|
|
@@ -1710,7 +1661,7 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1710
1661
|
setIsRecording(false);
|
|
1711
1662
|
setVoiceState("idle");
|
|
1712
1663
|
}, [clearAutoStopTimer]);
|
|
1713
|
-
const startRecording =
|
|
1664
|
+
const startRecording = React.useCallback(async () => {
|
|
1714
1665
|
const SpeechRecognitionAPI = getSpeechRecognition();
|
|
1715
1666
|
if (!SpeechRecognitionAPI) {
|
|
1716
1667
|
onError?.("Speech recognition not supported in this browser");
|
|
@@ -1800,15 +1751,15 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1800
1751
|
resetAutoStopTimer,
|
|
1801
1752
|
clearAutoStopTimer
|
|
1802
1753
|
]);
|
|
1803
|
-
const clearTranscript =
|
|
1754
|
+
const clearTranscript = React.useCallback(() => {
|
|
1804
1755
|
setTranscribedText("");
|
|
1805
1756
|
}, []);
|
|
1806
|
-
const reset =
|
|
1757
|
+
const reset = React.useCallback(() => {
|
|
1807
1758
|
stopRecording();
|
|
1808
1759
|
setTranscribedText("");
|
|
1809
1760
|
setVoiceState("idle");
|
|
1810
1761
|
}, [stopRecording]);
|
|
1811
|
-
|
|
1762
|
+
React.useEffect(() => {
|
|
1812
1763
|
return () => {
|
|
1813
1764
|
if (recognitionRef.current) {
|
|
1814
1765
|
try {
|
|
@@ -1948,9 +1899,9 @@ function buildContent(schema, values) {
|
|
|
1948
1899
|
}
|
|
1949
1900
|
return content;
|
|
1950
1901
|
}
|
|
1951
|
-
var PaymanChatContext =
|
|
1902
|
+
var PaymanChatContext = React.createContext(void 0);
|
|
1952
1903
|
function usePaymanChat() {
|
|
1953
|
-
const context =
|
|
1904
|
+
const context = React.useContext(PaymanChatContext);
|
|
1954
1905
|
if (!context) {
|
|
1955
1906
|
throw new Error("usePaymanChat must be used within a PaymanChat component");
|
|
1956
1907
|
}
|
|
@@ -2147,6 +2098,9 @@ function getConflictErrorMessage(errorDetails) {
|
|
|
2147
2098
|
}
|
|
2148
2099
|
function initSentryIfNeeded(dsn) {
|
|
2149
2100
|
if (!dsn) return;
|
|
2101
|
+
if (typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")) {
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2150
2104
|
if (Sentry__namespace.getClient()) return;
|
|
2151
2105
|
Sentry__namespace.init({
|
|
2152
2106
|
dsn,
|
|
@@ -2208,8 +2162,8 @@ function ImageLightbox({
|
|
|
2208
2162
|
alt = "",
|
|
2209
2163
|
onClose
|
|
2210
2164
|
}) {
|
|
2211
|
-
const [isMounted, setIsMounted] =
|
|
2212
|
-
const [isImageLoaded, setIsImageLoaded] =
|
|
2165
|
+
const [isMounted, setIsMounted] = React.useState(false);
|
|
2166
|
+
const [isImageLoaded, setIsImageLoaded] = React.useState(false);
|
|
2213
2167
|
const overlayStyle = {
|
|
2214
2168
|
position: "fixed",
|
|
2215
2169
|
inset: 0,
|
|
@@ -2230,14 +2184,14 @@ function ImageLightbox({
|
|
|
2230
2184
|
justifyContent: "center",
|
|
2231
2185
|
overflow: "hidden"
|
|
2232
2186
|
};
|
|
2233
|
-
|
|
2187
|
+
React.useEffect(() => {
|
|
2234
2188
|
setIsMounted(true);
|
|
2235
2189
|
return () => setIsMounted(false);
|
|
2236
2190
|
}, []);
|
|
2237
|
-
|
|
2191
|
+
React.useEffect(() => {
|
|
2238
2192
|
setIsImageLoaded(false);
|
|
2239
2193
|
}, [src]);
|
|
2240
|
-
|
|
2194
|
+
React.useEffect(() => {
|
|
2241
2195
|
if (typeof document === "undefined") return;
|
|
2242
2196
|
const previousOverflow = document.body.style.overflow;
|
|
2243
2197
|
document.body.style.overflow = "hidden";
|
|
@@ -2245,7 +2199,7 @@ function ImageLightbox({
|
|
|
2245
2199
|
document.body.style.overflow = previousOverflow;
|
|
2246
2200
|
};
|
|
2247
2201
|
}, []);
|
|
2248
|
-
|
|
2202
|
+
React.useEffect(() => {
|
|
2249
2203
|
if (typeof document === "undefined") return;
|
|
2250
2204
|
const handleKeyDown = (event) => {
|
|
2251
2205
|
if (event.key === "Escape") {
|
|
@@ -2341,15 +2295,15 @@ function MarkdownImage({
|
|
|
2341
2295
|
maxHeight: "18rem",
|
|
2342
2296
|
objectFit: "contain"
|
|
2343
2297
|
};
|
|
2344
|
-
const [isLoaded, setIsLoaded] =
|
|
2345
|
-
const [hasError, setHasError] =
|
|
2346
|
-
const [isLightboxOpen, setIsLightboxOpen] =
|
|
2347
|
-
const isUnresolvedRagImage =
|
|
2298
|
+
const [isLoaded, setIsLoaded] = React.useState(false);
|
|
2299
|
+
const [hasError, setHasError] = React.useState(false);
|
|
2300
|
+
const [isLightboxOpen, setIsLightboxOpen] = React.useState(false);
|
|
2301
|
+
const isUnresolvedRagImage = React.useMemo(
|
|
2348
2302
|
() => src ? isUnresolvedRagImageSource(src) : false,
|
|
2349
2303
|
[src]
|
|
2350
2304
|
);
|
|
2351
2305
|
const isResolvingRagImage = isResolving && isUnresolvedRagImage;
|
|
2352
|
-
|
|
2306
|
+
React.useEffect(() => {
|
|
2353
2307
|
setIsLoaded(false);
|
|
2354
2308
|
setHasError(false);
|
|
2355
2309
|
setIsLightboxOpen(false);
|
|
@@ -2476,9 +2430,9 @@ function AgentMessage({
|
|
|
2476
2430
|
const hasTraceData = !!(message.tracingData || message.executionId) && !isStreaming;
|
|
2477
2431
|
const isCancelled = message.isCancelled ?? false;
|
|
2478
2432
|
const currentExecutingStepId = message.currentExecutingStepId;
|
|
2479
|
-
const [isStepsExpanded, setIsStepsExpanded] =
|
|
2480
|
-
const wasStreamingRef =
|
|
2481
|
-
|
|
2433
|
+
const [isStepsExpanded, setIsStepsExpanded] = React.useState(false);
|
|
2434
|
+
const wasStreamingRef = React.useRef(isStreaming);
|
|
2435
|
+
React.useEffect(() => {
|
|
2482
2436
|
if (isStreaming && hasSteps) {
|
|
2483
2437
|
setIsStepsExpanded(true);
|
|
2484
2438
|
}
|
|
@@ -2495,13 +2449,13 @@ function AgentMessage({
|
|
|
2495
2449
|
const completedWithNoContent = !isStreaming && !isCancelled && content.length === 0 && (message.streamProgress === "completed" || message.streamProgress === "error");
|
|
2496
2450
|
const conflictErrorMessage = getConflictErrorMessage(message.errorDetails);
|
|
2497
2451
|
const isError = !!conflictErrorMessage || (isFriendlyWorkflowError(message.errorDetails) || looksLikeRawError(content)) && !hasMeaningfulContent || completedWithNoContent;
|
|
2498
|
-
const currentStep =
|
|
2452
|
+
const currentStep = React.useMemo(
|
|
2499
2453
|
() => message.steps?.find(
|
|
2500
2454
|
(s) => s.id === currentExecutingStepId && s.status === "in_progress"
|
|
2501
2455
|
),
|
|
2502
2456
|
[message.steps, currentExecutingStepId]
|
|
2503
2457
|
);
|
|
2504
|
-
const markdownRenderers =
|
|
2458
|
+
const markdownRenderers = React.useMemo(
|
|
2505
2459
|
() => createMarkdownComponents({
|
|
2506
2460
|
isResolvingImages: message.isResolvingImages
|
|
2507
2461
|
}),
|
|
@@ -2575,8 +2529,8 @@ function AgentMessage({
|
|
|
2575
2529
|
}) })
|
|
2576
2530
|
}
|
|
2577
2531
|
) });
|
|
2578
|
-
const stepsToggleRef =
|
|
2579
|
-
const handleStepsToggle =
|
|
2532
|
+
const stepsToggleRef = React.useRef(null);
|
|
2533
|
+
const handleStepsToggle = React.useCallback(() => {
|
|
2580
2534
|
setIsStepsExpanded((prev) => {
|
|
2581
2535
|
const next = !prev;
|
|
2582
2536
|
if (next) {
|
|
@@ -2904,23 +2858,23 @@ function MessageList({
|
|
|
2904
2858
|
isLoadingMoreMessages = false,
|
|
2905
2859
|
hasMoreMessages = false
|
|
2906
2860
|
}) {
|
|
2907
|
-
const scrollRef =
|
|
2908
|
-
const isNearBottomRef =
|
|
2909
|
-
const [showScrollBtn, setShowScrollBtn] =
|
|
2910
|
-
const prevMessageCountRef =
|
|
2911
|
-
const scrollHeightBeforePrependRef =
|
|
2912
|
-
const firstMessageIdRef =
|
|
2913
|
-
const getDistanceFromBottom =
|
|
2861
|
+
const scrollRef = React.useRef(null);
|
|
2862
|
+
const isNearBottomRef = React.useRef(true);
|
|
2863
|
+
const [showScrollBtn, setShowScrollBtn] = React.useState(false);
|
|
2864
|
+
const prevMessageCountRef = React.useRef(messages.length);
|
|
2865
|
+
const scrollHeightBeforePrependRef = React.useRef(null);
|
|
2866
|
+
const firstMessageIdRef = React.useRef(messages[0]?.id);
|
|
2867
|
+
const getDistanceFromBottom = React.useCallback(() => {
|
|
2914
2868
|
const el = scrollRef.current;
|
|
2915
2869
|
if (!el) return 0;
|
|
2916
2870
|
return el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
2917
2871
|
}, []);
|
|
2918
|
-
const scrollToBottom =
|
|
2872
|
+
const scrollToBottom = React.useCallback((behavior = "smooth") => {
|
|
2919
2873
|
const el = scrollRef.current;
|
|
2920
2874
|
if (!el) return;
|
|
2921
2875
|
el.scrollTo({ top: el.scrollHeight, behavior });
|
|
2922
2876
|
}, []);
|
|
2923
|
-
const handleScroll =
|
|
2877
|
+
const handleScroll = React.useCallback(() => {
|
|
2924
2878
|
const el = scrollRef.current;
|
|
2925
2879
|
if (!el) return;
|
|
2926
2880
|
const distance = getDistanceFromBottom();
|
|
@@ -2932,7 +2886,7 @@ function MessageList({
|
|
|
2932
2886
|
onLoadMoreMessages();
|
|
2933
2887
|
}
|
|
2934
2888
|
}, [getDistanceFromBottom, hasMoreMessages, isLoadingMoreMessages, onLoadMoreMessages]);
|
|
2935
|
-
|
|
2889
|
+
React.useLayoutEffect(() => {
|
|
2936
2890
|
const el = scrollRef.current;
|
|
2937
2891
|
const prevHeight = scrollHeightBeforePrependRef.current;
|
|
2938
2892
|
const newFirstId = messages[0]?.id;
|
|
@@ -2942,25 +2896,25 @@ function MessageList({
|
|
|
2942
2896
|
}
|
|
2943
2897
|
firstMessageIdRef.current = newFirstId;
|
|
2944
2898
|
}, [messages]);
|
|
2945
|
-
|
|
2899
|
+
React.useEffect(() => {
|
|
2946
2900
|
const prevCount = prevMessageCountRef.current;
|
|
2947
2901
|
prevMessageCountRef.current = messages.length;
|
|
2948
2902
|
if (messages.length > prevCount && isNearBottomRef.current) {
|
|
2949
2903
|
requestAnimationFrame(() => scrollToBottom());
|
|
2950
2904
|
}
|
|
2951
2905
|
}, [messages.length, scrollToBottom]);
|
|
2952
|
-
|
|
2906
|
+
React.useEffect(() => {
|
|
2953
2907
|
const lastMsg = messages[messages.length - 1];
|
|
2954
2908
|
if (!lastMsg?.isStreaming) return;
|
|
2955
2909
|
if (!isNearBottomRef.current) return;
|
|
2956
2910
|
requestAnimationFrame(() => scrollToBottom());
|
|
2957
2911
|
});
|
|
2958
|
-
|
|
2912
|
+
React.useEffect(() => {
|
|
2959
2913
|
if (messages.length > 0) {
|
|
2960
2914
|
setTimeout(() => scrollToBottom("instant"), 50);
|
|
2961
2915
|
}
|
|
2962
2916
|
}, []);
|
|
2963
|
-
|
|
2917
|
+
React.useEffect(() => {
|
|
2964
2918
|
const handleStepsToggle = () => {
|
|
2965
2919
|
requestAnimationFrame(() => {
|
|
2966
2920
|
requestAnimationFrame(() => {
|
|
@@ -2977,7 +2931,7 @@ function MessageList({
|
|
|
2977
2931
|
return () => el.removeEventListener("payman-steps-toggle", handleStepsToggle);
|
|
2978
2932
|
}
|
|
2979
2933
|
}, [getDistanceFromBottom, scrollToBottom]);
|
|
2980
|
-
const handleScrollToBottom =
|
|
2934
|
+
const handleScrollToBottom = React.useCallback(() => {
|
|
2981
2935
|
scrollToBottom();
|
|
2982
2936
|
}, [scrollToBottom]);
|
|
2983
2937
|
if (isLoading) {
|
|
@@ -3110,7 +3064,7 @@ function ResetSessionConfirmModal({
|
|
|
3110
3064
|
onClose,
|
|
3111
3065
|
onConfirm
|
|
3112
3066
|
}) {
|
|
3113
|
-
const handleKeyDown =
|
|
3067
|
+
const handleKeyDown = React.useCallback(
|
|
3114
3068
|
(event) => {
|
|
3115
3069
|
if (event.key === "Escape") {
|
|
3116
3070
|
onClose();
|
|
@@ -3118,7 +3072,7 @@ function ResetSessionConfirmModal({
|
|
|
3118
3072
|
},
|
|
3119
3073
|
[onClose]
|
|
3120
3074
|
);
|
|
3121
|
-
|
|
3075
|
+
React.useEffect(() => {
|
|
3122
3076
|
if (!isOpen || typeof document === "undefined") return;
|
|
3123
3077
|
document.addEventListener("keydown", handleKeyDown);
|
|
3124
3078
|
const previousOverflow = document.body.style.overflow;
|
|
@@ -3224,14 +3178,14 @@ function UserMessageV2({
|
|
|
3224
3178
|
retryDisabled = false,
|
|
3225
3179
|
actions
|
|
3226
3180
|
}) {
|
|
3227
|
-
const [copied, setCopied] =
|
|
3228
|
-
const [toast, setToast] =
|
|
3229
|
-
const copyResetTimerRef =
|
|
3230
|
-
const toastTimerRef =
|
|
3181
|
+
const [copied, setCopied] = React.useState(false);
|
|
3182
|
+
const [toast, setToast] = React.useState(null);
|
|
3183
|
+
const copyResetTimerRef = React.useRef(null);
|
|
3184
|
+
const toastTimerRef = React.useRef(null);
|
|
3231
3185
|
const showCopyAction = actions?.copy ?? true;
|
|
3232
3186
|
const showEditAction = actions?.edit ?? false;
|
|
3233
3187
|
const showRetryAction = actions?.retry ?? false;
|
|
3234
|
-
|
|
3188
|
+
React.useEffect(() => {
|
|
3235
3189
|
return () => {
|
|
3236
3190
|
if (copyResetTimerRef.current) clearTimeout(copyResetTimerRef.current);
|
|
3237
3191
|
if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
|
|
@@ -3387,18 +3341,18 @@ function MarkdownImageV2({
|
|
|
3387
3341
|
onImageClick
|
|
3388
3342
|
}) {
|
|
3389
3343
|
const cachedAlready = src ? loadedImageCache.has(src) : false;
|
|
3390
|
-
const [isVisible, setIsVisible] =
|
|
3391
|
-
const [isLoaded, setIsLoaded] =
|
|
3392
|
-
const [hasError, setHasError] =
|
|
3393
|
-
const [retryCount, setRetryCount] =
|
|
3394
|
-
const sentinelRef =
|
|
3395
|
-
const prevLoadedRef =
|
|
3396
|
-
const isUnresolvedRag =
|
|
3344
|
+
const [isVisible, setIsVisible] = React.useState(cachedAlready);
|
|
3345
|
+
const [isLoaded, setIsLoaded] = React.useState(cachedAlready);
|
|
3346
|
+
const [hasError, setHasError] = React.useState(false);
|
|
3347
|
+
const [retryCount, setRetryCount] = React.useState(0);
|
|
3348
|
+
const sentinelRef = React.useRef(null);
|
|
3349
|
+
const prevLoadedRef = React.useRef(cachedAlready);
|
|
3350
|
+
const isUnresolvedRag = React.useMemo(
|
|
3397
3351
|
() => src ? isUnresolvedRagImageSource2(src) : false,
|
|
3398
3352
|
[src]
|
|
3399
3353
|
);
|
|
3400
3354
|
const isResolvingRag = Boolean(isResolving && isUnresolvedRag);
|
|
3401
|
-
|
|
3355
|
+
React.useEffect(() => {
|
|
3402
3356
|
if (src && loadedImageCache.has(src)) {
|
|
3403
3357
|
setIsLoaded(true);
|
|
3404
3358
|
setIsVisible(true);
|
|
@@ -3407,7 +3361,7 @@ function MarkdownImageV2({
|
|
|
3407
3361
|
setHasError(false);
|
|
3408
3362
|
setRetryCount(0);
|
|
3409
3363
|
}, [src]);
|
|
3410
|
-
|
|
3364
|
+
React.useEffect(() => {
|
|
3411
3365
|
const el = sentinelRef.current;
|
|
3412
3366
|
if (!el || !src) return;
|
|
3413
3367
|
const rect = el.getBoundingClientRect();
|
|
@@ -3528,9 +3482,88 @@ function MarkdownImageV2({
|
|
|
3528
3482
|
}
|
|
3529
3483
|
) });
|
|
3530
3484
|
}
|
|
3531
|
-
|
|
3485
|
+
var RAG_SOURCE_CLASS = "payman-v2-rag-source";
|
|
3486
|
+
var RAG_FOLLOW_UP_CLASS = "payman-v2-rag-follow-up";
|
|
3487
|
+
var SOURCE_PATTERN = /^Sources?:\s*\S/i;
|
|
3488
|
+
var MAX_FOLLOW_UP_LENGTH = 320;
|
|
3489
|
+
var HR_PATTERN = /^-{3,}$|^_{3,}$|^\*{3,}$/;
|
|
3490
|
+
function normalizeRagParagraphText(text) {
|
|
3491
|
+
return text.replace(/\s+/g, " ").trim();
|
|
3492
|
+
}
|
|
3493
|
+
function isRagSourceParagraph(text) {
|
|
3494
|
+
return SOURCE_PATTERN.test(normalizeRagParagraphText(text));
|
|
3495
|
+
}
|
|
3496
|
+
function isRagFollowUpCandidate(text) {
|
|
3497
|
+
const normalized = normalizeRagParagraphText(text);
|
|
3498
|
+
return normalized.length > 0 && normalized.length <= MAX_FOLLOW_UP_LENGTH && normalized.endsWith("?");
|
|
3499
|
+
}
|
|
3500
|
+
function extractRagParagraphTexts(content) {
|
|
3501
|
+
const paragraphs = [];
|
|
3502
|
+
const currentParagraph = [];
|
|
3503
|
+
const flushParagraph = () => {
|
|
3504
|
+
const paragraph = normalizeRagParagraphText(currentParagraph.join(" "));
|
|
3505
|
+
currentParagraph.length = 0;
|
|
3506
|
+
if (paragraph) paragraphs.push(paragraph);
|
|
3507
|
+
};
|
|
3508
|
+
content.replace(/\\n/g, "\n").split(/\r?\n/).forEach((line) => {
|
|
3509
|
+
const trimmedLine = line.trim();
|
|
3510
|
+
if (!trimmedLine) {
|
|
3511
|
+
flushParagraph();
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
if (HR_PATTERN.test(trimmedLine)) {
|
|
3515
|
+
flushParagraph();
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
currentParagraph.push(trimmedLine);
|
|
3519
|
+
});
|
|
3520
|
+
flushParagraph();
|
|
3521
|
+
return paragraphs;
|
|
3522
|
+
}
|
|
3523
|
+
function getRagAnswerParagraphRoles(paragraphs) {
|
|
3524
|
+
const normalized = paragraphs.map(normalizeRagParagraphText);
|
|
3525
|
+
const roles = normalized.map(() => null);
|
|
3526
|
+
const sourceIndexes = normalized.map((paragraph, index) => isRagSourceParagraph(paragraph) ? index : -1).filter((index) => index >= 0);
|
|
3527
|
+
for (const sourceIndex of sourceIndexes) {
|
|
3528
|
+
roles[sourceIndex] = "source";
|
|
3529
|
+
for (const candidateIndex of [sourceIndex - 1, sourceIndex + 1]) {
|
|
3530
|
+
if (candidateIndex >= 0 && candidateIndex < normalized.length && roles[candidateIndex] == null && isRagFollowUpCandidate(normalized[candidateIndex] ?? "")) {
|
|
3531
|
+
roles[candidateIndex] = "follow-up";
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
return roles;
|
|
3536
|
+
}
|
|
3537
|
+
function getRagAnswerParagraphRole(paragraph, paragraphs) {
|
|
3538
|
+
const normalizedParagraph = normalizeRagParagraphText(paragraph);
|
|
3539
|
+
if (isRagSourceParagraph(normalizedParagraph)) return "source";
|
|
3540
|
+
const roles = getRagAnswerParagraphRoles(paragraphs);
|
|
3541
|
+
return paragraphs.map(normalizeRagParagraphText).some((candidate, index) => {
|
|
3542
|
+
return candidate === normalizedParagraph && roles[index] === "follow-up";
|
|
3543
|
+
}) ? "follow-up" : null;
|
|
3544
|
+
}
|
|
3545
|
+
function ragAnswerRoleClassName(role) {
|
|
3546
|
+
if (role === "source") return RAG_SOURCE_CLASS;
|
|
3547
|
+
if (role === "follow-up") return RAG_FOLLOW_UP_CLASS;
|
|
3548
|
+
return void 0;
|
|
3549
|
+
}
|
|
3550
|
+
function reactNodeText(node) {
|
|
3551
|
+
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
3552
|
+
if (Array.isArray(node)) return node.map(reactNodeText).join("");
|
|
3553
|
+
if (React__namespace.isValidElement(node)) {
|
|
3554
|
+
return reactNodeText(node.props.children);
|
|
3555
|
+
}
|
|
3556
|
+
return "";
|
|
3557
|
+
}
|
|
3558
|
+
function buildComponents(onImageClick, isResolvingRef, ragParagraphsRef) {
|
|
3532
3559
|
return {
|
|
3533
|
-
p: ({ children }) =>
|
|
3560
|
+
p: ({ children }) => {
|
|
3561
|
+
const role = getRagAnswerParagraphRole(
|
|
3562
|
+
reactNodeText(children),
|
|
3563
|
+
ragParagraphsRef?.current ?? []
|
|
3564
|
+
);
|
|
3565
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: ragAnswerRoleClassName(role), children });
|
|
3566
|
+
},
|
|
3534
3567
|
code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
|
|
3535
3568
|
pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { children }) }),
|
|
3536
3569
|
ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { children }),
|
|
@@ -3566,10 +3599,15 @@ function MarkdownRendererV2({
|
|
|
3566
3599
|
isResolvingImages,
|
|
3567
3600
|
onImageClick
|
|
3568
3601
|
}) {
|
|
3569
|
-
const isResolvingRef =
|
|
3602
|
+
const isResolvingRef = React.useRef(isResolvingImages);
|
|
3570
3603
|
isResolvingRef.current = isResolvingImages;
|
|
3571
|
-
const
|
|
3572
|
-
|
|
3604
|
+
const ragParagraphsRef = React.useRef([]);
|
|
3605
|
+
ragParagraphsRef.current = React.useMemo(
|
|
3606
|
+
() => extractRagParagraphTexts(content),
|
|
3607
|
+
[content]
|
|
3608
|
+
);
|
|
3609
|
+
const components = React.useMemo(
|
|
3610
|
+
() => buildComponents(onImageClick, isResolvingRef, ragParagraphsRef),
|
|
3573
3611
|
[onImageClick]
|
|
3574
3612
|
);
|
|
3575
3613
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3626,14 +3664,14 @@ function FeedbackReasonModal({
|
|
|
3626
3664
|
onClose,
|
|
3627
3665
|
onSubmit
|
|
3628
3666
|
}) {
|
|
3629
|
-
const [reason, setReason] =
|
|
3667
|
+
const [reason, setReason] = React.useState(
|
|
3630
3668
|
NEGATIVE_FEEDBACK_REASONS[0].value
|
|
3631
3669
|
);
|
|
3632
|
-
const [details, setDetails] =
|
|
3633
|
-
const [submitting, setSubmitting] =
|
|
3634
|
-
const [error, setError] =
|
|
3635
|
-
const [reasonOpen, setReasonOpen] =
|
|
3636
|
-
|
|
3670
|
+
const [details, setDetails] = React.useState("");
|
|
3671
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
3672
|
+
const [error, setError] = React.useState(null);
|
|
3673
|
+
const [reasonOpen, setReasonOpen] = React.useState(false);
|
|
3674
|
+
React.useEffect(() => {
|
|
3637
3675
|
if (open) {
|
|
3638
3676
|
setReason(NEGATIVE_FEEDBACK_REASONS[0].value);
|
|
3639
3677
|
setDetails("");
|
|
@@ -3642,7 +3680,7 @@ function FeedbackReasonModal({
|
|
|
3642
3680
|
setReasonOpen(false);
|
|
3643
3681
|
}
|
|
3644
3682
|
}, [open]);
|
|
3645
|
-
const handleKeyDown =
|
|
3683
|
+
const handleKeyDown = React.useCallback(
|
|
3646
3684
|
(event) => {
|
|
3647
3685
|
if (event.key !== "Escape") return;
|
|
3648
3686
|
if (reasonOpen) {
|
|
@@ -3653,7 +3691,7 @@ function FeedbackReasonModal({
|
|
|
3653
3691
|
},
|
|
3654
3692
|
[onClose, reasonOpen]
|
|
3655
3693
|
);
|
|
3656
|
-
|
|
3694
|
+
React.useEffect(() => {
|
|
3657
3695
|
if (!open || typeof document === "undefined") return;
|
|
3658
3696
|
document.addEventListener("keydown", handleKeyDown);
|
|
3659
3697
|
const previousOverflow = document.body.style.overflow;
|
|
@@ -3937,17 +3975,17 @@ function charDelay(char, speed, multiplier) {
|
|
|
3937
3975
|
function useTypingEffect(targetText, enabled, speed = RESPONSE_SPEED, initialDisplayedText, speedMultiplier = 1) {
|
|
3938
3976
|
const instant = speedMultiplier === 0;
|
|
3939
3977
|
const multiplier = instant ? 1 : Math.max(speedMultiplier, 0.1);
|
|
3940
|
-
const [displayedText, setDisplayedText] =
|
|
3941
|
-
const displayedRef =
|
|
3942
|
-
const targetRef =
|
|
3943
|
-
const enabledRef =
|
|
3944
|
-
const initialDisplayedRef =
|
|
3945
|
-
const timerRef =
|
|
3946
|
-
const runningRef =
|
|
3978
|
+
const [displayedText, setDisplayedText] = React.useState("");
|
|
3979
|
+
const displayedRef = React.useRef("");
|
|
3980
|
+
const targetRef = React.useRef(targetText);
|
|
3981
|
+
const enabledRef = React.useRef(enabled);
|
|
3982
|
+
const initialDisplayedRef = React.useRef(initialDisplayedText);
|
|
3983
|
+
const timerRef = React.useRef(null);
|
|
3984
|
+
const runningRef = React.useRef(false);
|
|
3947
3985
|
targetRef.current = targetText;
|
|
3948
3986
|
enabledRef.current = enabled;
|
|
3949
3987
|
initialDisplayedRef.current = initialDisplayedText;
|
|
3950
|
-
|
|
3988
|
+
React.useEffect(() => {
|
|
3951
3989
|
if (!enabled || instant) {
|
|
3952
3990
|
if (timerRef.current) {
|
|
3953
3991
|
clearTimeout(timerRef.current);
|
|
@@ -4030,26 +4068,26 @@ function AssistantMessageV2({
|
|
|
4030
4068
|
actions,
|
|
4031
4069
|
typingSpeed = 4
|
|
4032
4070
|
}) {
|
|
4033
|
-
const [copied, setCopied] =
|
|
4034
|
-
const [activeFeedback, setActiveFeedback] =
|
|
4071
|
+
const [copied, setCopied] = React.useState(false);
|
|
4072
|
+
const [activeFeedback, setActiveFeedback] = React.useState(
|
|
4035
4073
|
() => getFeedbackState(message)
|
|
4036
4074
|
);
|
|
4037
|
-
const [reasonModalOpen, setReasonModalOpen] =
|
|
4075
|
+
const [reasonModalOpen, setReasonModalOpen] = React.useState(false);
|
|
4038
4076
|
const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
|
|
4039
|
-
const [toast, setToast] =
|
|
4040
|
-
const copyResetTimerRef =
|
|
4041
|
-
const toastTimerRef =
|
|
4077
|
+
const [toast, setToast] = React.useState(null);
|
|
4078
|
+
const copyResetTimerRef = React.useRef(null);
|
|
4079
|
+
const toastTimerRef = React.useRef(null);
|
|
4042
4080
|
const showCopyAction = actions?.copy ?? true;
|
|
4043
4081
|
const showTraceAction = (actions?.trace ?? true) && !!onExecutionTraceClick;
|
|
4044
4082
|
const showThumbsUp = actions?.thumbsUp ?? true;
|
|
4045
4083
|
const showThumbsDown = actions?.thumbsDown ?? true;
|
|
4046
4084
|
const hydratedFeedback = message.feedback;
|
|
4047
|
-
const hasEverStreamed =
|
|
4085
|
+
const hasEverStreamed = React.useRef(!!message.isStreaming);
|
|
4048
4086
|
if (message.isStreaming) hasEverStreamed.current = true;
|
|
4049
|
-
|
|
4087
|
+
React.useEffect(() => {
|
|
4050
4088
|
setActiveFeedback(getFeedbackState(message));
|
|
4051
4089
|
}, [hydratedFeedback, message.id]);
|
|
4052
|
-
|
|
4090
|
+
React.useEffect(() => {
|
|
4053
4091
|
return () => {
|
|
4054
4092
|
if (copyResetTimerRef.current) clearTimeout(copyResetTimerRef.current);
|
|
4055
4093
|
if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
|
|
@@ -4062,6 +4100,7 @@ function AssistantMessageV2({
|
|
|
4062
4100
|
return message.isStreaming ? stripIncompleteImageToken(normalized) : normalized;
|
|
4063
4101
|
})();
|
|
4064
4102
|
const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
|
|
4103
|
+
const showTrailingProgress = !!message.isStreaming && !!rawResponseContent && !message.isError && !message.isCancelled && !!message.hasTrailingActivity;
|
|
4065
4104
|
const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
|
|
4066
4105
|
const { displayedText: displayContent, isTyping: isResponseTyping } = useTypingEffect(
|
|
4067
4106
|
rawResponseContent,
|
|
@@ -4220,7 +4259,7 @@ function AssistantMessageV2({
|
|
|
4220
4259
|
{
|
|
4221
4260
|
isStreaming: isThinkingStreaming,
|
|
4222
4261
|
stickyLabel,
|
|
4223
|
-
currentStepLabel
|
|
4262
|
+
currentStepLabel: currentStepLabel ?? message.currentMessage
|
|
4224
4263
|
}
|
|
4225
4264
|
)
|
|
4226
4265
|
},
|
|
@@ -4235,6 +4274,27 @@ function AssistantMessageV2({
|
|
|
4235
4274
|
onImageClick
|
|
4236
4275
|
}
|
|
4237
4276
|
) : !isThinkingStreaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
|
|
4277
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showTrailingProgress && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4278
|
+
framerMotion.motion.div,
|
|
4279
|
+
{
|
|
4280
|
+
initial: { opacity: 0, height: 0 },
|
|
4281
|
+
animate: { opacity: 1, height: "auto" },
|
|
4282
|
+
exit: { opacity: 0, height: 0 },
|
|
4283
|
+
transition: {
|
|
4284
|
+
duration: 0.32,
|
|
4285
|
+
ease: [0.2, 0, 0, 1]
|
|
4286
|
+
},
|
|
4287
|
+
style: { overflow: "hidden" },
|
|
4288
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4289
|
+
ThinkingBlockV2,
|
|
4290
|
+
{
|
|
4291
|
+
isStreaming: true,
|
|
4292
|
+
currentStepLabel: message.currentMessage ?? currentStepLabel
|
|
4293
|
+
}
|
|
4294
|
+
)
|
|
4295
|
+
},
|
|
4296
|
+
"trailing-progress"
|
|
4297
|
+
) }),
|
|
4238
4298
|
isCancelled && message.isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
|
|
4239
4299
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4240
4300
|
lucideReact.WifiOff,
|
|
@@ -4343,10 +4403,10 @@ function OtpInputV2({
|
|
|
4343
4403
|
disabled = false,
|
|
4344
4404
|
error = false
|
|
4345
4405
|
}) {
|
|
4346
|
-
const inputRefs =
|
|
4406
|
+
const inputRefs = React.useRef([]);
|
|
4347
4407
|
const safeMaxLength = Number.isInteger(maxLength) && maxLength > 0 ? Math.min(maxLength, MAX_SUPPORTED_LENGTH) : DEFAULT_MAX_LENGTH;
|
|
4348
4408
|
const digits = value.split("").concat(Array(safeMaxLength).fill("")).slice(0, safeMaxLength);
|
|
4349
|
-
|
|
4409
|
+
React.useEffect(() => {
|
|
4350
4410
|
if (disabled) return;
|
|
4351
4411
|
const timer = window.setTimeout(() => {
|
|
4352
4412
|
inputRefs.current[0]?.focus();
|
|
@@ -4445,31 +4505,31 @@ function VerificationInline({
|
|
|
4445
4505
|
onResend
|
|
4446
4506
|
}) {
|
|
4447
4507
|
const isNumeric = prompt.verificationType !== "ALPHANUMERIC_CODE";
|
|
4448
|
-
const codeLen =
|
|
4449
|
-
const [code, setCode] =
|
|
4450
|
-
const [errored, setErrored] =
|
|
4451
|
-
const [resendSec, setResendSec] =
|
|
4452
|
-
const [hiddenAfterResend, setHiddenAfterResend] =
|
|
4453
|
-
const lastSubmittedRef =
|
|
4454
|
-
const resendTimerRef =
|
|
4508
|
+
const codeLen = React.useMemo(() => codeLengthFromSchema(prompt), [prompt]);
|
|
4509
|
+
const [code, setCode] = React.useState("");
|
|
4510
|
+
const [errored, setErrored] = React.useState(false);
|
|
4511
|
+
const [resendSec, setResendSec] = React.useState(0);
|
|
4512
|
+
const [hiddenAfterResend, setHiddenAfterResend] = React.useState(false);
|
|
4513
|
+
const lastSubmittedRef = React.useRef(null);
|
|
4514
|
+
const resendTimerRef = React.useRef(void 0);
|
|
4455
4515
|
const status = prompt.status;
|
|
4456
4516
|
const busy = status === "submitting";
|
|
4457
4517
|
const stale = status === "stale";
|
|
4458
4518
|
const locked = busy || stale || expired;
|
|
4459
|
-
|
|
4519
|
+
React.useEffect(() => {
|
|
4460
4520
|
if (prompt.subAction === "SubmissionInvalid") {
|
|
4461
4521
|
setErrored(true);
|
|
4462
4522
|
setCode("");
|
|
4463
4523
|
lastSubmittedRef.current = null;
|
|
4464
4524
|
}
|
|
4465
4525
|
}, [prompt.subAction, prompt.userActionId]);
|
|
4466
|
-
|
|
4526
|
+
React.useEffect(() => {
|
|
4467
4527
|
setHiddenAfterResend(false);
|
|
4468
4528
|
}, [prompt.expirySeconds, prompt.message, prompt.subAction, prompt.userActionId]);
|
|
4469
|
-
|
|
4529
|
+
React.useEffect(() => {
|
|
4470
4530
|
if (code.length < codeLen) lastSubmittedRef.current = null;
|
|
4471
4531
|
}, [code, codeLen]);
|
|
4472
|
-
const doSubmit =
|
|
4532
|
+
const doSubmit = React.useCallback(
|
|
4473
4533
|
(value) => {
|
|
4474
4534
|
if (locked || !value) return;
|
|
4475
4535
|
if (lastSubmittedRef.current === value) return;
|
|
@@ -4481,20 +4541,20 @@ function VerificationInline({
|
|
|
4481
4541
|
},
|
|
4482
4542
|
[locked, onSubmit, prompt.userActionId]
|
|
4483
4543
|
);
|
|
4484
|
-
|
|
4544
|
+
React.useEffect(() => {
|
|
4485
4545
|
if (!isNumeric || locked) return;
|
|
4486
4546
|
if (code.length === codeLen && /^\d+$/.test(code)) {
|
|
4487
4547
|
doSubmit(code);
|
|
4488
4548
|
}
|
|
4489
4549
|
}, [code, codeLen, doSubmit, isNumeric, locked]);
|
|
4490
|
-
|
|
4550
|
+
React.useEffect(() => {
|
|
4491
4551
|
return () => {
|
|
4492
4552
|
if (typeof resendTimerRef.current === "number") {
|
|
4493
4553
|
window.clearInterval(resendTimerRef.current);
|
|
4494
4554
|
}
|
|
4495
4555
|
};
|
|
4496
4556
|
}, []);
|
|
4497
|
-
const startResendCooldown =
|
|
4557
|
+
const startResendCooldown = React.useCallback(() => {
|
|
4498
4558
|
setResendSec(RESEND_COOLDOWN_S);
|
|
4499
4559
|
if (typeof resendTimerRef.current === "number") {
|
|
4500
4560
|
window.clearInterval(resendTimerRef.current);
|
|
@@ -4512,7 +4572,7 @@ function VerificationInline({
|
|
|
4512
4572
|
});
|
|
4513
4573
|
}, 1e3);
|
|
4514
4574
|
}, []);
|
|
4515
|
-
const handleResend =
|
|
4575
|
+
const handleResend = React.useCallback(async () => {
|
|
4516
4576
|
if (locked || resendSec > 0) return;
|
|
4517
4577
|
setErrored(false);
|
|
4518
4578
|
setCode("");
|
|
@@ -4525,11 +4585,14 @@ function VerificationInline({
|
|
|
4525
4585
|
setHiddenAfterResend(false);
|
|
4526
4586
|
}
|
|
4527
4587
|
}, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
|
|
4528
|
-
const handleCancel =
|
|
4588
|
+
const handleCancel = React.useCallback(() => {
|
|
4529
4589
|
if (busy) return;
|
|
4530
4590
|
void onCancel(prompt.userActionId);
|
|
4531
4591
|
}, [busy, onCancel, prompt.userActionId]);
|
|
4532
|
-
const
|
|
4592
|
+
const promptMessage = prompt.message?.trim();
|
|
4593
|
+
const description = promptMessage || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
|
|
4594
|
+
const messageFormat = prompt.metadata?.["payman/messageFormat"];
|
|
4595
|
+
const renderMarkdown = !!promptMessage && messageFormat === "markdown";
|
|
4533
4596
|
if (hiddenAfterResend) return null;
|
|
4534
4597
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
|
|
4535
4598
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
|
|
@@ -4537,7 +4600,7 @@ function VerificationInline({
|
|
|
4537
4600
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Verification required" }),
|
|
4538
4601
|
typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
|
|
4539
4602
|
] }),
|
|
4540
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: description }),
|
|
4603
|
+
renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRendererV2, { content: description }) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: description }),
|
|
4541
4604
|
stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4542
4605
|
isNumeric ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-field", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4543
4606
|
OtpInputV2,
|
|
@@ -4615,13 +4678,13 @@ function SchemaFormInline({
|
|
|
4615
4678
|
onCancel
|
|
4616
4679
|
}) {
|
|
4617
4680
|
const schema = prompt.requestedSchema;
|
|
4618
|
-
const fields =
|
|
4619
|
-
const [values, setValues] =
|
|
4681
|
+
const fields = React.useMemo(() => renderableFields(schema), [schema]);
|
|
4682
|
+
const [values, setValues] = React.useState(() => {
|
|
4620
4683
|
const init2 = {};
|
|
4621
4684
|
for (const [key, field] of fields) init2[key] = defaultValueFor(field);
|
|
4622
4685
|
return init2;
|
|
4623
4686
|
});
|
|
4624
|
-
const [errors, setErrors] =
|
|
4687
|
+
const [errors, setErrors] = React.useState({});
|
|
4625
4688
|
const status = prompt.status;
|
|
4626
4689
|
const busy = status === "submitting";
|
|
4627
4690
|
const stale = status === "stale";
|
|
@@ -4761,14 +4824,15 @@ function NotificationInline({ notification, onDismiss }) {
|
|
|
4761
4824
|
] });
|
|
4762
4825
|
}
|
|
4763
4826
|
function useExpiryCountdown(prompt) {
|
|
4764
|
-
const
|
|
4765
|
-
const [secondsLeft, setSecondsLeft] =
|
|
4766
|
-
|
|
4767
|
-
|
|
4827
|
+
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;
|
|
4828
|
+
const [secondsLeft, setSecondsLeft] = React.useState(
|
|
4829
|
+
() => deadlineMs !== void 0 ? Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3)) : void 0
|
|
4830
|
+
);
|
|
4831
|
+
React.useEffect(() => {
|
|
4832
|
+
if (deadlineMs === void 0) {
|
|
4768
4833
|
setSecondsLeft(void 0);
|
|
4769
4834
|
return;
|
|
4770
4835
|
}
|
|
4771
|
-
const deadlineMs = Date.now() + initial * 1e3;
|
|
4772
4836
|
let intervalId;
|
|
4773
4837
|
const remaining = () => Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3));
|
|
4774
4838
|
const sync = () => {
|
|
@@ -4790,8 +4854,8 @@ function useExpiryCountdown(prompt) {
|
|
|
4790
4854
|
window.removeEventListener("focus", sync);
|
|
4791
4855
|
window.removeEventListener("pageshow", sync);
|
|
4792
4856
|
};
|
|
4793
|
-
}, [prompt.userActionId, prompt.subAction,
|
|
4794
|
-
const expired =
|
|
4857
|
+
}, [prompt.userActionId, prompt.subAction, deadlineMs]);
|
|
4858
|
+
const expired = deadlineMs !== void 0 && (getUserActionSecondsLeft(prompt) ?? secondsLeft ?? 0) <= 0;
|
|
4795
4859
|
return [secondsLeft, expired];
|
|
4796
4860
|
}
|
|
4797
4861
|
function UserActionInline({
|
|
@@ -4802,7 +4866,7 @@ function UserActionInline({
|
|
|
4802
4866
|
onExpired
|
|
4803
4867
|
}) {
|
|
4804
4868
|
const [secondsLeft, expired] = useExpiryCountdown(prompt);
|
|
4805
|
-
|
|
4869
|
+
React.useEffect(() => {
|
|
4806
4870
|
if (expired && prompt.kind !== "notification") onExpired?.();
|
|
4807
4871
|
}, [expired, onExpired, prompt.kind]);
|
|
4808
4872
|
let body;
|
|
@@ -4865,7 +4929,7 @@ function getPromptViewKey(prompt) {
|
|
|
4865
4929
|
prompt.message ?? ""
|
|
4866
4930
|
].join(PROMPT_KEY_SEPARATOR);
|
|
4867
4931
|
}
|
|
4868
|
-
var MessageListV2 =
|
|
4932
|
+
var MessageListV2 = React.forwardRef(
|
|
4869
4933
|
function MessageListV22({
|
|
4870
4934
|
messages,
|
|
4871
4935
|
isStreaming = false,
|
|
@@ -4885,25 +4949,25 @@ var MessageListV2 = react.forwardRef(
|
|
|
4885
4949
|
onSubmitFeedback,
|
|
4886
4950
|
typingSpeed = 4
|
|
4887
4951
|
}, ref) {
|
|
4888
|
-
const noop =
|
|
4952
|
+
const noop = React.useCallback(async () => {
|
|
4889
4953
|
}, []);
|
|
4890
|
-
const scrollRef =
|
|
4891
|
-
const scrollInnerRef =
|
|
4892
|
-
const isNearBottomRef =
|
|
4893
|
-
const [showScrollBtn, setShowScrollBtn] =
|
|
4894
|
-
const [expiredPromptViewState, setExpiredPromptViewState] =
|
|
4895
|
-
const expiredUserActionIdsRef =
|
|
4896
|
-
const prevCountRef =
|
|
4897
|
-
const pauseStickUntilUserMessageRef =
|
|
4898
|
-
const followingBottomRef =
|
|
4899
|
-
const lastPinAtRef =
|
|
4900
|
-
const prevScrollTopRef =
|
|
4901
|
-
const getDistanceFromBottom =
|
|
4954
|
+
const scrollRef = React.useRef(null);
|
|
4955
|
+
const scrollInnerRef = React.useRef(null);
|
|
4956
|
+
const isNearBottomRef = React.useRef(true);
|
|
4957
|
+
const [showScrollBtn, setShowScrollBtn] = React.useState(false);
|
|
4958
|
+
const [expiredPromptViewState, setExpiredPromptViewState] = React.useState({});
|
|
4959
|
+
const expiredUserActionIdsRef = React.useRef(/* @__PURE__ */ new Set());
|
|
4960
|
+
const prevCountRef = React.useRef(messages.length);
|
|
4961
|
+
const pauseStickUntilUserMessageRef = React.useRef(false);
|
|
4962
|
+
const followingBottomRef = React.useRef(true);
|
|
4963
|
+
const lastPinAtRef = React.useRef(0);
|
|
4964
|
+
const prevScrollTopRef = React.useRef(0);
|
|
4965
|
+
const getDistanceFromBottom = React.useCallback(() => {
|
|
4902
4966
|
const el = scrollRef.current;
|
|
4903
4967
|
if (!el) return 0;
|
|
4904
4968
|
return el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
4905
4969
|
}, []);
|
|
4906
|
-
const messageActivityFingerprint =
|
|
4970
|
+
const messageActivityFingerprint = React.useMemo(() => {
|
|
4907
4971
|
const last = messages[messages.length - 1];
|
|
4908
4972
|
const promptFingerprint = (userActionPrompts ?? []).map((prompt) => `${getPromptViewKey(prompt)}:${prompt.status}`).join(PROMPT_KEY_SEPARATOR);
|
|
4909
4973
|
const notificationFingerprint = (notifications ?? []).map((notification) => notification.id).join(PROMPT_KEY_SEPARATOR);
|
|
@@ -4929,7 +4993,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4929
4993
|
notificationFingerprint
|
|
4930
4994
|
].join(PROMPT_KEY_SEPARATOR);
|
|
4931
4995
|
}, [isStreaming, messages, notifications, userActionPrompts]);
|
|
4932
|
-
const handleUserActionExpired =
|
|
4996
|
+
const handleUserActionExpired = React.useCallback(
|
|
4933
4997
|
(promptKey, userActionId) => {
|
|
4934
4998
|
setExpiredPromptViewState((prev) => {
|
|
4935
4999
|
if (prev[promptKey]) return prev;
|
|
@@ -4946,7 +5010,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4946
5010
|
},
|
|
4947
5011
|
[messageActivityFingerprint, onExpireUserAction]
|
|
4948
5012
|
);
|
|
4949
|
-
|
|
5013
|
+
React.useEffect(() => {
|
|
4950
5014
|
setExpiredPromptViewState((prev) => {
|
|
4951
5015
|
let changed = false;
|
|
4952
5016
|
const next = {};
|
|
@@ -4961,7 +5025,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4961
5025
|
return changed ? next : prev;
|
|
4962
5026
|
});
|
|
4963
5027
|
}, [messageActivityFingerprint]);
|
|
4964
|
-
|
|
5028
|
+
React.useEffect(() => {
|
|
4965
5029
|
const livePromptKeys = new Set((userActionPrompts ?? []).map(getPromptViewKey));
|
|
4966
5030
|
const liveUserActionIds = new Set((userActionPrompts ?? []).map((p) => p.userActionId));
|
|
4967
5031
|
for (const userActionId of expiredUserActionIdsRef.current) {
|
|
@@ -4982,21 +5046,21 @@ var MessageListV2 = react.forwardRef(
|
|
|
4982
5046
|
return changed ? next : prev;
|
|
4983
5047
|
});
|
|
4984
5048
|
}, [userActionPrompts]);
|
|
4985
|
-
const visibleUserActionPrompts =
|
|
5049
|
+
const visibleUserActionPrompts = React.useMemo(
|
|
4986
5050
|
() => userActionPrompts?.filter((prompt) => {
|
|
4987
5051
|
const promptKey = getPromptViewKey(prompt);
|
|
4988
5052
|
return !expiredPromptViewState[promptKey]?.hidden;
|
|
4989
5053
|
}),
|
|
4990
5054
|
[expiredPromptViewState, userActionPrompts]
|
|
4991
5055
|
);
|
|
4992
|
-
const pinToBottom =
|
|
5056
|
+
const pinToBottom = React.useCallback((behavior = "instant") => {
|
|
4993
5057
|
const el = scrollRef.current;
|
|
4994
5058
|
if (!el) return;
|
|
4995
5059
|
lastPinAtRef.current = performance.now();
|
|
4996
5060
|
el.scrollTo({ top: el.scrollHeight, behavior });
|
|
4997
5061
|
prevScrollTopRef.current = el.scrollHeight;
|
|
4998
5062
|
}, []);
|
|
4999
|
-
const scrollToBottom =
|
|
5063
|
+
const scrollToBottom = React.useCallback(
|
|
5000
5064
|
(behavior = "smooth") => {
|
|
5001
5065
|
const el = scrollRef.current;
|
|
5002
5066
|
if (!el) return;
|
|
@@ -5006,7 +5070,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
5006
5070
|
},
|
|
5007
5071
|
[pinToBottom]
|
|
5008
5072
|
);
|
|
5009
|
-
|
|
5073
|
+
React.useImperativeHandle(
|
|
5010
5074
|
ref,
|
|
5011
5075
|
() => ({
|
|
5012
5076
|
scrollToBottom: (behavior = "smooth") => {
|
|
@@ -5019,7 +5083,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
5019
5083
|
}),
|
|
5020
5084
|
[scrollToBottom]
|
|
5021
5085
|
);
|
|
5022
|
-
const handleScroll =
|
|
5086
|
+
const handleScroll = React.useCallback(() => {
|
|
5023
5087
|
const el = scrollRef.current;
|
|
5024
5088
|
if (!el) return;
|
|
5025
5089
|
const currentScrollTop = el.scrollTop;
|
|
@@ -5040,7 +5104,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
5040
5104
|
pauseStickUntilUserMessageRef.current = false;
|
|
5041
5105
|
}
|
|
5042
5106
|
}, [getDistanceFromBottom]);
|
|
5043
|
-
|
|
5107
|
+
React.useEffect(() => {
|
|
5044
5108
|
const prevCount = prevCountRef.current;
|
|
5045
5109
|
prevCountRef.current = messages.length;
|
|
5046
5110
|
if (messages.length > prevCount) {
|
|
@@ -5054,7 +5118,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
5054
5118
|
}
|
|
5055
5119
|
}
|
|
5056
5120
|
}, [messages.length, scrollToBottom]);
|
|
5057
|
-
|
|
5121
|
+
React.useEffect(() => {
|
|
5058
5122
|
const inner = scrollInnerRef.current;
|
|
5059
5123
|
if (!inner) return;
|
|
5060
5124
|
const pinIfFollowing = () => {
|
|
@@ -5079,7 +5143,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
5079
5143
|
mo.disconnect();
|
|
5080
5144
|
};
|
|
5081
5145
|
}, [pinToBottom]);
|
|
5082
|
-
|
|
5146
|
+
React.useEffect(() => {
|
|
5083
5147
|
if (messages.length > 0) {
|
|
5084
5148
|
setTimeout(() => scrollToBottom("instant"), 50);
|
|
5085
5149
|
}
|
|
@@ -5168,7 +5232,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
5168
5232
|
] });
|
|
5169
5233
|
}
|
|
5170
5234
|
);
|
|
5171
|
-
var ChatInputV2 =
|
|
5235
|
+
var ChatInputV2 = React.forwardRef(
|
|
5172
5236
|
function ChatInputV22({
|
|
5173
5237
|
onSend,
|
|
5174
5238
|
disabled = false,
|
|
@@ -5192,20 +5256,20 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5192
5256
|
onAnalysisModeChange,
|
|
5193
5257
|
slashCommands = []
|
|
5194
5258
|
}, ref) {
|
|
5195
|
-
const [value, setValue] =
|
|
5196
|
-
const [isFocused, setIsFocused] =
|
|
5197
|
-
const [showActions, setShowActions] =
|
|
5198
|
-
const [showVoiceTooltip, setShowVoiceTooltip] =
|
|
5199
|
-
const [selectedCommandIndex, setSelectedCommandIndex] =
|
|
5200
|
-
const [inlineHint, setInlineHint] =
|
|
5201
|
-
const [commandMenuDismissed, setCommandMenuDismissed] =
|
|
5202
|
-
const textareaRef =
|
|
5203
|
-
const actionsRef =
|
|
5204
|
-
const preRecordTextRef =
|
|
5205
|
-
const voiceTooltipTimerRef =
|
|
5259
|
+
const [value, setValue] = React.useState("");
|
|
5260
|
+
const [isFocused, setIsFocused] = React.useState(false);
|
|
5261
|
+
const [showActions, setShowActions] = React.useState(false);
|
|
5262
|
+
const [showVoiceTooltip, setShowVoiceTooltip] = React.useState(false);
|
|
5263
|
+
const [selectedCommandIndex, setSelectedCommandIndex] = React.useState(0);
|
|
5264
|
+
const [inlineHint, setInlineHint] = React.useState(null);
|
|
5265
|
+
const [commandMenuDismissed, setCommandMenuDismissed] = React.useState(false);
|
|
5266
|
+
const textareaRef = React.useRef(null);
|
|
5267
|
+
const actionsRef = React.useRef(null);
|
|
5268
|
+
const preRecordTextRef = React.useRef("");
|
|
5269
|
+
const voiceTooltipTimerRef = React.useRef(
|
|
5206
5270
|
null
|
|
5207
5271
|
);
|
|
5208
|
-
const voiceDraftSyncActiveRef =
|
|
5272
|
+
const voiceDraftSyncActiveRef = React.useRef(false);
|
|
5209
5273
|
const isInputLocked = disabled || isRecording;
|
|
5210
5274
|
const hasAttachmentOptions = showUploadImageButton || showAttachFileButton;
|
|
5211
5275
|
const showAttachmentMenuButton = showAttachmentButton && hasAttachmentOptions;
|
|
@@ -5216,14 +5280,14 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5216
5280
|
(command) => command.name.toLowerCase().startsWith(commandQuery)
|
|
5217
5281
|
);
|
|
5218
5282
|
const showCommandSuggestions = !commandMenuDismissed && !isRecording && !disabled && commandSuggestions.length > 0;
|
|
5219
|
-
|
|
5283
|
+
React.useEffect(() => {
|
|
5220
5284
|
if (textareaRef.current) {
|
|
5221
5285
|
textareaRef.current.style.height = "auto";
|
|
5222
5286
|
const scrollHeight = textareaRef.current.scrollHeight;
|
|
5223
5287
|
textareaRef.current.style.height = `${Math.min(scrollHeight, 200)}px`;
|
|
5224
5288
|
}
|
|
5225
5289
|
}, [value]);
|
|
5226
|
-
|
|
5290
|
+
React.useEffect(() => {
|
|
5227
5291
|
if (disabled) {
|
|
5228
5292
|
setIsFocused(false);
|
|
5229
5293
|
setShowActions(false);
|
|
@@ -5233,7 +5297,7 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5233
5297
|
const frameId = requestAnimationFrame(() => textareaRef.current?.focus());
|
|
5234
5298
|
return () => cancelAnimationFrame(frameId);
|
|
5235
5299
|
}, [disabled]);
|
|
5236
|
-
|
|
5300
|
+
React.useImperativeHandle(
|
|
5237
5301
|
ref,
|
|
5238
5302
|
() => ({
|
|
5239
5303
|
setDraft: (message) => {
|
|
@@ -5250,7 +5314,7 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5250
5314
|
}),
|
|
5251
5315
|
[disabled]
|
|
5252
5316
|
);
|
|
5253
|
-
|
|
5317
|
+
React.useEffect(() => {
|
|
5254
5318
|
if (!showActions) return;
|
|
5255
5319
|
const handleClickOutside = (e) => {
|
|
5256
5320
|
if (actionsRef.current && !actionsRef.current.contains(e.target)) {
|
|
@@ -5260,29 +5324,29 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5260
5324
|
document.addEventListener("mousedown", handleClickOutside);
|
|
5261
5325
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
5262
5326
|
}, [showActions]);
|
|
5263
|
-
|
|
5327
|
+
React.useEffect(() => {
|
|
5264
5328
|
if (!showAttachmentMenuButton) {
|
|
5265
5329
|
setShowActions(false);
|
|
5266
5330
|
}
|
|
5267
5331
|
}, [showAttachmentMenuButton]);
|
|
5268
|
-
|
|
5332
|
+
React.useEffect(() => {
|
|
5269
5333
|
setSelectedCommandIndex(0);
|
|
5270
5334
|
setCommandMenuDismissed(false);
|
|
5271
5335
|
}, [commandQuery]);
|
|
5272
|
-
|
|
5336
|
+
React.useEffect(() => {
|
|
5273
5337
|
return () => {
|
|
5274
5338
|
if (voiceTooltipTimerRef.current) {
|
|
5275
5339
|
clearTimeout(voiceTooltipTimerRef.current);
|
|
5276
5340
|
}
|
|
5277
5341
|
};
|
|
5278
5342
|
}, []);
|
|
5279
|
-
|
|
5343
|
+
React.useEffect(() => {
|
|
5280
5344
|
if (!voiceDraftSyncActiveRef.current) return;
|
|
5281
5345
|
const base = preRecordTextRef.current;
|
|
5282
5346
|
const separator = base && !base.endsWith(" ") && transcribedText ? " " : "";
|
|
5283
5347
|
setValue(`${base}${separator}${transcribedText}`);
|
|
5284
5348
|
}, [isRecording, transcribedText]);
|
|
5285
|
-
const handleSend =
|
|
5349
|
+
const handleSend = React.useCallback(() => {
|
|
5286
5350
|
if (!value.trim() || disabled) return;
|
|
5287
5351
|
const commandHint = getSlashCommandValidationHint(value);
|
|
5288
5352
|
if (commandHint) {
|
|
@@ -5302,7 +5366,7 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5302
5366
|
}
|
|
5303
5367
|
});
|
|
5304
5368
|
}, [value, disabled, onClearEditing, onSend]);
|
|
5305
|
-
const selectCommand =
|
|
5369
|
+
const selectCommand = React.useCallback(
|
|
5306
5370
|
(command) => {
|
|
5307
5371
|
const insertText = command.insertText ?? `${command.name} `;
|
|
5308
5372
|
setValue(insertText);
|
|
@@ -5358,14 +5422,14 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5358
5422
|
onAttachFileClick?.();
|
|
5359
5423
|
setShowActions(false);
|
|
5360
5424
|
};
|
|
5361
|
-
const hideVoiceTooltip =
|
|
5425
|
+
const hideVoiceTooltip = React.useCallback(() => {
|
|
5362
5426
|
if (voiceTooltipTimerRef.current) {
|
|
5363
5427
|
clearTimeout(voiceTooltipTimerRef.current);
|
|
5364
5428
|
voiceTooltipTimerRef.current = null;
|
|
5365
5429
|
}
|
|
5366
5430
|
setShowVoiceTooltip(false);
|
|
5367
5431
|
}, []);
|
|
5368
|
-
const startVoiceTooltipTimer =
|
|
5432
|
+
const startVoiceTooltipTimer = React.useCallback(() => {
|
|
5369
5433
|
if (isVoiceButtonDisabled || isRecording) return;
|
|
5370
5434
|
if (voiceTooltipTimerRef.current) {
|
|
5371
5435
|
clearTimeout(voiceTooltipTimerRef.current);
|
|
@@ -5687,9 +5751,9 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5687
5751
|
var thinking_atom_default = "data:application/zip;base64,UEsDBBQAAAAIAMWrkFzFrMWzUgAAAF4AAAANAAAAbWFuaWZlc3QuanNvbqtWKkstKs7Mz1OyUjJS0lFKT81LLUosyS8C8h1S8kty8ktKMlP14SzdrGIHQz0zPZDaxLzM3MQSoN5iJavoaqXMFKAe38TMPIXgZKApSrWxtQBQSwMEFAAAAAgAxauQXLRghFpXBAAA1RwAABEAAABhL01haW4gU2NlbmUuanNvbu1YWW/jNhD+KwafJVWkqMtPbdEDfdiiQIq+GMZCcei1GvmApLRdGP7v/YYiddqbGNumR4wcomZGHM7B76N0ZLstm7N3Wb6b3a3UTjGHPTw8sLnvsA2bCx/X3811q+qMzY/sAx74stjXda7WeaGqL1alyup9OeNeEnmcnRxWZB9VWbH54sjqj2weOI2bH5+KYibhoirZnONSa0f7AzykGOQYkGP4X2dFpbq13ONx0mTVu6x6bNXZXosf4erI9OLwjwRw7Tv4WWLSP+ALa7I2Rs0RkvkzRhEZQdWz8iHCms48Z54RMEAs2gABkcGRYVFHBqXv8Sh2GBKgRzDNW00SIClag1GzugVNShk5OUd9zxMj4cI/2UhoSdUwVBJpn53EGPMTOd0hh8hAUwuodC1UoVZ1ud+9UjWog+jvb6hIv9D9egxT8aK8kfte5tD5kiTYCfpaqvUPyABb7beH9/57zlUi4zBwE+FnrsyUdDOfC/c+iNVqzVOVKoH06vwLLDsr1Y6q+VZLgX7+dxQDW29SDGmKsVNPr1iLSyB1Bkui0CDGAEdkEmmpeXghQwe/Td0slnRzyDQ5NwdH8L05htXHLGFqIWk0fQ+YPhc9P3+3VpvsoDrSYR9KFNHWpKvWdgfdV998/e3sF2w70Nb35f7pAFNdfn0z49QpNLHDVnqh0FL9IUXU1kG1eZmDO1rYzB06+imrN30/hNG9JmkiPLIVm9flE2ZFuRZoFzf0hIyXzkJfqXcgtLJGCSHyi1SR/URF9pNJYP8b2adeaEwwoqc6AQ2XS+obXTi7bdi6eGmWs8MmXyEN3+UFPaOzQGOdBcQ52BeeDGXqgB3jRNIlirBe7Vs2TYNkXeiGsFtcTS0w3XLPAp8x0MQ86uluEVPse66t9R4ZdbVd88Vw4tNpqTcZIZekEUrFQi/2fAS3hqPIwBMXSIqBp6yqVN1shjHH5NejpvUyOtdZzNyX93lNVfwPn+vGrHjDpiuxSXAvFIHDhSdFRNghvcSPnMQLhKRbo3dbg0bvNgYWsZ4xcz7txuIYrGOfOzz0/JiTUZB4acwdVwiNa43abfVGrbVnMA79fCXG3WGvPdLLlM5nc6czWiBpKFXxq07ptqBOudg7hDI49vRUOAJYuJggZiQDrqFSxA6CMgj2/8PCNqDty6oCjqlViaL8XOb0iC4JjWfU51Xb6CiLunz8CiPop0cnnIb6Ryd7YGpf4bqTUmhPSpSSYXxuSsBtk3ztGkQwPAJS7QevkWdOa2SLNLSn378smfqMTZF0yRzV8Ez4SToNf4i5drmWByfHdk1B5PxGQW+Xgix5jMlhTB5jchlS0CeNOiK74GREQROOGVLQhKFuFHSjoH+CgkSELwM3CrqCgjDJGQoKbhT0pikIH0oE+CAMgOmpF3HAof580grxHaWRdt9opjr9wWUyj6UWHnvSD4yZ1N9pBiIXshuF3Cjktd9izmHo61CIxeSAPpWd/gRQSwECFAAUAAAACADFq5BcxazFs1IAAABeAAAADQAAAAAAAAAAAAAAAAAAAAAAbWFuaWZlc3QuanNvblBLAQIUABQAAAAIAMWrkFy0YIRaVwQAANUcAAARAAAAAAAAAAAAAAAAAH0AAABhL01haW4gU2NlbmUuanNvblBLBQYAAAAAAgACAHoAAAADBQAAAAA=";
|
|
5688
5752
|
var DEFAULT_SIZE = 36;
|
|
5689
5753
|
function useElapsedSeconds(isStreaming) {
|
|
5690
|
-
const [elapsed, setElapsed] =
|
|
5691
|
-
const startRef =
|
|
5692
|
-
|
|
5754
|
+
const [elapsed, setElapsed] = React.useState(0);
|
|
5755
|
+
const startRef = React.useRef(null);
|
|
5756
|
+
React.useEffect(() => {
|
|
5693
5757
|
if (!isStreaming) {
|
|
5694
5758
|
startRef.current = null;
|
|
5695
5759
|
setElapsed(0);
|
|
@@ -5748,22 +5812,22 @@ function StreamingIndicatorV2({
|
|
|
5748
5812
|
) });
|
|
5749
5813
|
}
|
|
5750
5814
|
function ImageLightboxV2({ src, alt, onClose }) {
|
|
5751
|
-
const [isMounted, setIsMounted] =
|
|
5752
|
-
const [isImageLoaded, setIsImageLoaded] =
|
|
5753
|
-
|
|
5815
|
+
const [isMounted, setIsMounted] = React.useState(false);
|
|
5816
|
+
const [isImageLoaded, setIsImageLoaded] = React.useState(false);
|
|
5817
|
+
React.useEffect(() => {
|
|
5754
5818
|
setIsMounted(true);
|
|
5755
5819
|
return () => setIsMounted(false);
|
|
5756
5820
|
}, []);
|
|
5757
|
-
|
|
5821
|
+
React.useEffect(() => {
|
|
5758
5822
|
setIsImageLoaded(false);
|
|
5759
5823
|
}, [src]);
|
|
5760
|
-
const handleKeyDown =
|
|
5824
|
+
const handleKeyDown = React.useCallback(
|
|
5761
5825
|
(e) => {
|
|
5762
5826
|
if (e.key === "Escape") onClose();
|
|
5763
5827
|
},
|
|
5764
5828
|
[onClose]
|
|
5765
5829
|
);
|
|
5766
|
-
|
|
5830
|
+
React.useEffect(() => {
|
|
5767
5831
|
if (!src || typeof document === "undefined") return;
|
|
5768
5832
|
document.addEventListener("keydown", handleKeyDown);
|
|
5769
5833
|
const previousOverflow = document.body.style.overflow;
|
|
@@ -5912,10 +5976,10 @@ function TraceTimelineModal({
|
|
|
5912
5976
|
apiBaseUrl,
|
|
5913
5977
|
apiHeaders
|
|
5914
5978
|
}) {
|
|
5915
|
-
const [trace, setTrace] =
|
|
5916
|
-
const [loading, setLoading] =
|
|
5917
|
-
const [error, setError] =
|
|
5918
|
-
|
|
5979
|
+
const [trace, setTrace] = React.useState(null);
|
|
5980
|
+
const [loading, setLoading] = React.useState(false);
|
|
5981
|
+
const [error, setError] = React.useState(null);
|
|
5982
|
+
React.useEffect(() => {
|
|
5919
5983
|
if (!open || !executionId) return;
|
|
5920
5984
|
const ctrl = new AbortController();
|
|
5921
5985
|
setLoading(true);
|
|
@@ -5937,7 +6001,7 @@ function TraceTimelineModal({
|
|
|
5937
6001
|
}).finally(() => setLoading(false));
|
|
5938
6002
|
return () => ctrl.abort();
|
|
5939
6003
|
}, [open, executionId, apiBaseUrl, apiHeaders]);
|
|
5940
|
-
const totalMs =
|
|
6004
|
+
const totalMs = React.useMemo(() => {
|
|
5941
6005
|
if (!trace) return 0;
|
|
5942
6006
|
const meta = trace.runMetadata?.totalTimeMs;
|
|
5943
6007
|
if (typeof meta === "number" && meta > 0) return meta;
|
|
@@ -5946,7 +6010,7 @@ function TraceTimelineModal({
|
|
|
5946
6010
|
if (starts.length === 0 || ends.length === 0) return 0;
|
|
5947
6011
|
return Math.max(...ends) - Math.min(...starts);
|
|
5948
6012
|
}, [trace]);
|
|
5949
|
-
const accountedMs =
|
|
6013
|
+
const accountedMs = React.useMemo(
|
|
5950
6014
|
() => (trace?.pipelineSteps ?? []).reduce(
|
|
5951
6015
|
(sum, s) => sum + (s.durationMs ?? 0),
|
|
5952
6016
|
0
|
|
@@ -6207,12 +6271,12 @@ var NOOP_ASYNC = async () => {
|
|
|
6207
6271
|
var NOOP = () => {
|
|
6208
6272
|
};
|
|
6209
6273
|
function useSentryChatCallbacks(callbacks, config) {
|
|
6210
|
-
const sentryCtxRef =
|
|
6211
|
-
const callbacksRef =
|
|
6274
|
+
const sentryCtxRef = React.useRef({});
|
|
6275
|
+
const callbacksRef = React.useRef(callbacks);
|
|
6212
6276
|
callbacksRef.current = callbacks;
|
|
6213
6277
|
const initialSessionId = config.initialSessionId;
|
|
6214
6278
|
const apiHeaders = config.api.headers;
|
|
6215
|
-
|
|
6279
|
+
React.useEffect(() => {
|
|
6216
6280
|
sentryCtxRef.current.agentId = config.agentId;
|
|
6217
6281
|
sentryCtxRef.current.sessionOwnerId = config.sessionParams?.id;
|
|
6218
6282
|
if (initialSessionId) {
|
|
@@ -6228,13 +6292,13 @@ function useSentryChatCallbacks(callbacks, config) {
|
|
|
6228
6292
|
initialSessionId,
|
|
6229
6293
|
apiHeaders
|
|
6230
6294
|
]);
|
|
6231
|
-
|
|
6295
|
+
React.useEffect(() => {
|
|
6232
6296
|
const endpoint = config.api.streamEndpoint || "/api/playground/ask/stream";
|
|
6233
6297
|
return subscribeToCfRay(endpoint, (cfRay) => {
|
|
6234
6298
|
sentryCtxRef.current.cfRay = cfRay;
|
|
6235
6299
|
});
|
|
6236
6300
|
}, [config.api.streamEndpoint]);
|
|
6237
|
-
return
|
|
6301
|
+
return React.useMemo(
|
|
6238
6302
|
() => ({
|
|
6239
6303
|
onMessageSent: (msg) => callbacksRef.current?.onMessageSent?.(msg),
|
|
6240
6304
|
onStreamStart: () => callbacksRef.current?.onStreamStart?.(),
|
|
@@ -6283,7 +6347,7 @@ function useSentryChatCallbacks(callbacks, config) {
|
|
|
6283
6347
|
[]
|
|
6284
6348
|
);
|
|
6285
6349
|
}
|
|
6286
|
-
var PaymanChatInner =
|
|
6350
|
+
var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
|
|
6287
6351
|
config,
|
|
6288
6352
|
callbacks = {},
|
|
6289
6353
|
className,
|
|
@@ -6294,18 +6358,18 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6294
6358
|
hasMoreMessages = false,
|
|
6295
6359
|
chat
|
|
6296
6360
|
}, ref) {
|
|
6297
|
-
const [inputValue, setInputValue] =
|
|
6298
|
-
const prevInputValueRef =
|
|
6299
|
-
const [hasEverSentMessage, setHasEverSentMessage] =
|
|
6300
|
-
const [lightboxSrc, setLightboxSrc] =
|
|
6301
|
-
const [lightboxAlt, setLightboxAlt] =
|
|
6302
|
-
const [editingMessageId, setEditingMessageId] =
|
|
6303
|
-
const [isResetSessionConfirmOpen, setIsResetSessionConfirmOpen] =
|
|
6304
|
-
const [analysisMode, setAnalysisMode] =
|
|
6305
|
-
const chatInputV2Ref =
|
|
6306
|
-
const messageListV2Ref =
|
|
6307
|
-
const resetToEmptyStateRef =
|
|
6308
|
-
|
|
6361
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
6362
|
+
const prevInputValueRef = React.useRef(inputValue);
|
|
6363
|
+
const [hasEverSentMessage, setHasEverSentMessage] = React.useState(false);
|
|
6364
|
+
const [lightboxSrc, setLightboxSrc] = React.useState(null);
|
|
6365
|
+
const [lightboxAlt, setLightboxAlt] = React.useState("");
|
|
6366
|
+
const [editingMessageId, setEditingMessageId] = React.useState(null);
|
|
6367
|
+
const [isResetSessionConfirmOpen, setIsResetSessionConfirmOpen] = React.useState(false);
|
|
6368
|
+
const [analysisMode, setAnalysisMode] = React.useState("fast");
|
|
6369
|
+
const chatInputV2Ref = React.useRef(null);
|
|
6370
|
+
const messageListV2Ref = React.useRef(null);
|
|
6371
|
+
const resetToEmptyStateRef = React.useRef(false);
|
|
6372
|
+
React.useEffect(() => {
|
|
6309
6373
|
if (config.sentryDsn) {
|
|
6310
6374
|
initSentryIfNeeded(config.sentryDsn);
|
|
6311
6375
|
}
|
|
@@ -6321,7 +6385,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6321
6385
|
getSessionId,
|
|
6322
6386
|
getMessages
|
|
6323
6387
|
} = chat;
|
|
6324
|
-
|
|
6388
|
+
React.useEffect(() => {
|
|
6325
6389
|
if (resetToEmptyStateRef.current) {
|
|
6326
6390
|
if (messages.length === 0) {
|
|
6327
6391
|
setHasEverSentMessage(false);
|
|
@@ -6333,7 +6397,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6333
6397
|
setHasEverSentMessage(true);
|
|
6334
6398
|
}
|
|
6335
6399
|
}, [messages.length, hasEverSentMessage]);
|
|
6336
|
-
|
|
6400
|
+
React.useEffect(() => {
|
|
6337
6401
|
if (!editingMessageId) return;
|
|
6338
6402
|
const editingMessageStillExists = messages.some(
|
|
6339
6403
|
(message) => message.id === editingMessageId && message.role === "user"
|
|
@@ -6346,7 +6410,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6346
6410
|
const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
|
|
6347
6411
|
const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
|
|
6348
6412
|
const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
|
|
6349
|
-
const
|
|
6413
|
+
const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
|
|
6350
6414
|
const dismissNotification = chat.dismissNotification ?? NOOP;
|
|
6351
6415
|
const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
|
|
6352
6416
|
const {
|
|
@@ -6372,7 +6436,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6372
6436
|
}
|
|
6373
6437
|
}
|
|
6374
6438
|
);
|
|
6375
|
-
const contextValue =
|
|
6439
|
+
const contextValue = React.useMemo(
|
|
6376
6440
|
() => ({
|
|
6377
6441
|
resetSession,
|
|
6378
6442
|
clearMessages,
|
|
@@ -6399,8 +6463,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6399
6463
|
onAttachFileClick,
|
|
6400
6464
|
onMessageFeedback
|
|
6401
6465
|
} = callbacks;
|
|
6402
|
-
const [debugTraceExecutionId, setDebugTraceExecutionId] =
|
|
6403
|
-
const handleSubmitFeedback =
|
|
6466
|
+
const [debugTraceExecutionId, setDebugTraceExecutionId] = React.useState(null);
|
|
6467
|
+
const handleSubmitFeedback = React.useCallback(
|
|
6404
6468
|
async ({ messageId, executionId, feedback, details }) => {
|
|
6405
6469
|
if (!executionId) {
|
|
6406
6470
|
throw new Error("Cannot submit feedback before the response completes");
|
|
@@ -6431,14 +6495,14 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6431
6495
|
onMessageFeedback
|
|
6432
6496
|
]
|
|
6433
6497
|
);
|
|
6434
|
-
const onExecutionTraceClick =
|
|
6498
|
+
const onExecutionTraceClick = React.useMemo(() => {
|
|
6435
6499
|
if (!config.debug) return rawOnExecutionTraceClick;
|
|
6436
6500
|
return (data) => {
|
|
6437
6501
|
rawOnExecutionTraceClick?.(data);
|
|
6438
6502
|
if (data.executionId) setDebugTraceExecutionId(data.executionId);
|
|
6439
6503
|
};
|
|
6440
6504
|
}, [config.debug, rawOnExecutionTraceClick]);
|
|
6441
|
-
const performResetSession =
|
|
6505
|
+
const performResetSession = React.useCallback(() => {
|
|
6442
6506
|
resetToEmptyStateRef.current = true;
|
|
6443
6507
|
setEditingMessageId(null);
|
|
6444
6508
|
setIsResetSessionConfirmOpen(false);
|
|
@@ -6459,13 +6523,13 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6459
6523
|
resetSession,
|
|
6460
6524
|
stopRecording
|
|
6461
6525
|
]);
|
|
6462
|
-
const requestResetSession =
|
|
6526
|
+
const requestResetSession = React.useCallback(() => {
|
|
6463
6527
|
setIsResetSessionConfirmOpen(true);
|
|
6464
6528
|
}, []);
|
|
6465
|
-
const closeResetSessionConfirm =
|
|
6529
|
+
const closeResetSessionConfirm = React.useCallback(() => {
|
|
6466
6530
|
setIsResetSessionConfirmOpen(false);
|
|
6467
6531
|
}, []);
|
|
6468
|
-
|
|
6532
|
+
React.useImperativeHandle(ref, () => ({
|
|
6469
6533
|
resetSession: performResetSession,
|
|
6470
6534
|
clearMessages,
|
|
6471
6535
|
cancelStream,
|
|
@@ -6504,7 +6568,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6504
6568
|
slashCommands: slashCommandsConfig,
|
|
6505
6569
|
commandPermissions
|
|
6506
6570
|
} = config;
|
|
6507
|
-
const messageActions =
|
|
6571
|
+
const messageActions = React.useMemo(
|
|
6508
6572
|
() => ({
|
|
6509
6573
|
userMessageActions: {
|
|
6510
6574
|
copy: messageActionsConfig?.userMessageActions?.copy ?? true,
|
|
@@ -6520,18 +6584,18 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6520
6584
|
}),
|
|
6521
6585
|
[messageActionsConfig]
|
|
6522
6586
|
);
|
|
6523
|
-
const slashCommands =
|
|
6587
|
+
const slashCommands = React.useMemo(
|
|
6524
6588
|
() => enableSlashCommands ? filterSlashCommands(
|
|
6525
6589
|
slashCommandsConfig ?? DEFAULT_SLASH_COMMANDS,
|
|
6526
6590
|
commandPermissions
|
|
6527
6591
|
) : [],
|
|
6528
6592
|
[commandPermissions, enableSlashCommands, slashCommandsConfig]
|
|
6529
6593
|
);
|
|
6530
|
-
const isSessionParamsConfigured =
|
|
6594
|
+
const isSessionParamsConfigured = React.useMemo(() => {
|
|
6531
6595
|
if (!sessionParams) return false;
|
|
6532
6596
|
return !!(sessionParams.id?.trim() && sessionParams.name?.trim());
|
|
6533
6597
|
}, [sessionParams?.id, sessionParams?.name]);
|
|
6534
|
-
|
|
6598
|
+
React.useEffect(() => {
|
|
6535
6599
|
const wasEmpty = prevInputValueRef.current.trim() === "";
|
|
6536
6600
|
const isEmpty2 = inputValue.trim() === "";
|
|
6537
6601
|
prevInputValueRef.current = inputValue;
|
|
@@ -6598,7 +6662,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6598
6662
|
void sendMessage(text.trim(), { analysisMode: effectiveAnalysisMode });
|
|
6599
6663
|
}
|
|
6600
6664
|
};
|
|
6601
|
-
const handleVoicePress =
|
|
6665
|
+
const handleVoicePress = React.useCallback(async () => {
|
|
6602
6666
|
if (!voiceAvailable) return;
|
|
6603
6667
|
if (isRecording) {
|
|
6604
6668
|
stopRecording();
|
|
@@ -6607,7 +6671,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6607
6671
|
clearTranscript();
|
|
6608
6672
|
await startRecording();
|
|
6609
6673
|
}, [clearTranscript, isRecording, startRecording, stopRecording, voiceAvailable]);
|
|
6610
|
-
const handleEditMessageDraft =
|
|
6674
|
+
const handleEditMessageDraft = React.useCallback((messageId) => {
|
|
6611
6675
|
const targetMessage = messages.find((message) => message.id === messageId);
|
|
6612
6676
|
if (!targetMessage?.content.trim()) return;
|
|
6613
6677
|
setEditingMessageId(messageId);
|
|
@@ -6616,7 +6680,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6616
6680
|
messageListV2Ref.current?.scrollToBottom("smooth");
|
|
6617
6681
|
});
|
|
6618
6682
|
}, [messages]);
|
|
6619
|
-
const handleRetryUserMessage =
|
|
6683
|
+
const handleRetryUserMessage = React.useCallback((messageId) => {
|
|
6620
6684
|
if (isWaitingForResponse) return;
|
|
6621
6685
|
const targetMessage = messages.find(
|
|
6622
6686
|
(message) => message.id === messageId && message.role === "user"
|
|
@@ -6633,7 +6697,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6633
6697
|
});
|
|
6634
6698
|
});
|
|
6635
6699
|
}, [isWaitingForResponse, messages, sendMessage, effectiveAnalysisMode]);
|
|
6636
|
-
const handleClearEditing =
|
|
6700
|
+
const handleClearEditing = React.useCallback(() => {
|
|
6637
6701
|
setEditingMessageId(null);
|
|
6638
6702
|
}, []);
|
|
6639
6703
|
return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -6747,7 +6811,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6747
6811
|
onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
|
|
6748
6812
|
onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
|
|
6749
6813
|
onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
|
|
6750
|
-
onExpireUserAction: isUserActionSupported ?
|
|
6814
|
+
onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
|
|
6751
6815
|
onDismissNotification: dismissNotification,
|
|
6752
6816
|
onSubmitFeedback: handleSubmitFeedback
|
|
6753
6817
|
}
|
|
@@ -6823,7 +6887,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6823
6887
|
}
|
|
6824
6888
|
) });
|
|
6825
6889
|
});
|
|
6826
|
-
var PaymanChat =
|
|
6890
|
+
var PaymanChat = React.forwardRef(
|
|
6827
6891
|
function PaymanChat2(props, ref) {
|
|
6828
6892
|
const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
|
|
6829
6893
|
const chat = useChatV2(props.config, mergedCallbacks);
|