nyxora 26.6.27 โ 26.6.28
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 +23 -3
- package/bin/nyxora.mjs +16 -0
- package/dist/packages/core/src/agent/web3Agent.js +1 -1
- package/dist/packages/core/src/config/marketConfigManager.js +2 -1
- package/dist/packages/core/src/config/parser.js +0 -5
- package/dist/packages/core/src/gateway/WebSocketManager.js +2 -1
- package/dist/packages/core/src/gateway/server.js +71 -11
- package/dist/packages/core/src/memory/episodic.js +7 -2
- package/dist/packages/core/src/memory/logger.js +10 -0
- package/dist/packages/core/src/memory/reflection.js +15 -11
- package/dist/packages/core/src/memory/validator.js +1 -12
- package/dist/packages/core/src/web3/aggregator/providers/OpenOceanProvider.js +1 -1
- package/dist/packages/core/src/web3/skills/createMarketWatchAgent.js +26 -0
- package/dist/packages/core/src/web3/skills/getTxHistory.js +1 -1
- package/dist/packages/core/src/web3/skills/provideLiquidity.js +13 -7
- package/dist/packages/core/src/web3/utils/marketEngine.js +8 -3
- package/dist/packages/core/src/web3/utils/rpcEngine.js +7 -1
- package/dist/packages/policy/src/server.js +24 -11
- package/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/agent/web3Agent.ts +1 -1
- package/packages/core/src/config/marketConfigManager.ts +2 -2
- package/packages/core/src/config/parser.ts +3 -4
- package/packages/core/src/gateway/WebSocketManager.ts +2 -1
- package/packages/core/src/gateway/server.ts +71 -11
- package/packages/core/src/memory/episodic.ts +9 -2
- package/packages/core/src/memory/logger.ts +11 -0
- package/packages/core/src/memory/reflection.ts +18 -12
- package/packages/core/src/memory/validator.ts +1 -12
- package/packages/core/src/web3/aggregator/providers/OpenOceanProvider.ts +1 -1
- package/packages/core/src/web3/skills/createMarketWatchAgent.ts +24 -0
- package/packages/core/src/web3/skills/getTxHistory.ts +1 -1
- package/packages/core/src/web3/skills/provideLiquidity.ts +14 -7
- package/packages/core/src/web3/utils/marketEngine.ts +7 -3
- package/packages/core/src/web3/utils/rpcEngine.ts +5 -1
- package/packages/dashboard/dist/assets/index-BoFANMsj.js +16 -0
- package/packages/dashboard/dist/assets/index-K1CmXmAE.css +1 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/dist/server.js +17 -5
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/server.ts +20 -5
- package/packages/policy/package.json +1 -1
- package/packages/policy/src/server.ts +28 -13
- package/packages/signer/package.json +1 -1
- package/dist/packages/core/src/test_security.js +0 -43
- package/packages/dashboard/dist/assets/index-CjZWf1Ei.css +0 -1
- package/packages/dashboard/dist/assets/index-DlwR7UtR.js +0 -16
package/README.md
CHANGED
|
@@ -97,8 +97,21 @@ To dive deeper into the technical details of our Zero-Knowledge security archite
|
|
|
97
97
|
|
|
98
98
|
## ๐ Quick Start & Installation
|
|
99
99
|
|
|
100
|
-
### Option 1:
|
|
101
|
-
|
|
100
|
+
### Option 1: One-Line Installation (Recommended)
|
|
101
|
+
The fastest way to install Nyxora is via our smart installation wrapper. This script automatically prepares Node.js (if missing) and securely fetches the Nyxora daemon directly from the NPM Registry.
|
|
102
|
+
|
|
103
|
+
**Linux & macOS:**
|
|
104
|
+
```bash
|
|
105
|
+
curl -fsSL https://nyxoraai.github.io/Nyxora/install.sh | bash
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Windows (PowerShell):**
|
|
109
|
+
```powershell
|
|
110
|
+
iwr -useb https://nyxoraai.github.io/Nyxora/install.ps1 | iex
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Option 2: Global Installation (NPM)
|
|
114
|
+
If you already have Node.js installed, you can natively install Nyxora globally via NPM, allowing you to use the `nyxora` CLI command from anywhere on your machine.
|
|
102
115
|
|
|
103
116
|
```bash
|
|
104
117
|
# Install globally
|
|
@@ -124,7 +137,7 @@ cd Nyxora
|
|
|
124
137
|
# 1. Install Dependencies
|
|
125
138
|
npm install
|
|
126
139
|
|
|
127
|
-
# 2. Build the Dashboard UI
|
|
140
|
+
# 2. Build the Core, MCP Server, and Dashboard UI
|
|
128
141
|
npm run build
|
|
129
142
|
|
|
130
143
|
# 3. Interactive Setup Wizard
|
|
@@ -138,6 +151,13 @@ npm start
|
|
|
138
151
|
|
|
139
152
|
> **โ ๏ธ IMPORTANT:** Whenever you re-run `nyxora setup` or manually edit the config files, you **must restart the server** for the changes to take effect.
|
|
140
153
|
|
|
154
|
+
### ๐งน Uninstallation & Reset
|
|
155
|
+
If you ever need to securely wipe the AI's episodic memory, delete your API keys, and completely remove Nyxora's configuration from your operating system, simply run:
|
|
156
|
+
```bash
|
|
157
|
+
nyxora uninstall
|
|
158
|
+
```
|
|
159
|
+
This acts as a master reset switch to return your environment to a clean state.
|
|
160
|
+
|
|
141
161
|
---
|
|
142
162
|
|
|
143
163
|
## โ๏ธ Terms of Service
|
package/bin/nyxora.mjs
CHANGED
|
@@ -350,6 +350,20 @@ async function unlock() {
|
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
+
async function serveMcp() {
|
|
354
|
+
const compiledMcp = path.join(projectRoot, 'packages/mcp-server/dist/server.js');
|
|
355
|
+
const useCompiled = fs.existsSync(compiledMcp);
|
|
356
|
+
const cmd = useCompiled ? 'node' : 'npx';
|
|
357
|
+
const args = useCompiled ? [compiledMcp] : ['ts-node', '-T', 'packages/mcp-server/src/server.ts'];
|
|
358
|
+
const child = spawn(cmd, args, {
|
|
359
|
+
cwd: projectRoot,
|
|
360
|
+
stdio: 'inherit',
|
|
361
|
+
env: { ...process.env, TS_NODE_CACHE: 'false' }
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
await new Promise(resolve => child.on('close', resolve));
|
|
365
|
+
}
|
|
366
|
+
|
|
353
367
|
async function main() {
|
|
354
368
|
switch(command) {
|
|
355
369
|
case 'doctor': await runDoctor(); break;
|
|
@@ -366,6 +380,7 @@ async function main() {
|
|
|
366
380
|
case 'unlock': await unlock(); break;
|
|
367
381
|
case 'clean-logs': await cleanLogs(); break;
|
|
368
382
|
case 'autostart': await autostart(process.argv[3]); break;
|
|
383
|
+
case 'mcp': await serveMcp(); break;
|
|
369
384
|
case '-v':
|
|
370
385
|
case '--v':
|
|
371
386
|
case '--version':
|
|
@@ -383,6 +398,7 @@ Commands:
|
|
|
383
398
|
start Start the Nyxora background daemon
|
|
384
399
|
stop Stop the running daemon
|
|
385
400
|
restart Restart the daemon
|
|
401
|
+
mcp Start the MCP Server directly (for testing/debug)
|
|
386
402
|
chat Chat interactively with the AI in terminal
|
|
387
403
|
setup Run the interactive Setup Wizard
|
|
388
404
|
dashboard Open the dashboard in your browser
|
|
@@ -69,7 +69,7 @@ async function processWeb3Intent(input, role = 'user', onProgress, sessionId) {
|
|
|
69
69
|
const context = 'web3';
|
|
70
70
|
const response = await (0, llmUtils_1.executeWithRetry)(async (client) => {
|
|
71
71
|
// Debug log to find out why Gemini 400 error happens
|
|
72
|
-
console.log(`[LLM Debug] Sending ${messages.length} messages to LLM.`);
|
|
72
|
+
// console.log(`[LLM Debug] Sending ${messages.length} messages to LLM.`);
|
|
73
73
|
return await client.chat({
|
|
74
74
|
model: config.llm.model,
|
|
75
75
|
temperature: config.llm.temperature,
|
|
@@ -8,8 +8,8 @@ exports.saveMarketKeys = saveMarketKeys;
|
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const yaml_1 = __importDefault(require("yaml"));
|
|
10
10
|
const paths_1 = require("./paths");
|
|
11
|
-
const MARKET_KEYS_FILE = (0, paths_1.getPath)('market_keys.yaml');
|
|
12
11
|
function loadMarketKeys() {
|
|
12
|
+
const MARKET_KEYS_FILE = (0, paths_1.getPath)('market_keys.yaml');
|
|
13
13
|
if (!fs_1.default.existsSync(MARKET_KEYS_FILE)) {
|
|
14
14
|
return {};
|
|
15
15
|
}
|
|
@@ -34,6 +34,7 @@ function saveMarketKeys(keys) {
|
|
|
34
34
|
delete updated[k];
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
const MARKET_KEYS_FILE = (0, paths_1.getPath)('market_keys.yaml');
|
|
37
38
|
fs_1.default.writeFileSync(MARKET_KEYS_FILE, yaml_1.default.stringify(updated), 'utf8');
|
|
38
39
|
}
|
|
39
40
|
catch (error) {
|
|
@@ -150,11 +150,6 @@ function loadConfig() {
|
|
|
150
150
|
needsSave = true;
|
|
151
151
|
parsed.integrations.telegram.bot_token = decryptDataSync(parsed.integrations.telegram.bot_token);
|
|
152
152
|
}
|
|
153
|
-
if (parsed.web3?.explorer_api_key) {
|
|
154
|
-
if (parsed.web3.explorer_api_key.startsWith('ENC:'))
|
|
155
|
-
needsSave = true;
|
|
156
|
-
parsed.web3.explorer_api_key = decryptDataSync(parsed.web3.explorer_api_key);
|
|
157
|
-
}
|
|
158
153
|
// Auto-migration logic: move llm.credentials to root credentials
|
|
159
154
|
if (parsed.llm && parsed.llm.credentials) {
|
|
160
155
|
if (!parsed.credentials) {
|
|
@@ -26,7 +26,8 @@ class WebSocketManager {
|
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
// Validate Auth Token during Upgrade Handshake
|
|
29
|
-
|
|
29
|
+
const validation = (0, state_1.validateToken)(token);
|
|
30
|
+
if (!validation.valid) {
|
|
30
31
|
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
|
|
31
32
|
socket.destroy();
|
|
32
33
|
return;
|
|
@@ -211,6 +211,18 @@ app.get('/api/sessions', (req, res) => {
|
|
|
211
211
|
res.status(500).json({ error: error.message });
|
|
212
212
|
}
|
|
213
213
|
});
|
|
214
|
+
app.get('/api/sessions/search', (req, res) => {
|
|
215
|
+
try {
|
|
216
|
+
const q = req.query.q;
|
|
217
|
+
if (!q) {
|
|
218
|
+
return res.json(reasoning_1.logger.getSessions());
|
|
219
|
+
}
|
|
220
|
+
res.json(reasoning_1.logger.searchSessions(q));
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
res.status(500).json({ error: error.message });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
214
226
|
app.post('/api/sessions', (req, res) => {
|
|
215
227
|
try {
|
|
216
228
|
const { title } = req.body;
|
|
@@ -297,16 +309,20 @@ app.get('/api/defi-keys', (req, res) => {
|
|
|
297
309
|
const { aggregatorRegistry } = require('../web3/aggregator/providerRegistry');
|
|
298
310
|
const providers = aggregatorRegistry.getAllProviders();
|
|
299
311
|
const requirements = [];
|
|
312
|
+
const seenKeys = new Set();
|
|
300
313
|
for (const provider of providers) {
|
|
301
314
|
if (provider.manifest.requiredApiKeys) {
|
|
302
315
|
for (const req of provider.manifest.requiredApiKeys) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
316
|
+
if (!seenKeys.has(req.id)) {
|
|
317
|
+
seenKeys.add(req.id);
|
|
318
|
+
requirements.push({
|
|
319
|
+
id: req.id,
|
|
320
|
+
label: req.label,
|
|
321
|
+
required: req.required,
|
|
322
|
+
docsUrl: req.docsUrl,
|
|
323
|
+
configured: !!keys[req.id]
|
|
324
|
+
});
|
|
325
|
+
}
|
|
310
326
|
}
|
|
311
327
|
}
|
|
312
328
|
}
|
|
@@ -325,6 +341,17 @@ app.post('/api/defi-keys', (req, res) => {
|
|
|
325
341
|
res.status(500).json({ error: error.message });
|
|
326
342
|
}
|
|
327
343
|
});
|
|
344
|
+
app.delete('/api/defi-keys/:id', (req, res) => {
|
|
345
|
+
try {
|
|
346
|
+
const keys = (0, defiConfigManager_1.loadDefiKeys)();
|
|
347
|
+
delete keys[req.params.id];
|
|
348
|
+
(0, defiConfigManager_1.saveDefiKeys)(keys);
|
|
349
|
+
res.json({ success: true });
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
res.status(500).json({ error: error.message });
|
|
353
|
+
}
|
|
354
|
+
});
|
|
328
355
|
app.get('/api/market-keys', (req, res) => {
|
|
329
356
|
try {
|
|
330
357
|
const keys = (0, marketConfigManager_1.loadMarketKeys)();
|
|
@@ -834,12 +861,15 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
834
861
|
// --- Memory Triggers ---
|
|
835
862
|
let messageCounter = 0;
|
|
836
863
|
let idleTimer = null;
|
|
837
|
-
function resetIdleTimer() {
|
|
864
|
+
function resetIdleTimer(sessionId) {
|
|
838
865
|
if (idleTimer)
|
|
839
866
|
clearTimeout(idleTimer);
|
|
840
867
|
idleTimer = setTimeout(() => {
|
|
841
868
|
console.log('[Memory] Idle trigger activated. Running Reflection Engine...');
|
|
842
|
-
reflection_1.ReflectionEngine.runReflection()
|
|
869
|
+
reflection_1.ReflectionEngine.runReflection(sessionId).then(() => {
|
|
870
|
+
const { PromotionEngine } = require('../memory/promotionEngine');
|
|
871
|
+
PromotionEngine.runPromotionAndDecay();
|
|
872
|
+
}).catch(console.error);
|
|
843
873
|
}, 3 * 60 * 1000); // 3 minutes idle
|
|
844
874
|
}
|
|
845
875
|
app.post('/api/v1/trade', async (req, res) => {
|
|
@@ -867,13 +897,16 @@ app.post('/api/chat', async (req, res) => {
|
|
|
867
897
|
// Process input (this will automatically add to memory)
|
|
868
898
|
const response = await (0, reasoning_1.processUserInput)(message, 'user', undefined, session_id);
|
|
869
899
|
// Memory Triggers
|
|
870
|
-
resetIdleTimer();
|
|
900
|
+
resetIdleTimer(session_id);
|
|
871
901
|
messageCounter++;
|
|
872
902
|
if (messageCounter >= 5) {
|
|
873
903
|
console.log('[Memory] N-Message threshold reached. Running Reflection Engine...');
|
|
874
904
|
messageCounter = 0;
|
|
875
905
|
// Run asynchronously
|
|
876
|
-
reflection_1.ReflectionEngine.runReflection()
|
|
906
|
+
reflection_1.ReflectionEngine.runReflection(session_id).then(() => {
|
|
907
|
+
const { PromotionEngine } = require('../memory/promotionEngine');
|
|
908
|
+
PromotionEngine.runPromotionAndDecay();
|
|
909
|
+
}).catch(console.error);
|
|
877
910
|
}
|
|
878
911
|
res.json({ response });
|
|
879
912
|
}
|
|
@@ -891,6 +924,17 @@ app.get('/api/memory', (req, res) => {
|
|
|
891
924
|
res.status(500).json({ error: error.message });
|
|
892
925
|
}
|
|
893
926
|
});
|
|
927
|
+
app.delete('/api/memory/all', (req, res) => {
|
|
928
|
+
try {
|
|
929
|
+
episodic_1.episodicDB.clearAllMemories();
|
|
930
|
+
const { PromotionEngine } = require('../memory/promotionEngine');
|
|
931
|
+
PromotionEngine.runPromotionAndDecay();
|
|
932
|
+
res.json({ success: true, message: "Episodic memory wiped completely." });
|
|
933
|
+
}
|
|
934
|
+
catch (error) {
|
|
935
|
+
res.status(500).json({ error: error.message });
|
|
936
|
+
}
|
|
937
|
+
});
|
|
894
938
|
app.delete('/api/memory/:id', (req, res) => {
|
|
895
939
|
try {
|
|
896
940
|
const id = parseInt(req.params.id, 10);
|
|
@@ -1025,6 +1069,22 @@ async function startServer() {
|
|
|
1025
1069
|
(0, bridgeWatcher_1.startBridgeWatcher)();
|
|
1026
1070
|
// Start Event Listener for Limit Orders (V3)
|
|
1027
1071
|
eventListener_1.eventListener.start();
|
|
1072
|
+
// Resume Market Watch tasks
|
|
1073
|
+
try {
|
|
1074
|
+
const fs = require('fs');
|
|
1075
|
+
const path = require('path');
|
|
1076
|
+
const { getAppDir } = require('../config/paths');
|
|
1077
|
+
const tasksPath = path.join(getAppDir(), 'market_tasks.json');
|
|
1078
|
+
if (fs.existsSync(tasksPath)) {
|
|
1079
|
+
const tasks = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
|
1080
|
+
if (tasks.length > 0) {
|
|
1081
|
+
console.log(`[Market Watch] Resuming ${tasks.length} background watch tasks...`);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
catch (e) {
|
|
1086
|
+
console.error('[Market Watch] Failed to resume tasks:', e);
|
|
1087
|
+
}
|
|
1028
1088
|
});
|
|
1029
1089
|
server.on('error', (e) => {
|
|
1030
1090
|
if (e.code === 'EADDRINUSE') {
|
|
@@ -36,16 +36,17 @@ class EpisodicMemoryDB {
|
|
|
36
36
|
addCandidateFact(fact, confidenceScore = 0.5, category = 'general', ruleType = 'observation') {
|
|
37
37
|
// Upsert logic
|
|
38
38
|
const existing = this.db.prepare('SELECT id, occurrences, confidence FROM episodic_memories WHERE fact = ?').get(fact);
|
|
39
|
+
const safeScore = Math.min(1.0, confidenceScore);
|
|
39
40
|
if (existing) {
|
|
40
41
|
// Increment occurrences, boost confidence slightly up to max 1.0
|
|
41
42
|
const newOccurrences = existing.occurrences + 1;
|
|
42
|
-
const newConfidence = Math.min(1.0, existing.confidence + (
|
|
43
|
+
const newConfidence = Math.min(1.0, existing.confidence + (safeScore * 0.2)); // Dampened boost
|
|
43
44
|
const stmt = this.db.prepare('UPDATE episodic_memories SET occurrences = ?, confidence = ?, rule_type = ?, lastSeen = CURRENT_TIMESTAMP WHERE id = ?');
|
|
44
45
|
stmt.run(newOccurrences, newConfidence, ruleType, existing.id);
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
47
48
|
const stmt = this.db.prepare('INSERT INTO episodic_memories (fact, confidence, category, rule_type) VALUES (?, ?, ?, ?)');
|
|
48
|
-
stmt.run(fact,
|
|
49
|
+
stmt.run(fact, safeScore, category, ruleType);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
getMemories() {
|
|
@@ -64,6 +65,10 @@ class EpisodicMemoryDB {
|
|
|
64
65
|
`);
|
|
65
66
|
stmt.run(minConfidence, daysOld);
|
|
66
67
|
}
|
|
68
|
+
clearAllMemories() {
|
|
69
|
+
const stmt = this.db.prepare('DELETE FROM episodic_memories');
|
|
70
|
+
stmt.run();
|
|
71
|
+
}
|
|
67
72
|
close() {
|
|
68
73
|
try {
|
|
69
74
|
this.db.close();
|
|
@@ -150,6 +150,16 @@ class Logger {
|
|
|
150
150
|
renameSession(sessionId, newTitle) {
|
|
151
151
|
this.db.prepare('UPDATE sessions SET title = ? WHERE id = ?').run(newTitle, sessionId);
|
|
152
152
|
}
|
|
153
|
+
searchSessions(query) {
|
|
154
|
+
const term = `%${query}%`;
|
|
155
|
+
return this.db.prepare(`
|
|
156
|
+
SELECT DISTINCT s.*
|
|
157
|
+
FROM sessions s
|
|
158
|
+
LEFT JOIN messages m ON s.id = m.session_id
|
|
159
|
+
WHERE s.title LIKE ? OR m.content LIKE ?
|
|
160
|
+
ORDER BY s.timestamp DESC
|
|
161
|
+
`).all(term, term);
|
|
162
|
+
}
|
|
153
163
|
getHistory(sessionId, limit = 40) {
|
|
154
164
|
let rows;
|
|
155
165
|
// Phase 2: Sliding Window Algorithm (LLM Context Limit)
|
|
@@ -7,24 +7,27 @@ const logger_1 = require("./logger");
|
|
|
7
7
|
const validator_1 = require("./validator");
|
|
8
8
|
const episodic_1 = require("./episodic");
|
|
9
9
|
class ReflectionEngine {
|
|
10
|
-
static async runReflection() {
|
|
10
|
+
static async runReflection(sessionId) {
|
|
11
11
|
try {
|
|
12
12
|
// 1. Get recent session history
|
|
13
|
-
const history = logger_1.logger.getHistory();
|
|
14
|
-
if (history.length === 0)
|
|
13
|
+
const history = logger_1.logger.getHistory(sessionId);
|
|
14
|
+
if (history.length === 0) {
|
|
15
|
+
console.log('[ReflectionEngine] History is empty. Aborting reflection.');
|
|
15
16
|
return;
|
|
16
|
-
|
|
17
|
+
}
|
|
18
|
+
// Extract just the user and assistant text, ignoring tool messages
|
|
17
19
|
const recentChat = history
|
|
20
|
+
.filter(msg => msg.role !== 'tool')
|
|
18
21
|
.map(msg => `[${msg.role}]: ${msg.content}`)
|
|
19
22
|
.join('\n');
|
|
20
23
|
const config = (0, parser_1.loadConfig)();
|
|
21
24
|
const model = config.llm?.model || 'gpt-4o';
|
|
22
|
-
const
|
|
25
|
+
const llm = await (0, llmUtils_1.getLLMClient)();
|
|
23
26
|
// 2. Build the heavily constrained System Prompt
|
|
24
27
|
const systemPrompt = `
|
|
25
28
|
You are the Self-Reflection Engine for a Web3 AI Agent.
|
|
26
29
|
Your job is to analyze the following recent conversation and extract user habits, preferences, or corrections.
|
|
27
|
-
You MUST output ONLY valid JSON in the exact format specified.
|
|
30
|
+
You MUST output ONLY valid JSON in the exact format specified. Do not include markdown code blocks around the JSON.
|
|
28
31
|
|
|
29
32
|
CRITICAL RULES:
|
|
30
33
|
1. DO NOT extract or remember any Private Keys, Seed Phrases, Mnemonic Words, Passwords, API Keys, or Session Tokens.
|
|
@@ -48,19 +51,20 @@ Return a JSON object with an array "memories":
|
|
|
48
51
|
- rule_type "permanent": A strict reprimand or absolute preference (e.g., "Never use Ethereum!").
|
|
49
52
|
`;
|
|
50
53
|
// 3. Query LLM
|
|
51
|
-
const response = await
|
|
54
|
+
const response = await llm.chat({
|
|
52
55
|
model: model,
|
|
53
56
|
messages: [
|
|
54
57
|
{ role: 'system', content: systemPrompt },
|
|
55
58
|
{ role: 'user', content: recentChat }
|
|
56
59
|
],
|
|
57
|
-
response_format: { type: 'json_object' },
|
|
58
60
|
temperature: 0.1
|
|
59
61
|
});
|
|
60
|
-
const content = response.
|
|
62
|
+
const content = response.message?.content;
|
|
61
63
|
if (!content)
|
|
62
64
|
return;
|
|
63
|
-
|
|
65
|
+
// Strip markdown codeblocks if LLM incorrectly formatted it
|
|
66
|
+
const cleanContent = content.replace(/```json/gi, '').replace(/```/g, '').trim();
|
|
67
|
+
const data = JSON.parse(cleanContent);
|
|
64
68
|
const memories = data.memories || [];
|
|
65
69
|
// 4. Validate and Store
|
|
66
70
|
let addedCount = 0;
|
|
@@ -74,7 +78,7 @@ Return a JSON object with an array "memories":
|
|
|
74
78
|
// Fast-Track Override Logic
|
|
75
79
|
let confidence = 0.5; // default for observation
|
|
76
80
|
if (mem.rule_type === 'permanent')
|
|
77
|
-
confidence =
|
|
81
|
+
confidence = 1.0; // Fast-track override
|
|
78
82
|
if (mem.rule_type === 'temporary')
|
|
79
83
|
confidence = 0.8;
|
|
80
84
|
episodic_1.episodicDB.addCandidateFact(safeFact, confidence, mem.category || 'general', mem.rule_type || 'observation');
|
|
@@ -16,18 +16,7 @@ class MemoryValidator {
|
|
|
16
16
|
if (privateKeyRegex.test(text)) {
|
|
17
17
|
throw new Error('SECURITY VIOLATION: Potential Private Key detected in memory extraction.');
|
|
18
18
|
}
|
|
19
|
-
//
|
|
20
|
-
// We do a rough heuristic: 12-24 lowercase words separated by spaces in a single block.
|
|
21
|
-
// This is hard to do perfectly with regex, so we look for sequences of 12+ words
|
|
22
|
-
const words = text.toLowerCase().match(/\b[a-z]+\b/g) || [];
|
|
23
|
-
if (words.length >= 12) {
|
|
24
|
-
// Very basic check: are there exactly 12 or 24 words in a continuous string?
|
|
25
|
-
const mnemonicRegex = /\b([a-z]+(?:\s+[a-z]+){11,23})\b/g;
|
|
26
|
-
if (mnemonicRegex.test(text.toLowerCase())) {
|
|
27
|
-
// Log it as suspicious, but we'll reject it to be safe if it looks like a seed phrase
|
|
28
|
-
throw new Error('SECURITY VIOLATION: Potential Seed Phrase detected in memory extraction.');
|
|
29
|
-
}
|
|
30
|
-
}
|
|
19
|
+
// Removed overly aggressive 12-word mnemonic check to avoid false positives in conversational languages
|
|
31
20
|
// 3. Check for API/Bot Tokens (e.g., Telegram token format: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11)
|
|
32
21
|
const telegramTokenRegex = /\b\d{8,10}:[a-zA-Z0-9_-]{35}\b/g;
|
|
33
22
|
if (telegramTokenRegex.test(text)) {
|
|
@@ -62,7 +62,7 @@ class OpenOceanProvider {
|
|
|
62
62
|
routeId: `openocean-${crypto_1.default.randomUUID()}`,
|
|
63
63
|
fromChainId: chainId,
|
|
64
64
|
toChainId: chainId,
|
|
65
|
-
inputAmount: BigInt(
|
|
65
|
+
inputAmount: BigInt(request.amountInWei || "0"),
|
|
66
66
|
outputAmount: BigInt(data.outAmount),
|
|
67
67
|
executable: true,
|
|
68
68
|
expiresAt: Date.now() + 60000,
|
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.createMarketWatchAgentToolDefinition = void 0;
|
|
4
7
|
exports.createMarketWatchAgent = createMarketWatchAgent;
|
|
5
8
|
const config_1 = require("../config");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const paths_1 = require("../../config/paths");
|
|
6
12
|
async function createMarketWatchAgent(chainName, contractAddress, rules, durationDays) {
|
|
7
13
|
// In a real production environment, this would push a job to Redis/Celery
|
|
8
14
|
// or instantiate a background worker. For now, we simulate the agent spawning.
|
|
9
15
|
const jobId = `watch-${contractAddress.substring(0, 8)}-${Date.now()}`;
|
|
16
|
+
// Save to persistence disk
|
|
17
|
+
try {
|
|
18
|
+
const tasksPath = path_1.default.join((0, paths_1.getAppDir)(), 'market_tasks.json');
|
|
19
|
+
let tasks = [];
|
|
20
|
+
if (fs_1.default.existsSync(tasksPath)) {
|
|
21
|
+
tasks = JSON.parse(fs_1.default.readFileSync(tasksPath, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
tasks.push({
|
|
24
|
+
jobId,
|
|
25
|
+
chainName,
|
|
26
|
+
contractAddress,
|
|
27
|
+
rules,
|
|
28
|
+
durationDays,
|
|
29
|
+
createdAt: Date.now()
|
|
30
|
+
});
|
|
31
|
+
fs_1.default.writeFileSync(tasksPath, JSON.stringify(tasks, null, 2), 'utf8');
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
console.error('Failed to persist market watch task', e);
|
|
35
|
+
}
|
|
10
36
|
let report = `โ
**Autonomous Watchdog Agent Deployed!**\n\n`;
|
|
11
37
|
report += `**Job ID:** \`${jobId}\`\n`;
|
|
12
38
|
report += `**Target:** \`${contractAddress}\` on ${chainName.toUpperCase()}\n`;
|
|
@@ -27,7 +27,7 @@ async function getTxHistory(chainName, address, days = 30) {
|
|
|
27
27
|
}
|
|
28
28
|
const apiUrl = `https://api.etherscan.io/v2/api?chainid=${chainId}`;
|
|
29
29
|
const config = (0, parser_1.loadConfig)();
|
|
30
|
-
const apiKey = config.web3?.explorer_api_key || '
|
|
30
|
+
const apiKey = config.web3?.explorer_api_key || ''; // Public fallback with no key
|
|
31
31
|
const apiKeyParam = apiKey ? `&apikey=${apiKey}` : '';
|
|
32
32
|
const startTimestamp = Math.floor(Date.now() / 1000) - (days * 24 * 60 * 60);
|
|
33
33
|
// Fetch Native Txs
|
|
@@ -67,9 +67,15 @@ async function prepareProvideLiquidity(chainName, token0AddressOrSymbol, token1A
|
|
|
67
67
|
}
|
|
68
68
|
if (typeof actualSlippage !== 'number' || isNaN(actualSlippage))
|
|
69
69
|
actualSlippage = 0.5;
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
// If ticks are not provided, default to Full Range based on tickSpacing
|
|
71
|
+
let tLower = tickLower;
|
|
72
|
+
let tUpper = tickUpper;
|
|
73
|
+
if (tLower === undefined || tUpper === undefined) {
|
|
74
|
+
const MIN_TICK = -887272;
|
|
75
|
+
const MAX_TICK = 887272;
|
|
76
|
+
const tickSpacing = feeTier === 100 ? 1 : feeTier === 500 ? 10 : feeTier === 3000 ? 60 : 200;
|
|
77
|
+
tLower = Math.ceil(MIN_TICK / tickSpacing) * tickSpacing;
|
|
78
|
+
tUpper = Math.floor(MAX_TICK / tickSpacing) * tickSpacing;
|
|
73
79
|
}
|
|
74
80
|
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
75
81
|
const userAddress = await (0, config_1.getAddress)();
|
|
@@ -126,7 +132,7 @@ async function prepareProvideLiquidity(chainName, token0AddressOrSymbol, token1A
|
|
|
126
132
|
const amount0Min = (amount0Wei * (10000n - slippageFactor)) / 10000n;
|
|
127
133
|
const amount1Min = (amount1Wei * (10000n - slippageFactor)) / 10000n;
|
|
128
134
|
const mintParams = {
|
|
129
|
-
token0, token1, fee: feeTier, tickLower, tickUpper,
|
|
135
|
+
token0, token1, fee: feeTier, tickLower: tLower, tickUpper: tUpper,
|
|
130
136
|
amount0Desired: amount0Wei, amount1Desired: amount1Wei,
|
|
131
137
|
amount0Min, amount1Min,
|
|
132
138
|
recipient: account, deadline
|
|
@@ -147,7 +153,7 @@ async function prepareProvideLiquidity(chainName, token0AddressOrSymbol, token1A
|
|
|
147
153
|
return `Simulation failed! Cannot mint liquidity position. Check if ticks are valid for fee tier. Error: ${simError.message}`;
|
|
148
154
|
}
|
|
149
155
|
const tx = transactionManager_1.txManager.createPendingTransaction('univ3Mint', chainName, {
|
|
150
|
-
positionManagerAddress, token0, token1, amount0, amount1, tickLower, tickUpper,
|
|
156
|
+
positionManagerAddress, token0, token1, amount0, amount1, tickLower: tLower, tickUpper: tUpper,
|
|
151
157
|
gasEstimate: gasEstimate.toString()
|
|
152
158
|
});
|
|
153
159
|
return `โณ **Add Liquidity queued:** ${amount0} ${meta0.symbol} & ${amount1} ${meta1.symbol} | ${chainName.toUpperCase()} | Approve below.`;
|
|
@@ -170,8 +176,8 @@ exports.provideLiquidityToolDefinition = {
|
|
|
170
176
|
amount0Str: { type: "string" },
|
|
171
177
|
amount1Str: { type: "string" },
|
|
172
178
|
feeTier: { type: "number", description: "Uniswap fee tier (e.g. 500 for 0.05%, 3000 for 0.3%, 10000 for 1%)" },
|
|
173
|
-
tickLower: { type: "number", description: "
|
|
174
|
-
tickUpper: { type: "number", description: "
|
|
179
|
+
tickLower: { type: "number", description: "Optional. Leave empty for Full Range." },
|
|
180
|
+
tickUpper: { type: "number", description: "Optional. Leave empty for Full Range." }
|
|
175
181
|
},
|
|
176
182
|
required: ["chainName", "token0AddressOrSymbol", "token1AddressOrSymbol", "amount0Str", "amount1Str", "feeTier"]
|
|
177
183
|
}
|
|
@@ -29,7 +29,12 @@ async function analyzeMarketEngine(chainName, tokenAddressOrSymbol) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
catch (e) {
|
|
32
|
-
|
|
32
|
+
if (keys.cmc_key) {
|
|
33
|
+
console.warn("CoinGecko analysis failed, falling back to CMC...", e.message);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.warn("CoinGecko analysis failed, falling back to DexScreener...", e.message);
|
|
37
|
+
}
|
|
33
38
|
}
|
|
34
39
|
// Tier 1 & 2: CoinMarketCap (Pro if key exists)
|
|
35
40
|
// Note: CMC doesn't have a truly open public endpoint like CG without keys,
|
|
@@ -54,7 +59,7 @@ async function analyzeMarketEngine(chainName, tokenAddressOrSymbol) {
|
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
catch (e) {
|
|
57
|
-
console.warn("CMC analysis failed, falling back to DexScreener...", e);
|
|
62
|
+
console.warn("CMC analysis failed, falling back to DexScreener...", e.message);
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
// Tier 2: Ultimate Fallback Engine: DexScreener Cross-Chain Search
|
|
@@ -70,7 +75,7 @@ async function analyzeMarketEngine(chainName, tokenAddressOrSymbol) {
|
|
|
70
75
|
try {
|
|
71
76
|
const data = await (0, httpClient_1.safeFetchJson)(dexSearchUrl);
|
|
72
77
|
if (!data.pairs || data.pairs.length === 0) {
|
|
73
|
-
return
|
|
78
|
+
return `โ Market Engine Error: The token '${tokenAddressOrSymbol}' does not exist on CoinGecko, CoinMarketCap, or DexScreener across any supported chain. Please inform the user that this token is either not launched, completely dead, or the symbol is incorrect.`;
|
|
74
79
|
}
|
|
75
80
|
let pair = data.pairs.find((p) => p.chainId === chainName);
|
|
76
81
|
if (!pair)
|
|
@@ -24,7 +24,7 @@ function getPublicClient(chainName) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
// Fallback public RPCs (Top tier from Chainlist.org prioritized)
|
|
27
|
-
if (
|
|
27
|
+
if (transports.length === 0) {
|
|
28
28
|
if (chainName === 'ethereum') {
|
|
29
29
|
transports.push((0, viem_1.http)('https://rpc.mevblocker.io', { timeout: 5000, batch: { batchSize: 100 } })); // Primary MEV protection
|
|
30
30
|
transports.push((0, viem_1.http)('https://rpc.flashbots.net', { timeout: 5000, batch: { batchSize: 100 } })); // Secondary MEV protection
|
|
@@ -63,6 +63,10 @@ function getPublicClient(chainName) {
|
|
|
63
63
|
transports.push((0, viem_1.http)('https://optimism-sepolia-rpc.publicnode.com', { timeout: 5000 }));
|
|
64
64
|
transports.push((0, viem_1.http)('https://sepolia.optimism.io', { timeout: 5000 }));
|
|
65
65
|
}
|
|
66
|
+
else if (chainName === 'base_sepolia') {
|
|
67
|
+
transports.push((0, viem_1.http)('https://base-sepolia-rpc.publicnode.com', { timeout: 5000 }));
|
|
68
|
+
transports.push((0, viem_1.http)('https://sepolia.base.org', { timeout: 5000 }));
|
|
69
|
+
}
|
|
66
70
|
}
|
|
67
71
|
// Always append the default public RPC (like cloudflare) as the last resort
|
|
68
72
|
transports.push((0, viem_1.http)(undefined, { timeout: 5000 }));
|
|
@@ -111,6 +115,8 @@ function getWsClient(chainName) {
|
|
|
111
115
|
wsUrl = 'wss://arbitrum-sepolia-rpc.publicnode.com';
|
|
112
116
|
else if (chainName === 'optimism_sepolia')
|
|
113
117
|
wsUrl = 'wss://optimism-sepolia-rpc.publicnode.com';
|
|
118
|
+
else if (chainName === 'base_sepolia')
|
|
119
|
+
wsUrl = 'wss://base-sepolia-rpc.publicnode.com';
|
|
114
120
|
}
|
|
115
121
|
// If WSS is totally unavailable, fallback to HTTP polling transparently
|
|
116
122
|
if (!wsUrl) {
|