nyxora 26.6.11-2 → 26.6.13

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 (54) hide show
  1. package/README.md +3 -3
  2. package/dist/packages/core/src/agent/bridgeWatcher.js +34 -0
  3. package/dist/packages/core/src/agent/reasoning.js +44 -11
  4. package/dist/packages/core/src/agent/transactionManager.js +47 -0
  5. package/dist/packages/core/src/gateway/server.js +28 -1
  6. package/dist/packages/core/src/gateway/telegram.js +34 -1
  7. package/dist/packages/core/src/memory/logger.js +82 -0
  8. package/dist/packages/core/src/system/skills/analyzeDocument.js +15 -12
  9. package/dist/packages/core/src/system/skills/generateExcel.js +6 -8
  10. package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +10 -3
  11. package/dist/packages/core/src/web3/aggregator/aggregatorTestnet.js +25 -11
  12. package/dist/packages/core/src/web3/eventListener.js +102 -0
  13. package/dist/packages/core/src/web3/skills/bridgeToken.js +1 -1
  14. package/dist/packages/core/src/web3/skills/createLimitOrder.js +48 -0
  15. package/dist/packages/core/src/web3/skills/customTx.js +1 -1
  16. package/dist/packages/core/src/web3/skills/defiLending.js +2 -2
  17. package/dist/packages/core/src/web3/skills/mintNft.js +1 -1
  18. package/dist/packages/core/src/web3/skills/nativeOpBridge.js +70 -0
  19. package/dist/packages/core/src/web3/skills/provideLiquidity.js +3 -3
  20. package/dist/packages/core/src/web3/skills/revokeApprovals.js +1 -1
  21. package/dist/packages/core/src/web3/skills/swapToken.js +1 -1
  22. package/dist/packages/core/src/web3/skills/transfer.js +1 -1
  23. package/dist/packages/core/src/web3/skills/yieldVault.js +2 -2
  24. package/package.json +8 -3
  25. package/packages/core/package.json +4 -2
  26. package/packages/core/src/agent/bridgeWatcher.ts +39 -0
  27. package/packages/core/src/agent/reasoning.ts +54 -13
  28. package/packages/core/src/agent/transactionManager.ts +66 -0
  29. package/packages/core/src/gateway/server.ts +30 -1
  30. package/packages/core/src/gateway/telegram.ts +31 -1
  31. package/packages/core/src/memory/logger.ts +113 -0
  32. package/packages/core/src/system/skills/analyzeDocument.ts +15 -14
  33. package/packages/core/src/system/skills/generateExcel.ts +6 -10
  34. package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +12 -3
  35. package/packages/core/src/web3/aggregator/aggregatorTestnet.ts +35 -13
  36. package/packages/core/src/web3/eventListener.ts +103 -0
  37. package/packages/core/src/web3/skills/bridgeToken.ts +1 -1
  38. package/packages/core/src/web3/skills/createLimitOrder.ts +56 -0
  39. package/packages/core/src/web3/skills/customTx.ts +1 -1
  40. package/packages/core/src/web3/skills/defiLending.ts +2 -2
  41. package/packages/core/src/web3/skills/mintNft.ts +1 -1
  42. package/packages/core/src/web3/skills/nativeOpBridge.ts +83 -0
  43. package/packages/core/src/web3/skills/provideLiquidity.ts +3 -3
  44. package/packages/core/src/web3/skills/revokeApprovals.ts +1 -1
  45. package/packages/core/src/web3/skills/swapToken.ts +1 -1
  46. package/packages/core/src/web3/skills/transfer.ts +1 -1
  47. package/packages/core/src/web3/skills/yieldVault.ts +2 -2
  48. package/packages/dashboard/dist/assets/index-BhKhEfi_.js +13 -0
  49. package/packages/dashboard/dist/index.html +1 -1
  50. package/packages/dashboard/package.json +1 -1
  51. package/packages/mcp-server/package.json +1 -1
  52. package/packages/policy/package.json +1 -1
  53. package/packages/signer/package.json +1 -1
  54. package/packages/dashboard/dist/assets/index-CCgm_N9M.js +0 -13
package/README.md CHANGED
@@ -13,14 +13,14 @@ Nyxora is a **secure, non-custodial runtime infrastructure for autonomous onchai
13
13
 
14
14
  **Nyxora now natively supports the Model Context Protocol (MCP)**. You can transform your external AI agents (like Claude Desktop and Cursor) into secure Web3 actors that execute swaps and fetch balances using Nyxora's secure signer vault. [View the MCP Integration Guide](https://nyxoraai.github.io/Nyxora/guide/mcp-integration)
