nyxora 26.6.11-2 → 26.6.12

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 (29) hide show
  1. package/dist/packages/core/src/agent/reasoning.js +42 -13
  2. package/dist/packages/core/src/gateway/server.js +25 -1
  3. package/dist/packages/core/src/gateway/telegram.js +9 -0
  4. package/dist/packages/core/src/memory/logger.js +82 -0
  5. package/dist/packages/core/src/system/skills/analyzeDocument.js +15 -12
  6. package/dist/packages/core/src/system/skills/generateExcel.js +6 -8
  7. package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +7 -2
  8. package/dist/packages/core/src/web3/aggregator/aggregatorTestnet.js +6 -1
  9. package/dist/packages/core/src/web3/eventListener.js +102 -0
  10. package/dist/packages/core/src/web3/skills/createLimitOrder.js +48 -0
  11. package/package.json +5 -3
  12. package/packages/core/package.json +4 -2
  13. package/packages/core/src/agent/reasoning.ts +51 -14
  14. package/packages/core/src/gateway/server.ts +26 -1
  15. package/packages/core/src/gateway/telegram.ts +7 -0
  16. package/packages/core/src/memory/logger.ts +113 -0
  17. package/packages/core/src/system/skills/analyzeDocument.ts +15 -14
  18. package/packages/core/src/system/skills/generateExcel.ts +6 -10
  19. package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +8 -2
  20. package/packages/core/src/web3/aggregator/aggregatorTestnet.ts +7 -1
  21. package/packages/core/src/web3/eventListener.ts +103 -0
  22. package/packages/core/src/web3/skills/createLimitOrder.ts +56 -0
  23. package/packages/dashboard/dist/assets/index-BhKhEfi_.js +13 -0
  24. package/packages/dashboard/dist/index.html +1 -1
  25. package/packages/dashboard/package.json +1 -1
  26. package/packages/mcp-server/package.json +1 -1
  27. package/packages/policy/package.json +1 -1
  28. package/packages/signer/package.json +1 -1
  29. package/packages/dashboard/dist/assets/index-CCgm_N9M.js +0 -13
@@ -31,6 +31,7 @@ const defiLending_1 = require("../web3/skills/defiLending");
31
31
  const yieldVault_1 = require("../web3/skills/yieldVault");
32
32
  const provideLiquidity_1 = require("../web3/skills/provideLiquidity");
33
33
  const getTxHistory_1 = require("../web3/skills/getTxHistory");
34
+ const createLimitOrder_1 = require("../web3/skills/createLimitOrder");
34
35
  const updateProfile_1 = require("./updateProfile");
35
36
  const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
36
37
  const analyzeDocument_1 = require("../system/skills/analyzeDocument");
