@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.4 → 0.4.5
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/aitPermissionPropagation.d.ts +16 -0
- package/dist/aitPermissionPropagation.d.ts.map +1 -0
- package/dist/aitPermissionPropagation.js +21 -0
- package/dist/context/ChatBotContext.d.ts.map +1 -1
- package/dist/context/ChatBotContext.js +97 -58
- package/dist/piiMessageState.d.ts +16 -0
- package/dist/piiMessageState.d.ts.map +1 -0
- package/dist/piiMessageState.js +18 -0
- package/dist/suggestionState.d.ts +4 -0
- package/dist/suggestionState.d.ts.map +1 -0
- package/dist/suggestionState.js +14 -0
- package/dist/types/ChatBotTypes.d.ts +1 -1
- package/dist/types/ChatBotTypes.d.ts.map +1 -1
- package/dist/ui/PresetMessages.d.ts.map +1 -1
- package/dist/ui/PresetMessages.js +7 -4
- package/package.json +1 -1
- package/src/aitPermissionPropagation.ts +40 -0
- package/src/context/ChatBotContext.tsx +105 -58
- package/src/piiMessageState.ts +35 -0
- package/src/suggestionState.ts +20 -0
- package/src/types/ChatBotTypes.ts +1 -1
- package/src/ui/PresetMessages.tsx +9 -6
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type WaitForAITPermissionsOptions<T> = {
|
|
2
|
+
readAIT: () => Promise<T | undefined>;
|
|
3
|
+
getPermissions: (ait: T) => readonly string[];
|
|
4
|
+
expectedPermissions: readonly string[];
|
|
5
|
+
attempts?: number;
|
|
6
|
+
delayMs?: number;
|
|
7
|
+
sleep?: (delayMs: number) => Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export declare function includesAITPermissions(actualPermissions: readonly string[], expectedPermissions: readonly string[]): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Wait until the AIT read path can observe a newly submitted permission set.
|
|
12
|
+
* Stale reads are deliberately discarded so callers never downgrade their
|
|
13
|
+
* optimistic local state while the chain/RPC read path is catching up.
|
|
14
|
+
*/
|
|
15
|
+
export declare function waitForAITPermissions<T>({ readAIT, getPermissions, expectedPermissions, attempts, delayMs, sleep, }: WaitForAITPermissionsOptions<T>): Promise<T | undefined>;
|
|
16
|
+
//# sourceMappingURL=aitPermissionPropagation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aitPermissionPropagation.d.ts","sourceRoot":"","sources":["../src/aitPermissionPropagation.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,4BAA4B,CAAC,CAAC,IAAI;IAC5C,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACtC,cAAc,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,SAAS,MAAM,EAAE,CAAC;IAC9C,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,iBAAiB,EAAE,SAAS,MAAM,EAAE,EACpC,mBAAmB,EAAE,SAAS,MAAM,EAAE,GACrC,OAAO,CAGT;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,EAC7C,OAAO,EACP,cAAc,EACd,mBAAmB,EACnB,QAAa,EACb,OAAa,EACb,KAA6E,GAC9E,EAAE,4BAA4B,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAU1D"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function includesAITPermissions(actualPermissions, expectedPermissions) {
|
|
2
|
+
return actualPermissions.includes('ALL')
|
|
3
|
+
|| expectedPermissions.every((permission) => actualPermissions.includes(permission));
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Wait until the AIT read path can observe a newly submitted permission set.
|
|
7
|
+
* Stale reads are deliberately discarded so callers never downgrade their
|
|
8
|
+
* optimistic local state while the chain/RPC read path is catching up.
|
|
9
|
+
*/
|
|
10
|
+
export async function waitForAITPermissions({ readAIT, getPermissions, expectedPermissions, attempts = 10, delayMs = 750, sleep = (duration) => new Promise((resolve) => setTimeout(resolve, duration)), }) {
|
|
11
|
+
const totalAttempts = Math.max(1, attempts);
|
|
12
|
+
for (let attempt = 0; attempt < totalAttempts; attempt += 1) {
|
|
13
|
+
const ait = await readAIT();
|
|
14
|
+
if (ait && includesAITPermissions(getPermissions(ait), expectedPermissions)) {
|
|
15
|
+
return ait;
|
|
16
|
+
}
|
|
17
|
+
if (attempt + 1 < totalAttempts)
|
|
18
|
+
await sleep(delayMs);
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatBotContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatBotContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA8B/B,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAEb,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatBotContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatBotContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA8B/B,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAEb,MAAM,uBAAuB,CAAC;AAW/B,eAAO,MAAM,UAAU,0BAMtB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAohGlD,CAAC"}
|
|
@@ -6,8 +6,11 @@ import { flushSync } from 'react-dom';
|
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid';
|
|
7
7
|
import { createNxtlinqApi, setApiHosts, synthesizeSpeechToBuffer, streamSpeechToAudioContext, useLocalStorage, useSessionStorage, useSpeechToTextFromMic, useVoiceMode, metakeepClient, getEthers, sleep, walletTextUtils, } from '@bytexbyte/nxtlinq-ai-agent-web-development';
|
|
8
8
|
import { authorizeTextFrontendTool, hasRecordedWalletVerification, validateWalletSession, } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
9
|
+
import { waitForAITPermissions } from '../aitPermissionPropagation';
|
|
9
10
|
import { AuthorizationLoadingState } from '../authorizationLoadingState';
|
|
10
11
|
import { PendingTextToolRetryController } from '../pendingTextToolRetry';
|
|
12
|
+
import { completePiiScan } from '../piiMessageState';
|
|
13
|
+
import { filterSuggestions } from '../suggestionState';
|
|
11
14
|
const MIC_ENABLED_SESSION_KEY = 'chatbot-mic-enabled';
|
|
12
15
|
const ChatBotContext = React.createContext(undefined);
|
|
13
16
|
export const useChatBot = () => {
|
|
@@ -187,6 +190,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
187
190
|
const textInputRef = React.useRef(null);
|
|
188
191
|
const lastPartialRangeRef = React.useRef(null);
|
|
189
192
|
const lastAutoSentTranscriptRef = React.useRef('');
|
|
193
|
+
const presetSendInFlightRef = React.useRef(false);
|
|
190
194
|
const autoSendTimerRef = React.useRef(null);
|
|
191
195
|
const isCorrectingRef = React.useRef(false);
|
|
192
196
|
const lastCustomErrorRef = React.useRef(undefined);
|
|
@@ -973,6 +977,48 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
973
977
|
setIsAITLoading(false);
|
|
974
978
|
}
|
|
975
979
|
};
|
|
980
|
+
const waitForPersistedAITPermissions = async (expectedPermissions) => {
|
|
981
|
+
const currentHitAddress = hitAddressRef.current || hitAddress;
|
|
982
|
+
const currentToken = nxtlinqAITServiceAccessTokenRef.current || nxtlinqAITServiceAccessToken;
|
|
983
|
+
if (!currentHitAddress || !currentToken)
|
|
984
|
+
return false;
|
|
985
|
+
authorizationLoadingStateRef.current.setAITLoading(true);
|
|
986
|
+
setIsAITLoading(true);
|
|
987
|
+
try {
|
|
988
|
+
const persistedAIT = await waitForAITPermissions({
|
|
989
|
+
expectedPermissions,
|
|
990
|
+
readAIT: async () => {
|
|
991
|
+
try {
|
|
992
|
+
const response = await nxtlinqApi.ait.getAITByServiceIdAndController({
|
|
993
|
+
serviceId,
|
|
994
|
+
controller: currentHitAddress,
|
|
995
|
+
customUsername: (!requireWalletIDVVerification && customUsername)
|
|
996
|
+
? getFinalCustomUsername(customUsername)
|
|
997
|
+
: undefined,
|
|
998
|
+
}, currentToken);
|
|
999
|
+
return 'error' in response ? undefined : response;
|
|
1000
|
+
}
|
|
1001
|
+
catch (error) {
|
|
1002
|
+
console.warn('AIT permission propagation check failed:', error);
|
|
1003
|
+
return undefined;
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
getPermissions: (currentAIT) => currentAIT.metadata?.permissions || [],
|
|
1007
|
+
});
|
|
1008
|
+
if (!persistedAIT)
|
|
1009
|
+
return false;
|
|
1010
|
+
const persistedPermissions = persistedAIT.metadata?.permissions || [];
|
|
1011
|
+
aitRef.current = persistedAIT;
|
|
1012
|
+
permissionsRef.current = persistedPermissions;
|
|
1013
|
+
setAit(persistedAIT);
|
|
1014
|
+
setPermissions(persistedPermissions);
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
1017
|
+
finally {
|
|
1018
|
+
authorizationLoadingStateRef.current.setAITLoading(false);
|
|
1019
|
+
setIsAITLoading(false);
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
976
1022
|
// Check if user needs to sign in
|
|
977
1023
|
const isNeedSignInWithWallet = React.useMemo(() => {
|
|
978
1024
|
if (!hitAddress)
|
|
@@ -1252,7 +1298,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1252
1298
|
}
|
|
1253
1299
|
return effectiveAvailableModels[safeIndex];
|
|
1254
1300
|
}, [effectiveAvailableModels, selectedModelIndex]);
|
|
1255
|
-
const updateSuggestions = React.useCallback(async (pseudoId, externalId) => {
|
|
1301
|
+
const updateSuggestions = React.useCallback(async (pseudoId, externalId, lastUserMessage) => {
|
|
1256
1302
|
const result = await nxtlinqApi.agent.generateSuggestions({
|
|
1257
1303
|
...authFields(),
|
|
1258
1304
|
pseudoId,
|
|
@@ -1263,11 +1309,8 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1263
1309
|
setSuggestions([]);
|
|
1264
1310
|
return;
|
|
1265
1311
|
}
|
|
1266
|
-
setSuggestions(result.suggestions.map((sug) => ({
|
|
1267
|
-
|
|
1268
|
-
autoSend: true
|
|
1269
|
-
})));
|
|
1270
|
-
}, []);
|
|
1312
|
+
setSuggestions(filterSuggestions(result.suggestions.map((sug) => ({ text: sug, autoSend: true })), lastUserMessage));
|
|
1313
|
+
}, [nxtlinqApi, authFields, setSuggestions]);
|
|
1271
1314
|
// Updated sendMessage function to support different AI models and attachments
|
|
1272
1315
|
const sendMessage = async (content, retryCount = 0, isPresetMessage = false, attachments, clientPipelineOverride) => {
|
|
1273
1316
|
const hasContent = content.trim() || (attachments && attachments.length > 0);
|
|
@@ -1387,15 +1430,15 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1387
1430
|
const updated = [...prev];
|
|
1388
1431
|
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1389
1432
|
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1433
|
+
if (step === 'scan_complete') {
|
|
1434
|
+
// PII work is complete independently of any later tool
|
|
1435
|
+
// authorization. Never leave the bubble in Sending while
|
|
1436
|
+
// waiting for wallet recovery or permission elevation.
|
|
1437
|
+
updated[i] = completePiiScan(updated[i], data);
|
|
1438
|
+
}
|
|
1439
|
+
else {
|
|
1440
|
+
updated[i] = { ...updated[i], piiStep };
|
|
1397
1441
|
}
|
|
1398
|
-
updated[i] = { ...updated[i], ...patch };
|
|
1399
1442
|
break;
|
|
1400
1443
|
}
|
|
1401
1444
|
}
|
|
@@ -1403,6 +1446,26 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1403
1446
|
});
|
|
1404
1447
|
} : undefined,
|
|
1405
1448
|
});
|
|
1449
|
+
// PII scanning is complete when the Agent response arrives, regardless
|
|
1450
|
+
// of whether a later frontend-tool authorization step can continue.
|
|
1451
|
+
if (piiDisplayMode === 'redacted') {
|
|
1452
|
+
const anonymizedUserMsg = response.piiProtection?.anonymizedUserMessage;
|
|
1453
|
+
const piiMappingData = response.piiProtection?.mapping ?? undefined;
|
|
1454
|
+
setMessages(prev => {
|
|
1455
|
+
const updated = [...prev];
|
|
1456
|
+
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1457
|
+
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
1458
|
+
updated[i] = completePiiScan(updated[i], {
|
|
1459
|
+
entityCount: piiMappingData ? Object.keys(piiMappingData).length : 0,
|
|
1460
|
+
anonymizedUserMessage: anonymizedUserMsg,
|
|
1461
|
+
mapping: piiMappingData,
|
|
1462
|
+
});
|
|
1463
|
+
break;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return updated;
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1406
1469
|
if (!('error' in response && response.error)) {
|
|
1407
1470
|
const tr = response;
|
|
1408
1471
|
if (tr.ttsVoice &&
|
|
@@ -1527,7 +1590,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1527
1590
|
if (!replyText.trim()) {
|
|
1528
1591
|
replyText = 'Sorry, I cannot understand your question';
|
|
1529
1592
|
}
|
|
1530
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1593
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1531
1594
|
setMessages(prev => prev.map(m => m.id === streamAssistantId
|
|
1532
1595
|
? {
|
|
1533
1596
|
...m,
|
|
@@ -1666,7 +1729,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1666
1729
|
// Don't block the UI if update fails
|
|
1667
1730
|
}
|
|
1668
1731
|
}
|
|
1669
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1732
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1670
1733
|
// Skip creating a new botResponse since we already updated the streaming message
|
|
1671
1734
|
}
|
|
1672
1735
|
else {
|
|
@@ -1705,7 +1768,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1705
1768
|
// Don't block the UI if update fails
|
|
1706
1769
|
}
|
|
1707
1770
|
}
|
|
1708
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1771
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1709
1772
|
const newBotResponse = {
|
|
1710
1773
|
id: (Date.now() + 1).toString(),
|
|
1711
1774
|
content: mergedContent,
|
|
@@ -1731,7 +1794,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1731
1794
|
.map((item) => item.text)
|
|
1732
1795
|
.join(' ') || 'Sorry, I cannot understand your question'
|
|
1733
1796
|
: response.reply || 'Sorry, I cannot understand your question';
|
|
1734
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1797
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1735
1798
|
const newBotResponse = {
|
|
1736
1799
|
id: (Date.now() + 1).toString(),
|
|
1737
1800
|
content: replyText,
|
|
@@ -1769,38 +1832,6 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1769
1832
|
setMessages(prev => [...prev, newBotResponse]);
|
|
1770
1833
|
botResponse = newBotResponse;
|
|
1771
1834
|
}
|
|
1772
|
-
// ===== PII Protection: Update user message with anonymized version =====
|
|
1773
|
-
const anonymizedUserMsg = response.piiProtection?.anonymizedUserMessage;
|
|
1774
|
-
const piiMappingData = response.piiProtection?.mapping ?? undefined;
|
|
1775
|
-
if (anonymizedUserMsg) {
|
|
1776
|
-
setMessages(prev => {
|
|
1777
|
-
const updated = [...prev];
|
|
1778
|
-
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1779
|
-
if (updated[i].role === 'user') {
|
|
1780
|
-
updated[i] = {
|
|
1781
|
-
...updated[i],
|
|
1782
|
-
piiProtection: { anonymizedContent: anonymizedUserMsg, mapping: piiMappingData },
|
|
1783
|
-
piiStatus: 'complete',
|
|
1784
|
-
};
|
|
1785
|
-
break;
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
return updated;
|
|
1789
|
-
});
|
|
1790
|
-
}
|
|
1791
|
-
else if (piiDisplayMode === 'redacted') {
|
|
1792
|
-
// No PII detected — set piiStatus to 'none' to trigger "No sensitive data" indicator
|
|
1793
|
-
setMessages(prev => {
|
|
1794
|
-
const updated = [...prev];
|
|
1795
|
-
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1796
|
-
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
1797
|
-
updated[i] = { ...updated[i], piiStatus: 'none' };
|
|
1798
|
-
break;
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
return updated;
|
|
1802
|
-
});
|
|
1803
|
-
}
|
|
1804
1835
|
// Execute redirect after all message processing is complete
|
|
1805
1836
|
if (redirectUrl) {
|
|
1806
1837
|
// Use setTimeout to ensure the message is displayed before redirect
|
|
@@ -2141,20 +2172,18 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2141
2172
|
return { url: result.url };
|
|
2142
2173
|
}, [nxtlinqApi, authFields, pseudoId]);
|
|
2143
2174
|
// Handle preset message
|
|
2144
|
-
const handlePresetMessage = (message) => {
|
|
2175
|
+
const handlePresetMessage = async (message) => {
|
|
2145
2176
|
// If preset is configured as auto-send, avoid duplicate sends when user clicks repeatedly
|
|
2146
2177
|
if (message.autoSend) {
|
|
2147
2178
|
const trimmedText = (message.text || '').trim();
|
|
2148
2179
|
if (!trimmedText)
|
|
2149
2180
|
return;
|
|
2150
2181
|
// Prevent sending messages while AI Agent is processing
|
|
2151
|
-
if (isLoading) {
|
|
2152
|
-
return;
|
|
2153
|
-
}
|
|
2154
|
-
// If this exact preset text was just sent (by auto-send / manual / preset), skip to prevent duplicates
|
|
2155
|
-
if (lastAutoSentTranscriptRef.current === trimmedText) {
|
|
2182
|
+
if (isLoading || presetSendInFlightRef.current) {
|
|
2156
2183
|
return;
|
|
2157
2184
|
}
|
|
2185
|
+
presetSendInFlightRef.current = true;
|
|
2186
|
+
setSuggestions(previous => filterSuggestions(previous, trimmedText));
|
|
2158
2187
|
// For preset messages, we need to add the user message first since sendMessage won't add it on retries
|
|
2159
2188
|
const userMessage = {
|
|
2160
2189
|
id: Date.now().toString(),
|
|
@@ -2171,7 +2200,12 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2171
2200
|
// Mark as last sent to guard against rapid re-clicks and other duplicate flows
|
|
2172
2201
|
lastAutoSentTranscriptRef.current = trimmedText;
|
|
2173
2202
|
// Pass a flag to indicate this is a preset message so sendMessage won't add user message again
|
|
2174
|
-
|
|
2203
|
+
try {
|
|
2204
|
+
await sendMessage(trimmedText, 0, true);
|
|
2205
|
+
}
|
|
2206
|
+
finally {
|
|
2207
|
+
presetSendInFlightRef.current = false;
|
|
2208
|
+
}
|
|
2175
2209
|
}
|
|
2176
2210
|
else {
|
|
2177
2211
|
setInputValue(message.text);
|
|
@@ -2337,8 +2371,13 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2337
2371
|
// For auto-enable, we should create a regular AIT (not AI Agent AIT) if user doesn't have existing AIT
|
|
2338
2372
|
const shouldCreateAsAIAgent = !!aitRef.current; // Only create as AI Agent if user already has an AIT
|
|
2339
2373
|
await generateAndRegisterAITWithSigner(validPermissions, shouldCreateAsAIAgent, currentSigner, currentHitAddress);
|
|
2374
|
+
const permissionsPersisted = await waitForPersistedAITPermissions(validPermissions);
|
|
2375
|
+
if (!permissionsPersisted) {
|
|
2376
|
+
showWarning('AIT permission was submitted but is still synchronizing. Please try again shortly.');
|
|
2377
|
+
setIsAITEnabling(false);
|
|
2378
|
+
return false;
|
|
2379
|
+
}
|
|
2340
2380
|
showSuccess('AIT permission enabled successfully! You can now use the AI agent.');
|
|
2341
|
-
await refreshAIT(true);
|
|
2342
2381
|
setIsAITEnabling(false);
|
|
2343
2382
|
return true;
|
|
2344
2383
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type PiiMessage = {
|
|
2
|
+
piiStatus?: 'scanning' | 'complete' | 'none';
|
|
3
|
+
piiStep?: 0 | 1 | 2;
|
|
4
|
+
piiProtection?: {
|
|
5
|
+
anonymizedContent?: string;
|
|
6
|
+
mapping?: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export type PiiScanCompleteData = {
|
|
10
|
+
entityCount?: number;
|
|
11
|
+
anonymizedUserMessage?: string | null;
|
|
12
|
+
mapping?: Record<string, string> | null;
|
|
13
|
+
};
|
|
14
|
+
export declare function completePiiScan<T extends PiiMessage>(message: T, data?: PiiScanCompleteData): T;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=piiMessageState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piiMessageState.d.ts","sourceRoot":"","sources":["../src/piiMessageState.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG;IAChB,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;IAC7C,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpB,aAAa,CAAC,EAAE;QACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CACzC,CAAC;AAEF,wBAAgB,eAAe,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,CAAC,CAmB/F"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function completePiiScan(message, data) {
|
|
2
|
+
const mapping = data?.mapping || {};
|
|
3
|
+
const entityCount = typeof data?.entityCount === 'number'
|
|
4
|
+
? data.entityCount
|
|
5
|
+
: Object.keys(mapping).length;
|
|
6
|
+
if (entityCount <= 0 || !data?.anonymizedUserMessage) {
|
|
7
|
+
return { ...message, piiStatus: 'none', piiStep: 1 };
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
...message,
|
|
11
|
+
piiStatus: 'complete',
|
|
12
|
+
piiStep: 1,
|
|
13
|
+
piiProtection: {
|
|
14
|
+
anonymizedContent: data.anonymizedUserMessage,
|
|
15
|
+
mapping,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PresetMessage } from './types/ChatBotTypes';
|
|
2
|
+
export declare function normalizeSuggestionText(value: string): string;
|
|
3
|
+
export declare function filterSuggestions(suggestions: readonly PresetMessage[], lastUserMessage?: string): PresetMessage[];
|
|
4
|
+
//# sourceMappingURL=suggestionState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggestionState.d.ts","sourceRoot":"","sources":["../src/suggestionState.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,SAAS,aAAa,EAAE,EACrC,eAAe,CAAC,EAAE,MAAM,GACvB,aAAa,EAAE,CAUjB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function normalizeSuggestionText(value) {
|
|
2
|
+
return value.trim().replace(/\s+/g, ' ').toLocaleLowerCase();
|
|
3
|
+
}
|
|
4
|
+
export function filterSuggestions(suggestions, lastUserMessage) {
|
|
5
|
+
const lastUserText = normalizeSuggestionText(lastUserMessage || '');
|
|
6
|
+
const seen = new Set();
|
|
7
|
+
return suggestions.filter((suggestion) => {
|
|
8
|
+
const normalized = normalizeSuggestionText(suggestion.text || '');
|
|
9
|
+
if (!normalized || normalized === lastUserText || seen.has(normalized))
|
|
10
|
+
return false;
|
|
11
|
+
seen.add(normalized);
|
|
12
|
+
return true;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -138,7 +138,7 @@ export interface ChatBotContextType {
|
|
|
138
138
|
retryPendingTextTool: () => Promise<boolean>;
|
|
139
139
|
sendMessage: (content: string, retryCount?: number, isPresetMessage?: boolean, attachments?: Attachment[]) => Promise<void>;
|
|
140
140
|
handleSubmit: (e: React.FormEvent, attachments?: Attachment[]) => Promise<void>;
|
|
141
|
-
handlePresetMessage: (message: PresetMessage) => void
|
|
141
|
+
handlePresetMessage: (message: PresetMessage) => Promise<void>;
|
|
142
142
|
savePermissions: (newPermissions?: string[]) => Promise<void>;
|
|
143
143
|
enableAIT: (toolName: string) => Promise<boolean>;
|
|
144
144
|
handleVerifyWalletClick: (method: 'berifyme' | 'custom') => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatBotTypes.d.ts","sourceRoot":"","sources":["../../src/types/ChatBotTypes.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ChatBotTypes.d.ts","sourceRoot":"","sources":["../../src/types/ChatBotTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAErI,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CACV,OAAO,EAAE,OAAO,EAChB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,KAAK,IAAI,KACP,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC7B,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,SAAS,CAAC,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC,WAAW,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAAC;IAElD,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAErC,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC,YAAY,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACvC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,eAAe,CAAC,EAAE;QAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sGAAsG;IACtG,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IAEjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,oBAAoB,EAAE,iBAAiB,EAAE,CAAC;IAC1C,kBAAkB,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,GAAG,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE;QACZ,IAAI,EAAE,OAAO,CAAC;QACd,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;QAC/C,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,YAAY,EAAE,OAAO,CAAC;IACtB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAChD,eAAe,EAAE,OAAO,CAAC;IAEzB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,aAAa,EAAE,CAAC;IAG7B,cAAc,EAAE,OAAO,GAAG,UAAU,CAAC;IAErC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IAGvB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,qBAAqB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,uBAAuB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAChD,aAAa,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,eAAe,EAAE,CAAC,YAAY,EAAE,GAAG,KAAK,IAAI,CAAC;IAE7C,qBAAqB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,WAAW,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IACvD,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAGnD,aAAa,EAAE,CAAC,qBAAqB,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC,CAAC;IACxF,YAAY,EAAE,CAAC,sBAAsB,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrE,oBAAoB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5H,YAAY,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,uBAAuB,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,EAAE,CAAC,sBAAsB,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B,iBAAiB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,MAAM,OAAO,CAAC;IAE/B,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/E,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,cAAc,EAAE,MAAM,IAAI,CAAC;IAG3B,MAAM,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,eAAe,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC,CAAC;IAC3D,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACjC,sBAAsB,EAAE,OAAO,CAAC;IAChC,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PresetMessages.d.ts","sourceRoot":"","sources":["../../src/ui/PresetMessages.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"PresetMessages.d.ts","sourceRoot":"","sources":["../../src/ui/PresetMessages.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EA4ClC,CAAC"}
|
|
@@ -2,9 +2,12 @@ import { jsx as _jsx } from "@emotion/react/jsx-runtime";
|
|
|
2
2
|
import { css } from '@emotion/react';
|
|
3
3
|
import { useChatBot } from '../context/ChatBotContext';
|
|
4
4
|
import { actionButton } from './styles/isolatedStyles';
|
|
5
|
+
import { filterSuggestions, normalizeSuggestionText } from '../suggestionState';
|
|
5
6
|
export const PresetMessages = () => {
|
|
6
|
-
const { suggestions, handlePresetMessage } = useChatBot();
|
|
7
|
-
|
|
7
|
+
const { suggestions, messages, handlePresetMessage } = useChatBot();
|
|
8
|
+
const lastUserMessage = [...messages].reverse().find(message => message.role === 'user')?.content;
|
|
9
|
+
const visibleSuggestions = filterSuggestions(suggestions || [], lastUserMessage);
|
|
10
|
+
if (visibleSuggestions.length === 0) {
|
|
8
11
|
return null;
|
|
9
12
|
}
|
|
10
13
|
return (_jsx("div", { css: css `
|
|
@@ -15,7 +18,7 @@ export const PresetMessages = () => {
|
|
|
15
18
|
display: flex !important;
|
|
16
19
|
gap: 10px !important;
|
|
17
20
|
align-items: center !important;
|
|
18
|
-
`, children:
|
|
21
|
+
`, children: visibleSuggestions.map((preset) => (_jsx("button", { onClick: () => { void handlePresetMessage(preset); }, css: css `
|
|
19
22
|
${actionButton}
|
|
20
23
|
padding: 5px 10px !important;
|
|
21
24
|
font-size: 12px !important;
|
|
@@ -29,5 +32,5 @@ export const PresetMessages = () => {
|
|
|
29
32
|
&:active {
|
|
30
33
|
background-color: #004085 !important;
|
|
31
34
|
}
|
|
32
|
-
`, children: preset.text },
|
|
35
|
+
`, children: preset.text }, normalizeSuggestionText(preset.text)))) }));
|
|
33
36
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type WaitForAITPermissionsOptions<T> = {
|
|
2
|
+
readAIT: () => Promise<T | undefined>;
|
|
3
|
+
getPermissions: (ait: T) => readonly string[];
|
|
4
|
+
expectedPermissions: readonly string[];
|
|
5
|
+
attempts?: number;
|
|
6
|
+
delayMs?: number;
|
|
7
|
+
sleep?: (delayMs: number) => Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function includesAITPermissions(
|
|
11
|
+
actualPermissions: readonly string[],
|
|
12
|
+
expectedPermissions: readonly string[],
|
|
13
|
+
): boolean {
|
|
14
|
+
return actualPermissions.includes('ALL')
|
|
15
|
+
|| expectedPermissions.every((permission) => actualPermissions.includes(permission));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wait until the AIT read path can observe a newly submitted permission set.
|
|
20
|
+
* Stale reads are deliberately discarded so callers never downgrade their
|
|
21
|
+
* optimistic local state while the chain/RPC read path is catching up.
|
|
22
|
+
*/
|
|
23
|
+
export async function waitForAITPermissions<T>({
|
|
24
|
+
readAIT,
|
|
25
|
+
getPermissions,
|
|
26
|
+
expectedPermissions,
|
|
27
|
+
attempts = 10,
|
|
28
|
+
delayMs = 750,
|
|
29
|
+
sleep = (duration) => new Promise((resolve) => setTimeout(resolve, duration)),
|
|
30
|
+
}: WaitForAITPermissionsOptions<T>): Promise<T | undefined> {
|
|
31
|
+
const totalAttempts = Math.max(1, attempts);
|
|
32
|
+
for (let attempt = 0; attempt < totalAttempts; attempt += 1) {
|
|
33
|
+
const ait = await readAIT();
|
|
34
|
+
if (ait && includesAITPermissions(getPermissions(ait), expectedPermissions)) {
|
|
35
|
+
return ait;
|
|
36
|
+
}
|
|
37
|
+
if (attempt + 1 < totalAttempts) await sleep(delayMs);
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
@@ -36,8 +36,11 @@ import {
|
|
|
36
36
|
ChatBotProps,
|
|
37
37
|
PresetMessage
|
|
38
38
|
} from '../types/ChatBotTypes';
|
|
39
|
+
import { waitForAITPermissions } from '../aitPermissionPropagation';
|
|
39
40
|
import { AuthorizationLoadingState } from '../authorizationLoadingState';
|
|
40
41
|
import { PendingTextToolRetryController } from '../pendingTextToolRetry';
|
|
42
|
+
import { completePiiScan } from '../piiMessageState';
|
|
43
|
+
import { filterSuggestions } from '../suggestionState';
|
|
41
44
|
|
|
42
45
|
const MIC_ENABLED_SESSION_KEY = 'chatbot-mic-enabled';
|
|
43
46
|
|
|
@@ -268,6 +271,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
268
271
|
const textInputRef = React.useRef<HTMLInputElement | null>(null);
|
|
269
272
|
const lastPartialRangeRef = React.useRef<{ start: number; end: number } | null>(null);
|
|
270
273
|
const lastAutoSentTranscriptRef = React.useRef<string>('');
|
|
274
|
+
const presetSendInFlightRef = React.useRef(false);
|
|
271
275
|
const autoSendTimerRef = React.useRef<NodeJS.Timeout | null>(null);
|
|
272
276
|
const isCorrectingRef = React.useRef(false);
|
|
273
277
|
const lastCustomErrorRef = React.useRef<string | undefined>(undefined);
|
|
@@ -1125,6 +1129,50 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1125
1129
|
}
|
|
1126
1130
|
};
|
|
1127
1131
|
|
|
1132
|
+
const waitForPersistedAITPermissions = async (
|
|
1133
|
+
expectedPermissions: string[],
|
|
1134
|
+
): Promise<boolean> => {
|
|
1135
|
+
const currentHitAddress = hitAddressRef.current || hitAddress;
|
|
1136
|
+
const currentToken = nxtlinqAITServiceAccessTokenRef.current || nxtlinqAITServiceAccessToken;
|
|
1137
|
+
if (!currentHitAddress || !currentToken) return false;
|
|
1138
|
+
|
|
1139
|
+
authorizationLoadingStateRef.current.setAITLoading(true);
|
|
1140
|
+
setIsAITLoading(true);
|
|
1141
|
+
try {
|
|
1142
|
+
const persistedAIT = await waitForAITPermissions({
|
|
1143
|
+
expectedPermissions,
|
|
1144
|
+
readAIT: async () => {
|
|
1145
|
+
try {
|
|
1146
|
+
const response = await nxtlinqApi.ait.getAITByServiceIdAndController({
|
|
1147
|
+
serviceId,
|
|
1148
|
+
controller: currentHitAddress,
|
|
1149
|
+
customUsername: (!requireWalletIDVVerification && customUsername)
|
|
1150
|
+
? getFinalCustomUsername(customUsername)
|
|
1151
|
+
: undefined,
|
|
1152
|
+
}, currentToken);
|
|
1153
|
+
return 'error' in response ? undefined : response;
|
|
1154
|
+
} catch (error) {
|
|
1155
|
+
console.warn('AIT permission propagation check failed:', error);
|
|
1156
|
+
return undefined;
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
getPermissions: (currentAIT) => currentAIT.metadata?.permissions || [],
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
if (!persistedAIT) return false;
|
|
1163
|
+
|
|
1164
|
+
const persistedPermissions = persistedAIT.metadata?.permissions || [];
|
|
1165
|
+
aitRef.current = persistedAIT;
|
|
1166
|
+
permissionsRef.current = persistedPermissions;
|
|
1167
|
+
setAit(persistedAIT);
|
|
1168
|
+
setPermissions(persistedPermissions);
|
|
1169
|
+
return true;
|
|
1170
|
+
} finally {
|
|
1171
|
+
authorizationLoadingStateRef.current.setAITLoading(false);
|
|
1172
|
+
setIsAITLoading(false);
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1128
1176
|
// Check if user needs to sign in
|
|
1129
1177
|
const isNeedSignInWithWallet = React.useMemo(() => {
|
|
1130
1178
|
if (!hitAddress) return false;
|
|
@@ -1433,7 +1481,11 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1433
1481
|
return effectiveAvailableModels[safeIndex];
|
|
1434
1482
|
}, [effectiveAvailableModels, selectedModelIndex]);
|
|
1435
1483
|
|
|
1436
|
-
const updateSuggestions = React.useCallback(async (
|
|
1484
|
+
const updateSuggestions = React.useCallback(async (
|
|
1485
|
+
pseudoId: string,
|
|
1486
|
+
externalId?: string,
|
|
1487
|
+
lastUserMessage?: string,
|
|
1488
|
+
) => {
|
|
1437
1489
|
const result = await nxtlinqApi.agent.generateSuggestions({
|
|
1438
1490
|
...authFields(),
|
|
1439
1491
|
pseudoId,
|
|
@@ -1446,11 +1498,11 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1446
1498
|
return;
|
|
1447
1499
|
}
|
|
1448
1500
|
|
|
1449
|
-
setSuggestions(
|
|
1450
|
-
text: sug,
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
}, [])
|
|
1501
|
+
setSuggestions(filterSuggestions(
|
|
1502
|
+
result.suggestions.map((sug: string) => ({ text: sug, autoSend: true })),
|
|
1503
|
+
lastUserMessage,
|
|
1504
|
+
));
|
|
1505
|
+
}, [nxtlinqApi, authFields, setSuggestions])
|
|
1454
1506
|
|
|
1455
1507
|
// Updated sendMessage function to support different AI models and attachments
|
|
1456
1508
|
const sendMessage = async (
|
|
@@ -1586,15 +1638,14 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1586
1638
|
const updated = [...prev];
|
|
1587
1639
|
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1588
1640
|
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
};
|
|
1641
|
+
if (step === 'scan_complete') {
|
|
1642
|
+
// PII work is complete independently of any later tool
|
|
1643
|
+
// authorization. Never leave the bubble in Sending while
|
|
1644
|
+
// waiting for wallet recovery or permission elevation.
|
|
1645
|
+
updated[i] = completePiiScan(updated[i], data);
|
|
1646
|
+
} else {
|
|
1647
|
+
updated[i] = { ...updated[i], piiStep };
|
|
1596
1648
|
}
|
|
1597
|
-
updated[i] = { ...updated[i], ...patch };
|
|
1598
1649
|
break;
|
|
1599
1650
|
}
|
|
1600
1651
|
}
|
|
@@ -1603,6 +1654,27 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1603
1654
|
} : undefined,
|
|
1604
1655
|
});
|
|
1605
1656
|
|
|
1657
|
+
// PII scanning is complete when the Agent response arrives, regardless
|
|
1658
|
+
// of whether a later frontend-tool authorization step can continue.
|
|
1659
|
+
if (piiDisplayMode === 'redacted') {
|
|
1660
|
+
const anonymizedUserMsg = response.piiProtection?.anonymizedUserMessage;
|
|
1661
|
+
const piiMappingData = response.piiProtection?.mapping ?? undefined;
|
|
1662
|
+
setMessages(prev => {
|
|
1663
|
+
const updated = [...prev];
|
|
1664
|
+
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1665
|
+
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
1666
|
+
updated[i] = completePiiScan(updated[i], {
|
|
1667
|
+
entityCount: piiMappingData ? Object.keys(piiMappingData).length : 0,
|
|
1668
|
+
anonymizedUserMessage: anonymizedUserMsg,
|
|
1669
|
+
mapping: piiMappingData,
|
|
1670
|
+
});
|
|
1671
|
+
break;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
return updated;
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1606
1678
|
if (!('error' in response && (response as { error?: string }).error)) {
|
|
1607
1679
|
const tr = response as AgentResponse;
|
|
1608
1680
|
if (
|
|
@@ -1732,7 +1804,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1732
1804
|
replyText = 'Sorry, I cannot understand your question';
|
|
1733
1805
|
}
|
|
1734
1806
|
|
|
1735
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1807
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1736
1808
|
|
|
1737
1809
|
setMessages(prev => prev.map(m =>
|
|
1738
1810
|
m.id === streamAssistantId
|
|
@@ -1893,7 +1965,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1893
1965
|
}
|
|
1894
1966
|
}
|
|
1895
1967
|
|
|
1896
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1968
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1897
1969
|
|
|
1898
1970
|
// Skip creating a new botResponse since we already updated the streaming message
|
|
1899
1971
|
} else {
|
|
@@ -1933,7 +2005,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1933
2005
|
}
|
|
1934
2006
|
}
|
|
1935
2007
|
|
|
1936
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
2008
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1937
2009
|
const newBotResponse: Message = {
|
|
1938
2010
|
id: (Date.now() + 1).toString(),
|
|
1939
2011
|
content: mergedContent,
|
|
@@ -1959,7 +2031,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1959
2031
|
.join(' ') || 'Sorry, I cannot understand your question'
|
|
1960
2032
|
: response.reply || 'Sorry, I cannot understand your question';
|
|
1961
2033
|
|
|
1962
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
2034
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1963
2035
|
|
|
1964
2036
|
const newBotResponse: Message = {
|
|
1965
2037
|
id: (Date.now() + 1).toString(),
|
|
@@ -1999,38 +2071,6 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1999
2071
|
botResponse = newBotResponse;
|
|
2000
2072
|
}
|
|
2001
2073
|
|
|
2002
|
-
// ===== PII Protection: Update user message with anonymized version =====
|
|
2003
|
-
const anonymizedUserMsg = response.piiProtection?.anonymizedUserMessage;
|
|
2004
|
-
const piiMappingData = response.piiProtection?.mapping ?? undefined;
|
|
2005
|
-
if (anonymizedUserMsg) {
|
|
2006
|
-
setMessages(prev => {
|
|
2007
|
-
const updated = [...prev];
|
|
2008
|
-
for (let i = updated.length - 1; i >= 0; i--) {
|
|
2009
|
-
if (updated[i].role === 'user') {
|
|
2010
|
-
updated[i] = {
|
|
2011
|
-
...updated[i],
|
|
2012
|
-
piiProtection: { anonymizedContent: anonymizedUserMsg, mapping: piiMappingData },
|
|
2013
|
-
piiStatus: 'complete',
|
|
2014
|
-
};
|
|
2015
|
-
break;
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
return updated;
|
|
2019
|
-
});
|
|
2020
|
-
} else if (piiDisplayMode === 'redacted') {
|
|
2021
|
-
// No PII detected — set piiStatus to 'none' to trigger "No sensitive data" indicator
|
|
2022
|
-
setMessages(prev => {
|
|
2023
|
-
const updated = [...prev];
|
|
2024
|
-
for (let i = updated.length - 1; i >= 0; i--) {
|
|
2025
|
-
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
2026
|
-
updated[i] = { ...updated[i], piiStatus: 'none' };
|
|
2027
|
-
break;
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
return updated;
|
|
2031
|
-
});
|
|
2032
|
-
}
|
|
2033
|
-
|
|
2034
2074
|
// Execute redirect after all message processing is complete
|
|
2035
2075
|
if (redirectUrl) {
|
|
2036
2076
|
// Use setTimeout to ensure the message is displayed before redirect
|
|
@@ -2400,21 +2440,19 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
2400
2440
|
}, [nxtlinqApi, authFields, pseudoId]);
|
|
2401
2441
|
|
|
2402
2442
|
// Handle preset message
|
|
2403
|
-
const handlePresetMessage = (message: PresetMessage) => {
|
|
2443
|
+
const handlePresetMessage = async (message: PresetMessage): Promise<void> => {
|
|
2404
2444
|
// If preset is configured as auto-send, avoid duplicate sends when user clicks repeatedly
|
|
2405
2445
|
if (message.autoSend) {
|
|
2406
2446
|
const trimmedText = (message.text || '').trim();
|
|
2407
2447
|
if (!trimmedText) return;
|
|
2408
2448
|
|
|
2409
2449
|
// Prevent sending messages while AI Agent is processing
|
|
2410
|
-
if (isLoading) {
|
|
2450
|
+
if (isLoading || presetSendInFlightRef.current) {
|
|
2411
2451
|
return;
|
|
2412
2452
|
}
|
|
2413
2453
|
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
return;
|
|
2417
|
-
}
|
|
2454
|
+
presetSendInFlightRef.current = true;
|
|
2455
|
+
setSuggestions(previous => filterSuggestions(previous, trimmedText));
|
|
2418
2456
|
|
|
2419
2457
|
// For preset messages, we need to add the user message first since sendMessage won't add it on retries
|
|
2420
2458
|
const userMessage: Message = {
|
|
@@ -2434,7 +2472,11 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
2434
2472
|
lastAutoSentTranscriptRef.current = trimmedText;
|
|
2435
2473
|
|
|
2436
2474
|
// Pass a flag to indicate this is a preset message so sendMessage won't add user message again
|
|
2437
|
-
|
|
2475
|
+
try {
|
|
2476
|
+
await sendMessage(trimmedText, 0, true);
|
|
2477
|
+
} finally {
|
|
2478
|
+
presetSendInFlightRef.current = false;
|
|
2479
|
+
}
|
|
2438
2480
|
} else {
|
|
2439
2481
|
setInputValue(message.text);
|
|
2440
2482
|
}
|
|
@@ -2634,8 +2676,13 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
2634
2676
|
const shouldCreateAsAIAgent = !!aitRef.current; // Only create as AI Agent if user already has an AIT
|
|
2635
2677
|
|
|
2636
2678
|
await generateAndRegisterAITWithSigner(validPermissions, shouldCreateAsAIAgent, currentSigner, currentHitAddress);
|
|
2679
|
+
const permissionsPersisted = await waitForPersistedAITPermissions(validPermissions);
|
|
2680
|
+
if (!permissionsPersisted) {
|
|
2681
|
+
showWarning('AIT permission was submitted but is still synchronizing. Please try again shortly.');
|
|
2682
|
+
setIsAITEnabling(false);
|
|
2683
|
+
return false;
|
|
2684
|
+
}
|
|
2637
2685
|
showSuccess('AIT permission enabled successfully! You can now use the AI agent.');
|
|
2638
|
-
await refreshAIT(true);
|
|
2639
2686
|
setIsAITEnabling(false);
|
|
2640
2687
|
return true;
|
|
2641
2688
|
} catch (error) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
type PiiMessage = {
|
|
2
|
+
piiStatus?: 'scanning' | 'complete' | 'none';
|
|
3
|
+
piiStep?: 0 | 1 | 2;
|
|
4
|
+
piiProtection?: {
|
|
5
|
+
anonymizedContent?: string;
|
|
6
|
+
mapping?: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type PiiScanCompleteData = {
|
|
11
|
+
entityCount?: number;
|
|
12
|
+
anonymizedUserMessage?: string | null;
|
|
13
|
+
mapping?: Record<string, string> | null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function completePiiScan<T extends PiiMessage>(message: T, data?: PiiScanCompleteData): T {
|
|
17
|
+
const mapping = data?.mapping || {};
|
|
18
|
+
const entityCount = typeof data?.entityCount === 'number'
|
|
19
|
+
? data.entityCount
|
|
20
|
+
: Object.keys(mapping).length;
|
|
21
|
+
|
|
22
|
+
if (entityCount <= 0 || !data?.anonymizedUserMessage) {
|
|
23
|
+
return { ...message, piiStatus: 'none', piiStep: 1 } as T;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
...message,
|
|
28
|
+
piiStatus: 'complete',
|
|
29
|
+
piiStep: 1,
|
|
30
|
+
piiProtection: {
|
|
31
|
+
anonymizedContent: data.anonymizedUserMessage,
|
|
32
|
+
mapping,
|
|
33
|
+
},
|
|
34
|
+
} as T;
|
|
35
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PresetMessage } from './types/ChatBotTypes';
|
|
2
|
+
|
|
3
|
+
export function normalizeSuggestionText(value: string): string {
|
|
4
|
+
return value.trim().replace(/\s+/g, ' ').toLocaleLowerCase();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function filterSuggestions(
|
|
8
|
+
suggestions: readonly PresetMessage[],
|
|
9
|
+
lastUserMessage?: string,
|
|
10
|
+
): PresetMessage[] {
|
|
11
|
+
const lastUserText = normalizeSuggestionText(lastUserMessage || '');
|
|
12
|
+
const seen = new Set<string>();
|
|
13
|
+
|
|
14
|
+
return suggestions.filter((suggestion) => {
|
|
15
|
+
const normalized = normalizeSuggestionText(suggestion.text || '');
|
|
16
|
+
if (!normalized || normalized === lastUserText || seen.has(normalized)) return false;
|
|
17
|
+
seen.add(normalized);
|
|
18
|
+
return true;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -167,7 +167,7 @@ export interface ChatBotContextType {
|
|
|
167
167
|
retryPendingTextTool: () => Promise<boolean>;
|
|
168
168
|
sendMessage: (content: string, retryCount?: number, isPresetMessage?: boolean, attachments?: Attachment[]) => Promise<void>;
|
|
169
169
|
handleSubmit: (e: React.FormEvent, attachments?: Attachment[]) => Promise<void>;
|
|
170
|
-
handlePresetMessage: (message: PresetMessage) => void
|
|
170
|
+
handlePresetMessage: (message: PresetMessage) => Promise<void>;
|
|
171
171
|
savePermissions: (newPermissions?: string[]) => Promise<void>;
|
|
172
172
|
enableAIT: (toolName: string) => Promise<boolean>;
|
|
173
173
|
handleVerifyWalletClick: (method: 'berifyme' | 'custom') => Promise<void>;
|
|
@@ -4,11 +4,14 @@ import { css } from '@emotion/react';
|
|
|
4
4
|
import { useChatBot } from '../context/ChatBotContext';
|
|
5
5
|
import { PresetMessage } from '../types/ChatBotTypes';
|
|
6
6
|
import { actionButton } from './styles/isolatedStyles';
|
|
7
|
+
import { filterSuggestions, normalizeSuggestionText } from '../suggestionState';
|
|
7
8
|
|
|
8
9
|
export const PresetMessages: React.FC = () => {
|
|
9
|
-
const { suggestions, handlePresetMessage } = useChatBot();
|
|
10
|
+
const { suggestions, messages, handlePresetMessage } = useChatBot();
|
|
11
|
+
const lastUserMessage = [...messages].reverse().find(message => message.role === 'user')?.content;
|
|
12
|
+
const visibleSuggestions = filterSuggestions(suggestions || [], lastUserMessage);
|
|
10
13
|
|
|
11
|
-
if (
|
|
14
|
+
if (visibleSuggestions.length === 0) {
|
|
12
15
|
return null;
|
|
13
16
|
}
|
|
14
17
|
|
|
@@ -22,10 +25,10 @@ export const PresetMessages: React.FC = () => {
|
|
|
22
25
|
gap: 10px !important;
|
|
23
26
|
align-items: center !important;
|
|
24
27
|
`}>
|
|
25
|
-
{
|
|
28
|
+
{visibleSuggestions.map((preset: PresetMessage) => (
|
|
26
29
|
<button
|
|
27
|
-
key={
|
|
28
|
-
onClick={() => handlePresetMessage(preset)}
|
|
30
|
+
key={normalizeSuggestionText(preset.text)}
|
|
31
|
+
onClick={() => { void handlePresetMessage(preset); }}
|
|
29
32
|
css={css`
|
|
30
33
|
${actionButton}
|
|
31
34
|
padding: 5px 10px !important;
|
|
@@ -47,4 +50,4 @@ export const PresetMessages: React.FC = () => {
|
|
|
47
50
|
))}
|
|
48
51
|
</div>
|
|
49
52
|
);
|
|
50
|
-
};
|
|
53
|
+
};
|