15
15
 
16
- It operates under an institutional-grade **Cryptographically Bound Human-in-the-Loop** execution model, ensuring that Remote AIs (LLMs) never have unilateral access to your funds.
16
+ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human-in-the-Loop** execution model, ensuring that Remote AIs (LLMs) never have unilateral access to your funds.
17
17
 
18
18
  ---
19
19
 
20
20
  ## 🔥 Key Features
21
21
 
22
22
  ### Advanced Security Architecture
23
- * **🛡️ On-Chain AI Kill-Switch**: Nyxora is governed by an Arbitrum Smart Contract (`NyxoraAgentRegistry`). Users have absolute cryptographic power to instantly paralyze the AI's on-chain execution if compromised, solving the Web3 AI safety dilemma. [Read more about our Arbitrum Architecture ->](https://nyxoraai.github.io/Nyxora/security/smart-contract)
23
+ * **🛡️ On-Chain AI Kill-Switch**: Nyxora is governed by an Arbitrum Smart Contract (`NyxoraAgentRegistry`). Users have absolute cryptographic power to instantly paralyze the AI's on-chain execution if compromised, solving the Web3 AI safety dilemma. [Read more about our Arbitrum Architecture](https://nyxoraai.github.io/Nyxora/security/smart-contract)
24
24
  * **3-Tier IPC Architecture**: Nyxora is split into isolated processes: **Core** (LLM Runtime), **Policy Engine** (Guardrails on port 3001), and **Signer Vault** (Isolated Key Manager on Unix Sockets).
25
25
  * **DeFi Configuration BYOK & UI Masking**: All aggregator and provider API keys are strictly isolated via a Bring Your Own Keys (BYOK) architecture into a heavily guarded `~/.nyxora/defi_keys.yaml` file. The local web Dashboard masks these injected secrets using `***********` and `IS_SET` censorship, completely neutralizing malicious browser extensions from exfiltrating your keys.
26
26
  * **Approval Replay Protection (Nonce Guard)**: Transactions requested by the AI are drafted as hashes and signed with a randomized 16-byte Nonce. The `/api/transactions/:id/approve` endpoint strictly enforces Nonce matching to completely eliminate double-spending and Replay Attacks.
@@ -32,7 +32,7 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
32
32
  * **Security Scanner**: Nyxora can scan smart contracts via GoPlus Labs to detect Honeypots, Hidden Taxes, and malicious proxy upgrades before you buy.
33
33
  * **Advanced DeFi Optimization**: Autonomously supply assets to Aave V3, deposit into Beefy/Yearn Auto-Compounder Vaults, manage Uniswap V3 Liquidity (LP), and instantly revoke infinite approvals to secure your wallet. Features intelligent Transaction Chaining to auto-approve allowances prior to execution.
34
34
  * **6-Engine Meta-Aggregator & Anti-MEV**: The core engine interfaces with a powerful 6-Engine Meta-Aggregator (**1inch, 0x, LI.FI, Relay, OpenOcean, and KyberSwap**) to route tokens cross-chain, ensuring absolute maximum liquidity depth.
35
- * **Adaptive Auto Slippage Protection**: Nyxora enforces a dynamic and adaptive **'auto' slippage** by default to leverage dynamic MEV-protection from these enterprise aggregators. However, the user retains absolute control to override this dynamically—either globally via the Dashboard Settings or on a per-transaction basis through NLP chat commands (e.g., *"Swap 1 ETH to PEPE with 10% slippage"*).
35
+ * **Adaptive Auto Slippage Protection**: Nyxora enforces a dynamic and adaptive **'auto' slippage** by default to leverage dynamic MEV-protection from these industry-standard aggregators. However, the user retains absolute control to override this dynamically—either globally via the Dashboard Settings or on a per-transaction basis through NLP chat commands (e.g., *"Swap 1 ETH to PEPE with 10% slippage"*).
36
36
 
37
37
  * **Cross-Chain Hybrid Market Scanner**: Real-time asset tracking combining CoinGecko global data with DexScreener on-chain metrics across Ethereum, Base, Solana, BSC, and more.
