nyxora 26.6.21 → 26.6.23
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/README.md +3 -4
- package/bin/nyxora.mjs +14 -2
- package/dist/packages/core/src/agent/cronManager.js +107 -0
- package/dist/packages/core/src/agent/reasoning.js +85 -22
- package/dist/packages/core/src/agent/transactionManager.js +2 -2
- package/dist/packages/core/src/agent/updateIdentity.js +71 -0
- package/dist/packages/core/src/config/paths.js +5 -20
- package/dist/packages/core/src/gateway/WebSocketManager.js +98 -0
- package/dist/packages/core/src/gateway/chat.js +38 -0
- package/dist/packages/core/src/gateway/cli.js +20 -20
- package/dist/packages/core/src/gateway/server.js +63 -8
- package/dist/packages/core/src/gateway/setup.js +9 -6
- package/dist/packages/core/src/gateway/telegram.js +43 -0
- package/dist/packages/core/src/gateway/tracker.js +63 -0
- package/dist/packages/core/src/memory/logger.js +1 -1
- package/dist/packages/core/src/system/skills/cancelTask.js +40 -0
- package/dist/packages/core/src/system/skills/editFile.js +5 -0
- package/dist/packages/core/src/system/skills/scheduleTask.js +39 -0
- package/dist/packages/core/src/system/skills/searchWeb.js +61 -8
- package/dist/packages/core/src/system/skills/writeFile.js +5 -0
- package/dist/packages/core/src/web3/skills/getPrice.js +1 -1
- package/dist/packages/core/src/web3/utils/vaultClient.js +79 -29
- package/dist/packages/policy/src/server.js +29 -1
- package/package.json +7 -1
- package/packages/core/package.json +8 -1
- package/packages/core/src/agent/cronManager.ts +87 -0
- package/packages/core/src/agent/reasoning.ts +91 -21
- package/packages/core/src/agent/transactionManager.ts +2 -1
- package/packages/core/src/agent/updateIdentity.ts +68 -0
- package/packages/core/src/config/parser.ts +1 -1
- package/packages/core/src/config/paths.ts +5 -23
- package/packages/core/src/gateway/WebSocketManager.ts +114 -0
- package/packages/core/src/gateway/chat.ts +40 -1
- package/packages/core/src/gateway/cli.ts +10 -10
- package/packages/core/src/gateway/server.ts +66 -7
- package/packages/core/src/gateway/setup.ts +8 -5
- package/packages/core/src/gateway/telegram.ts +49 -0
- package/packages/core/src/gateway/tracker.ts +61 -0
- package/packages/core/src/memory/logger.ts +1 -1
- package/packages/core/src/system/skills/cancelTask.ts +38 -0
- package/packages/core/src/system/skills/editFile.ts +7 -0
- package/packages/core/src/system/skills/scheduleTask.ts +38 -0
- package/packages/core/src/system/skills/searchWeb.ts +56 -8
- package/packages/core/src/system/skills/writeFile.ts +7 -0
- package/packages/core/src/web3/skills/getPrice.ts +1 -1
- package/packages/core/src/web3/utils/vaultClient.ts +86 -26
- package/packages/dashboard/dist/assets/index-CjZWf1Ei.css +1 -0
- package/packages/dashboard/dist/assets/index-CmWZofn_.js +16 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/dist/routers/0x.png +0 -0
- package/packages/dashboard/dist/routers/1inch.png +0 -0
- package/packages/dashboard/dist/routers/cmc.png +0 -0
- package/packages/dashboard/dist/routers/kyberswap.png +0 -0
- package/packages/dashboard/dist/routers/lifi.png +0 -0
- package/packages/dashboard/dist/routers/openocean.png +0 -0
- package/packages/dashboard/dist/routers/relay.png +0 -0
- package/packages/dashboard/dist/routers/zerion.png +0 -0
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +4 -3
- package/packages/policy/src/server.ts +29 -1
- package/packages/signer/package.json +1 -1
- package/packages/dashboard/dist/assets/index-CQNHWZtN.css +0 -1
- package/packages/dashboard/dist/assets/index-Di9x08yk.js +0 -16
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cronManager } from '../../agent/cronManager';
|
|
2
|
+
|
|
3
|
+
export const scheduleTaskDefinition = {
|
|
4
|
+
type: "function",
|
|
5
|
+
function: {
|
|
6
|
+
name: "schedule_task",
|
|
7
|
+
description: "Schedule a recurring background task for the AI to execute automatically using a cron expression. Use this when the user asks you to remind them or monitor something periodically (e.g. 'check price every hour', 'monitor my wallet every 5 minutes'). The AI will execute the provided prompt at the scheduled interval and send the result via Telegram notification.",
|
|
8
|
+
parameters: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
cronExpression: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "A standard 5-field cron expression (minute hour day month day-of-week). Examples: '*/5 * * * *' (every 5 mins), '0 * * * *' (every hour), '0 8 * * *' (every day at 8 AM)."
|
|
14
|
+
},
|
|
15
|
+
prompt: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "The prompt/command that the AI should execute when the cron triggers. E.g., 'What is the current price of Ethereum?'"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
required: ["cronExpression", "prompt"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function executeScheduleTask(args: any): Promise<string> {
|
|
26
|
+
const { cronExpression, prompt } = args;
|
|
27
|
+
|
|
28
|
+
if (!cronExpression || !prompt) {
|
|
29
|
+
return "Error: Missing required parameters cronExpression or prompt.";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const jobId = cronManager.addJob(cronExpression, prompt);
|
|
34
|
+
return `Success! I have scheduled the background task.\nJob ID: ${jobId}\nSchedule: ${cronExpression}\nPrompt to execute: "${prompt}"\n\nYou will receive a notification via Telegram every time this task completes.`;
|
|
35
|
+
} catch (error: any) {
|
|
36
|
+
return `Failed to schedule task: ${error.message}. Please ensure the cron expression is valid.`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { loadConfig, loadApiKeys } from '../../config/parser';
|
|
2
|
+
import { search, SafeSearchType } from 'duck-duck-scrape';
|
|
2
3
|
|
|
3
4
|
interface SearchQueryResult {
|
|
4
5
|
title: string;
|
|
@@ -90,6 +91,26 @@ async function searchSearxng(query: string, depth: number = 1): Promise<SearchQu
|
|
|
90
91
|
throw new Error('[SearXNG Error] All decentralized instances failed.');
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
async function searchDuckDuckGo(query: string, depth: number = 1): Promise<SearchQueryResult[]> {
|
|
95
|
+
try {
|
|
96
|
+
const searchResults = await search(query, {
|
|
97
|
+
safeSearch: SafeSearchType.MODERATE
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!searchResults.noResults && searchResults.results.length > 0) {
|
|
101
|
+
const maxResults = depth > 1 ? 15 : 8;
|
|
102
|
+
return searchResults.results.slice(0, maxResults).map(r => ({
|
|
103
|
+
title: r.title,
|
|
104
|
+
url: r.url,
|
|
105
|
+
content: r.description || r.title
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
return [];
|
|
109
|
+
} catch (e: any) {
|
|
110
|
+
throw new Error(`[DuckDuckGo Error] Failed to scrape: ${e.message}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
93
114
|
const searchCache = new Map<string, {data: SearchQueryResult[], timestamp: number}>();
|
|
94
115
|
|
|
95
116
|
export async function searchWeb(query: string, depth: number = 1): Promise<string> {
|
|
@@ -134,12 +155,22 @@ export async function searchWeb(query: string, depth: number = 1): Promise<strin
|
|
|
134
155
|
try {
|
|
135
156
|
results = await searchBrave(finalQuery, creds.brave_key, depth);
|
|
136
157
|
} catch (e2: any) {
|
|
137
|
-
console.warn('[WebSearch] Backup provider (Brave) failed. Falling back to
|
|
138
|
-
|
|
158
|
+
console.warn('[WebSearch] Backup provider (Brave) failed. Falling back to DuckDuckGo (L3)...');
|
|
159
|
+
try {
|
|
160
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
161
|
+
} catch (e3) {
|
|
162
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
163
|
+
results = await searchSearxng(finalQuery, depth);
|
|
164
|
+
}
|
|
139
165
|
}
|
|
140
166
|
} else {
|
|
141
|
-
console.warn('[WebSearch] No backup provider found. Falling back to
|
|
142
|
-
|
|
167
|
+
console.warn('[WebSearch] No backup premium provider found. Falling back to DuckDuckGo (L3)...');
|
|
168
|
+
try {
|
|
169
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
170
|
+
} catch (e3) {
|
|
171
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
172
|
+
results = await searchSearxng(finalQuery, depth);
|
|
173
|
+
}
|
|
143
174
|
}
|
|
144
175
|
} else {
|
|
145
176
|
throw e;
|
|
@@ -155,17 +186,34 @@ export async function searchWeb(query: string, depth: number = 1): Promise<strin
|
|
|
155
186
|
try {
|
|
156
187
|
results = await searchTavily(finalQuery, creds.tavily_key, depth);
|
|
157
188
|
} catch (e2: any) {
|
|
158
|
-
console.warn('[WebSearch] Backup provider (Tavily) failed. Falling back to
|
|
159
|
-
|
|
189
|
+
console.warn('[WebSearch] Backup provider (Tavily) failed. Falling back to DuckDuckGo (L3)...');
|
|
190
|
+
try {
|
|
191
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
192
|
+
} catch (e3) {
|
|
193
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
194
|
+
results = await searchSearxng(finalQuery, depth);
|
|
195
|
+
}
|
|
160
196
|
}
|
|
161
197
|
} else {
|
|
162
|
-
console.warn('[WebSearch] No backup provider found. Falling back to
|
|
163
|
-
|
|
198
|
+
console.warn('[WebSearch] No backup premium provider found. Falling back to DuckDuckGo (L3)...');
|
|
199
|
+
try {
|
|
200
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
201
|
+
} catch (e3) {
|
|
202
|
+
console.warn('[WebSearch] DuckDuckGo failed. Falling back to SearXNG Mesh...');
|
|
203
|
+
results = await searchSearxng(finalQuery, depth);
|
|
204
|
+
}
|
|
164
205
|
}
|
|
165
206
|
} else {
|
|
166
207
|
throw e;
|
|
167
208
|
}
|
|
168
209
|
}
|
|
210
|
+
} else if (provider === 'duckduckgo') {
|
|
211
|
+
try {
|
|
212
|
+
results = await searchDuckDuckGo(finalQuery, depth);
|
|
213
|
+
} catch (e: any) {
|
|
214
|
+
console.warn('[WebSearch] Primary provider (DuckDuckGo) failed. Falling back to SearXNG Mesh...');
|
|
215
|
+
results = await searchSearxng(finalQuery, depth);
|
|
216
|
+
}
|
|
169
217
|
} else {
|
|
170
218
|
results = await searchSearxng(finalQuery, depth);
|
|
171
219
|
}
|
|
@@ -4,6 +4,13 @@ import path from 'path';
|
|
|
4
4
|
export function writeLocalFile(filePath: string, content: string): string {
|
|
5
5
|
try {
|
|
6
6
|
const absolutePath = path.resolve(filePath);
|
|
7
|
+
|
|
8
|
+
// Security Firewall: Block modification of core configuration files
|
|
9
|
+
const basename = path.basename(absolutePath);
|
|
10
|
+
if (['config.yaml', 'rpc_key.yaml', 'policy.yaml'].includes(basename)) {
|
|
11
|
+
return `Error: Access Denied. You are strictly forbidden from modifying core configuration files directly. If you need to update your agent name, use the update_identity tool instead.`;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
const dir = path.dirname(absolutePath);
|
|
8
15
|
|
|
9
16
|
if (!fs.existsSync(dir)) {
|
|
@@ -4,7 +4,7 @@ export const getPriceToolDefinition = {
|
|
|
4
4
|
type: "function",
|
|
5
5
|
function: {
|
|
6
6
|
name: "get_price",
|
|
7
|
-
description: "Fetches the current price of a cryptocurrency.
|
|
7
|
+
description: "Fetches the current price of a cryptocurrency.",
|
|
8
8
|
parameters: {
|
|
9
9
|
type: "object",
|
|
10
10
|
properties: {
|
|
@@ -2,6 +2,8 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
import { pack } from 'msgpackr';
|
|
5
7
|
|
|
6
8
|
function getInternalToken(): string | undefined {
|
|
7
9
|
try {
|
|
@@ -15,17 +17,46 @@ function getInternalToken(): string | undefined {
|
|
|
15
17
|
|
|
16
18
|
export async function getAddress(): Promise<string> {
|
|
17
19
|
const token = getInternalToken();
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const POLICY_SOCKET = '/tmp/nyxora-policy.sock';
|
|
22
|
+
const options = {
|
|
23
|
+
socketPath: fs.existsSync(POLICY_SOCKET) ? POLICY_SOCKET : undefined,
|
|
24
|
+
host: fs.existsSync(POLICY_SOCKET) ? undefined : '127.0.0.1',
|
|
25
|
+
port: fs.existsSync(POLICY_SOCKET) ? undefined : (process.env.POLICY_PORT || 3001),
|
|
26
|
+
path: '/address',
|
|
27
|
+
method: 'GET',
|
|
28
|
+
headers: {
|
|
29
|
+
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const req = http.request(options, (res) => {
|
|
34
|
+
let data = '';
|
|
35
|
+
res.on('data', chunk => data += chunk);
|
|
36
|
+
res.on('end', () => {
|
|
37
|
+
try {
|
|
38
|
+
if (res.statusCode !== 200) {
|
|
39
|
+
reject(new Error(data));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const parsed = JSON.parse(data);
|
|
43
|
+
resolve(parsed.address);
|
|
44
|
+
} catch (e: any) {
|
|
45
|
+
reject(new Error(`Failed to get address from vault: ${e.message}`));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
22
48
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
49
|
+
|
|
50
|
+
req.on('error', (error) => {
|
|
51
|
+
reject(new Error(`Failed to get address from vault: ${error.message}`));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
req.setTimeout(30000, () => {
|
|
55
|
+
req.destroy(new Error('Timeout'));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
req.end();
|
|
59
|
+
});
|
|
29
60
|
}
|
|
30
61
|
|
|
31
62
|
export async function submitTransaction(txPayload: any): Promise<string> {
|
|
@@ -39,25 +70,54 @@ export async function submitTransaction(txPayload: any): Promise<string> {
|
|
|
39
70
|
const secret = getInternalToken() || '';
|
|
40
71
|
txPayload.internalSignature = crypto.createHmac('sha256', secret).update(txPayload.chainName + amountWei).digest('hex');
|
|
41
72
|
}
|
|
42
|
-
|
|
43
|
-
const
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const POLICY_SOCKET = '/tmp/nyxora-policy.sock';
|
|
75
|
+
const payloadBuffer = pack(txPayload);
|
|
76
|
+
|
|
77
|
+
const options = {
|
|
78
|
+
socketPath: fs.existsSync(POLICY_SOCKET) ? POLICY_SOCKET : undefined,
|
|
79
|
+
host: fs.existsSync(POLICY_SOCKET) ? undefined : '127.0.0.1',
|
|
80
|
+
port: fs.existsSync(POLICY_SOCKET) ? undefined : (process.env.POLICY_PORT || 3001),
|
|
81
|
+
path: '/request-tx',
|
|
44
82
|
method: 'POST',
|
|
45
83
|
headers: {
|
|
46
|
-
'Content-Type': 'application/
|
|
84
|
+
'Content-Type': 'application/msgpack',
|
|
85
|
+
'Content-Length': payloadBuffer.length,
|
|
47
86
|
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const req = http.request(options, (res) => {
|
|
91
|
+
let data = '';
|
|
92
|
+
res.on('data', chunk => data += chunk);
|
|
93
|
+
res.on('end', () => {
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(data);
|
|
96
|
+
if (res.statusCode !== 200) {
|
|
97
|
+
reject(new Error(parsed.error || 'Failed to submit transaction'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (parsed.status === 'pending') {
|
|
101
|
+
resolve(`Pending (ID: ${parsed.txId})`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
resolve(parsed.signedHash || parsed.hash || parsed.txHash || parsed.status || 'Transaction executed successfully (No Hash returned)');
|
|
105
|
+
} catch (e: any) {
|
|
106
|
+
reject(new Error(`Failed to parse vault response: ${e.message}`));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
51
109
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
110
|
+
|
|
111
|
+
req.on('error', (error) => {
|
|
112
|
+
reject(new Error(`Transaction submission failed: ${error.message}`));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
req.setTimeout(30000, () => {
|
|
116
|
+
req.destroy(new Error('Timeout'));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
req.write(payloadBuffer);
|
|
120
|
+
req.end();
|
|
121
|
+
});
|
|
62
122
|
}
|
|
63
123
|
|
|
@@ -0,0 +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;--terminal-bg:#1e222a;--btn-primary-bg:#81a1c1}body.light-theme{--bg-color:#fdfbf6;--bg-secondary:#f4f1e1;--bg-sidebar:#fdfbf6;--text-primary:#1a2b2b;--text-secondary:#5c6b6b;--accent:#8a9a86;--accent-hover:#72826e;--glass-bg:#fdfbf6cc;--glass-border:#1a2b2b14;--chat-user:#eef0e5;--chat-user-text:#1a2b2b;--chat-agent:#fff;--tool-bg:#f4f1e1;--success:#6b8e23;--danger:#cd5c5c;--terminal-bg:#eef0e5;--nav-text:var(--text-secondary);--nav-section:var(--text-secondary);--btn-primary-bg:var(--text-secondary)}*{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;overflow-x:hidden}.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:var(--text-primary);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:hidden auto}.sidebar-scroll-area::-webkit-scrollbar{width:4px}.sidebar-scroll-area::-webkit-scrollbar-thumb{background:#ffffff0d}.sidebar-section{color:var(--nav-section,#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:var(--nav-text,#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:var(--text-primary);background-color:#ffffff08;transform:translate(4px)}.nav-item:hover .nav-icon{color:var(--text-primary);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:var(--text-primary);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{background-color:var(--accent);color:#000;cursor:pointer;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:var(--text-primary);border-radius:8px;padding:10px 14px;font-size:.85rem;transition:background .1s}.network-dropdown-item:hover{background:var(--glass-border)}.network-dropdown-item.active{background:var(--tool-bg);color:var(--accent);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:var(--text-primary);background:0 0;border:none;outline:none;flex:1;padding:12px 16px;font-family:inherit;font-size:.95rem}.send-button{background:var(--accent);color:var(--text-primary);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:var(--text-primary);border-color:#ef4444}.action-menu-container{background:0 0;border-radius:12px;align-items:center;margin-right:8px;padding:0;transition:all .3s cubic-bezier(.4,0,.2,1);display:flex;position:relative;overflow:hidden}.action-menu-container:hover{background:var(--bg-secondary)}.action-menu-items{opacity:0;pointer-events:none;align-items:center;gap:4px;width:0;transition:all .3s cubic-bezier(.4,0,.2,1);display:flex;transform:translate(-10px)}.action-menu-container:hover .action-menu-items{opacity:1;pointer-events:auto;width:96px;margin-left:4px;margin-right:4px;transform:translate(0)}.plus-button{z-index:2;background:0 0;border:none;transition:transform .3s}.action-menu-container:hover .plus-button{color:var(--accent);transform:rotate(45deg)}.action-menu-items .voice-button{background:0 0;border:none;width:40px;height:40px}.action-menu-items .voice-button:hover{background:#ffffff1a;border-color:#0000}.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:var(--text-secondary);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;font-size:.75rem;font-weight:700;display:block}.nord-pill-input{background-color:var(--glass-bg);border:1px solid var(--glass-border);width:100%;color:var(--accent);border-radius:9999px;outline:none;padding:10px 20px;font-size:.9rem;font-weight:600;transition:all .2s}.nord-pill-input:focus{border-color:var(--accent);box-shadow:0 0 10px var(--accent)}.nord-pill-input::placeholder{color:var(--text-secondary);opacity:.7}.nord-input{background-color:var(--bg-sidebar);border:1px solid var(--glass-border);width:100%;color:var(--text-primary);border-radius:6px;outline:none;padding:10px 14px;font-size:.9rem;transition:all .2s}.nord-input:focus{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}.nord-input::placeholder{color:var(--text-secondary);opacity:.7}.nord-slider{appearance:none;background:var(--tool-bg);border-radius:3px;outline:none;width:100%;height:6px;margin-top:10px}.nord-slider::-webkit-slider-thumb{appearance:none;background:var(--text-primary);cursor:pointer;border:2px solid var(--accent);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{background:var(--text-primary);cursor:pointer;border:2px solid var(--accent);border-radius:50%;width:18px;height:18px;transition:all .2s}.nord-btn-primary{background-color:var(--btn-primary-bg);color:var(--bg-sidebar);cursor:pointer;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:var(--accent)}.nord-btn-primary:disabled{background-color:var(--tool-bg);color:#8fbcbb;cursor:not-allowed}.nord-panel-header{border-bottom:1px solid var(--glass-border);align-items:center;gap:10px;margin-bottom:20px;padding-bottom:12px;display:flex}.nord-panel-header h3{color:var(--text-primary);margin:0;font-size:1.1rem;font-weight:600}.sidebar.collapsed{width:68px}.nav-label,.agent-info,.sidebar-section,.delete-session-btn,.agent-avatar,.sessions-list,.agent-identity-card,.sidebar-nav,.nav-item{white-space:nowrap;transition:all .3s cubic-bezier(.4,0,.2,1)}.sidebar.collapsed .nav-label,.sidebar.collapsed .agent-info,.sidebar.collapsed .sidebar-section,.sidebar.collapsed .delete-session-btn,.sidebar.collapsed .agent-avatar,.sidebar.collapsed .sessions-list{opacity:0!important;width:0!important;height:0!important;margin:0!important;padding:0!important;overflow:hidden!important}.sidebar.collapsed .agent-identity-card{justify-content:center;border-bottom-color:#0000!important;gap:0!important;min-height:60px!important;margin-bottom:0!important;padding:16px 0 4px!important}.sidebar.collapsed .sidebar-toggle-btn{border-radius:50%;margin-top:0;padding:8px;position:static!important}.sidebar.collapsed .sidebar-toggle-btn:hover{background-color:#ffffff0d!important}.sidebar.collapsed .sidebar-nav{align-items:center;gap:2px!important;padding:4px 0 0!important}.sidebar.collapsed .nav-item{justify-content:center;border-width:0!important;border-color:#0000!important;border-radius:50%!important;gap:0!important;width:36px!important;height:36px!important;margin:0 auto!important;padding:0!important}.sidebar.collapsed .nav-item.active{background:#3b82f633!important;border-width:0!important;border-color:#0000!important}.sidebar.collapsed .session-item{border-radius:50%}.sidebar.collapsed .nav-item .nav-icon{margin:0}.overview-container{height:calc(100vh - 64px);color:var(--text-primary);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:var(--bg-secondary);border:1px solid var(--glass-border);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{background:var(--bg-color);border:1px solid var(--glass-border);width:100%;color:var(--text-primary);border-radius:8px;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:var(--text-primary);cursor:pointer;background:#3b82f6;border:none;border-radius:6px;padding:8px 16px;font-size:.9rem}.btn-secondary{color:var(--text-primary);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:var(--bg-secondary);border:1px solid var(--glass-border);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:var(--terminal-bg);border:1px solid var(--glass-border);border-radius:12px;flex-direction:column;height:300px;display:flex;overflow:hidden}.log-header{background:var(--glass-border);border-bottom:1px solid var(--glass-border);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:var(--glass-border);border-radius:4px}.log-content::-webkit-scrollbar-thumb:hover{background:var(--glass-border)}.log-content::-webkit-scrollbar-corner{background:0 0}.memory-log-container::-webkit-scrollbar{width:6px;height:6px}.memory-log-container::-webkit-scrollbar-thumb{background:var(--glass-border);border-radius:4px}.memory-log-container::-webkit-scrollbar-thumb:hover{background:var(--glass-border)}.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}.search-chat-container{flex-direction:column;width:100%;max-width:800px;margin:0 auto;padding:40px 20px;animation:.3s ease-out fadeIn;display:flex}.search-chat-header{justify-content:center;margin-bottom:40px;display:flex}.search-input-wrapper{width:100%;max-width:600px;position:relative}.search-icon{color:var(--text-secondary);position:absolute;top:50%;left:20px;transform:translateY(-50%)}.search-input{border:1px solid var(--glass-border);background:var(--bg-sidebar);width:100%;color:var(--text-primary);border-radius:30px;outline:none;padding:16px 20px 16px 50px;font-size:1.1rem;transition:all .2s;box-shadow:0 4px 20px #0000000d}.search-input:focus{border-color:var(--accent);background:var(--bg-color);box-shadow:0 4px 25px #0000001a}.search-results-area{flex-direction:column;display:flex}.search-results-title{color:var(--text-secondary);margin-bottom:16px;padding-left:8px;font-size:.9rem;font-weight:500}.search-result-item{cursor:pointer;border-radius:12px;align-items:center;padding:16px;transition:background-color .2s;display:flex}.search-result-item:hover{background-color:var(--bg-sidebar)}.result-title{color:var(--text-primary);align-items:center;gap:12px;font-size:1rem;display:flex}.result-icon{color:var(--text-secondary)}.search-no-results{text-align:center;color:var(--text-secondary);padding:40px;font-style:italic}
|