nyxora 26.7.2 → 26.7.4

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 (192) hide show
  1. package/CHANGELOG.md +112 -47
  2. package/README.md +25 -21
  3. package/bin/nyxora.mjs +4 -6
  4. package/dist/launcher.js +44 -8
  5. package/dist/packages/core/src/agent/bridgeWatcher.js +3 -2
  6. package/dist/packages/core/src/agent/cronManager.js +2 -2
  7. package/dist/packages/core/src/agent/llmProvider.js +245 -0
  8. package/dist/packages/core/src/agent/nyxDaemon.js +97 -0
  9. package/dist/packages/core/src/agent/osAgent.js +351 -29
  10. package/dist/packages/core/src/agent/reasoning.js +353 -60
  11. package/dist/packages/core/src/agent/reasoningScratchpad.js +47 -0
  12. package/dist/packages/core/src/agent/transactionManager.js +36 -31
  13. package/dist/packages/core/src/agent/web3Agent.js +263 -37
  14. package/dist/packages/core/src/cognitive/cognitiveManager.js +63 -10
  15. package/dist/packages/core/src/config/parser.js +10 -4
  16. package/dist/packages/core/src/gateway/chat.js +56 -13
  17. package/dist/packages/core/src/gateway/cli.js +3 -0
  18. package/dist/packages/core/src/gateway/discordAdapter.js +81 -0
  19. package/dist/packages/core/src/gateway/googleAuthModule.js +2 -0
  20. package/dist/packages/core/src/gateway/server.js +58 -9
  21. package/dist/packages/core/src/gateway/setup-ml.js +64 -0
  22. package/dist/packages/core/src/gateway/setup.js +39 -3
  23. package/dist/packages/core/src/gateway/telegram.js +123 -27
  24. package/dist/packages/core/src/memory/episodic.js +58 -3
  25. package/dist/packages/core/src/memory/logger.js +120 -2
  26. package/dist/packages/core/src/memory/promotionEngine.js +18 -5
  27. package/dist/packages/core/src/system/agentskills.js +10 -0
  28. package/dist/packages/core/src/system/plugins/GoogleWorkspacePlugin.js +10 -2
  29. package/dist/packages/core/src/system/plugins/SystemCorePlugin.js +1 -1
  30. package/dist/packages/core/src/system/plugins/SystemExternalPlugin.js +1 -1
  31. package/dist/packages/core/src/system/plugins/SystemPluginInstallerPlugin.js +1 -1
  32. package/dist/packages/core/src/system/plugins/SystemSocialPlugin.js +1 -1
  33. package/dist/packages/core/src/system/plugins/SystemWebPlugin.js +1 -1
  34. package/dist/packages/core/src/system/plugins/SystemWorkspacePlugin.js +3 -3
  35. package/dist/packages/core/src/system/plugins/createSkill.js +1 -1
  36. package/dist/packages/core/src/system/skillExtractor.js +16 -5
  37. package/dist/packages/core/src/system/skills/executeShell.js +31 -4
  38. package/dist/packages/core/src/system/skills/forgetMemory.js +6 -4
  39. package/dist/packages/core/src/system/skills/googleWorkspace.js +106 -1
  40. package/dist/packages/core/src/system/skills/scheduleTask.js +7 -2
  41. package/dist/packages/core/src/system/skills/searchWeb.js +13 -6
  42. package/dist/packages/core/src/test_mainnet.js +45 -0
  43. package/dist/packages/core/src/utils/contextSummarizer.js +82 -0
  44. package/dist/packages/core/src/utils/skillManager.js +1 -1
  45. package/dist/packages/core/src/utils/streamSimulator.js +85 -0
  46. package/dist/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.js +28 -11
  47. package/dist/packages/core/src/web3/aggregator/providers/OpBridgeProvider.js +41 -27
  48. package/dist/packages/core/src/web3/aggregator/providers/TestnetSwapProvider.js +65 -0
  49. package/dist/packages/core/src/web3/aggregator/routeSelector.js +7 -1
  50. package/dist/packages/core/src/web3/plugins/Web3DefiPlugin.js +1 -1
  51. package/dist/packages/core/src/web3/plugins/Web3MarketPlugin.js +5 -125
  52. package/dist/packages/core/src/web3/plugins/Web3SecurityPlugin.js +1 -1
  53. package/dist/packages/core/src/web3/plugins/Web3WalletPlugin.js +1 -1
  54. package/dist/packages/core/src/web3/skills/bridgeToken.js +19 -4
  55. package/dist/packages/core/src/web3/skills/checkAddress.js +2 -0
  56. package/dist/packages/core/src/web3/skills/checkPortfolio.js +22 -2
  57. package/dist/packages/core/src/web3/skills/checkSecurity.js +2 -0
  58. package/dist/packages/core/src/web3/skills/confirmPendingTx.js +1 -1
  59. package/dist/packages/core/src/web3/skills/createLimitOrder.js +15 -1
  60. package/dist/packages/core/src/web3/skills/customTx.js +3 -0
  61. package/dist/packages/core/src/web3/skills/defiLending.js +2 -0
  62. package/dist/packages/core/src/web3/skills/getBalance.js +2 -0
  63. package/dist/packages/core/src/web3/skills/getPrice.js +134 -27
  64. package/dist/packages/core/src/web3/skills/getTxHistory.js +2 -0
  65. package/dist/packages/core/src/web3/skills/marketAnalysis.js +28 -191
  66. package/dist/packages/core/src/web3/skills/mintNft.js +3 -0
  67. package/dist/packages/core/src/web3/skills/provideLiquidity.js +16 -12
  68. package/dist/packages/core/src/web3/skills/revokeApprovals.js +2 -0
  69. package/dist/packages/core/src/web3/skills/swapToken.js +20 -2
  70. package/dist/packages/core/src/web3/skills/transfer.js +2 -0
  71. package/dist/packages/core/src/web3/skills/yieldVault.js +2 -0
  72. package/dist/packages/core/src/web3/utils/chains.js +19 -0
  73. package/dist/packages/core/src/web3/utils/marketEngine.js +26 -33
  74. package/dist/packages/signer/src/NyxoraSigner.js +181 -0
  75. package/dist/packages/signer/src/index.js +17 -0
  76. package/dist/packages/signer/src/server.js +25 -161
  77. package/launcher.ts +38 -9
  78. package/package.json +8 -3
  79. package/packages/core/package.json +21 -10
  80. package/packages/core/src/agent/bridgeWatcher.ts +3 -2
  81. package/packages/core/src/agent/cronManager.ts +2 -2
  82. package/packages/core/src/agent/llmProvider.ts +221 -0
  83. package/packages/core/src/agent/nyxDaemon.ts +104 -0
  84. package/packages/core/src/agent/osAgent.ts +381 -32
  85. package/packages/core/src/agent/reasoning.ts +376 -64
  86. package/packages/core/src/agent/reasoningScratchpad.ts +45 -0
  87. package/packages/core/src/agent/transactionManager.ts +36 -28
  88. package/packages/core/src/agent/web3Agent.ts +291 -40
  89. package/packages/core/src/cognitive/cognitiveManager.ts +65 -10
  90. package/packages/core/src/cognitive/prompts/web3/market-analysis.md +24 -0
  91. package/packages/core/src/cognitive/prompts/web3/portfolio-review.md +29 -0
  92. package/packages/core/src/cognitive/prompts/web3/risk-assessment.md +28 -0
  93. package/packages/core/src/cognitive/prompts/web3/trade-planning.md +30 -0
  94. package/packages/core/src/config/parser.ts +15 -4
  95. package/packages/core/src/gateway/chat.ts +57 -15
  96. package/packages/core/src/gateway/cli.ts +4 -0
  97. package/packages/core/src/gateway/discordAdapter.ts +87 -0
  98. package/packages/core/src/gateway/googleAuthModule.ts +2 -0
  99. package/packages/core/src/gateway/server.ts +66 -10
  100. package/packages/core/src/gateway/setup-ml.ts +68 -0
  101. package/packages/core/src/gateway/setup.ts +41 -3
  102. package/packages/core/src/gateway/telegram.ts +141 -30
  103. package/packages/core/src/memory/episodic.ts +76 -3
  104. package/packages/core/src/memory/logger.ts +142 -2
  105. package/packages/core/src/memory/promotionEngine.ts +19 -5
  106. package/packages/core/src/system/agentskills.ts +10 -0
  107. package/packages/core/src/system/plugins/GoogleWorkspacePlugin.ts +15 -3
  108. package/packages/core/src/system/plugins/SystemCorePlugin.ts +1 -1
  109. package/packages/core/src/system/plugins/SystemExternalPlugin.ts +1 -1
  110. package/packages/core/src/system/plugins/SystemPluginInstallerPlugin.ts +1 -1
  111. package/packages/core/src/system/plugins/SystemSocialPlugin.ts +1 -1
  112. package/packages/core/src/system/plugins/SystemWebPlugin.ts +1 -1
  113. package/packages/core/src/system/plugins/SystemWorkspacePlugin.ts +3 -3
  114. package/packages/core/src/system/plugins/createSkill.ts +1 -1
  115. package/packages/core/src/system/skillExtractor.ts +16 -5
  116. package/packages/core/src/system/skills/executeShell.ts +38 -7
  117. package/packages/core/src/system/skills/forgetMemory.ts +7 -4
  118. package/packages/core/src/system/skills/googleWorkspace.ts +109 -0
  119. package/packages/core/src/system/skills/scheduleTask.ts +7 -2
  120. package/packages/core/src/system/skills/searchWeb.ts +12 -6
  121. package/packages/core/src/utils/contextSummarizer.ts +100 -0
  122. package/packages/core/src/utils/skillManager.ts +1 -1
  123. package/packages/core/src/utils/streamSimulator.ts +88 -0
  124. package/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.ts +30 -11
  125. package/packages/core/src/web3/aggregator/providers/OpBridgeProvider.ts +43 -29
  126. package/packages/core/src/web3/aggregator/routeSelector.ts +6 -1
  127. package/packages/core/src/web3/plugins/Web3DefiPlugin.ts +1 -1
  128. package/packages/core/src/web3/plugins/Web3MarketPlugin.ts +6 -125
  129. package/packages/core/src/web3/plugins/Web3SecurityPlugin.ts +1 -1
  130. package/packages/core/src/web3/plugins/Web3WalletPlugin.ts +1 -1
  131. package/packages/core/src/web3/skills/bridgeToken.ts +21 -7
  132. package/packages/core/src/web3/skills/checkAddress.ts +2 -0
  133. package/packages/core/src/web3/skills/checkPortfolio.ts +23 -2
  134. package/packages/core/src/web3/skills/checkSecurity.ts +2 -0
  135. package/packages/core/src/web3/skills/confirmPendingTx.ts +1 -1
  136. package/packages/core/src/web3/skills/createLimitOrder.ts +17 -2
  137. package/packages/core/src/web3/skills/customTx.ts +3 -0
  138. package/packages/core/src/web3/skills/defiLending.ts +2 -0
  139. package/packages/core/src/web3/skills/getBalance.ts +2 -0
  140. package/packages/core/src/web3/skills/getPrice.ts +134 -30
  141. package/packages/core/src/web3/skills/getTxHistory.ts +2 -0
  142. package/packages/core/src/web3/skills/marketAnalysis.ts +45 -188
  143. package/packages/core/src/web3/skills/mintNft.ts +3 -0
  144. package/packages/core/src/web3/skills/provideLiquidity.ts +16 -10
  145. package/packages/core/src/web3/skills/revokeApprovals.ts +2 -0
  146. package/packages/core/src/web3/skills/swapToken.ts +20 -3
  147. package/packages/core/src/web3/skills/transfer.ts +2 -0
  148. package/packages/core/src/web3/skills/yieldVault.ts +2 -0
  149. package/packages/core/src/web3/utils/chains.ts +12 -0
  150. package/packages/core/src/web3/utils/marketEngine.ts +27 -33
  151. package/packages/dashboard/dist/assets/index-Czxksiao.js +18 -0
  152. package/packages/dashboard/dist/assets/index-DK2sTU47.css +1 -0
  153. package/packages/dashboard/dist/index.html +2 -2
  154. package/packages/dashboard/package.json +10 -10
  155. package/packages/mcp-server/dist/server.js +5 -1
  156. package/packages/mcp-server/package.json +6 -6
  157. package/packages/mcp-server/src/server.ts +11 -7
  158. package/packages/ml-engine/__pycache__/config.cpython-313.pyc +0 -0
  159. package/packages/ml-engine/__pycache__/main.cpython-313.pyc +0 -0
  160. package/packages/ml-engine/config.py +59 -0
  161. package/packages/ml-engine/main.py +34 -0
  162. package/packages/ml-engine/requirements.txt +22 -0
  163. package/packages/ml-engine/routers/__init__.py +1 -0
  164. package/packages/ml-engine/routers/__pycache__/__init__.cpython-313.pyc +0 -0
  165. package/packages/ml-engine/routers/__pycache__/cognitive.cpython-313.pyc +0 -0
  166. package/packages/ml-engine/routers/__pycache__/critic.cpython-313.pyc +0 -0
  167. package/packages/ml-engine/routers/__pycache__/llm.cpython-313.pyc +0 -0
  168. package/packages/ml-engine/routers/__pycache__/market.cpython-313.pyc +0 -0
  169. package/packages/ml-engine/routers/__pycache__/memory.cpython-313.pyc +0 -0
  170. package/packages/ml-engine/routers/cognitive.py +98 -0
  171. package/packages/ml-engine/routers/critic.py +111 -0
  172. package/packages/ml-engine/routers/llm.py +38 -0
  173. package/packages/ml-engine/routers/market.py +332 -0
  174. package/packages/ml-engine/routers/memory.py +125 -0
  175. package/packages/policy/package.json +3 -3
  176. package/packages/signer/package.json +16 -6
  177. package/packages/signer/src/NyxoraSigner.ts +161 -0
  178. package/packages/signer/src/index.ts +1 -0
  179. package/packages/signer/src/server.ts +25 -135
  180. package/dist/packages/core/src/agent/honchoDaemon.js +0 -91
  181. package/dist/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -54
  182. package/dist/packages/core/src/gateway/test.js +0 -15
  183. package/dist/packages/core/src/web3/Web3DefiPlugin.js +0 -28
  184. package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +0 -260
  185. package/dist/packages/core/src/web3/aggregator/aggregatorTestnet.js +0 -119
  186. package/dist/packages/core/src/web3/skills/nativeOpBridge.js +0 -71
  187. package/packages/core/src/agent/honchoDaemon.ts +0 -96
  188. package/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -41
  189. package/packages/core/src/plugin/registry.test.ts +0 -46
  190. package/packages/core/src/utils/formatter.test.ts +0 -41
  191. package/packages/dashboard/dist/assets/index-CSoNa5cx.css +0 -1
  192. package/packages/dashboard/dist/assets/index-DbRWEoSr.js +0 -16