38
38
  * **"Lean Degen" Auto-Whitelist**: Automatically intercepts Contract Addresses (CAs) whenever you check balances or swap tokens, saving them to your localized `user_whitelist.json` for future tracking.
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startBridgeWatcher = startBridgeWatcher;
4
+ const transactionManager_1 = require("./transactionManager");
5
+ const telegram_1 = require("../gateway/telegram");
6
+ const parser_1 = require("../config/parser");
7
+ // In a real production environment, this would use the @eth-optimism/sdk
8
+ // to fetch the Merkle Proof and call proveWithdrawalTransaction on L1.
9
+ // For the scope of this architecture prototype, we simulate the Challenge Period
10
+ // watcher by using a time-delay, representing the exact asynchronous behavior.
11
+ const CHALLENGE_PERIOD_MS = 2 * 60 * 1000; // Simulating a 2-minute challenge period for testnet demo
12
+ function startBridgeWatcher() {
13
+ console.log('[Bridge Watcher] Started background daemon for asynchronous L2 withdrawals');
14
+ setInterval(async () => {
15
+ const config = (0, parser_1.loadConfig)();
16
+ const authId = config.integrations?.telegram?.authorized_chat_id;
17
+ if (!authId)
18
+ return;
19
+ const pending = transactionManager_1.txManager.getPendingWithdrawals();
20
+ const now = Date.now();
21
+ for (const w of pending) {
22
+ if (w.status === 'WAITING_FOR_CHALLENGE') {
23
+ if (now - w.createdAt > CHALLENGE_PERIOD_MS) {
24
+ // The simulated challenge period is over. State root is "published".
25
+ console.log(`[Bridge Watcher] Withdrawal ${w.id} is ready for L1 claim!`);
26
+ transactionManager_1.txManager.updateWithdrawalStatus(w.id, 'READY_FOR_CLAIM');
27
+ const amountDisplay = Number(w.amount) / 1e18; // assuming 18 decimals
28
+ const message = `🔔 **Bridge Ready to Claim!**\n\nYour withdrawal of ${amountDisplay} ETH from ${w.l2Chain} to ${w.l1Chain} has completed its challenge period.\n\nShall I execute the Prove & Claim transaction on L1 now?`;
29
+ await (0, telegram_1.sendPushNotification)(authId, message, w.id);
30
+ }
31
+ }
32
+ }
33
+ }, 30000); // Check every 30 seconds
34
+ }
@@ -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,9 +288,11 @@ 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 = [];
291
+ // Enabled fastReturnTools to eliminate 2nd LLM latency for transaction popups
264
292
  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'
293
+ 'transfer_token', 'transfer_native', 'swap_token', 'bridge_token',
294
+ 'mint_nft', 'custom_tx', 'revoke_approval', 'supply_aave',
295
+ 'deposit_yield_vault', 'provide_liquidity_v3'
267
296
  ];
268
297
  for (const _toolCall of responseMessage.tool_calls) {
269
298
  const toolCall = _toolCall;
@@ -377,6 +406,10 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
377
406
  result = await (0, getTxHistory_1.getTxHistory)(args.chainName, args.address, args.days);
378
407
  break;
379
408
  }
