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.
Files changed (48) hide show
  1. package/README.md +23 -3
  2. package/bin/nyxora.mjs +16 -0
  3. package/dist/packages/core/src/agent/web3Agent.js +1 -1
  4. package/dist/packages/core/src/config/marketConfigManager.js +2 -1
  5. package/dist/packages/core/src/config/parser.js +0 -5
  6. package/dist/packages/core/src/gateway/WebSocketManager.js +2 -1
  7. package/dist/packages/core/src/gateway/server.js +71 -11
  8. package/dist/packages/core/src/memory/episodic.js +7 -2
  9. package/dist/packages/core/src/memory/logger.js +10 -0
  10. package/dist/packages/core/src/memory/reflection.js +15 -11
  11. package/dist/packages/core/src/memory/validator.js +1 -12
  12. package/dist/packages/core/src/web3/aggregator/providers/OpenOceanProvider.js +1 -1
  13. package/dist/packages/core/src/web3/skills/createMarketWatchAgent.js +26 -0
  14. package/dist/packages/core/src/web3/skills/getTxHistory.js +1 -1
  15. package/dist/packages/core/src/web3/skills/provideLiquidity.js +13 -7
  16. package/dist/packages/core/src/web3/utils/marketEngine.js +8 -3
  17. package/dist/packages/core/src/web3/utils/rpcEngine.js +7 -1
  18. package/dist/packages/policy/src/server.js +24 -11
  19. package/package.json +1 -1
  20. package/packages/core/package.json +1 -1
  21. package/packages/core/src/agent/web3Agent.ts +1 -1
  22. package/packages/core/src/config/marketConfigManager.ts +2 -2
  23. package/packages/core/src/config/parser.ts +3 -4
  24. package/packages/core/src/gateway/WebSocketManager.ts +2 -1
  25. package/packages/core/src/gateway/server.ts +71 -11
  26. package/packages/core/src/memory/episodic.ts +9 -2
  27. package/packages/core/src/memory/logger.ts +11 -0
  28. package/packages/core/src/memory/reflection.ts +18 -12
  29. package/packages/core/src/memory/validator.ts +1 -12
  30. package/packages/core/src/web3/aggregator/providers/OpenOceanProvider.ts +1 -1
  31. package/packages/core/src/web3/skills/createMarketWatchAgent.ts +24 -0
  32. package/packages/core/src/web3/skills/getTxHistory.ts +1 -1
  33. package/packages/core/src/web3/skills/provideLiquidity.ts +14 -7
  34. package/packages/core/src/web3/utils/marketEngine.ts +7 -3
  35. package/packages/core/src/web3/utils/rpcEngine.ts +5 -1
  36. package/packages/dashboard/dist/assets/index-BoFANMsj.js +16 -0
  37. package/packages/dashboard/dist/assets/index-K1CmXmAE.css +1 -0
  38. package/packages/dashboard/dist/index.html +2 -2
  39. package/packages/dashboard/package.json +1 -1
  40. package/packages/mcp-server/dist/server.js +17 -5
  41. package/packages/mcp-server/package.json +2 -2
  42. package/packages/mcp-server/src/server.ts +20 -5
  43. package/packages/policy/package.json +1 -1
  44. package/packages/policy/src/server.ts +28 -13
  45. package/packages/signer/package.json +1 -1
  46. package/dist/packages/core/src/test_security.js +0 -43
  47. package/packages/dashboard/dist/assets/index-CjZWf1Ei.css +0 -1
  48. 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: Global Installation (Recommended)
101
- Nyxora can be installed globally via NPM, allowing you to use the `nyxora` CLI command from anywhere on your machine.
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
- if (!(0, state_1.validateToken)(token)) {
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
- requirements.push({
304
- id: req.id,
305
- label: req.label,
306
- required: req.required,
307
- docsUrl: req.docsUrl,
308
- configured: !!(keys[req.envKey] || keys[req.id])
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 + (confidenceScore * 0.2)); // Dampened boost
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, confidenceScore, category, ruleType);
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
- // Extract just the user and assistant text
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 openai = await (0, llmUtils_1.getOpenAI)();
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 openai.chat.completions.create({
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.choices[0]?.message?.content;
62
+ const content = response.message?.content;
61
63
  if (!content)
62
64
  return;
63
- const data = JSON.parse(content);
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 = 2.0; // Fast-track override
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
- // 2. Check for typical 12-24 word BIP-39 Mnemonic patterns.
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(amount),
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 || 'YourApiKeyToken'; // Public fallback
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
- // CRITICAL SAFETY REQUIREMENT: AI MUST ASK USER FOR TICKS
71
- if (tickLower === undefined || tickUpper === undefined) {
72
- return `ACTION REQUIRED: I cannot calculate the Uniswap V3 price range (tickLower and tickUpper) autonomously for safety reasons. Please ask the user to provide the exact tickLower and tickUpper values they want for this liquidity pool before I can prepare the transaction.`;
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: "MUST BE PROVIDED BY USER." },
174
- tickUpper: { type: "number", description: "MUST BE PROVIDED BY USER." }
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
- console.warn("CoinGecko analysis failed, falling back to CMC...", e);
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 `No market data found for '${tokenAddressOrSymbol}' on CMC, CoinGecko, or DexScreener across any chain.`;
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 (!customRpcRaw) {
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) {