@@ -0,0 +1,24 @@
1
+ # Market Analysis Skill
2
+
3
+ <skill_instructions>
4
+ You are now in Market Analysis Mode. Apply this structured framework before forming any market opinion:
5
+
6
+ 1. PRICE ACTION: Assess the current price vs MA50. Is it trading above (bullish) or below (bearish)? By what percentage?
7
+
8
+ 2. MOMENTUM (RSI):
9
+ - RSI > 70: Overbought — warn user, suggest caution or partial exit.
10
+ - RSI < 30: Oversold — potential entry opportunity, confirm with volume.
11
+ - RSI 40-60: Neutral — wait for a breakout signal.
12
+
13
+ 3. MULTI-EXCHANGE CONSENSUS: If momentum data comes from multiple exchanges, weight your signal by how many agreed. 3+ exchanges aligned = high conviction signal.
14
+
15
+ 4. LIQUIDITY CHECK: Low liquidity (<$50k) = high manipulation risk. Always flag this explicitly.
16
+
17
+ 5. CONFIDENCE DECLARATION: At the end of your analysis, always state:
18
+ - `Signal: [BULLISH / BEARISH / NEUTRAL]`
19
+ - `Conviction: [HIGH / MEDIUM / LOW]`
20
+ - `Reason: one sentence`
21
+
22
+ NEVER give a recommendation without stating the signal and conviction level.
23
+ NEVER fabricate price data — always call analyze_market tool first.
24
+ </skill_instructions>
@@ -0,0 +1,29 @@
1
+ # Portfolio Review Skill
2
+
3
+ <skill_instructions>
4
+ You are now in Portfolio Review Mode. Follow this structured review process:
5
+
6
+ 1. FETCH PORTFOLIO: Always call check_portfolio first. Never guess holdings.
7
+
8
+ 2. ASSET BREAKDOWN:
9
+ - List all assets with: token, quantity, USD value, % of portfolio
10
+ - Identify the top 3 positions by value
11
+
12
+ 3. CONCENTRATION ANALYSIS:
13
+ - If any single asset > 50% of portfolio: flag as OVER-CONCENTRATED. Suggest diversification.
14
+ - If all assets on 1 chain: flag as CHAIN RISK. Suggest cross-chain diversification.
15
+
16
+ 4. PERFORMANCE TRIAGE (if price change data available):
17
+ - Assets down >20% in 24h: flag for review — "is this a dip or a dump?"
18
+ - Assets up >30% in 24h: flag for partial profit-taking consideration
19
+
20
+ 5. REBALANCING SUGGESTION:
21
+ - If user asks "should I rebalance?", suggest based on their risk_level from user profile.
22
+ - Conservative: target 60% stable / 40% volatile
23
+ - Moderate: 40% stable / 60% volatile
24
+ - Aggressive: 20% stable / 80% volatile
25
+
26
+ 6. ACTIONABLE SUMMARY: End with a bullet list of max 3 concrete actions user can take now.
27
+
28
+ NEVER fabricate portfolio values. Always use live data from tools.
29
+ </skill_instructions>
@@ -0,0 +1,28 @@
1
+ # Risk Assessment Skill
2
+
3
+ <skill_instructions>
4
+ You are now in Risk Assessment Mode. Evaluate risk systematically before advising any action:
5
+
6
+ 1. LIQUIDITY RISK: Score from market data.
7
+ - Score < 3: CRITICAL — warn strongly. Token can be rugged or dumped.
8
+ - Score 3-6: MODERATE — acceptable for small positions only.
9
+ - Score > 6: LOW — standard caution applies.
10
+
11
+ 2. CONCENTRATION RISK:
12
+ - Top 10 holders > 60%: HIGH manipulation risk. Explicitly warn.
13
+ - Top 10 holders 30-60%: MODERATE. Note it.
14
+ - Top 10 holders < 30%: DISTRIBUTED. Safer.
15
+
16
+ 3. MOMENTUM RISK (RSI):
17
+ - Overbought (>70): Chasing risk. User is buying at a local top.
18
+ - Oversold (<30): Could be a falling knife. Ask if user has conviction.
19
+
20
+ 4. SECURITY CHECK: If contract address is provided, ALWAYS call check_token_security before advising entry.
21
+
22
+ 5. POSITION SIZE GUIDANCE (based on overall risk score):
23
+ - Overall < 4: "This is high-risk. Position size should be < 1% of portfolio."
24
+ - Overall 4-7: "This is moderate-risk. Max 3-5% portfolio allocation."
25
+ - Overall > 7: "This is lower-risk. Standard 5-10% allocation is reasonable."
26
+
27
+ MANDATORY: Always end risk assessment with a 1-line "Risk Verdict" before any recommendation.
28
+ </skill_instructions>
@@ -0,0 +1,30 @@
1
+ # Trade Planning Skill
2
+
3
+ <skill_instructions>
4
+ You are now in Trade Planning Mode. Before executing or recommending any trade, build an explicit plan:
5
+
6
+ 1. DEFINE THE THESIS: In one sentence, why does this trade make sense right now? (e.g., "RSI oversold + price above MA50 = momentum recovery play")
7
+
8
+ 2. ENTRY PLAN:
9
+ - Suggested entry price / range
10
+ - Entry type: market vs. limit (prefer limit for DEX to avoid slippage)
11
+
12
+ 3. EXIT PLAN (MANDATORY — never plan entry without exit):
13
+ - Take profit target: at what price? What % gain?
14
+ - Stop loss: at what price? What % drawdown is acceptable?
15
+ - Time-based exit: if no movement in X days, reassess
16
+
17
+ 4. POSITION SIZE: Based on risk profile. Never suggest more than the user's stated risk tolerance.
18
+
19
+ 5. EXECUTION SEQUENCE:
20
+ - If swap: confirm chain, token pair, slippage tolerance before calling swap_token.
21
+ - If bridge: confirm source chain, destination chain, and estimated fees.
22
+ - If limit order: use create_limit_order, not swap_token.
23
+
24
+ 6. FINAL SANITY CHECK before calling any execution tool:
25
+ - "Am I buying a honeypot?" → if contract address, call check_token_security first.
26
+ - "Is liquidity sufficient?" → check liquidityUsd from market data.
27
+ - "Am I OK with the fee?" → confirm gas estimate with user if >$5.
28
+
29
+ DO NOT execute any trade without completing steps 1-3 first.
30
+ </skill_instructions>
@@ -79,7 +79,9 @@ export function loadRpcConfig(): Record<string, string | string[]> {
79
79
  export function saveRpcConfig(rpcUrls: Record<string, string | string[]>): void {
80
80
  const rpcPath = getPath('rpc_key.yaml');
81
81
  try {
82
- fs.writeFileSync(rpcPath, yaml.stringify(rpcUrls), 'utf8');
82
+ const tempPath = rpcPath + '.tmp.' + Date.now();
83
+ fs.writeFileSync(tempPath, yaml.stringify(rpcUrls), 'utf8');
84
+ fs.renameSync(tempPath, rpcPath);
83
85
  } catch (error) {
84
86
  console.error('Failed to save rpc_key.yaml', error);
85
87
  }
@@ -141,6 +143,11 @@ export interface NyxoraConfig {
141
143
  bot_token?: string;
142
144
  authorized_chat_id?: number;
143
145
  };
146
+ discord?: {
147
+ enabled: boolean;
148
+ bot_token?: string;
149
+ client_id?: string;
150
+ };
144
151
  };
145
152
  security?: {
146
153
  dashboard_password?: string;
@@ -248,7 +255,8 @@ export function loadConfig(): NyxoraConfig {
248
255
  memory: parsed.memory || { type: 'file', path: './memory.json' },
249
256
  web3: { ...parsed.web3, rpc_urls: rpcUrls },
250
257
  integrations: parsed.integrations || {
251
- telegram: { enabled: false }
258
+ telegram: { enabled: false },
259
+ discord: { enabled: false }
252
260
  },
253
261
  security: parsed.security || { dashboard_password: '123456' },
254
262
  skills: parsed.skills
@@ -287,7 +295,8 @@ export function loadConfig(): NyxoraConfig {
287
295
  memory: { type: 'file', path: './memory.json' },
288
296
  web3: { rpc_urls: rpcUrls },
289
297
  integrations: {
290
- telegram: { enabled: false }
298
+ telegram: { enabled: false },
299
+ discord: { enabled: false }
291
300
  }
292
301
  };
293
302
 
@@ -310,7 +319,9 @@ export function saveConfig(newConfig: NyxoraConfig): void {
310
319
  // Keys are no longer encrypted before saving. They are stored in plain text.
311
320
 
312
321
  const yamlStr = yaml.stringify(configToSave);
313
- fs.writeFileSync(configPath, yamlStr, 'utf8');
322
+ const tempPath = configPath + '.tmp.' + Date.now();
323
+ fs.writeFileSync(tempPath, yamlStr, 'utf8');
324
+ fs.renameSync(tempPath, configPath);
314
325
  } catch (error) {
315
326
  console.error('Failed to save config.yaml', error);
316
327
  }
@@ -49,13 +49,15 @@ export async function chatInteractive() {
49
49
  s.start('Thinking...');
50
50
 
51
51
  try {
52
- const response = await fetch('http://localhost:3000/api/chat', {
53
- method: 'POST',
54
- headers: {
55
- 'Content-Type': 'application/json',
56
- 'x-nyxora-token': token,
57
- },
58
- body: JSON.stringify({ message: messageStr, session_id: 'cli-chat' })
52
+ // Use SSE streaming endpoint for real-time token output
53
+ const params = new URLSearchParams({
54
+ message: messageStr,
55
+ session_id: 'cli-chat',
56
+ token,
57
+ });
58
+
59
+ const response = await fetch(`http://localhost:3000/api/chat/stream?${params}`, {
60
+ headers: { 'x-nyxora-token': token },
59
61
  });
60
62
 
61
63
  if (!response.ok) {
@@ -69,14 +71,54 @@ export async function chatInteractive() {
69
71
  continue;
70
72
  }
71
73
 
72
- const data = await response.json();
73
- s.stop(pc.green('Nyxora:'));
74
-
75
- let finalReply = data.response || '';
76
- // Strip <think> tags for clean UI
77
- finalReply = finalReply.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
78
-
79
- console.log(finalReply + '\n');
74
+ let firstChunk = true;
75
+ let finalReply = '';
76
+
77
+ // Read SSE stream line by line
78
+ const reader = response.body?.getReader();
79
+ const decoder = new TextDecoder();
80
+
81
+ if (reader) {
82
+ let buffer = '';
83
+ while (true) {
84
+ const { done, value } = await reader.read();
85
+ if (done) break;
86
+ buffer += decoder.decode(value, { stream: true });
87
+ const lines = buffer.split('\n');
88
+ buffer = lines.pop() || '';
89
+ for (const line of lines) {
90
+ if (!line.startsWith('data: ')) continue;
91
+ const raw = line.slice(6).trim();
92
+ if (raw === '[DONE]') break;
93
+ try {
94
+ const data = JSON.parse(raw);
95
+ if (data.progress) {
96
+ // Show tool progress on same line, overwriting previous
97
+ if (firstChunk) {
98
+ s.stop(pc.cyan('Nyxora:'));
99
+ firstChunk = false;
100
+ }
101
+ process.stdout.write(`\r${pc.italic(pc.gray(data.progress))} `);
102
+ }
103
+ if (data.chunk) {
104
+ if (firstChunk) {
105
+ s.stop(pc.green('Nyxora:'));
106
+ process.stdout.write('\n');
107
+ firstChunk = false;
108
+ }
109
+ // Clear progress line if any, then write chunk
110
+ finalReply += data.chunk;
111
+ process.stdout.write(data.chunk);
112
+ }
113
+ } catch {}
114
+ }
115
+ }
116
+ }
117
+
118
+ // Newline after streaming completes
119
+ if (!firstChunk) process.stdout.write('\n\n');
120
+
121
+
80
122
 
81
123
  // Check for pending transactions
82
124
  try {
@@ -3,6 +3,10 @@
3
3
  import { initSafeLogger } from '../utils/safeLogger';
4
4
  initSafeLogger();
5
5
 
6
+ import dns from 'dns';
7
+ // Fix Node 18+ native fetch randomly failing on dual-stack VPS (IPv6 issues)
8
+ dns.setDefaultResultOrder('ipv4first');
9
+
6
10
  import fs from 'fs';
7
11
  import path from 'path';
8
12
  import os from 'os';
@@ -0,0 +1,87 @@
1
+ import { Client, GatewayIntentBits, Message, TextChannel } from 'discord.js';
2
+ import { processUserInput } from '../agent/reasoning';
3
+ import { loadConfig } from '../config/parser';
4
+
5
+ let discordClient: Client | null = null;
6
+
7
+ export function startDiscordBot() {
8
+ const config = loadConfig();
9
+ const token = config.integrations?.discord?.bot_token;
10
+
11
+ if (!token || !config.integrations?.discord?.enabled) {
12
+ console.log('[Discord] Bot is disabled or missing bot_token in config.yaml.');
13
+ return;
14
+ }
15
+
16
+ discordClient = new Client({
17
+ intents: [
18
+ GatewayIntentBits.Guilds,
19
+ GatewayIntentBits.GuildMessages,
20
+ GatewayIntentBits.MessageContent,
21
+ ],
22
+ });
23
+
24
+ discordClient.once('ready', () => {
25
+ console.log(`🤖 Discord Bot is online and logged in as ${discordClient?.user?.tag}`);
26
+ });
27
+
28
+ discordClient.on('messageCreate', async (message: Message) => {
29
+ // Ignore messages from bots
30
+ if (message.author.bot) return;
31
+
32
+ // Check if the bot is mentioned
33
+ if (discordClient?.user && message.mentions.has(discordClient.user)) {
34
+ // Strip the mention from the message
35
+ const prompt = message.content.replace(new RegExp(`<@!?${discordClient.user.id}>`, 'g'), '').trim();
36
+
37
+ if (!prompt) return;
38
+
39
+ console.log(`[Discord] Received mention from ${message.author.username} in #${(message.channel as TextChannel).name}: ${prompt}`);
40
+
41
+ // Send initial thinking indicator
42
+ let replyMessage: Message | null = null;
43
+ try {
44
+ replyMessage = await message.reply('⏳ *Thinking...*');
45
+ } catch (err) {
46
+ console.error('[Discord] Failed to send initial reply', err);
47
+ return;
48
+ }
49
+
50
+ const onProgress = async (progressText: string) => {
51
+ try {
52
+ if (replyMessage) {
53
+ await replyMessage.edit(`*${progressText}*`);
54
+ }
55
+ } catch (err) {
56
+ // Ignore minor edit errors
57
+ }
58
+ };
59
+
60
+ try {
61
+ const sessionId = `discord_${message.author.id}`;
62
+ const response = await processUserInput(prompt, 'user', onProgress, sessionId);
63
+
64
+ // Clean up thought blocks if any
65
+ let finalResponse = response.replace(/<thought>[\s\S]*?<\/thought>\n?/g, '').replace(/<think>[\s\S]*?<\/think>\n?/g, '');
66
+
67
+ if (replyMessage) {
68
+ // Discord has a 2000 character limit per message
69
+ if (finalResponse.length > 2000) {
70
+ await replyMessage.edit(finalResponse.slice(0, 1997) + '...');
71
+ } else {
72
+ await replyMessage.edit(finalResponse);
73
+ }
74
+ }
75
+ } catch (error: any) {
76
+ console.error('[Discord] Error processing message:', error);
77
+ if (replyMessage) {
78
+ await replyMessage.edit('❌ Sorry, I encountered an error while processing your request.');
79
+ }
80
+ }
81
+ }
82
+ });
83
+
84
+ discordClient.login(token).catch(err => {
85
+ console.error('[Discord] Failed to login:', err);
86
+ });
87
+ }
@@ -7,7 +7,9 @@ const FALLBACK_TOKEN_PATH = getPath('google-tokens.json');
7
7
 
8
8
  const SCOPES = [
9
9
  'https://www.googleapis.com/auth/gmail.readonly',
10
+ 'https://www.googleapis.com/auth/gmail.send',
10
11
  'https://www.googleapis.com/auth/calendar.readonly',
12
+ 'https://www.googleapis.com/auth/calendar.events',
11
13
  'https://www.googleapis.com/auth/documents.readonly',
12
14
  'https://www.googleapis.com/auth/spreadsheets',
13
15
  'https://www.googleapis.com/auth/forms.responses.readonly',
@@ -27,7 +27,7 @@ import { validateToken, getSessionToken } from '../utils/state';
27
27
  import { initWebSocket } from './WebSocketManager';
28
28
  import fs from 'fs';
29
29
  import yaml from 'yaml';
30
- import { processUserInput, logger } from '../agent/reasoning';
30
+ import { processUserInput, processUserInputStream, logger } from '../agent/reasoning';
31
31
  import { loadConfig, saveConfig, loadRpcConfig, saveRpcConfig } from '../config/parser';
32
32
  import { loadDefiKeys, saveDefiKeys } from '../config/defiConfigManager';
33
33
  import { loadMarketKeys, saveMarketKeys } from '../config/marketConfigManager';
@@ -52,6 +52,7 @@ import { executeCustomTx } from '../web3/skills/customTx';
52
52
  import { executeApprove, executeAaveSupply, executeVaultDeposit, executeUniv3Mint } from '../web3/skills/executeDefi';
53
53
  import { executeRevokeApproval } from '../web3/skills/revokeApprovals';
54
54
  import { startTelegramBot } from './telegram';
55
+ import { startDiscordBot } from './discordAdapter';
55
56
  import { startBridgeWatcher } from '../agent/bridgeWatcher';
56
57
  import { eventListener } from '../web3/eventListener';
57
58
  import { formatTransactionSuccess, formatTransactionError } from '../utils/formatter';
@@ -60,17 +61,20 @@ import { generatePrivacyPolicyHtml, generateTosHtml } from './legalGenerator';
60
61
  import { episodicDB } from '../memory/episodic';
61
62
  import { ReflectionEngine } from '../memory/reflection';
62
63
 
63
- import { honchoDaemon } from '../agent/honchoDaemon';
64
+ import { nyxDaemon } from '../agent/nyxDaemon';
64
65
 
65
66
  // Initialize Google Auth
66
67
  initGoogleAuth();
67
68
 
68
- // Start Background Honcho Daemon
69
- honchoDaemon.start();
69
+ // Start Background Nyx Daemon
70
+ nyxDaemon.start();
70
71
 
71
72
  // Synchronize all active skills to config.yaml on startup
72
73
  syncAllSkillsToConfig();
73
74
 
75
+ // Start messaging adapters
76
+ startDiscordBot();
77
+
74
78
  import util from 'util';
75
79
 
76
80
  // Intercept console.log and console.error
@@ -129,7 +133,7 @@ app.use('/api', (req, res, next) => {
129
133
  return next();
130
134
  }
131
135
 
132
- const token = req.headers['x-nyxora-token'] as string;
136
+ const token = (req.headers['x-nyxora-token'] as string) || (req.query.token as string);
133
137
  const validation = validateToken(token);
134
138
 
135
139
  if (!validation.valid) {
@@ -198,7 +202,9 @@ app.post('/api/upload-google-credentials', (req, res) => {
198
202
  // The format needs to wrap it in "web" or "installed"
199
203
  const finalPayload = credentials.client_id ? { installed: credentials } : credentials;
200
204
 
201
- fs.writeFileSync(credsPath, JSON.stringify(finalPayload, null, 2));
205
+ const tempPath = credsPath + '.tmp.' + Date.now();
206
+ fs.writeFileSync(tempPath, JSON.stringify(finalPayload, null, 2));
207
+ fs.renameSync(tempPath, credsPath);
202
208
 
203
209
  // Re-initialize google auth module
204
210
  initGoogleAuth();
@@ -672,7 +678,7 @@ app.post('/api/transactions/:id/approve', async (req, res) => {
672
678
  res.json({ success: true, status: 'processing', message: 'Transaction submitted to background processing.' });
673
679
 
674
680
  // Execute in background
675
- (async () => {
681
+ const txPromise = (async () => {
676
682
  try {
677
683
  let result = '';
678
684
  if (tx.type === 'transfer') {
@@ -744,6 +750,8 @@ app.post('/api/transactions/:id/approve', async (req, res) => {
744
750
  logger.addEntry({ role: 'assistant', content: `❌ **Transaction Failed**\n\n${err.message}` }, sessionId);
745
751
  }
746
752
  })();
753
+
754
+ txManager.trackPromise(txPromise);
747
755
  } catch (err: any) {
748
756
  txManager.updateStatus(req.params.id, 'failed', err.message);
749
757
  res.status(500).json({ error: err.message });
@@ -1049,7 +1057,51 @@ app.post('/api/chat', async (req, res) => {
1049
1057
  }
1050
1058
  });
1051
1059
 
1052
- // --- Memory API Endpoints ---
1060
+ // --- Streaming Chat Endpoint (SSE) ---
1061
+ // Sends LLM tokens to the client as they arrive via Server-Sent Events.
1062
+ // The old /api/chat endpoint remains untouched for backward compatibility.
1063
+ app.get('/api/chat/stream', async (req, res) => {
1064
+ const { message, session_id } = req.query as Record<string, string>;
1065
+ if (!message) {
1066
+ res.status(400).json({ error: 'Message is required' });
1067
+ return;
1068
+ }
1069
+
1070
+ // Setup SSE headers
1071
+ res.setHeader('Content-Type', 'text/event-stream');
1072
+ res.setHeader('Cache-Control', 'no-cache');
1073
+ res.setHeader('Connection', 'keep-alive');
1074
+ res.setHeader('X-Accel-Buffering', 'no'); // Disable Nginx buffering
1075
+ res.flushHeaders();
1076
+
1077
+ const sendEvent = (data: object) => {
1078
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
1079
+ };
1080
+
1081
+ const onChunk = (text: string) => sendEvent({ chunk: text });
1082
+ const onProgress = (msg: string) => sendEvent({ progress: msg });
1083
+
1084
+ try {
1085
+ await processUserInputStream(message, onChunk, onProgress, session_id);
1086
+ // Trigger memory mechanisms after response completes
1087
+ resetIdleTimer(session_id);
1088
+ messageCounter++;
1089
+ if (messageCounter >= 5) {
1090
+ messageCounter = 0;
1091
+ ReflectionEngine.runReflection(session_id).then(() => {
1092
+ const { PromotionEngine } = require('../memory/promotionEngine');
1093
+ PromotionEngine.runPromotionAndDecay();
1094
+ }).catch(console.error);
1095
+ }
1096
+ } catch (err: any) {
1097
+ sendEvent({ error: err.message });
1098
+ } finally {
1099
+ res.write('data: [DONE]\n\n');
1100
+ res.end();
1101
+ }
1102
+ });
1103
+
1104
+
1053
1105
  app.get('/api/memory', (req, res) => {
1054
1106
  try {
1055
1107
  const memories = episodicDB.getMemories();
@@ -1062,9 +1114,10 @@ app.get('/api/memory', (req, res) => {
1062
1114
  app.delete('/api/memory/all', (req, res) => {
1063
1115
  try {
1064
1116
  episodicDB.clearAllMemories();
1117
+ episodicDB.clearAllPersonas();
1065
1118
  const { PromotionEngine } = require('../memory/promotionEngine');
1066
1119
  PromotionEngine.runPromotionAndDecay();
1067
- res.json({ success: true, message: "Episodic memory wiped completely." });
1120
+ res.json({ success: true, message: "Episodic memory and persona traits wiped completely." });
1068
1121
  } catch (error: any) {
1069
1122
  res.status(500).json({ error: error.message });
1070
1123
  }
@@ -1241,11 +1294,14 @@ export async function startServer() {
1241
1294
  });
1242
1295
 
1243
1296
  let isShuttingDown = false;
1244
- const gracefulShutdown = () => {
1297
+ const gracefulShutdown = async () => {
1245
1298
  if (isShuttingDown) return;
1246
1299
  isShuttingDown = true;
1247
1300
  console.log('[Nyxora Gateway] Received shutdown signal. Closing server...');
1248
1301
 
1302
+ // Wait for active transactions
1303
+ await txManager.waitForAll(10000);
1304
+
1249
1305
  if (server.closeAllConnections) {
1250
1306
  server.closeAllConnections();
1251
1307
  }
@@ -0,0 +1,68 @@
1
+ import { spawn } from 'child_process';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import os from 'os';
5
+ import pc from 'picocolors';
6
+ import { spinner, note, outro } from '@clack/prompts';
7
+
8
+ const appDir = path.join(os.homedir(), '.nyxora');
9
+ const mlEngineDir = path.join(appDir, 'ml-engine');
10
+ const venvDir = path.join(mlEngineDir, 'venv');
11
+ const sourceReqPath = path.join(process.cwd(), 'packages', 'ml-engine', 'requirements.txt');
12
+
13
+ function runCommand(command: string, args: string[], cwd: string): Promise<void> {
14
+ return new Promise((resolve, reject) => {
15
+ const child = spawn(command, args, { cwd, stdio: 'ignore' });
16
+ child.on('close', (code) => {
17
+ if (code === 0) resolve();
18
+ else reject(new Error(`Command ${command} ${args.join(' ')} failed with code ${code}`));
19
+ });
20
+ child.on('error', reject);
21
+ });
22
+ }
23
+
24
+ export async function setupMlEngine() {
25
+ console.log(pc.cyan('\n⚙️ Initializing Universal Python ML Engine...\n'));
26
+ const s = spinner();
27
+
28
+ try {
29
+ if (!fs.existsSync(mlEngineDir)) {
30
+ fs.mkdirSync(mlEngineDir, { recursive: true });
31
+ }
32
+
33
+ s.start('Creating Python Virtual Environment (venv)...');
34
+ if (!fs.existsSync(venvDir)) {
35
+ await runCommand('python3', ['-m', 'venv', venvDir], mlEngineDir);
36
+ }
37
+ s.stop(pc.green('Virtual Environment created successfully.'));
38
+
39
+ s.start('Installing heavy Data Science & AI dependencies (this may take a while)...');
40
+ const pipPath = path.join(venvDir, 'bin', 'pip');
41
+
42
+ // Upgrade pip
43
+ await runCommand(pipPath, ['install', '--upgrade', 'pip'], mlEngineDir);
44
+
45
+ // Install requirements
46
+ if (fs.existsSync(sourceReqPath)) {
47
+ await runCommand(pipPath, ['install', '-r', sourceReqPath], mlEngineDir);
48
+ s.stop(pc.green('Dependencies installed successfully.'));
49
+ } else {
50
+ s.stop(pc.yellow('requirements.txt not found. Skipping dependency installation.'));
51
+ }
52
+
53
+ note(
54
+ 'The Python Sidecar is ready.\nIt will automatically start in the background when you run `nyxora start`.',
55
+ 'ML Engine Configured'
56
+ );
57
+
58
+ } catch (error: any) {
59
+ s.stop(pc.red('Failed to setup ML Engine.'));
60
+ console.error(pc.red(`Error: ${error.message}`));
61
+ console.log(pc.yellow('\nPlease ensure python3 and python3-venv are installed on your system.'));
62
+ }
63
+ }
64
+
65
+ // Allow running directly
66
+ if (require.main === module) {
67
+ setupMlEngine().then(() => process.exit(0));
68
+ }
@@ -25,6 +25,28 @@ export async function runSetupWizard() {
25
25
  console.log(pc.cyan(logo));
26
26
  intro(pc.inverse(' Nyxora CLI Setup '));
27
27
 
28
+ try {
29
+ const nodeVersion = parseInt(process.versions.node.split('.')[0], 10);
30
+ if (nodeVersion < 18) {
31
+ console.error(pc.red(`\n❌ Unsupported Node.js version. Nyxora requires Node.js 18 or higher. You are running v${process.versions.node}`));
32
+ process.exit(1);
33
+ }
34
+
35
+ const { execSync } = require('child_process');
36
+ const pythonCmd = process.platform === 'win32' ? 'python' : 'python3';
37
+ const pyVersionStr = execSync(`${pythonCmd} -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
38
+ const [major, minor] = pyVersionStr.split('.').map(Number);
39
+
40
+ if (major < 3 || (major === 3 && minor < 10)) {
41
+ console.error(pc.red(`\n❌ Unsupported Python version. Nyxora ML Engine requires Python 3.10+. You are running v${pyVersionStr}`));
42
+ process.exit(1);
43
+ }
44
+ note(`Node.js: v${process.versions.node}\nPython: v${pyVersionStr}`, 'System Requirements Met');
45
+ } catch (error) {
46
+ console.error(pc.red(`\n❌ Python 3 is not installed or not in your PATH. Nyxora requires Python 3.10+ for the ML Engine.`));
47
+ process.exit(1);
48
+ }
49
+
28
50
  const appDir = getAppDir();
29
51
  const config = loadConfig();
30
52
 
@@ -32,8 +54,8 @@ export async function runSetupWizard() {
32
54
  `Nyxora is a Web3 Assistant that operates with full access under your control.
33
55
 
34
56
  Critical Precautions:
35
- - Your Private Key is the lifeblood of your assets. NEVER copy or share the keystore.json file.
36
- - Any instructions you provide via Telegram or Dashboard can trigger on-chain transactions.
57
+ - Your Private Key is the lifeblood of your assets. NEVER copy or share your vault.key file or OS Keyring password.
58
+ - Any instructions you provide via Telegram, Discord, or the Dashboard can trigger on-chain transactions.
37
59
  - It is recommended to use a smart AI model for maximum accuracy.
38
60
 
39
61
  By using Nyxora, you retain full control over your own keys.`;
@@ -311,6 +333,7 @@ Provider: ${config.llm.provider}`;
311
333
  message: '💬 Select Integration Channels to enable:',
312
334
  options: [
313
335
  { value: 'telegram', label: 'Telegram Bot', hint: 'Requires Token' },
336
+ { value: 'discord', label: 'Discord Bot', hint: 'Requires Token' },
314
337
  { value: 'dashboard', label: 'Local Web Dashboard', hint: 'enabled by default' },
315
338
  ],
316
339
  initialValues: ['dashboard'],
@@ -417,6 +440,15 @@ Provider: ${config.llm.provider}`;
417
440
  }
418
441
  }
419
442
 
443
+ const setupDiscord = activeChannels.includes('discord');
444
+ let discordToken = '';
445
+ if (setupDiscord) {
446
+ discordToken = (await password({
447
+ message: 'Enter Discord Bot Token (Leave empty if already set):',
448
+ })) as string;
449
+ if (isCancel(discordToken)) return process.exit(0);
450
+ }
451
+
420
452
 
421
453
 
422
454
  // --- SAVING ---
@@ -470,6 +502,12 @@ Provider: ${config.llm.provider}`;
470
502
  delete config.integrations.telegram.authorized_chat_id;
471
503
  }
472
504
 
505
+ if (!config.integrations.discord) config.integrations.discord = { enabled: false };
506
+ config.integrations.discord.enabled = setupDiscord as boolean;
507
+ if (setupDiscord && discordToken) {
508
+ config.integrations.discord.bot_token = discordToken as string;
509
+ }
510
+
473
511
  saveConfig(config);
474
512
 
475
513
 
@@ -527,7 +565,7 @@ Provider: ${config.llm.provider}`;
527
565
  getBalance: 'get_balance',
528
566
  getMyAddress: 'get_my_address',
529
567
  checkPortfolio: 'check_portfolio',
530
- getPrice: 'get_price',
568
+ getPrice: 'get_price_and_fiat_value',
531
569
  marketAnalysis: 'analyze_market',
532
570
  getTxHistory: 'get_tx_history',
533
571
  checkSecurity: 'check_token_security',