nyxora 26.6.18 → 26.6.20
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/packages/core/src/agent/reasoning.js +1 -4
- package/dist/packages/core/src/gateway/server.js +19 -1
- package/dist/packages/core/src/gateway/setup.js +58 -6
- package/dist/packages/core/src/web3/skills/getPrice.js +11 -6
- package/package.json +2 -2
- package/packages/core/package.json +1 -1
- package/packages/core/src/agent/reasoning.ts +1 -5
- package/packages/core/src/gateway/server.ts +19 -1
- package/packages/core/src/gateway/setup.ts +60 -6
- package/packages/core/src/web3/skills/getPrice.ts +11 -6
- package/packages/dashboard/dist/assets/{index-DnQrbB4c.css → index-CQNHWZtN.css} +1 -1
- package/packages/dashboard/dist/assets/{index-BAXifdMN.js → index-O2m42q4p.js} +3 -3
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +1 -1
- package/packages/signer/package.json +1 -1
|
@@ -245,9 +245,6 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
245
245
|
})
|
|
246
246
|
];
|
|
247
247
|
try {
|
|
248
|
-
if (config.llm.provider !== 'openai' && config.llm.provider !== 'ollama' && config.llm.provider !== 'gemini' && config.llm.provider !== 'openrouter') {
|
|
249
|
-
return `Provider ${config.llm.provider} is configured, but currently only OpenAI, OpenRouter, Ollama, and Gemini adapters are implemented.`;
|
|
250
|
-
}
|
|
251
248
|
const lowerInput = input.toLowerCase();
|
|
252
249
|
const hasWeb3Keyword = /swap|transfer|price|token|crypto|bridge|wallet|balance|portfolio|buy|sell|send|receive|address|market|limit|mint|nft/i.test(lowerInput);
|
|
253
250
|
const hasGoogleKeyword = /email|gmail|calendar|sheet|doc|form|event/i.test(lowerInput);
|
|
@@ -330,7 +327,7 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
330
327
|
break;
|
|
331
328
|
}
|
|
332
329
|
case 'get_price': {
|
|
333
|
-
result = await (0, getPrice_1.getPrice)(args.coinId);
|
|
330
|
+
result = await (0, getPrice_1.getPrice)(args.coinId, args.currency);
|
|
334
331
|
break;
|
|
335
332
|
}
|
|
336
333
|
case 'swap_token': {
|
|
@@ -124,7 +124,8 @@ app.use('/api/', apiLimiter);
|
|
|
124
124
|
app.use('/api', (req, res, next) => {
|
|
125
125
|
// Bypass auth for Google OAuth callback and URLs since they are handled externally or by the browser
|
|
126
126
|
const allowedPaths = ['/api/auth/google/url', '/api/auth/google/callback', '/api/auth/google/status', '/api/auth/google'];
|
|
127
|
-
|
|
127
|
+
const currentPath = req.originalUrl.split('?')[0];
|
|
128
|
+
if (allowedPaths.includes(currentPath) || allowedPaths.includes(currentPath.replace(/\/$/, ''))) {
|
|
128
129
|
return next();
|
|
129
130
|
}
|
|
130
131
|
const token = req.headers['x-nyxora-token'];
|
|
@@ -562,6 +563,7 @@ app.post('/api/transactions/:id/reject', async (req, res) => {
|
|
|
562
563
|
let cachedTrending = null;
|
|
563
564
|
let lastTrendingFetch = 0;
|
|
564
565
|
let cachedPrices = {};
|
|
566
|
+
let cachedPriceChanges = {};
|
|
565
567
|
let lastPricesFetch = 0;
|
|
566
568
|
app.get('/api/trending', async (req, res) => {
|
|
567
569
|
const now = Date.now();
|
|
@@ -590,6 +592,15 @@ app.get('/api/trending', async (req, res) => {
|
|
|
590
592
|
res.status(500).json({ error: err.message });
|
|
591
593
|
}
|
|
592
594
|
});
|
|
595
|
+
app.get('/api/wallet', async (req, res) => {
|
|
596
|
+
try {
|
|
597
|
+
const userAddress = await (0, config_1.getAddress)();
|
|
598
|
+
res.json({ address: userAddress });
|
|
599
|
+
}
|
|
600
|
+
catch (error) {
|
|
601
|
+
res.status(500).json({ error: error.message });
|
|
602
|
+
}
|
|
603
|
+
});
|
|
593
604
|
app.get('/api/portfolio', async (req, res) => {
|
|
594
605
|
try {
|
|
595
606
|
const userAddress = await (0, config_1.getAddress)();
|
|
@@ -680,9 +691,11 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
680
691
|
const uniqueAddrs = Array.from(addressesToFetch);
|
|
681
692
|
const now = Date.now();
|
|
682
693
|
let priceMap = cachedPrices;
|
|
694
|
+
let changeMap = cachedPriceChanges;
|
|
683
695
|
if (uniqueAddrs.length > 0 && now - lastPricesFetch > 2 * 60 * 1000) {
|
|
684
696
|
try {
|
|
685
697
|
const newPrices = {};
|
|
698
|
+
const newChanges = {};
|
|
686
699
|
await Promise.all(uniqueAddrs.map(async (addr) => {
|
|
687
700
|
try {
|
|
688
701
|
const res = await (0, httpClient_1.safeFetch)(`https://api.dexscreener.com/latest/dex/tokens/${addr}`);
|
|
@@ -705,9 +718,11 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
705
718
|
const quoteAddr = bestPair.quoteToken?.address?.toLowerCase();
|
|
706
719
|
if (baseAddr === addr) {
|
|
707
720
|
newPrices[addr] = parseFloat(bestPair.priceUsd);
|
|
721
|
+
newChanges[addr] = bestPair.priceChange?.h24 || 0;
|
|
708
722
|
}
|
|
709
723
|
else if (quoteAddr === addr && bestPair.priceNative && parseFloat(bestPair.priceNative) > 0) {
|
|
710
724
|
newPrices[addr] = parseFloat(bestPair.priceUsd) / parseFloat(bestPair.priceNative);
|
|
725
|
+
newChanges[addr] = bestPair.priceChange?.h24 || 0;
|
|
711
726
|
}
|
|
712
727
|
}
|
|
713
728
|
}
|
|
@@ -719,7 +734,9 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
719
734
|
}));
|
|
720
735
|
console.log('DexScreener Fetched Prices:', newPrices);
|
|
721
736
|
cachedPrices = { ...cachedPrices, ...newPrices };
|
|
737
|
+
cachedPriceChanges = { ...cachedPriceChanges, ...newChanges };
|
|
722
738
|
priceMap = cachedPrices;
|
|
739
|
+
changeMap = cachedPriceChanges;
|
|
723
740
|
lastPricesFetch = now;
|
|
724
741
|
}
|
|
725
742
|
catch (e) {
|
|
@@ -734,6 +751,7 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
734
751
|
lookupAddr = ((tokens_1.TOKEN_MAP[chain]?.[wToken]) || '').toLowerCase();
|
|
735
752
|
}
|
|
736
753
|
t.priceUsd = priceMap[lookupAddr] || 0;
|
|
754
|
+
t.priceChange24h = changeMap[lookupAddr] || 0;
|
|
737
755
|
}
|
|
738
756
|
}
|
|
739
757
|
res.json(portfolio);
|
|
@@ -439,18 +439,70 @@ Provider: ${config.llm.provider}`;
|
|
|
439
439
|
'audioTranscribe', 'summarizeText'
|
|
440
440
|
];
|
|
441
441
|
const disabledSkills = [];
|
|
442
|
+
const skillMapping = {
|
|
443
|
+
// OS Skills
|
|
444
|
+
readFile: 'read_local_file',
|
|
445
|
+
writeFile: 'write_local_file',
|
|
446
|
+
editFile: 'edit_local_file',
|
|
447
|
+
generateExcel: 'generate_excel_file',
|
|
448
|
+
analyzeDocument: 'analyze_document',
|
|
449
|
+
run_terminal: 'run_terminal_command',
|
|
450
|
+
gitManager: 'execute_git_command',
|
|
451
|
+
updateSecurityPolicy: 'update_security_policy',
|
|
452
|
+
browseWeb: 'browse_website',
|
|
453
|
+
searchWeb: 'search_web',
|
|
454
|
+
googleWorkspace: [
|
|
455
|
+
'read_gmail_inbox',
|
|
456
|
+
'list_calendar_events',
|
|
457
|
+
'append_row_to_sheets',
|
|
458
|
+
'read_google_docs',
|
|
459
|
+
'read_google_form_responses'
|
|
460
|
+
],
|
|
461
|
+
notionWorkspace: 'manage_notion',
|
|
462
|
+
xManager: 'manage_twitter',
|
|
463
|
+
audioTranscribe: 'transcribe_audio',
|
|
464
|
+
summarizeText: 'summarize_text',
|
|
465
|
+
// Web3 Skills
|
|
466
|
+
transfer: 'transfer_token',
|
|
467
|
+
swapToken: 'swap_token',
|
|
468
|
+
bridgeToken: 'bridge_token',
|
|
469
|
+
customTx: 'custom_tx',
|
|
470
|
+
mintNft: 'mint_nft',
|
|
471
|
+
defiLending: 'supply_aave',
|
|
472
|
+
provideLiquidity: 'provide_liquidity_v3',
|
|
473
|
+
yieldVault: 'deposit_yield_vault',
|
|
474
|
+
revokeApprovals: 'revoke_approval',
|
|
475
|
+
getBalance: 'get_balance',
|
|
476
|
+
getMyAddress: 'get_my_address',
|
|
477
|
+
checkPortfolio: 'check_portfolio',
|
|
478
|
+
getPrice: 'get_price',
|
|
479
|
+
marketAnalysis: 'analyze_market',
|
|
480
|
+
getTxHistory: 'get_tx_history',
|
|
481
|
+
checkSecurity: 'check_token_security',
|
|
482
|
+
checkAddress: 'check_address',
|
|
483
|
+
checkRegistryStatus: 'check_registry_status',
|
|
484
|
+
manageCustomTokens: 'manage_custom_tokens'
|
|
485
|
+
};
|
|
486
|
+
const processDisabledSkill = (skill) => {
|
|
487
|
+
const mapped = skillMapping[skill];
|
|
488
|
+
if (Array.isArray(mapped)) {
|
|
489
|
+
disabledSkills.push(...mapped);
|
|
490
|
+
}
|
|
491
|
+
else if (mapped) {
|
|
492
|
+
disabledSkills.push(mapped);
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
disabledSkills.push(skill);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
442
498
|
allWeb3Skills.forEach(skill => {
|
|
443
499
|
if (!activeWeb3Skills.includes(skill))
|
|
444
|
-
|
|
500
|
+
processDisabledSkill(skill);
|
|
445
501
|
});
|
|
446
502
|
allOsSkills.forEach(skill => {
|
|
447
503
|
if (!activeOsSkills.includes(skill))
|
|
448
|
-
|
|
504
|
+
processDisabledSkill(skill);
|
|
449
505
|
});
|
|
450
|
-
// Note: the backend uses 'run_terminal_command', but the UI/wizard used 'run_terminal'
|
|
451
|
-
// I need to map it just in case:
|
|
452
|
-
if (!activeOsSkills.includes('run_terminal'))
|
|
453
|
-
disabledSkills.push('run_terminal_command');
|
|
454
506
|
fs_1.default.writeFileSync((0, paths_1.getPath)('disabled_skills.json'), JSON.stringify(disabledSkills, null, 2));
|
|
455
507
|
// Save Private Key to OS Keyring or fallback to .env
|
|
456
508
|
if (privateKey) {
|
|
@@ -14,27 +14,32 @@ exports.getPriceToolDefinition = {
|
|
|
14
14
|
coinId: {
|
|
15
15
|
type: "string",
|
|
16
16
|
description: "The CoinGecko ID of the coin (e.g., 'ethereum', 'bitcoin', 'solana')."
|
|
17
|
+
},
|
|
18
|
+
currency: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "The fiat currency to convert to (e.g., 'usd', 'idr', 'eur', 'jpy'). Defaults to 'usd'."
|
|
17
21
|
}
|
|
18
22
|
},
|
|
19
23
|
required: ["coinId"]
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
};
|
|
23
|
-
async function getPrice(coinId) {
|
|
27
|
+
async function getPrice(coinId, currency = 'usd') {
|
|
24
28
|
try {
|
|
25
|
-
const
|
|
29
|
+
const cur = currency.toLowerCase();
|
|
30
|
+
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${coinId}&vs_currencies=${cur}&include_24hr_change=true`;
|
|
26
31
|
const data = await (0, httpClient_1.safeFetchJson)(url);
|
|
27
32
|
if (!data[coinId]) {
|
|
28
33
|
return `❌ Could not find price data for **${coinId.toUpperCase()}**. Please verify the coin name.`;
|
|
29
34
|
}
|
|
30
|
-
const price = data[coinId]
|
|
31
|
-
const change24h = data[coinId]
|
|
35
|
+
const price = data[coinId][cur];
|
|
36
|
+
const change24h = data[coinId][`${cur}_24h_change`];
|
|
32
37
|
const isPositive = change24h >= 0;
|
|
33
38
|
const arrow = isPositive ? '📈 🟩 +' : '📉 🟥 ';
|
|
34
|
-
const priceFormatted = new Intl.NumberFormat('en-US', { style: 'currency', currency:
|
|
39
|
+
const priceFormatted = new Intl.NumberFormat(cur === 'idr' ? 'id-ID' : 'en-US', { style: 'currency', currency: cur.toUpperCase() }).format(price);
|
|
35
40
|
const changeFormatted = change24h ? change24h.toFixed(2) : '0.00';
|
|
36
41
|
return `💰 **Price Update for ${coinId.toUpperCase()}**\n\n` +
|
|
37
|
-
`- **Price (
|
|
42
|
+
`- **Price (${cur.toUpperCase()})**: \`${priceFormatted}\`\n` +
|
|
38
43
|
`- **24h Change**: ${arrow}${changeFormatted}%`;
|
|
39
44
|
}
|
|
40
45
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.20",
|
|
4
4
|
"description": "Your Personal Web3 Assistant",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web3",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"nyxora": "bin/nyxora.mjs"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
|
-
"
|
|
39
|
+
"prepublishOnly": "npm run build",
|
|
40
40
|
"dev": "concurrently -n \"BACKEND,FRONTEND\" -c \"blue,green\" \"NODE_NO_WARNINGS=1 ./node_modules/.bin/ts-node -T launcher.ts\" \"npm run dev --workspace=nyxora-dashboard\"",
|
|
41
41
|
"start": "node ./bin/nyxora.mjs start",
|
|
42
42
|
"stop": "node ./bin/nyxora.mjs stop",
|
|
@@ -270,10 +270,6 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
270
270
|
];
|
|
271
271
|
|
|
272
272
|
try {
|
|
273
|
-
if (config.llm.provider !== 'openai' && config.llm.provider !== 'ollama' && config.llm.provider !== 'gemini' && config.llm.provider !== 'openrouter') {
|
|
274
|
-
return `Provider ${config.llm.provider} is configured, but currently only OpenAI, OpenRouter, Ollama, and Gemini adapters are implemented.`;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
273
|
const lowerInput = input.toLowerCase();
|
|
278
274
|
const hasWeb3Keyword = /swap|transfer|price|token|crypto|bridge|wallet|balance|portfolio|buy|sell|send|receive|address|market|limit|mint|nft/i.test(lowerInput);
|
|
279
275
|
const hasGoogleKeyword = /email|gmail|calendar|sheet|doc|form|event/i.test(lowerInput);
|
|
@@ -386,7 +382,7 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
386
382
|
break;
|
|
387
383
|
}
|
|
388
384
|
case 'get_price': {
|
|
389
|
-
result = await getPrice(args.coinId);
|
|
385
|
+
result = await getPrice(args.coinId, args.currency);
|
|
390
386
|
break;
|
|
391
387
|
}
|
|
392
388
|
case 'swap_token': {
|
|
@@ -137,7 +137,8 @@ app.use('/api/', apiLimiter);
|
|
|
137
137
|
app.use('/api', (req, res, next) => {
|
|
138
138
|
// Bypass auth for Google OAuth callback and URLs since they are handled externally or by the browser
|
|
139
139
|
const allowedPaths = ['/api/auth/google/url', '/api/auth/google/callback', '/api/auth/google/status', '/api/auth/google'];
|
|
140
|
-
|
|
140
|
+
const currentPath = req.originalUrl.split('?')[0];
|
|
141
|
+
if (allowedPaths.includes(currentPath) || allowedPaths.includes(currentPath.replace(/\/$/, ''))) {
|
|
141
142
|
return next();
|
|
142
143
|
}
|
|
143
144
|
|
|
@@ -602,6 +603,7 @@ let cachedTrending: string[] | null = null;
|
|
|
602
603
|
let lastTrendingFetch = 0;
|
|
603
604
|
|
|
604
605
|
let cachedPrices: Record<string, number> = {};
|
|
606
|
+
let cachedPriceChanges: Record<string, number> = {};
|
|
605
607
|
let lastPricesFetch = 0;
|
|
606
608
|
|
|
607
609
|
app.get('/api/trending', async (req, res) => {
|
|
@@ -628,6 +630,15 @@ app.get('/api/trending', async (req, res) => {
|
|
|
628
630
|
}
|
|
629
631
|
});
|
|
630
632
|
|
|
633
|
+
app.get('/api/wallet', async (req, res) => {
|
|
634
|
+
try {
|
|
635
|
+
const userAddress = await getAddress();
|
|
636
|
+
res.json({ address: userAddress });
|
|
637
|
+
} catch (error: any) {
|
|
638
|
+
res.status(500).json({ error: error.message });
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
631
642
|
app.get('/api/portfolio', async (req, res) => {
|
|
632
643
|
try {
|
|
633
644
|
const userAddress = await getAddress();
|
|
@@ -721,10 +732,12 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
721
732
|
const uniqueAddrs = Array.from(addressesToFetch);
|
|
722
733
|
const now = Date.now();
|
|
723
734
|
let priceMap = cachedPrices;
|
|
735
|
+
let changeMap = cachedPriceChanges;
|
|
724
736
|
|
|
725
737
|
if (uniqueAddrs.length > 0 && now - lastPricesFetch > 2 * 60 * 1000) {
|
|
726
738
|
try {
|
|
727
739
|
const newPrices: Record<string, number> = {};
|
|
740
|
+
const newChanges: Record<string, number> = {};
|
|
728
741
|
|
|
729
742
|
await Promise.all(uniqueAddrs.map(async (addr) => {
|
|
730
743
|
try {
|
|
@@ -750,8 +763,10 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
750
763
|
|
|
751
764
|
if (baseAddr === addr) {
|
|
752
765
|
newPrices[addr] = parseFloat(bestPair.priceUsd);
|
|
766
|
+
newChanges[addr] = bestPair.priceChange?.h24 || 0;
|
|
753
767
|
} else if (quoteAddr === addr && bestPair.priceNative && parseFloat(bestPair.priceNative) > 0) {
|
|
754
768
|
newPrices[addr] = parseFloat(bestPair.priceUsd) / parseFloat(bestPair.priceNative);
|
|
769
|
+
newChanges[addr] = bestPair.priceChange?.h24 || 0;
|
|
755
770
|
}
|
|
756
771
|
}
|
|
757
772
|
}
|
|
@@ -764,7 +779,9 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
764
779
|
console.log('DexScreener Fetched Prices:', newPrices);
|
|
765
780
|
|
|
766
781
|
cachedPrices = { ...cachedPrices, ...newPrices };
|
|
782
|
+
cachedPriceChanges = { ...cachedPriceChanges, ...newChanges };
|
|
767
783
|
priceMap = cachedPrices;
|
|
784
|
+
changeMap = cachedPriceChanges;
|
|
768
785
|
lastPricesFetch = now;
|
|
769
786
|
} catch (e) {
|
|
770
787
|
console.error('DexScreener fetch error:', e);
|
|
@@ -779,6 +796,7 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
779
796
|
lookupAddr = (((TOKEN_MAP as any)[chain]?.[wToken]) || '').toLowerCase();
|
|
780
797
|
}
|
|
781
798
|
t.priceUsd = priceMap[lookupAddr] || 0;
|
|
799
|
+
t.priceChange24h = changeMap[lookupAddr] || 0;
|
|
782
800
|
}
|
|
783
801
|
}
|
|
784
802
|
|
|
@@ -463,17 +463,71 @@ Provider: ${config.llm.provider}`;
|
|
|
463
463
|
];
|
|
464
464
|
|
|
465
465
|
const disabledSkills: string[] = [];
|
|
466
|
+
|
|
467
|
+
const skillMapping: Record<string, string | string[]> = {
|
|
468
|
+
// OS Skills
|
|
469
|
+
readFile: 'read_local_file',
|
|
470
|
+
writeFile: 'write_local_file',
|
|
471
|
+
editFile: 'edit_local_file',
|
|
472
|
+
generateExcel: 'generate_excel_file',
|
|
473
|
+
analyzeDocument: 'analyze_document',
|
|
474
|
+
run_terminal: 'run_terminal_command',
|
|
475
|
+
gitManager: 'execute_git_command',
|
|
476
|
+
updateSecurityPolicy: 'update_security_policy',
|
|
477
|
+
browseWeb: 'browse_website',
|
|
478
|
+
searchWeb: 'search_web',
|
|
479
|
+
googleWorkspace: [
|
|
480
|
+
'read_gmail_inbox',
|
|
481
|
+
'list_calendar_events',
|
|
482
|
+
'append_row_to_sheets',
|
|
483
|
+
'read_google_docs',
|
|
484
|
+
'read_google_form_responses'
|
|
485
|
+
],
|
|
486
|
+
notionWorkspace: 'manage_notion',
|
|
487
|
+
xManager: 'manage_twitter',
|
|
488
|
+
audioTranscribe: 'transcribe_audio',
|
|
489
|
+
summarizeText: 'summarize_text',
|
|
490
|
+
|
|
491
|
+
// Web3 Skills
|
|
492
|
+
transfer: 'transfer_token',
|
|
493
|
+
swapToken: 'swap_token',
|
|
494
|
+
bridgeToken: 'bridge_token',
|
|
495
|
+
customTx: 'custom_tx',
|
|
496
|
+
mintNft: 'mint_nft',
|
|
497
|
+
defiLending: 'supply_aave',
|
|
498
|
+
provideLiquidity: 'provide_liquidity_v3',
|
|
499
|
+
yieldVault: 'deposit_yield_vault',
|
|
500
|
+
revokeApprovals: 'revoke_approval',
|
|
501
|
+
getBalance: 'get_balance',
|
|
502
|
+
getMyAddress: 'get_my_address',
|
|
503
|
+
checkPortfolio: 'check_portfolio',
|
|
504
|
+
getPrice: 'get_price',
|
|
505
|
+
marketAnalysis: 'analyze_market',
|
|
506
|
+
getTxHistory: 'get_tx_history',
|
|
507
|
+
checkSecurity: 'check_token_security',
|
|
508
|
+
checkAddress: 'check_address',
|
|
509
|
+
checkRegistryStatus: 'check_registry_status',
|
|
510
|
+
manageCustomTokens: 'manage_custom_tokens'
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const processDisabledSkill = (skill: string) => {
|
|
514
|
+
const mapped = skillMapping[skill];
|
|
515
|
+
if (Array.isArray(mapped)) {
|
|
516
|
+
disabledSkills.push(...mapped);
|
|
517
|
+
} else if (mapped) {
|
|
518
|
+
disabledSkills.push(mapped);
|
|
519
|
+
} else {
|
|
520
|
+
disabledSkills.push(skill);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
|
|
466
524
|
allWeb3Skills.forEach(skill => {
|
|
467
|
-
if (!activeWeb3Skills.includes(skill))
|
|
525
|
+
if (!activeWeb3Skills.includes(skill)) processDisabledSkill(skill);
|
|
468
526
|
});
|
|
469
527
|
allOsSkills.forEach(skill => {
|
|
470
|
-
if (!activeOsSkills.includes(skill))
|
|
528
|
+
if (!activeOsSkills.includes(skill)) processDisabledSkill(skill);
|
|
471
529
|
});
|
|
472
530
|
|
|
473
|
-
// Note: the backend uses 'run_terminal_command', but the UI/wizard used 'run_terminal'
|
|
474
|
-
// I need to map it just in case:
|
|
475
|
-
if (!activeOsSkills.includes('run_terminal')) disabledSkills.push('run_terminal_command');
|
|
476
|
-
|
|
477
531
|
fs.writeFileSync(getPath('disabled_skills.json'), JSON.stringify(disabledSkills, null, 2));
|
|
478
532
|
|
|
479
533
|
// Save Private Key to OS Keyring or fallback to .env
|
|
@@ -11,6 +11,10 @@ export const getPriceToolDefinition = {
|
|
|
11
11
|
coinId: {
|
|
12
12
|
type: "string",
|
|
13
13
|
description: "The CoinGecko ID of the coin (e.g., 'ethereum', 'bitcoin', 'solana')."
|
|
14
|
+
},
|
|
15
|
+
currency: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "The fiat currency to convert to (e.g., 'usd', 'idr', 'eur', 'jpy'). Defaults to 'usd'."
|
|
14
18
|
}
|
|
15
19
|
},
|
|
16
20
|
required: ["coinId"]
|
|
@@ -18,25 +22,26 @@ export const getPriceToolDefinition = {
|
|
|
18
22
|
}
|
|
19
23
|
};
|
|
20
24
|
|
|
21
|
-
export async function getPrice(coinId: string): Promise<string> {
|
|
25
|
+
export async function getPrice(coinId: string, currency: string = 'usd'): Promise<string> {
|
|
22
26
|
try {
|
|
23
|
-
const
|
|
27
|
+
const cur = currency.toLowerCase();
|
|
28
|
+
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${coinId}&vs_currencies=${cur}&include_24hr_change=true`;
|
|
24
29
|
const data = await safeFetchJson<any>(url);
|
|
25
30
|
|
|
26
31
|
if (!data[coinId]) {
|
|
27
32
|
return `❌ Could not find price data for **${coinId.toUpperCase()}**. Please verify the coin name.`;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
const price = data[coinId]
|
|
31
|
-
const change24h = data[coinId]
|
|
35
|
+
const price = data[coinId][cur];
|
|
36
|
+
const change24h = data[coinId][`${cur}_24h_change`];
|
|
32
37
|
|
|
33
38
|
const isPositive = change24h >= 0;
|
|
34
39
|
const arrow = isPositive ? '📈 🟩 +' : '📉 🟥 ';
|
|
35
|
-
const priceFormatted = new Intl.NumberFormat('en-US', { style: 'currency', currency:
|
|
40
|
+
const priceFormatted = new Intl.NumberFormat(cur === 'idr' ? 'id-ID' : 'en-US', { style: 'currency', currency: cur.toUpperCase() }).format(price);
|
|
36
41
|
const changeFormatted = change24h ? change24h.toFixed(2) : '0.00';
|
|
37
42
|
|
|
38
43
|
return `💰 **Price Update for ${coinId.toUpperCase()}**\n\n` +
|
|
39
|
-
`- **Price (
|
|
44
|
+
`- **Price (${cur.toUpperCase()})**: \`${priceFormatted}\`\n` +
|
|
40
45
|
`- **24h Change**: ${arrow}${changeFormatted}%`;
|
|
41
46
|
} catch (error: any) {
|
|
42
47
|
return `❌ **Failed to fetch price:** ${error.message}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--bg-color:#3b4252;--bg-secondary:#434c5e;--bg-sidebar:#2e3440;--text-primary:#eceff4;--text-secondary:#d8dee9;--accent:#88c0d0;--accent-hover:#81a1c1;--glass-bg:#2e3440b3;--glass-border:#d8dee91a;--chat-user:#88c0d0;--chat-user-text:#2e3440;--chat-agent:#2e3440;--tool-bg:#4c566a;--success:#a3be8c;--danger:#bf616a}*{box-sizing:border-box;margin:0;padding:0}body{background-color:var(--bg-color);color:var(--text-primary);height:100vh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;overflow:hidden}#root{width:100vw;height:100vh;display:flex}.sidebar{background-color:var(--bg-sidebar);border-right:1px solid var(--glass-border);z-index:100;flex-direction:column;width:280px;display:flex;box-shadow:10px 0 30px -10px #00000080}.agent-identity-card{background:linear-gradient(#3b82f60d 0%,#0000 100%);border-bottom:1px solid #ffffff0d;align-items:center;gap:16px;margin-bottom:8px;padding:24px;display:flex}.agent-avatar{justify-content:center;align-items:center;display:flex}.agent-info{flex-direction:column;gap:4px;display:flex}.agent-name{color:#fff;letter-spacing:-.025em;font-size:1.1rem;font-weight:700}.agent-status{color:#4ade80;letter-spacing:.05em;align-items:center;gap:6px;font-size:.75rem;font-weight:600;display:flex}.status-dot{background-color:#4ade80;border-radius:50%;width:8px;height:8px;animation:2s infinite pulse-green;box-shadow:0 0 8px #4ade80}@keyframes pulse-green{0%{transform:scale(.95);box-shadow:0 0 #4ade80b3}70%{transform:scale(1);box-shadow:0 0 0 6px #4ade8000}to{transform:scale(.95);box-shadow:0 0 #4ade8000}}.sidebar-scroll-area{flex:1;padding-bottom:24px;overflow-y:auto}.sidebar-scroll-area::-webkit-scrollbar{width:4px}.sidebar-scroll-area::-webkit-scrollbar-thumb{background:#ffffff0d}.sidebar-section{color:#8b9bb4;padding:24px 24px 8px;font-size:.75rem;font-weight:500}.sidebar-nav{flex-direction:column;gap:4px;padding:0 16px;display:flex}.nav-item{color:#94a3b8;cursor:pointer;border:1px solid #0000;border-radius:8px;align-items:center;gap:10px;padding:8px 12px;font-size:.8rem;font-weight:500;transition:all .25s cubic-bezier(.4,0,.2,1);display:flex}.nav-icon{transition:transform .25s cubic-bezier(.4,0,.2,1)}.nav-item:hover{color:#fff;background-color:#ffffff08;transform:translate(4px)}.nav-item:hover .nav-icon{color:#fff;transform:scale(1.1)}.sessions-list{flex:none!important;justify-content:flex-start!important;gap:0!important;height:max-content!important}.session-item{justify-content:space-between;flex:none!important;height:max-content!important;min-height:28px!important;margin-top:0!important;margin-bottom:2px!important;padding:4px 10px!important}.delete-session-btn{color:#bf616a;cursor:pointer;opacity:0;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:4px;transition:opacity .2s,transform .2s;display:flex}.delete-session-btn:hover{background:#bf616a33;transform:scale(1.1)}.session-item:hover .delete-session-btn{opacity:1}.nav-item.active{color:#fff;border-left:3px solid var(--accent);background:linear-gradient(90deg,#3b82f626 0%,#0000 100%);border-radius:4px 12px 12px 4px;font-weight:600}.nav-item.active .nav-icon{color:var(--accent)}.main-content{background-image:radial-gradient(at 0 0,#3b82f61a 0,#0000 50%),radial-gradient(at 100% 100%,#8b5cf61a 0,#0000 50%);flex-direction:column;flex:1;display:flex}.topbar{border-bottom:1px solid var(--glass-border);background:var(--bg-color);justify-content:space-between;align-items:center;height:64px;padding:0 24px;display:flex}.topbar-left{color:var(--text-secondary);align-items:center;gap:16px;font-size:.95rem;font-weight:500;display:flex}.topbar-right{align-items:center;gap:12px;display:flex}.custom-network-selector{position:relative}.network-selector-pill{color:#000;cursor:pointer;background:#88c0d0;border:none;border-radius:9999px;outline:none;align-items:center;gap:8px;padding:8px 16px;font-family:inherit;font-size:.85rem;font-weight:600;transition:all .2s;display:flex}.network-selector-pill:hover{opacity:.9;box-shadow:0 0 10px #88c0d066}.network-selector-pill:focus-visible{box-shadow:0 0 0 2px #88c0d080}.network-icon{flex-shrink:0}.network-chevron{opacity:.7;margin-left:4px}.network-dropdown-menu{background:var(--bg-secondary);border:1px solid var(--glass-border);z-index:100;border-radius:12px;min-width:180px;margin:0;padding:6px;list-style:none;animation:.2s ease-out dropdownSlideIn;position:absolute;top:calc(100% + 8px);right:0;box-shadow:0 8px 24px #0006}@keyframes dropdownSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.network-dropdown-item{cursor:pointer;color:#e5e7eb;border-radius:8px;padding:10px 14px;font-size:.85rem;transition:background .1s}.network-dropdown-item:hover{background:#ffffff0d}.network-dropdown-item.active{color:#88c0d0;background:#88c0d01a;font-weight:600}.workspace-container{width:100%;height:calc(100vh - 64px);display:flex}.chat-wrapper{flex-direction:column;height:100%;padding:24px 0;display:flex}.resizer{background:var(--glass-border);cursor:col-resize;z-index:10;width:6px;transition:background .2s}.resizer:hover,.resizer:active{background:var(--accent)}.canvas-panel{background:var(--bg-sidebar);background-image:radial-gradient(#3b82f60d 0,#0000 80%);flex-direction:column;flex:1;padding:32px;display:flex;position:relative;overflow-y:auto}.canvas-header{border-bottom:1px solid #ffffff0d;justify-content:space-between;align-items:center;margin-bottom:32px;padding-bottom:16px;display:flex}.canvas-title{color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;align-items:center;gap:8px;font-family:monospace;font-size:.85rem;display:flex}.canvas-empty{color:#475569;flex-direction:column;justify-content:center;align-items:center;gap:16px;height:100%;display:flex}.chat-container{flex-direction:column;flex:1;gap:20px;padding:0 24px;display:flex;overflow-y:auto}.chat-container::-webkit-scrollbar{width:6px}.chat-container::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:4px}.message-wrapper{flex-direction:column;max-width:85%;animation:.3s ease-out forwards fadeIn;display:flex}.message-wrapper.agent{flex-direction:row;align-self:flex-start;align-items:flex-end;gap:8px}.message-wrapper.user{flex-direction:row-reverse;align-self:flex-end;align-items:flex-end;gap:8px}.copy-btn{color:var(--text-secondary);cursor:pointer;opacity:0;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:6px;transition:opacity .2s,background .2s;display:flex}.copy-btn:hover{background:var(--bg-secondary);color:var(--text-primary)}.message-wrapper:hover .copy-btn{opacity:1}.message-bubble{white-space:pre-wrap;border-radius:18px;padding:14px 18px;font-size:.95rem;line-height:1.6;box-shadow:0 4px 6px -1px #0000001a}.user .message-bubble{background-color:var(--chat-user);color:var(--chat-user-text);border-bottom-right-radius:4px}.agent .message-bubble{background-color:var(--chat-agent);border:1px solid var(--glass-border);color:var(--text-primary);border-bottom-left-radius:4px}.tool-call{color:var(--text-secondary);background:var(--tool-bg);border:1px solid var(--glass-border);border-radius:12px;align-items:center;gap:8px;margin-top:10px;padding:10px 14px;font-size:.85rem;display:flex}.tool-call code{color:var(--accent);font-family:monospace}.input-area{padding:20px 24px 0}.input-form{background:var(--bg-secondary);border:1px solid var(--glass-border);border-radius:16px;gap:12px;padding:8px;transition:all .2s;display:flex}.input-form:focus-within{border-color:var(--accent);box-shadow:0 0 0 2px #3b82f633}.chat-input{color:#fff;background:0 0;border:none;outline:none;flex:1;padding:12px 16px;font-family:inherit;font-size:.95rem}.send-button{background:var(--accent);color:#fff;cursor:pointer;border:none;border-radius:12px;justify-content:center;align-items:center;width:44px;height:44px;transition:all .2s;display:flex}.voice-button{background:var(--bg-secondary);color:var(--text-secondary);border:1px solid var(--glass-border);cursor:pointer;border-radius:12px;justify-content:center;align-items:center;width:44px;height:44px;transition:all .2s;display:flex}.voice-button:hover{color:#fff;border-color:#ef4444}.voice-button.listening{color:#ef4444;background:#ef444433;border-color:#ef4444;animation:1.5s infinite pulse-red}.voice-button.active-mode{color:#3b82f6;border-color:#3b82f6}.voice-button.speaking{color:#3b82f6;background:#3b82f633;border-color:#3b82f6;animation:1.5s infinite pulse-blue}@keyframes pulse-red{0%{box-shadow:0 0 #ef444466}70%{box-shadow:0 0 0 10px #ef444400}to{box-shadow:0 0 #ef444400}}@keyframes pulse-blue{0%{box-shadow:0 0 #3b82f666}70%{box-shadow:0 0 0 10px #3b82f600}to{box-shadow:0 0 #3b82f600}}.send-button:hover:not(:disabled){background:var(--accent-hover);transform:scale(1.05)}.send-button:disabled{opacity:.5;cursor:not-allowed}.typing-indicator{background-color:var(--chat-agent);border:1px solid var(--glass-border);border-radius:18px 18px 18px 4px;align-self:flex-start;gap:4px;width:fit-content;padding:14px 18px;display:flex}.dot{background:var(--text-secondary);border-radius:50%;width:6px;height:6px;animation:1.4s ease-in-out infinite both bounce}.dot:first-child{animation-delay:-.32s}.dot:nth-child(2){animation-delay:-.16s}@keyframes bounce{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}@keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.prompt-suggestions{gap:12px;margin-bottom:12px;display:flex;overflow-x:auto}.prompt-suggestions button{background:var(--bg-secondary);color:var(--text-secondary);border:1px solid var(--glass-border);cursor:pointer;white-space:nowrap;border-radius:20px;padding:8px 16px;font-size:.85rem;transition:all .2s}.prompt-suggestions button:hover{border-color:var(--accent);color:var(--text-primary)}.trending-tokens{color:var(--text-secondary);align-items:center;gap:12px;margin-top:12px;font-size:.8rem;display:flex}.token-tag{background:var(--accent);color:var(--bg-color);cursor:pointer;border-radius:12px;padding:4px 10px;font-weight:600;transition:transform .2s,filter .2s}.token-tag:hover{filter:brightness(1.2);transform:translateY(-2px)}.nord-label{color:#81a1c1;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;font-size:.75rem;font-weight:700;display:block}.nord-pill-input{color:#88c0d0;background-color:#2e3440b3;border:1px solid #d8dee91a;border-radius:9999px;outline:none;width:100%;padding:10px 20px;font-size:.9rem;font-weight:600;transition:all .2s}.nord-pill-input:focus{border-color:#88c0d0;box-shadow:0 0 10px #88c0d033}.nord-pill-input::placeholder{color:#4c566a}.nord-input{color:#eceff4;background-color:#2e3440;border:1px solid #434c5e;border-radius:6px;outline:none;width:100%;padding:10px 14px;font-size:.9rem;transition:all .2s}.nord-input:focus{border-color:#88c0d0;box-shadow:0 0 0 2px #88c0d033}.nord-input::placeholder{color:#4c566a}.nord-slider{appearance:none;background:#4c566a;border-radius:3px;outline:none;width:100%;height:6px;margin-top:10px}.nord-slider::-webkit-slider-thumb{appearance:none;cursor:pointer;background:#eceff4;border:2px solid #88c0d0;border-radius:50%;width:18px;height:18px;transition:all .2s}.nord-slider::-webkit-slider-thumb:hover{background:#fff;transform:scale(1.1)}.nord-slider::-moz-range-thumb{cursor:pointer;background:#eceff4;border:2px solid #88c0d0;border-radius:50%;width:18px;height:18px;transition:all .2s}.nord-btn-primary{color:#2e3440;cursor:pointer;background-color:#81a1c1;border:none;border-radius:6px;justify-content:center;align-items:center;padding:10px 20px;font-size:.9rem;font-weight:600;transition:background-color .2s;display:flex}.nord-btn-primary:hover{background-color:#88c0d0}.nord-btn-primary:disabled{color:#8fbcbb;cursor:not-allowed;background-color:#4c566a}.nord-panel-header{border-bottom:1px solid #d8dee90d;align-items:center;gap:10px;margin-bottom:20px;padding-bottom:12px;display:flex}.nord-panel-header h3{color:#eceff4;margin:0;font-size:1.1rem;font-weight:600}.overview-container{color:#fff;max-width:1200px;height:calc(100vh - 64px);margin:0 auto;padding:24px;font-family:Inter,sans-serif;overflow-y:auto}.overview-container::-webkit-scrollbar{width:6px}.overview-container::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:4px}.overview-header h1{margin-bottom:4px;font-size:1.5rem;font-weight:600}.overview-header p{color:var(--text-secondary);margin-bottom:24px;font-size:.9rem}.panel{background:#14182099;border:1px solid #ffffff0d;border-radius:12px;margin-bottom:24px;padding:20px}.panel-header h3{margin-bottom:4px;font-size:1.1rem;font-weight:600}.panel-header p{color:var(--text-secondary);margin-bottom:16px;font-size:.85rem}.form-group{margin-bottom:16px}.form-row{gap:16px;display:flex}.flex-1{flex:1}label{text-transform:uppercase;color:var(--text-secondary);letter-spacing:.05em;margin-bottom:8px;font-size:.75rem;display:block}input,select{color:#fff;background:#0000004d;border:1px solid #ffffff1a;border-radius:8px;width:100%;padding:10px 12px;font-family:monospace;font-size:.9rem}input:read-only{color:var(--text-secondary)}.input-with-icon{align-items:center;display:flex;position:relative}.input-with-icon svg{color:var(--text-secondary);cursor:pointer;position:absolute;right:12px}.form-actions{align-items:center;gap:12px;margin-top:20px;display:flex}.btn-primary{color:#fff;cursor:pointer;background:#3b82f6;border:none;border-radius:6px;padding:8px 16px;font-size:.9rem}.btn-secondary{color:#fff;cursor:pointer;background:0 0;border:1px solid #fff3;border-radius:6px;padding:8px 16px;font-size:.9rem}.action-hint{color:var(--text-secondary);margin-left:8px;font-size:.85rem}.snapshot-grid{grid-template-columns:repeat(4,1fr);gap:20px;display:grid}.stat-val{margin-bottom:4px;font-size:1.5rem;font-weight:700}.text-green{color:#22c55e}.stat-block p{color:var(--text-secondary);margin-top:8px;font-size:.8rem;line-height:1.4}.metrics-grid{grid-template-columns:repeat(5,1fr);gap:16px;margin-bottom:32px;display:grid}.metric-card{background:#14182099;border:1px solid #ffffff0d;border-radius:12px;padding:16px}.metric-val{margin-bottom:4px;font-size:1.5rem;font-weight:700}.metric-sub{color:var(--text-secondary);font-size:.75rem}.section-title{text-transform:uppercase;color:var(--text-secondary);letter-spacing:.05em;margin-bottom:12px;font-size:.75rem}.session-item{justify-content:space-between;margin-bottom:24px;padding:16px 20px;display:flex}.text-secondary{color:var(--text-secondary)}.attention-panel{background:#eab3081a;border:1px solid #eab30833;border-radius:12px;flex-direction:column;margin-bottom:24px;padding:16px 20px;display:flex}.attention-header{color:#eab308;align-items:center;gap:8px;margin-bottom:8px;display:flex}.attention-header h4{font-size:1rem;font-weight:600}.attention-content p{margin-bottom:4px;font-size:.95rem}.attention-content span{font-size:.85rem}.logs-grid{grid-template-columns:1fr 1fr;gap:16px;display:grid}.log-panel{background:#0a0c10cc;border:1px solid #ffffff0d;border-radius:12px;flex-direction:column;height:300px;display:flex;overflow:hidden}.log-header{background:#ffffff0d;border-bottom:1px solid #ffffff0d;padding:12px 16px;font-size:.85rem;font-weight:600}.badge{background:#ffffff1a;border-radius:10px;margin-left:8px;padding:2px 6px;font-size:.7rem}.log-content{flex-direction:column;gap:4px;padding:12px 16px;display:flex;overflow:auto}.log-content::-webkit-scrollbar{width:6px;height:6px}.log-content::-webkit-scrollbar-thumb{background:#434c5e;border-radius:4px}.log-content::-webkit-scrollbar-thumb:hover{background:#4c566a}.log-content::-webkit-scrollbar-corner{background:0 0}.memory-log-container::-webkit-scrollbar{width:6px;height:6px}.memory-log-container::-webkit-scrollbar-thumb{background:#434c5e;border-radius:4px}.memory-log-container::-webkit-scrollbar-thumb:hover{background:#4c566a}.memory-log-container::-webkit-scrollbar-corner{background:0 0}.log-content code,.log-json{color:#a3a3a3;word-break:break-all;font-family:Consolas,Monaco,monospace;font-size:.75rem;line-height:1.4}.log-row{gap:12px;margin-bottom:4px;font-family:Consolas,Monaco,monospace;font-size:.75rem;display:flex}.log-time{color:#fb923c;min-width:60px}.log-msg{color:#d1d5db}.log-meta{color:#6b7280}.gateway-row{margin-bottom:2px}
|
|
1
|
+
:root{--bg-color:#3b4252;--bg-secondary:#434c5e;--bg-sidebar:#2e3440;--text-primary:#eceff4;--text-secondary:#d8dee9;--accent:#88c0d0;--accent-hover:#81a1c1;--glass-bg:#2e3440b3;--glass-border:#d8dee91a;--chat-user:#88c0d0;--chat-user-text:#2e3440;--chat-agent:#2e3440;--tool-bg:#4c566a;--success:#a3be8c;--danger:#bf616a}*{box-sizing:border-box;margin:0;padding:0}body{background-color:var(--bg-color);color:var(--text-primary);height:100vh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;overflow:hidden}#root{width:100vw;height:100vh;display:flex}.sidebar{background-color:var(--bg-sidebar);border-right:1px solid var(--glass-border);z-index:100;flex-direction:column;width:280px;display:flex;box-shadow:10px 0 30px -10px #00000080}.agent-identity-card{background:linear-gradient(#3b82f60d 0%,#0000 100%);border-bottom:1px solid #ffffff0d;align-items:center;gap:16px;margin-bottom:8px;padding:24px;display:flex}.agent-avatar{justify-content:center;align-items:center;display:flex}.agent-info{flex-direction:column;gap:4px;display:flex}.agent-name{color:#fff;letter-spacing:-.025em;font-size:1.1rem;font-weight:700}.agent-status{color:#4ade80;letter-spacing:.05em;align-items:center;gap:6px;font-size:.75rem;font-weight:600;display:flex}.status-dot{background-color:#4ade80;border-radius:50%;width:8px;height:8px;animation:2s infinite pulse-green;box-shadow:0 0 8px #4ade80}@keyframes pulse-green{0%{transform:scale(.95);box-shadow:0 0 #4ade80b3}70%{transform:scale(1);box-shadow:0 0 0 6px #4ade8000}to{transform:scale(.95);box-shadow:0 0 #4ade8000}}.sidebar-scroll-area{flex:1;padding-bottom:24px;overflow-y:auto}.sidebar-scroll-area::-webkit-scrollbar{width:4px}.sidebar-scroll-area::-webkit-scrollbar-thumb{background:#ffffff0d}.sidebar-section{color:#8b9bb4;padding:24px 24px 8px;font-size:.75rem;font-weight:500}.sidebar-nav{flex-direction:column;gap:4px;padding:0 16px;display:flex}.nav-item{color:#94a3b8;cursor:pointer;border:1px solid #0000;border-radius:8px;align-items:center;gap:10px;padding:8px 12px;font-size:.8rem;font-weight:500;transition:all .25s cubic-bezier(.4,0,.2,1);display:flex}.nav-icon{transition:transform .25s cubic-bezier(.4,0,.2,1)}.nav-item:hover{color:#fff;background-color:#ffffff08;transform:translate(4px)}.nav-item:hover .nav-icon{color:#fff;transform:scale(1.1)}.sessions-list{flex:none!important;justify-content:flex-start!important;gap:0!important;height:max-content!important}.session-item{justify-content:space-between;flex:none!important;height:max-content!important;min-height:28px!important;margin-top:0!important;margin-bottom:2px!important;padding:4px 10px!important}.delete-session-btn{color:#bf616a;cursor:pointer;opacity:0;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:4px;transition:opacity .2s,transform .2s;display:flex}.delete-session-btn:hover{background:#bf616a33;transform:scale(1.1)}.session-item:hover .delete-session-btn{opacity:1}.nav-item.active{color:#fff;border-left:3px solid var(--accent);background:linear-gradient(90deg,#3b82f626 0%,#0000 100%);border-radius:4px 12px 12px 4px;font-weight:600}.nav-item.active .nav-icon{color:var(--accent)}.main-content{background-image:radial-gradient(at 0 0,#3b82f61a 0,#0000 50%),radial-gradient(at 100% 100%,#8b5cf61a 0,#0000 50%);flex-direction:column;flex:1;display:flex}.topbar{border-bottom:1px solid var(--glass-border);background:var(--bg-color);justify-content:space-between;align-items:center;height:64px;padding:0 24px;display:flex}.topbar-left{color:var(--text-secondary);align-items:center;gap:16px;font-size:.95rem;font-weight:500;display:flex}.topbar-right{align-items:center;gap:12px;display:flex}.custom-network-selector{position:relative}.network-selector-pill{color:#000;cursor:pointer;background:#88c0d0;border:none;border-radius:9999px;outline:none;align-items:center;gap:8px;padding:8px 16px;font-family:inherit;font-size:.85rem;font-weight:600;transition:all .2s;display:flex}.network-selector-pill:hover{opacity:.9;box-shadow:0 0 10px #88c0d066}.network-selector-pill:focus-visible{box-shadow:0 0 0 2px #88c0d080}.network-icon{flex-shrink:0}.network-chevron{opacity:.7;margin-left:4px}.network-dropdown-menu{background:var(--bg-secondary);border:1px solid var(--glass-border);z-index:100;border-radius:12px;min-width:180px;margin:0;padding:6px;list-style:none;animation:.2s ease-out dropdownSlideIn;position:absolute;top:calc(100% + 8px);right:0;box-shadow:0 8px 24px #0006}@keyframes dropdownSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.network-dropdown-item{cursor:pointer;color:#e5e7eb;border-radius:8px;padding:10px 14px;font-size:.85rem;transition:background .1s}.network-dropdown-item:hover{background:#ffffff0d}.network-dropdown-item.active{color:#88c0d0;background:#88c0d01a;font-weight:600}.workspace-container{width:100%;height:calc(100vh - 64px);display:flex}.chat-wrapper{flex-direction:column;height:100%;padding:24px 0;display:flex}.resizer{background:var(--glass-border);cursor:col-resize;z-index:10;width:6px;transition:background .2s}.resizer:hover,.resizer:active{background:var(--accent)}.canvas-panel{background:var(--bg-sidebar);background-image:radial-gradient(#3b82f60d 0,#0000 80%);flex-direction:column;flex:1;padding:32px;display:flex;position:relative;overflow-y:auto}.canvas-header{border-bottom:1px solid #ffffff0d;justify-content:space-between;align-items:center;margin-bottom:32px;padding-bottom:16px;display:flex}.canvas-title{color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;align-items:center;gap:8px;font-family:monospace;font-size:.85rem;display:flex}.canvas-empty{color:#475569;flex-direction:column;justify-content:center;align-items:center;gap:16px;height:100%;display:flex}.chat-container{flex-direction:column;flex:1;gap:20px;padding:0 24px;display:flex;overflow-y:auto}.chat-container::-webkit-scrollbar{width:6px}.chat-container::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:4px}.message-wrapper{flex-direction:column;max-width:85%;animation:.3s ease-out forwards fadeIn;display:flex}.message-wrapper.agent{flex-direction:row;align-self:flex-start;align-items:flex-end;gap:8px}.message-wrapper.user{flex-direction:row-reverse;align-self:flex-end;align-items:flex-end;gap:8px}.copy-btn{color:var(--text-secondary);cursor:pointer;opacity:0;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:6px;transition:opacity .2s,background .2s;display:flex}.copy-btn:hover{background:var(--bg-secondary);color:var(--text-primary)}.message-wrapper:hover .copy-btn{opacity:1}.message-bubble{white-space:pre-wrap;border-radius:18px;padding:14px 18px;font-size:.95rem;line-height:1.6;box-shadow:0 4px 6px -1px #0000001a}.user .message-bubble{background-color:var(--chat-user);color:var(--chat-user-text);border-bottom-right-radius:4px}.agent .message-bubble{background-color:var(--chat-agent);border:1px solid var(--glass-border);color:var(--text-primary);border-bottom-left-radius:4px}.tool-call{color:var(--text-secondary);background:var(--tool-bg);border:1px solid var(--glass-border);border-radius:12px;align-items:center;gap:8px;margin-top:10px;padding:10px 14px;font-size:.85rem;display:flex}.tool-call code{color:var(--accent);font-family:monospace}.input-area{padding:20px 24px 0}.input-form{background:var(--bg-secondary);border:1px solid var(--glass-border);border-radius:16px;gap:12px;padding:8px;transition:all .2s;display:flex}.input-form:focus-within{border-color:var(--accent);box-shadow:0 0 0 2px #3b82f633}.chat-input{color:#fff;background:0 0;border:none;outline:none;flex:1;padding:12px 16px;font-family:inherit;font-size:.95rem}.send-button{background:var(--accent);color:#fff;cursor:pointer;border:none;border-radius:12px;justify-content:center;align-items:center;width:44px;height:44px;transition:all .2s;display:flex}.voice-button{background:var(--bg-secondary);color:var(--text-secondary);border:1px solid var(--glass-border);cursor:pointer;border-radius:12px;justify-content:center;align-items:center;width:44px;height:44px;transition:all .2s;display:flex}.voice-button:hover{color:#fff;border-color:#ef4444}.voice-button.listening{color:#ef4444;background:#ef444433;border-color:#ef4444;animation:1.5s infinite pulse-red}.voice-button.active-mode{color:#3b82f6;border-color:#3b82f6}.voice-button.speaking{color:#3b82f6;background:#3b82f633;border-color:#3b82f6;animation:1.5s infinite pulse-blue}@keyframes pulse-red{0%{box-shadow:0 0 #ef444466}70%{box-shadow:0 0 0 10px #ef444400}to{box-shadow:0 0 #ef444400}}@keyframes pulse-blue{0%{box-shadow:0 0 #3b82f666}70%{box-shadow:0 0 0 10px #3b82f600}to{box-shadow:0 0 #3b82f600}}.send-button:hover:not(:disabled){background:var(--accent-hover);transform:scale(1.05)}.send-button:disabled{opacity:.5;cursor:not-allowed}.typing-indicator{background-color:var(--chat-agent);border:1px solid var(--glass-border);border-radius:18px 18px 18px 4px;align-self:flex-start;gap:4px;width:fit-content;padding:14px 18px;display:flex}.dot{background:var(--text-secondary);border-radius:50%;width:6px;height:6px;animation:1.4s ease-in-out infinite both bounce}.dot:first-child{animation-delay:-.32s}.dot:nth-child(2){animation-delay:-.16s}@keyframes bounce{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}@keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.prompt-suggestions{gap:12px;margin-bottom:12px;display:flex;overflow-x:auto}.prompt-suggestions button{background:var(--bg-secondary);color:var(--text-secondary);border:1px solid var(--glass-border);cursor:pointer;white-space:nowrap;border-radius:20px;padding:8px 16px;font-size:.85rem;transition:all .2s}.prompt-suggestions button:hover{border-color:var(--accent);color:var(--text-primary)}.trending-tokens{color:var(--text-secondary);align-items:center;gap:12px;margin-top:12px;font-size:.8rem;display:flex}.token-tag{background:var(--accent);color:var(--bg-color);cursor:pointer;border-radius:12px;padding:4px 10px;font-weight:600;transition:transform .2s,filter .2s}.token-tag:hover{filter:brightness(1.2);transform:translateY(-2px)}.nord-label{color:#81a1c1;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;font-size:.75rem;font-weight:700;display:block}.nord-pill-input{color:#88c0d0;background-color:#2e3440b3;border:1px solid #d8dee91a;border-radius:9999px;outline:none;width:100%;padding:10px 20px;font-size:.9rem;font-weight:600;transition:all .2s}.nord-pill-input:focus{border-color:#88c0d0;box-shadow:0 0 10px #88c0d033}.nord-pill-input::placeholder{color:#4c566a}.nord-input{color:#eceff4;background-color:#2e3440;border:1px solid #434c5e;border-radius:6px;outline:none;width:100%;padding:10px 14px;font-size:.9rem;transition:all .2s}.nord-input:focus{border-color:#88c0d0;box-shadow:0 0 0 2px #88c0d033}.nord-input::placeholder{color:#4c566a}.nord-slider{appearance:none;background:#4c566a;border-radius:3px;outline:none;width:100%;height:6px;margin-top:10px}.nord-slider::-webkit-slider-thumb{appearance:none;cursor:pointer;background:#eceff4;border:2px solid #88c0d0;border-radius:50%;width:18px;height:18px;transition:all .2s}.nord-slider::-webkit-slider-thumb:hover{background:#fff;transform:scale(1.1)}.nord-slider::-moz-range-thumb{cursor:pointer;background:#eceff4;border:2px solid #88c0d0;border-radius:50%;width:18px;height:18px;transition:all .2s}.nord-btn-primary{color:#2e3440;cursor:pointer;background-color:#81a1c1;border:none;border-radius:6px;justify-content:center;align-items:center;padding:10px 20px;font-size:.9rem;font-weight:600;transition:background-color .2s;display:flex}.nord-btn-primary:hover{background-color:#88c0d0}.nord-btn-primary:disabled{color:#8fbcbb;cursor:not-allowed;background-color:#4c566a}.nord-panel-header{border-bottom:1px solid #d8dee90d;align-items:center;gap:10px;margin-bottom:20px;padding-bottom:12px;display:flex}.nord-panel-header h3{color:#eceff4;margin:0;font-size:1.1rem;font-weight:600}.overview-container{color:#fff;height:calc(100vh - 64px);padding:24px;font-family:Inter,sans-serif;overflow-y:auto}.overview-container::-webkit-scrollbar{width:6px}.overview-container::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:4px}.overview-header h1{margin-bottom:4px;font-size:1.5rem;font-weight:600}.overview-header p{color:var(--text-secondary);margin-bottom:24px;font-size:.9rem}.panel{background:#14182099;border:1px solid #ffffff0d;border-radius:12px;margin-bottom:24px;padding:20px}.panel-header h3{margin-bottom:4px;font-size:1.1rem;font-weight:600}.panel-header p{color:var(--text-secondary);margin-bottom:16px;font-size:.85rem}.form-group{margin-bottom:16px}.form-row{gap:16px;display:flex}.flex-1{flex:1}label{text-transform:uppercase;color:var(--text-secondary);letter-spacing:.05em;margin-bottom:8px;font-size:.75rem;display:block}input,select{color:#fff;background:#0000004d;border:1px solid #ffffff1a;border-radius:8px;width:100%;padding:10px 12px;font-family:monospace;font-size:.9rem}input:read-only{color:var(--text-secondary)}.input-with-icon{align-items:center;display:flex;position:relative}.input-with-icon svg{color:var(--text-secondary);cursor:pointer;position:absolute;right:12px}.form-actions{align-items:center;gap:12px;margin-top:20px;display:flex}.btn-primary{color:#fff;cursor:pointer;background:#3b82f6;border:none;border-radius:6px;padding:8px 16px;font-size:.9rem}.btn-secondary{color:#fff;cursor:pointer;background:0 0;border:1px solid #fff3;border-radius:6px;padding:8px 16px;font-size:.9rem}.action-hint{color:var(--text-secondary);margin-left:8px;font-size:.85rem}.snapshot-grid{grid-template-columns:repeat(4,1fr);gap:20px;display:grid}.stat-val{margin-bottom:4px;font-size:1.5rem;font-weight:700}.text-green{color:#22c55e}.stat-block p{color:var(--text-secondary);margin-top:8px;font-size:.8rem;line-height:1.4}.metrics-grid{grid-template-columns:repeat(5,1fr);gap:16px;margin-bottom:32px;display:grid}.metric-card{background:#14182099;border:1px solid #ffffff0d;border-radius:12px;padding:16px}.metric-val{margin-bottom:4px;font-size:1.5rem;font-weight:700}.metric-sub{color:var(--text-secondary);font-size:.75rem}.section-title{text-transform:uppercase;color:var(--text-secondary);letter-spacing:.05em;margin-bottom:12px;font-size:.75rem}.session-item{justify-content:space-between;margin-bottom:24px;padding:16px 20px;display:flex}.text-secondary{color:var(--text-secondary)}.attention-panel{background:#eab3081a;border:1px solid #eab30833;border-radius:12px;flex-direction:column;margin-bottom:24px;padding:16px 20px;display:flex}.attention-header{color:#eab308;align-items:center;gap:8px;margin-bottom:8px;display:flex}.attention-header h4{font-size:1rem;font-weight:600}.attention-content p{margin-bottom:4px;font-size:.95rem}.attention-content span{font-size:.85rem}.logs-grid{grid-template-columns:1fr 1fr;gap:16px;display:grid}.log-panel{background:#0a0c10cc;border:1px solid #ffffff0d;border-radius:12px;flex-direction:column;height:300px;display:flex;overflow:hidden}.log-header{background:#ffffff0d;border-bottom:1px solid #ffffff0d;padding:12px 16px;font-size:.85rem;font-weight:600}.badge{background:#ffffff1a;border-radius:10px;margin-left:8px;padding:2px 6px;font-size:.7rem}.log-content{flex-direction:column;gap:4px;padding:12px 16px;display:flex;overflow:auto}.log-content::-webkit-scrollbar{width:6px;height:6px}.log-content::-webkit-scrollbar-thumb{background:#434c5e;border-radius:4px}.log-content::-webkit-scrollbar-thumb:hover{background:#4c566a}.log-content::-webkit-scrollbar-corner{background:0 0}.memory-log-container::-webkit-scrollbar{width:6px;height:6px}.memory-log-container::-webkit-scrollbar-thumb{background:#434c5e;border-radius:4px}.memory-log-container::-webkit-scrollbar-thumb:hover{background:#4c566a}.memory-log-container::-webkit-scrollbar-corner{background:0 0}.log-content code,.log-json{color:#a3a3a3;word-break:break-all;font-family:Consolas,Monaco,monospace;font-size:.75rem;line-height:1.4}.log-row{gap:12px;margin-bottom:4px;font-family:Consolas,Monaco,monospace;font-size:.75rem;display:flex}.log-time{color:#fb923c;min-width:60px}.log-msg{color:#d1d5db}.log-meta{color:#6b7280}.gateway-row{margin-bottom:2px}
|