409
+ case 'create_limit_order': {
410
+ result = await (0, createLimitOrder_1.createLimitOrder)(args.tokenSymbol, args.tokenAddress, args.triggerCondition, args.triggerPriceUsd, args.action, args.amountUsd, args.slippageTolerance);
411
+ break;
412
+ }
380
413
  case 'update_profile': {
381
414
  result = (0, updateProfile_1.updateProfile)(args.content, args.mode);
382
415
  break;
@@ -5,8 +5,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.txManager = void 0;
7
7
  const crypto_1 = __importDefault(require("crypto"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
8
10
  class TransactionManager {
9
11
  transactions = new Map();
12
+ withdrawals = new Map();
13
+ dbPath;
14
+ constructor() {
15
+ this.dbPath = path_1.default.join(process.cwd(), '.nyxora_withdrawals.json');
16
+ this.loadWithdrawals();
17
+ }
18
+ loadWithdrawals() {
19
+ if (fs_1.default.existsSync(this.dbPath)) {
20
+ try {
21
+ const data = fs_1.default.readFileSync(this.dbPath, 'utf8');
22
+ const parsed = JSON.parse(data);
23
+ parsed.forEach(w => this.withdrawals.set(w.id, w));
24
+ }
25
+ catch (e) {
26
+ console.error("Failed to load withdrawals DB:", e);
27
+ }
28
+ }
29
+ }
30
+ saveWithdrawals() {
31
+ const data = Array.from(this.withdrawals.values());
32
+ fs_1.default.writeFileSync(this.dbPath, JSON.stringify(data, null, 2));
33
+ }
10
34
  createPendingTransaction(type, chainName, details) {
11
35
  const id = crypto_1.default.randomUUID();
12
36
  const nonce = crypto_1.default.randomBytes(16).toString('hex');
@@ -36,5 +60,28 @@ class TransactionManager {
36
60
  tx.result = result;
37
61
  }
38
62
  }
63
+ // --- WITHDRAWAL LOGIC ---
64
+ createPendingWithdrawal(data) {
65
+ const id = crypto_1.default.randomUUID();
66
+ const withdrawal = {
67
+ ...data,
68
+ id,
69
+ status: 'WAITING_FOR_CHALLENGE',
70
+ createdAt: Date.now()
71
+ };
72
+ this.withdrawals.set(id, withdrawal);
73
+ this.saveWithdrawals();
74
+ return withdrawal;
75
+ }
76
+ getPendingWithdrawals() {
77
+ return Array.from(this.withdrawals.values()).filter(w => w.status !== 'COMPLETED');
78
+ }
79
+ updateWithdrawalStatus(id, status) {
80
+ const w = this.withdrawals.get(id);
81
+ if (w) {
82
+ w.status = status;
83
+ this.saveWithdrawals();
84
+ }
85
+ }
39
86
  }
40
87
  exports.txManager = new TransactionManager();
@@ -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,8 @@ 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 bridgeWatcher_1 = require("../agent/bridgeWatcher");
74
+ const eventListener_1 = require("../web3/eventListener");
72
75
  const googleAuthModule_1 = require("./googleAuthModule");
73
76
  const legalGenerator_1 = require("./legalGenerator");
74
77
  const episodic_1 = require("../memory/episodic");
@@ -309,7 +312,8 @@ app.get('/api/skills', (req, res) => {
309
312
  revokeApprovals_2.revokeApprovalToolDefinition,
310
313
  yieldVault_1.vaultDepositToolDefinition,
311
314
  provideLiquidity_1.provideLiquidityToolDefinition,
312
- getTxHistory_1.getTxHistoryToolDefinition
315
+ getTxHistory_1.getTxHistoryToolDefinition,
316
+ createLimitOrder_1.createLimitOrderToolDefinition
313
317
  ];
314
318
  const skillsWithStatus = allSkills.map(skill => ({
315
319
  ...skill,
@@ -778,6 +782,25 @@ app.delete('/api/memory/:id', (req, res) => {
778
782
  res.status(500).json({ error: error.message });
779
783
  }
780
784
  });
785
+ // --- User Persona / Risk Profile Endpoints (V3) ---
786
+ app.get('/api/profile', (req, res) => {
787
+ try {
788
+ const profile = reasoning_1.logger.getUserProfile();
789
+ res.json(profile || { risk_level: 'Moderate', max_slippage: 1.0, avoid_memecoins: false, custom_rules: '' });
790
+ }
791
+ catch (error) {
792
+ res.status(500).json({ error: error.message });
793
+ }
794
+ });
795
+ app.post('/api/profile', (req, res) => {
796
+ try {
797
+ reasoning_1.logger.updateUserProfile(req.body);
798
+ res.json({ success: true });
799
+ }
800
+ catch (error) {
801
+ res.status(500).json({ error: error.message });
802
+ }
803
+ });
781
804
  // Fallback for React Router (Single Page Application)
782
805
  app.use((req, res, next) => {
783
806
  if (req.method === 'GET' && !req.path.startsWith('/api')) {
@@ -827,6 +850,10 @@ function startServer() {
827
850
  console.log(`🤖 Nyxora API Server running on port ${PORT}`);
828
851
  // Start the Telegram bot listener
829
852
  (0, telegram_1.startTelegramBot)();
853
+ // Start Asynchronous Bridge Watcher
854
+ (0, bridgeWatcher_1.startBridgeWatcher)();
855
+ // Start Event Listener for Limit Orders (V3)
856
+ eventListener_1.eventListener.start();
830
857
  });
831
858
  server.on('error', (e) => {
832
859
  if (e.code === 'EADDRINUSE') {
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.formatToTelegramHTML = formatToTelegramHTML;
7
7
  exports.startTelegramBot = startTelegramBot;
8
+ exports.sendPushNotification = sendPushNotification;
8
9
  const telegraf_1 = require("telegraf");
9
10
  const reasoning_1 = require("../agent/reasoning");
10
11
  const parser_1 = require("../config/parser");
@@ -18,6 +19,7 @@ const executeDefi_1 = require("../web3/skills/executeDefi");
18
19
  const revokeApprovals_1 = require("../web3/skills/revokeApprovals");
19
20
  const formatter_1 = require("../utils/formatter");
20
21
  const picocolors_1 = __importDefault(require("picocolors"));
22
+ let globalBotInstance = null;
21
23
  function formatToTelegramHTML(text) {
22
24
  if (!text)
23
25
  return "";
@@ -32,9 +34,11 @@ function formatToTelegramHTML(text) {
32
34
  // Convert code blocks and inline code
33
35
  html = html.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
34
36
  html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
35
- // Strip <thought> blocks completely for user-friendly output
37
+ // Strip <thought> and <think> blocks completely for user-friendly output
36
38
  html = html.replace(/&lt;thought&gt;[\s\S]*?&lt;\/thought&gt;\n?/g, '');
37
39
  html = html.replace(/<thought>[\s\S]*?<\/thought>\n?/g, '');
40
+ html = html.replace(/&lt;think&gt;[\s\S]*?&lt;\/think&gt;\n?/g, '');
41
+ html = html.replace(/<think>[\s\S]*?<\/think>\n?/g, '');
38
42
  // Transform Markdown Tables to <pre> monospaced blocks so they don't break on mobile
39
43
  const tableRegex = /(?:\|.*\|(?:\n|$))+/g;
40
44
  html = html.replace(tableRegex, (match) => {
@@ -51,6 +55,7 @@ function startTelegramBot() {
51
55
  }
52
56
  try {
53
57
  const bot = new telegraf_1.Telegraf(token);
58
+ globalBotInstance = bot;
54
59
  // Pairing state variables
55
60
  const isPaired = !!config.integrations?.telegram?.authorized_chat_id;
56
61
  let generatedPin = '';
@@ -201,6 +206,15 @@ function startTelegramBot() {
201
206
  else if (tx.type === 'revokeApproval') {
202
207
  result = await (0, revokeApprovals_1.executeRevokeApproval)(tx.chainName, tx.details, true);
203
208
  }
209
+ else if (tx.type === 'limit_order') {
210
+ const success = reasoning_1.logger.activateLimitOrder(tx.details.orderId);
211
+ if (success) {
212
+ result = `Limit Order ${tx.details.orderId} is now ACTIVE. The Event-Driven Engine is monitoring the market.`;
213
+ }
214
+ else {
215
+ throw new Error(`Failed to activate Limit Order. ID not found in database.`);
216
+ }
217
+ }
204
218
  transactionManager_1.txManager.updateStatus(txId, 'executed', result);
205
219
  // Pass session history to formatTransactionSuccess to detect language
206
220
  const sessionId = ctx.chat?.id.toString() || 'default';
@@ -248,3 +262,22 @@ function startTelegramBot() {
248
262
  console.error('[Telegram] Failed to initialize bot:', error);
249
263
  }
250
264
  }
265
+ async function sendPushNotification(chatId, message, withdrawalId) {
266
+ if (!globalBotInstance)
267
+ return;
268
+ try {
269
+ let extraParams = { parse_mode: 'HTML' };
270
+ if (withdrawalId) {
271
+ extraParams = {
272
+ ...extraParams,
273
+ ...telegraf_1.Markup.inlineKeyboard([
274
+ [telegraf_1.Markup.button.callback(`✅ Approve Claim`, `claim_${withdrawalId}`)]
275
+ ])
276
+ };
277
+ }
278
+ await globalBotInstance.telegram.sendMessage(chatId, formatToTelegramHTML(message), extraParams);
279
+ }
280
+ catch (error) {
281
+ console.error('[Telegram] Failed to send push notification:', error);
282
+ }
283
+ }
@@ -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,
@@ -233,6 +238,8 @@ async function fetchKyberSwap(fromChain, fromToken, toToken, amount, address, sl
233
238
  if (!buildRes.ok)
234
239
  throw new Error(await buildRes.text());
235
240
  const buildData = await buildRes.json();
241
+ const isNative = fromToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ||
242
+ fromToken === '0x0000000000000000000000000000000000000000';
236
243
  return {
237
244
  provider: 'KyberSwap',
238
245
  expectedOutput: (Number(routeData.data.routeSummary.amountOut) / 1e18).toString(),
@@ -241,7 +248,7 @@ async function fetchKyberSwap(fromChain, fromToken, toToken, amount, address, sl
241
248
  txPayload: {
242
249
  to: buildData.data.routerAddress,
243
250
  data: buildData.data.data,
244
- value: routeData.data.routeSummary.amountInUsd // simplified value mapping
251
+ value: isNative ? amount : "0" // FIXED: Mengirim jumlah asli dalam WEI jika Native ETH, atau 0 jika ERC20
245
252
  },
246
253
  rawQuote: buildData.data
247
254
  };