@@ -122,26 +123,35 @@ async function executeWithRetry(requestBuilder, maxRetries = 3) {
122
123
  function getSystemPrompt() {
123
124
  const config = (0, parser_1.loadConfig)();
124
125
  const currentDateTime = new Date().toLocaleString('en-US', { timeZoneName: 'short' });
125
- let basePrompt = `You are an autonomous Web3 agent operating on EVM chains.
126
+ let basePrompt = `[CORE DIRECTIVES]
127
+ You are an autonomous Web3 agent operating on EVM chains.
126
128
  You are equipped with a native wallet.
127
129
  The current real-world date and time is: ${currentDateTime}. Use this for any time-related questions.
130
+ Default Chain: ${config.agent.default_chain}
128
131
 
132
+ CRITICAL: You MUST use a Chain of Thought approach for every response. You must enclose your internal reasoning steps within <think>...</think> XML tags BEFORE taking any action or providing a final response. This allows you to plan your tool usage, recall the rules, and avoid hallucinations.
133
+ IMPORTANT: The <think> block is strictly for your internal hidden monologue. NEVER put your final answer, conversational text, or questions to the user inside the <think> block. The actual response that the user will see MUST be written OUTSIDE and AFTER the </think> tag.
134
+
135
+ [EXECUTION WORKFLOW]
129
136
  CRITICAL RULE 1: NEVER expose internal JSON tool calls to the user. Always parse them and explain the outcome naturally.
130
137
  CRITICAL RULE 2: STRICT LANGUAGE MATCHING. You MUST strictly reply in the exact same language as the user's LATEST prompt.
131
- CRITICAL RULE 3: FORMATTING & CONCISENESS. Be concise. Use markdown tables for lists of assets/transactions. Use commas for thousands.
138
+ CRITICAL RULE 3: FORMATTING & CONCISENESS. Provide concise analytical summaries of data rather than just dumping raw markdown tables. Be analytical but brief. Use commas for thousands.
132
139
  CRITICAL RULE 4: TOOL PRIORITIZATION. Web3 tasks must use Web3 Skills exclusively. OS Skills (search, browse) are fallbacks only. Use get_my_address to show wallet address, and check_portfolio to show balances.
133
140
  CRITICAL RULE 5: DEFAULT CHAIN HANDLING. Default to: ${config.agent.default_chain} unless specified. If overridden, confirm the chain politely. For 2-chain txs (bridge), default source to ${config.agent.default_chain}.
134
- CRITICAL RULE 6: NETWORK SAFETY VALIDATION. If a request implies cross-chain or mainnet/testnet mixing, or the token symbol is ambiguous (USDC vs USDC.e), YOU MUST NOT GUESS. Ask for confirmation.
135
- CRITICAL RULE 7: TOOL CONFIDENCE & HALUCINATION PREVENTION. NEVER fabricate blockchain data. If a tool fails or data is missing, state it explicitly. Do not estimate balances, prices, APY, or gas.
136
141
  CRITICAL RULE 8: CONDITIONAL PARALLEL EXECUTION. Parallel tool execution is ONLY allowed if there are zero data dependencies between them.
137
- CRITICAL RULE 9: DEFI CONFIGURATION FALLBACK. If a tool fails due to Rate Limits, Unauthorized, or Missing API Keys, instruct the user to visit the "DeFi Configuration 🔑" menu in the dashboard.
138
142
  CRITICAL RULE 10: PLANNING & RISK DISCLOSURE. For high-level instructions (e.g. "Get yield"), formulate a plan and briefly disclose major risks (smart contract risk, impermanent loss) before asking for approval.
139
- CRITICAL RULE 11: FAST RETURN RULE. If parameters for read-only tools are complete, execute them IMMEDIATELY without preamble or conversational filler.
140
- CRITICAL RULE 12: SMART SLIPPAGE AWARENESS. For low-liquidity assets, warn the user that default slippage might not be enough. NEVER invent specific slippage percentage numbers.
143
+ CRITICAL RULE 11: ADAPTIVE RESPONSE RULE. You must process Web3 data (portfolio, price) and provide a concise, to-the-point analysis based on the context. Do not use useless filler greetings/closings. Provide deep analysis only if the data requires it to protect the user's portfolio.
141
144
  CRITICAL RULE 13: WALLET CONTEXT CACHING. Portfolio data in chat history is potentially stale. Do not use cached data for transactional planning; refresh the balance via tools first.
142
145
  CRITICAL RULE 14: TRANSACTION EXECUTION. For ALL state-changing transactions (swap, bridge, transfer, stake), do NOT ask for verbal confirmation. Execute the tool IMMEDIATELY. The tool itself will trigger a secure popup in the user's dashboard UI for final approval.
146
+ CRITICAL RULE 17: MINIMIZE UNNECESSARY TOOL CALLS. Do not call tools if the answer exists in recent verified context and freshness is not strictly required. Use history to save latency.
147
+
148
+ [ANTI-HALLUCINATION PROTOCOL]
149
+ CRITICAL RULE 6: NETWORK SAFETY VALIDATION. If a request implies cross-chain or mainnet/testnet mixing, or the token symbol is ambiguous (USDC vs USDC.e), YOU MUST NOT GUESS. Ask for confirmation.
150
+ CRITICAL RULE 7: TOOL CONFIDENCE & HALUCINATION PREVENTION. NEVER fabricate blockchain data. If a tool fails or data is missing, state it explicitly. Do not estimate balances, prices, APY, or gas.
151
+ CRITICAL RULE 9: DEFI CONFIGURATION FALLBACK. If a tool fails due to Rate Limits, Unauthorized, or Missing API Keys, instruct the user to visit the "DeFi Configuration 🔑" menu in the dashboard.
152
+ CRITICAL RULE 12: SMART SLIPPAGE AWARENESS. For low-liquidity assets, warn the user that default slippage might not be enough. NEVER invent specific slippage percentage numbers.
143
153
  CRITICAL RULE 16: CAPABILITY HONESTY. NEVER claim a capability not available through installed tools. If asked for an unsupported action, state honestly that the skill is missing.
144
- CRITICAL RULE 17: MINIMIZE UNNECESSARY TOOL CALLS. Do not call tools if the answer exists in recent verified context and freshness is not strictly required. Use history to save latency.`;
154
+ CRITICAL RULE 19: MARKET CONFIDENCE SCORE. When analyzing market data, token security, or preparing trades, you MUST explicitly declare a 'Confidence Score (0-100%)' INSIDE your <think> block. If your score is below 40%, you must firmly WARN the user and advise against the trade in your final response.`;
145
155
  // Read IDENTITY.md for core AI persona
146
156
  try {
147
157
  const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
@@ -188,6 +198,23 @@ CRITICAL RULE 17: MINIMIZE UNNECESSARY TOOL CALLS. Do not call tools if the answ
188
198
  catch (error) {
189
199
  // Ignore db errors if not initialized
190
200
  }
201
+ // V3: Inject Personalized Risk Profile
202
+ try {
203
+ const profile = exports.logger.getUserProfile();
204
+ if (profile) {
205
+ basePrompt += `\n\n--- [USER_PERSONA] RISK PROFILE & PREFERENCES ---\n`;
206
+ basePrompt += `Risk Level: ${profile.risk_level}\n`;
207
+ basePrompt += `Max Slippage Tolerance: ${profile.max_slippage}%\n`;
208
+ basePrompt += `Avoid Memecoins: ${profile.avoid_memecoins ? 'YES' : 'NO'}\n`;
209
+ if (profile.custom_rules) {
210
+ basePrompt += `Custom Rules: ${profile.custom_rules}\n`;
211
+ }
212
+ basePrompt += `CRITICAL: You MUST adhere to these risk parameters when advising the user or executing tools. If a requested action violates these parameters (e.g., buying a high-risk memecoin when 'Avoid Memecoins' is YES), you MUST warn the user and refuse execution unless they explicitly override.\n`;
213
+ }
214
+ }
215
+ catch (e) {
216
+ // Ignore if db not ready
217
+ }
191
218
  return basePrompt;
192
219
  }
193
220
  async function processUserInput(input, role = 'user', onProgress, sessionId) {
@@ -223,7 +250,7 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
223
250
  const hasGoogleKeyword = /email|gmail|calendar|sheet|doc|form|event/i.test(lowerInput);
224
251
  let tools = [];
225
252
  if ((0, skillManager_1.isSkillActive)('web3')) {
226
- tools.push(getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition, bridgeToken_1.bridgeTokenToolDefinition, mintNft_1.mintNftToolDefinition, customTx_1.customTxToolDefinition, checkSecurity_1.checkSecurityToolDefinition, marketAnalysis_1.marketAnalysisToolDefinition, checkPortfolio_1.checkPortfolioToolDefinition, checkAddress_1.checkAddressToolDefinition, getMyAddress_1.getMyAddressToolDefinition, manageCustomTokens_1.manageCustomTokensDefinition, revokeApprovals_1.revokeApprovalToolDefinition, defiLending_1.aaveSupplyToolDefinition, yieldVault_1.vaultDepositToolDefinition, provideLiquidity_1.provideLiquidityToolDefinition, getTxHistory_1.getTxHistoryToolDefinition);
253
+ tools.push(getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition, bridgeToken_1.bridgeTokenToolDefinition, mintNft_1.mintNftToolDefinition, customTx_1.customTxToolDefinition, checkSecurity_1.checkSecurityToolDefinition, marketAnalysis_1.marketAnalysisToolDefinition, checkPortfolio_1.checkPortfolioToolDefinition, checkAddress_1.checkAddressToolDefinition, getMyAddress_1.getMyAddressToolDefinition, manageCustomTokens_1.manageCustomTokensDefinition, revokeApprovals_1.revokeApprovalToolDefinition, defiLending_1.aaveSupplyToolDefinition, yieldVault_1.vaultDepositToolDefinition, provideLiquidity_1.provideLiquidityToolDefinition, getTxHistory_1.getTxHistoryToolDefinition, createLimitOrder_1.createLimitOrderToolDefinition);
227
254
  }
228
255
  const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, generateExcel_1.generateExcelToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, installSkill_1.installExternalSkillToolDefinition, editFile_1.editLocalFileToolDefinition, gitManager_1.gitManagerToolDefinition, xManager_1.xManagerToolDefinition, notionWorkspace_1.notionWorkspaceToolDefinition, audioTranscribe_1.audioTranscribeToolDefinition, summarizeText_1.summarizeTextToolDefinition];
229
256
  const GOOGLE_TOOLS = [googleWorkspace_1.readGmailInboxToolDefinition, googleWorkspace_1.listCalendarEventsToolDefinition, googleWorkspace_1.appendRowToSheetsToolDefinition, googleWorkspace_1.readGoogleDocsToolDefinition, googleWorkspace_1.readGoogleFormResponsesToolDefinition];
@@ -261,10 +288,8 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
261
288
  if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
262
289
  let canFastReturnAll = true;
263
290
  let accumulatedResults = [];
264
- const fastReturnTools = [
265
- 'check_portfolio', 'check_address', 'get_price', 'get_my_address',
266
- 'analyze_market', 'check_token_security', 'search_web', 'read_gmail_inbox', 'list_calendar_events'
267
- ];
291
+ // Disabled fastReturnTools to enforce Web3 Reasoning (V3 feature)
292
+ const fastReturnTools = [];
268
293
  for (const _toolCall of responseMessage.tool_calls) {
269
294
  const toolCall = _toolCall;
270
295
  let result = "";
@@ -377,6 +402,10 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
377
402
  result = await (0, getTxHistory_1.getTxHistory)(args.chainName, args.address, args.days);
378
403
  break;
379
404
  }
405
+ case 'create_limit_order': {
406
+ result = await (0, createLimitOrder_1.createLimitOrder)(args.tokenSymbol, args.tokenAddress, args.triggerCondition, args.triggerPriceUsd, args.action, args.amountUsd, args.slippageTolerance);
407
+ break;
408
+ }
380
409
  case 'update_profile': {
381
410
  result = (0, updateProfile_1.updateProfile)(args.content, args.mode);
382
411
  break;
@@ -51,6 +51,7 @@ const yieldVault_1 = require("../web3/skills/yieldVault");
51
51
  const provideLiquidity_1 = require("../web3/skills/provideLiquidity");
52
52
  const getTxHistory_1 = require("../web3/skills/getTxHistory");
53
53
  const checkRegistryStatus_1 = require("../web3/skills/checkRegistryStatus");
54
+ const createLimitOrder_1 = require("../web3/skills/createLimitOrder");
54
55
  // System Skills
55
56
  const browseWeb_1 = require("../system/skills/browseWeb");
56
57
  const executeShell_1 = require("../system/skills/executeShell");
@@ -69,6 +70,7 @@ const analyzeDocument_1 = require("../system/skills/analyzeDocument");
69
70
  const searchWeb_1 = require("../system/skills/searchWeb");
70
71
  const googleWorkspace_1 = require("../system/skills/googleWorkspace");
71
72
  const telegram_1 = require("./telegram");
73
+ const eventListener_1 = require("../web3/eventListener");
72
74
  const googleAuthModule_1 = require("./googleAuthModule");
73
75
  const legalGenerator_1 = require("./legalGenerator");
74
76
  const episodic_1 = require("../memory/episodic");
@@ -309,7 +311,8 @@ app.get('/api/skills', (req, res) => {
309
311
  revokeApprovals_2.revokeApprovalToolDefinition,
310
312
  yieldVault_1.vaultDepositToolDefinition,
311
313
  provideLiquidity_1.provideLiquidityToolDefinition,
312
- getTxHistory_1.getTxHistoryToolDefinition
314
+ getTxHistory_1.getTxHistoryToolDefinition,
315
+ createLimitOrder_1.createLimitOrderToolDefinition
313
316
  ];
314
317
  const skillsWithStatus = allSkills.map(skill => ({
315
318
  ...skill,
@@ -778,6 +781,25 @@ app.delete('/api/memory/:id', (req, res) => {
778
781
  res.status(500).json({ error: error.message });
779
782
  }
780
783
  });
784
+ // --- User Persona / Risk Profile Endpoints (V3) ---
785
+ app.get('/api/profile', (req, res) => {
786
+ try {
787
+ const profile = reasoning_1.logger.getUserProfile();
788
+ res.json(profile || { risk_level: 'Moderate', max_slippage: 1.0, avoid_memecoins: false, custom_rules: '' });
789
+ }
790
+ catch (error) {
791
+ res.status(500).json({ error: error.message });
792
+ }
793
+ });
794
+ app.post('/api/profile', (req, res) => {
795
+ try {
796
+ reasoning_1.logger.updateUserProfile(req.body);
797
+ res.json({ success: true });
798
+ }
799
+ catch (error) {
800
+ res.status(500).json({ error: error.message });
801
+ }
802
+ });
781
803
  // Fallback for React Router (Single Page Application)
782
804
  app.use((req, res, next) => {
783
805
  if (req.method === 'GET' && !req.path.startsWith('/api')) {
@@ -827,6 +849,8 @@ function startServer() {
827
849
  console.log(`🤖 Nyxora API Server running on port ${PORT}`);
828
850
  // Start the Telegram bot listener
829
851
  (0, telegram_1.startTelegramBot)();
852
+ // Start Event Listener for Limit Orders (V3)
853
+ eventListener_1.eventListener.start();
830
854
  });
831
855
  server.on('error', (e) => {
832
856
  if (e.code === 'EADDRINUSE') {
@@ -201,6 +201,15 @@ function startTelegramBot() {
201
201
  else if (tx.type === 'revokeApproval') {
202
202
  result = await (0, revokeApprovals_1.executeRevokeApproval)(tx.chainName, tx.details, true);
203
203
  }
204
+ else if (tx.type === 'limit_order') {
205
+ const success = reasoning_1.logger.activateLimitOrder(tx.details.orderId);
206
+ if (success) {
207
+ result = `Limit Order ${tx.details.orderId} is now ACTIVE. The Event-Driven Engine is monitoring the market.`;
208
+ }
209
+ else {
210
+ throw new Error(`Failed to activate Limit Order. ID not found in database.`);
211
+ }
212
+ }
204
213
  transactionManager_1.txManager.updateStatus(txId, 'executed', result);
205
214
  // Pass session history to formatTransactionSuccess to detect language
206
215
  const sessionId = ctx.chat?.id.toString() || 'default';
@@ -53,6 +53,34 @@ class Logger {
53
53
  // Phase 1: SQLite Index Optimization
54
54
  this.db.exec(`
55
55
  CREATE INDEX IF NOT EXISTS idx_session_id ON messages(session_id);
56
+ `);
57
+ // V3: Limit Orders & Event-Driven Engine
58
+ this.db.exec(`
59
+ CREATE TABLE IF NOT EXISTS limit_orders (
60
+ id TEXT PRIMARY KEY,
61
+ token_address TEXT NOT NULL,
62
+ token_symbol TEXT NOT NULL,
63
+ trigger_condition TEXT NOT NULL,
64
+ trigger_price_usd REAL NOT NULL,
65
+ action TEXT NOT NULL,
66
+ amount_usd REAL NOT NULL,
67
+ slippage_tolerance REAL DEFAULT 5.0,
68
+ status TEXT DEFAULT 'ACTIVE',
69
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
70
+ expires_at DATETIME,
71
+ tx_hash TEXT
72
+ )
73
+ `);
74
+ // V3: Personalized Risk Profile
75
+ this.db.exec(`
76
+ CREATE TABLE IF NOT EXISTS user_profiles (
77
+ id TEXT PRIMARY KEY,
78
+ risk_level TEXT DEFAULT 'Moderate',
79
+ max_slippage REAL DEFAULT 1.0,
80
+ avoid_memecoins BOOLEAN DEFAULT 0,
81
+ custom_rules TEXT,
82
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
83
+ )
56
84
  `);
57
85
  // Ensure session_id exists for older DBs
58
86
  try {
@@ -193,6 +221,60 @@ class Logger {
193
221
  console.error('[Nyxora Memory] Error closing database:', e);
194
222
  }
195
223
  }
224
+ // V3: User Persona & Risk Profile
225
+ getUserProfile() {
226
+ try {
227
+ const row = this.db.prepare('SELECT * FROM user_profiles WHERE id = ?').get('default');
228
+ if (row) {
229
+ return {
230
+ id: row.id,
231
+ risk_level: row.risk_level,
232
+ max_slippage: row.max_slippage,
233
+ avoid_memecoins: Boolean(row.avoid_memecoins),
234
+ custom_rules: row.custom_rules
235
+ };
236
+ }
237
+ return null;
238
+ }
239
+ catch (e) {
240
+ return null;
241
+ }
242
+ }
243
+ updateUserProfile(profile) {
244
+ const existing = this.getUserProfile() || {
245
+ id: 'default',
246
+ risk_level: 'Moderate',
247
+ max_slippage: 1.0,
248
+ avoid_memecoins: false,
249
+ custom_rules: null
250
+ };
251
+ const updated = { ...existing, ...profile };
252
+ this.db.prepare(`
253
+ INSERT INTO user_profiles (id, risk_level, max_slippage, avoid_memecoins, custom_rules, updated_at)
254
+ VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
255
+ ON CONFLICT(id) DO UPDATE SET
256
+ risk_level = excluded.risk_level,
257
+ max_slippage = excluded.max_slippage,
258
+ avoid_memecoins = excluded.avoid_memecoins,
259
+ custom_rules = excluded.custom_rules,
260
+ updated_at = excluded.updated_at
261
+ `).run('default', updated.risk_level, updated.max_slippage, updated.avoid_memecoins ? 1 : 0, updated.custom_rules);
262
+ }
263
+ // V3: Limit Orders
264
+ createLimitOrder(order) {
265
+ const id = crypto_1.default.randomUUID();
266
+ this.db.prepare(`
267
+ INSERT INTO limit_orders (
268
+ id, token_address, token_symbol, trigger_condition, trigger_price_usd, action, amount_usd, slippage_tolerance, status
269
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
270
+ `).run(id, order.token_address, order.token_symbol, order.trigger_condition, order.trigger_price_usd, order.action, order.amount_usd, order.slippage_tolerance || 5.0, 'PENDING_APPROVAL' // Requires user approval in Dashboard/Telegram
271
+ );
272
+ return id;
273
+ }
274
+ activateLimitOrder(orderId) {
275
+ const result = this.db.prepare(`UPDATE limit_orders SET status = 'ACTIVE' WHERE id = ?`).run(orderId);
276
+ return result.changes > 0;
277
+ }
196
278
  }
197
279
  exports.Logger = Logger;
198
280
  exports.logger = new Logger();
@@ -91,22 +91,25 @@ async function analyzeDocument(filePath) {
91
91
  return text;
92
92
  }
93
93
  if (ext === '.xlsx' || ext === '.csv') {
94
- const ExcelJS = await Promise.resolve().then(() => __importStar(require('exceljs')));
95
- const workbook = new ExcelJS.Workbook();
94
+ let text = `--- Spreadsheet Data from ${path_1.default.basename(absolutePath)} ---\n`;
96
95
  if (ext === '.csv') {
97
- await workbook.csv.readFile(absolutePath);
96
+ const { parse } = await Promise.resolve().then(() => __importStar(require('csv-parse/sync')));
97
+ const content = fs_1.default.readFileSync(absolutePath, 'utf8');
98
+ const records = parse(content, { skip_empty_lines: true });
99
+ records.forEach((row) => {
100
+ text += row.join(',') + '\n';
101
+ });
98
102
  }
99
103
  else {
100
- await workbook.xlsx.readFile(absolutePath);
101
- }
102
- let text = `--- Spreadsheet Data from ${path_1.default.basename(absolutePath)} ---\n`;
103
- workbook.eachSheet((worksheet) => {
104
- text += `\n[Sheet: ${worksheet.name}]\n`;
105
- worksheet.eachRow((row) => {
106
- const values = Array.isArray(row.values) ? row.values.slice(1) : Object.values(row.values);
107
- text += values.join(',') + '\n';
104
+ const readXlsxFile = (await Promise.resolve().then(() => __importStar(require('read-excel-file/node')))).default;
105
+ const sheets = await readXlsxFile(absolutePath);
106
+ sheets.forEach((s) => {
107
+ text += `\n[Sheet: ${s.sheet}]\n`;
108
+ s.data.forEach((row) => {
109
+ text += row.map(cell => cell === null ? '' : String(cell)).join(',') + '\n';
110
+ });
108
111
  });
109
- });
112
+ }
110
113
  if (text.length > 20000) {
111
114
  text = text.substring(0, 20000) + "... [Content Truncated]";
112
115
  }
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.generateExcelToolDefinition = void 0;
7
7
  exports.generateExcelFile = generateExcelFile;
8
- const exceljs_1 = __importDefault(require("exceljs"));
8
+ const node_1 = __importDefault(require("write-excel-file/node"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  async function generateExcelFile(data, filePath) {
@@ -19,14 +19,12 @@ async function generateExcelFile(data, filePath) {
19
19
  if (!reportData || reportData.length === 0) {
20
20
  reportData = [{ Message: 'No data available' }];
21
21
  }
22
- const workbook = new exceljs_1.default.Workbook();
23
- const worksheet = workbook.addWorksheet('Report');
24
22
  const headers = Object.keys(reportData[0]);
25
- worksheet.columns = headers.map(h => ({ header: h, key: h }));
26
- reportData.forEach(row => {
27
- worksheet.addRow(row);
28
- });
29
- await workbook.xlsx.writeFile(absolutePath);
23
+ const excelData = [
24
+ headers.map(h => ({ value: h, fontWeight: 'bold' })),
25
+ ...reportData.map(row => headers.map(h => ({ value: String(row[h] || '') })))
26
+ ];
27
+ await (0, node_1.default)(excelData).toFile(absolutePath);
30
28
  return `Success: Excel file generated at ${absolutePath}`;
31
29
  }
32
30
  catch (error) {
@@ -167,12 +167,17 @@ async function fetchLifi(fromChain, toChain, fromToken, toToken, amount, address
167
167
  }
168
168
  async function fetchRelay(fromChain, toChain, fromToken, toToken, amount, address, slippage, key) {
169
169
  try {
170
+ // Relay API strictly requires the zero address for Native ETH instead of 0xeeee...
171
+ const relayOriginCurrency = fromToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
172
+ ? '0x0000000000000000000000000000000000000000' : fromToken;
173
+ const relayDestCurrency = toToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
174
+ ? '0x0000000000000000000000000000000000000000' : toToken;
170
175
  const payload = {
171
176
  user: address,
172
177
  originChainId: CHAIN_IDS[fromChain].toString(),
173
178
  destinationChainId: CHAIN_IDS[toChain].toString(),
174
- originCurrency: fromToken,
175
- destinationCurrency: toToken,
179
+ originCurrency: relayOriginCurrency,
180
+ destinationCurrency: relayDestCurrency,
176
181
  recipient: address,
177
182
  tradeType: 'EXACT_INPUT',
178
183
  amount: amount,
@@ -43,9 +43,14 @@ async function fetchRelayTestnet(fromChain, toChain, fromToken, toToken, amount,
43
43
  const destChainId = RELAY_CHAIN_MAP[toChain];
44
44
  if (!originChainId || !destChainId)
45
45
  return null;
46
+ // Relay API strictly requires the zero address for Native ETH instead of 0xeeee...
47
+ const relayOriginCurrency = fromToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
48
+ ? '0x0000000000000000000000000000000000000000' : fromToken;
49
+ const relayDestCurrency = toToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
50
+ ? '0x0000000000000000000000000000000000000000' : toToken;
46
51
  const payload = {
47
52
  user: address, originChainId, destinationChainId: destChainId,
48
- originCurrency: fromToken, destinationCurrency: toToken,
53
+ originCurrency: relayOriginCurrency, destinationCurrency: relayDestCurrency,
49
54
  recipient: address, tradeType: 'EXACT_INPUT', amount,
50
55
  referrer: 'nyxora', useExternalLiquidity: false
51
56
  };
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.eventListener = void 0;
7
+ const logger_1 = require("../memory/logger");
8
+ const getPrice_1 = require("./skills/getPrice");
9
+ const swapToken_1 = require("./skills/swapToken");
10
+ const transactionManager_1 = require("../agent/transactionManager");
11
+ const picocolors_1 = __importDefault(require("picocolors"));
12
+ class EventListener {
13
+ timer = null;
14
+ isRunning = false;
15
+ start() {
16
+ if (this.isRunning)
17
+ return;
18
+ this.isRunning = true;
19
+ console.log(picocolors_1.default.green('[Event Listener] Real-Time Multi-Source Radar started. Monitoring DexScreener & Oracles...'));
20
+ this.poll();
21
+ }
22
+ stop() {
23
+ this.isRunning = false;
24
+ if (this.timer)
25
+ clearTimeout(this.timer);
26
+ console.log(picocolors_1.default.yellow('[Event Listener] Radar stopped.'));
27
+ }
28
+ async poll() {
29
+ if (!this.isRunning)
30
+ return;
31
+ try {
32
+ // 1. Fetch active limit orders from database
33
+ const rows = logger_1.logger['db'].prepare(`SELECT * FROM limit_orders WHERE status = 'ACTIVE'`).all();
34
+ for (const order of rows) {
35
+ try {
36
+ // 2. Fetch current price via DexScreener / Aggregator (getPrice skill)
37
+ const priceResult = await (0, getPrice_1.getPrice)(order.token_address);
38
+ // Parse price from result string (rough parsing for MVP)
39
+ const priceMatch = priceResult.match(/Current Price:\s*\$([\d.]+)/i);
40
+ if (!priceMatch)
41
+ continue;
42
+ const currentPrice = parseFloat(priceMatch[1]);
43
+ const targetPrice = order.trigger_price_usd;
44
+ let triggered = false;
45
+ if (order.trigger_condition === 'PRICE_DROPS_BELOW' && currentPrice <= targetPrice)
46
+ triggered = true;
47
+ if (order.trigger_condition === 'PRICE_RISES_ABOVE' && currentPrice >= targetPrice)
48
+ triggered = true;
49
+ if (triggered) {
50
+ console.log(picocolors_1.default.bgRed(picocolors_1.default.white(`\n[🚨 LIMIT ORDER TRIGGERED] ${order.token_symbol} hit target price $${targetPrice}! Executing ${order.action}...`)));
51
+ // 3. Mark as EXECUTING to prevent double-execution
52
+ logger_1.logger['db'].prepare(`UPDATE limit_orders SET status = 'EXECUTING' WHERE id = ?`).run(order.id);
53
+ // 4. Policy Engine & Executor: Prepare and Execute Swap with Slippage Tolerance
54
+ // Note: In V3, this delegates to our existing swap engine which inherently uses 1inch/0x aggregators that support slippage param
55
+ if (order.action === 'BUY') {
56
+ // Buying token using USDC as base for example.
57
+ // In full implementation, we'd look up the user's base asset (USDC/ETH).
58
+ const amountStr = order.amount_usd.toString(); // assuming base is USD stablecoin
59
+ const txMsg = await (0, swapToken_1.prepareSwapToken)('base', '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', order.token_address, amountStr, 'auto', 'auto', order.slippage_tolerance);
60
+ // Extract txId
61
+ const txMatch = txMsg.match(/Transaction ID: ([\w-]+)/);
62
+ if (txMatch) {
63
+ const txId = txMatch[1];
64
+ const pendingTx = transactionManager_1.txManager.getTransaction(txId);
65
+ if (pendingTx) {
66
+ const result = await (0, swapToken_1.executeSwap)(pendingTx.chainName, pendingTx.details, true);
67
+ logger_1.logger['db'].prepare(`UPDATE limit_orders SET status = 'COMPLETED', tx_hash = ? WHERE id = ?`).run('Executed via router', order.id);
68
+ console.log(picocolors_1.default.green(`[Event Listener] Order ${order.id} executed successfully. Result: ${result}`));
69
+ // Notify user (would need a callback to Telegram in full implementation)
70
+ }
71
+ }
72
+ }
73
+ else if (order.action === 'SELL') {
74
+ // Selling token for USDC
75
+ const amountStr = order.amount_usd.toString(); // Ideally convert USD to Token Amount via oracle
76
+ const txMsg = await (0, swapToken_1.prepareSwapToken)('base', order.token_address, '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', amountStr, 'auto', 'auto', order.slippage_tolerance);
77
+ // Same extraction logic...
78
+ const txMatch = txMsg.match(/Transaction ID: ([\w-]+)/);
79
+ if (txMatch) {
80
+ const txId = txMatch[1];
81
+ const pendingTx = transactionManager_1.txManager.getTransaction(txId);
82
+ if (pendingTx) {
83
+ const result = await (0, swapToken_1.executeSwap)(pendingTx.chainName, pendingTx.details, true);
84
+ logger_1.logger['db'].prepare(`UPDATE limit_orders SET status = 'COMPLETED', tx_hash = ? WHERE id = ?`).run('Executed via router', order.id);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ catch (err) {
91
+ console.error(`[Event Listener] Error processing order ${order.id}:`, err);
92
+ }
93
+ }
94
+ }
95
+ catch (e) {
96
+ console.error('[Event Listener] Critical Poll Error:', e);
97
+ }
98
+ // Schedule next poll (Graceful Degradation to HTTP Polling if WSS isn't used)
99
+ this.timer = setTimeout(() => this.poll(), 10000); // Poll every 10s for prototype
100
+ }
101
+ }
102
+ exports.eventListener = new EventListener();
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLimitOrderToolDefinition = void 0;
4
+ exports.createLimitOrder = createLimitOrder;
5
+ const logger_1 = require("../../memory/logger");
6
+ const transactionManager_1 = require("../../agent/transactionManager");
7
+ async function createLimitOrder(tokenSymbol, tokenAddress, triggerCondition, triggerPriceUsd, action, amountUsd, slippageTolerance) {
8
+ try {
9
+ const orderData = {
10
+ token_symbol: tokenSymbol,
11
+ token_address: tokenAddress,
12
+ trigger_condition: triggerCondition,
13
+ trigger_price_usd: triggerPriceUsd,
14
+ action,
15
+ amount_usd: amountUsd,
16
+ slippage_tolerance: slippageTolerance || 5.0
17
+ };
18
+ const orderId = logger_1.logger.createLimitOrder(orderData);
19
+ const tx = transactionManager_1.txManager.createPendingTransaction('limit_order', 'any', {
20
+ orderId,
21
+ ...orderData
22
+ });
23
+ return `[UNSAFE/HIGH RISK ALERT] Limit Order drafted successfully.\nTrigger: If ${tokenSymbol} ${triggerCondition === 'PRICE_DROPS_BELOW' ? 'drops below' : 'rises above'} $${triggerPriceUsd}, ${action} $${amountUsd} worth of ${tokenSymbol}.\nTransaction ID: ${tx.id}\nStatus: PENDING_APPROVAL. Waiting for your explicit confirmation via UI/Telegram before the Event-Driven Engine activates.`;
24
+ }
25
+ catch (error) {
26
+ return `Failed to create Limit Order: ${error.message}`;
27
+ }
28
+ }
29
+ exports.createLimitOrderToolDefinition = {
30
+ type: "function",
31
+ function: {
32
+ name: "create_limit_order",
33
+ description: "[HIGH RISK] Create a decentralized limit order (trigger) that will automatically execute a trade when a token's price hits a specific target. Use this when the user asks to buy or sell a token if the price goes up or down to a certain level.",
34
+ parameters: {
35
+ type: "object",
36
+ properties: {
37
+ tokenSymbol: { type: "string", description: "Symbol of the token (e.g. PEPE, ETH)" },
38
+ tokenAddress: { type: "string", description: "Contract address of the token" },
39
+ triggerCondition: { type: "string", enum: ["PRICE_DROPS_BELOW", "PRICE_RISES_ABOVE"], description: "The condition to trigger the order" },
40
+ triggerPriceUsd: { type: "number", description: "The target price in USD" },
41
+ action: { type: "string", enum: ["BUY", "SELL"], description: "The action to take when triggered" },
42
+ amountUsd: { type: "number", description: "The amount in USD to buy or sell" },
43
+ slippageTolerance: { type: "number", description: "Maximum slippage tolerance in percentage (e.g. 5.0)" }
44
+ },
45
+ required: ["tokenSymbol", "tokenAddress", "triggerCondition", "triggerPriceUsd", "action", "amountUsd"],
46
+ },
47
+ },
48
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nyxora",
3
- "version": "26.6.11-2",
3
+ "version": "26.6.12",
4
4
  "description": "Your Personal Web3 Assistant",
5
5
  "keywords": [
6
6
  "web3",
@@ -54,9 +54,9 @@
54
54
  "@napi-rs/keyring": "^1.3.0",
55
55
  "@notionhq/client": "^5.22.0",
56
56
  "cors": "^2.8.6",
57
+ "csv-parse": "^6.2.1",
57
58
  "dotenv": "^17.4.2",
58
59
  "duck-duck-scrape": "^2.2.7",
59
- "exceljs": "^4.4.0",
60
60
  "express": "^5.2.1",
61
61
  "express-rate-limit": "^7.5.0",
62
62
  "helmet": "^8.0.0",
@@ -68,11 +68,13 @@
68
68
  "pdf-parse": "^2.4.5",
69
69
  "picocolors": "^1.1.1",
70
70
  "playwright": "^1.60.0",
71
+ "read-excel-file": "^9.2.0",
71
72
  "telegraf": "^4.16.3",
72
73
  "ts-node": "^10.9.2",
73
- "typescript": "^6.0.3",
74
74
  "twitter-api-v2": "^1.29.0",
75
+ "typescript": "^6.0.3",
75
76
  "viem": "^2.51.0",
77
+ "write-excel-file": "^4.1.1",
76
78
  "yaml": "^2.9.0",
77
79
  "zod": "^3.23.8"
78
80
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nyxora-agent-core",
3
- "version": "26.6.11",
3
+ "version": "26.6.12",
4
4
  "private": true,
5
5
  "main": "src/gateway/server.ts",
6
6
  "dependencies": {
@@ -8,8 +8,8 @@
8
8
  "@inquirer/search": "^4.2.1",
9
9
  "@notionhq/client": "^5.22.0",
10
10
  "cors": "^2.8.6",
11
+ "csv-parse": "^6.2.1",
11
12
  "duck-duck-scrape": "^2.2.7",
12
- "exceljs": "^4.4.0",
13
13
  "express": "^5.2.1",
14
14
  "express-rate-limit": "^7.5.0",
15
15
  "helmet": "^8.0.0",
@@ -19,8 +19,10 @@
19
19
  "openai": "^6.39.0",
20
20
  "pdf-parse": "^2.4.5",
21
21
  "playwright": "^1.60.0",
22
+ "read-excel-file": "^9.2.0",
22
23
  "telegraf": "^4.16.3",
23
24
  "twitter-api-v2": "^1.29.0",
25
+ "write-excel-file": "^4.1.1",
24
26
  "yaml": "^2.9.0",
25
27
  "zod": "^3.25.76"
26
28
  },