nyxora 26.7.2 → 26.7.3

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 (167) hide show
  1. package/CHANGELOG.md +95 -47
  2. package/README.md +23 -19
  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 +242 -0
  8. package/dist/packages/core/src/agent/nyxDaemon.js +97 -0
  9. package/dist/packages/core/src/agent/osAgent.js +203 -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/server.js +58 -9
  20. package/dist/packages/core/src/gateway/setup-ml.js +64 -0
  21. package/dist/packages/core/src/gateway/setup.js +39 -3
  22. package/dist/packages/core/src/gateway/telegram.js +120 -24
  23. package/dist/packages/core/src/memory/episodic.js +47 -3
  24. package/dist/packages/core/src/memory/logger.js +120 -2
  25. package/dist/packages/core/src/memory/promotionEngine.js +18 -5
  26. package/dist/packages/core/src/system/agentskills.js +10 -0
  27. package/dist/packages/core/src/system/plugins/GoogleWorkspacePlugin.js +1 -1
  28. package/dist/packages/core/src/system/plugins/SystemCorePlugin.js +1 -1
  29. package/dist/packages/core/src/system/plugins/SystemExternalPlugin.js +1 -1
  30. package/dist/packages/core/src/system/plugins/SystemPluginInstallerPlugin.js +1 -1
  31. package/dist/packages/core/src/system/plugins/SystemSocialPlugin.js +1 -1
  32. package/dist/packages/core/src/system/plugins/SystemWebPlugin.js +1 -1
  33. package/dist/packages/core/src/system/plugins/SystemWorkspacePlugin.js +3 -3
  34. package/dist/packages/core/src/system/plugins/createSkill.js +1 -1
  35. package/dist/packages/core/src/system/skillExtractor.js +16 -5
  36. package/dist/packages/core/src/system/skills/forgetMemory.js +6 -4
  37. package/dist/packages/core/src/system/skills/scheduleTask.js +7 -2
  38. package/dist/packages/core/src/test_mainnet.js +45 -0
  39. package/dist/packages/core/src/utils/contextSummarizer.js +82 -0
  40. package/dist/packages/core/src/utils/skillManager.js +1 -1
  41. package/dist/packages/core/src/utils/streamSimulator.js +85 -0
  42. package/dist/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.js +28 -11
  43. package/dist/packages/core/src/web3/aggregator/providers/OpBridgeProvider.js +41 -27
  44. package/dist/packages/core/src/web3/aggregator/providers/TestnetSwapProvider.js +65 -0
  45. package/dist/packages/core/src/web3/aggregator/routeSelector.js +7 -1
  46. package/dist/packages/core/src/web3/plugins/Web3DefiPlugin.js +1 -1
  47. package/dist/packages/core/src/web3/plugins/Web3MarketPlugin.js +5 -125
  48. package/dist/packages/core/src/web3/plugins/Web3SecurityPlugin.js +1 -1
  49. package/dist/packages/core/src/web3/plugins/Web3WalletPlugin.js +1 -1
  50. package/dist/packages/core/src/web3/skills/bridgeToken.js +19 -4
  51. package/dist/packages/core/src/web3/skills/checkAddress.js +2 -0
  52. package/dist/packages/core/src/web3/skills/checkPortfolio.js +22 -2
  53. package/dist/packages/core/src/web3/skills/checkSecurity.js +2 -0
  54. package/dist/packages/core/src/web3/skills/confirmPendingTx.js +1 -1
  55. package/dist/packages/core/src/web3/skills/createLimitOrder.js +15 -1
  56. package/dist/packages/core/src/web3/skills/customTx.js +3 -0
  57. package/dist/packages/core/src/web3/skills/defiLending.js +2 -0
  58. package/dist/packages/core/src/web3/skills/getBalance.js +2 -0
  59. package/dist/packages/core/src/web3/skills/getPrice.js +134 -27
  60. package/dist/packages/core/src/web3/skills/getTxHistory.js +2 -0
  61. package/dist/packages/core/src/web3/skills/marketAnalysis.js +28 -191
  62. package/dist/packages/core/src/web3/skills/mintNft.js +3 -0
  63. package/dist/packages/core/src/web3/skills/provideLiquidity.js +16 -12
  64. package/dist/packages/core/src/web3/skills/revokeApprovals.js +2 -0
  65. package/dist/packages/core/src/web3/skills/swapToken.js +20 -2
  66. package/dist/packages/core/src/web3/skills/transfer.js +2 -0
  67. package/dist/packages/core/src/web3/skills/yieldVault.js +2 -0
  68. package/dist/packages/core/src/web3/utils/chains.js +19 -0
  69. package/dist/packages/core/src/web3/utils/marketEngine.js +26 -33
  70. package/dist/packages/signer/src/NyxoraSigner.js +181 -0
  71. package/dist/packages/signer/src/index.js +17 -0
  72. package/dist/packages/signer/src/server.js +25 -161
  73. package/launcher.ts +38 -9
  74. package/package.json +6 -2
  75. package/packages/core/package.json +21 -10
  76. package/packages/core/src/agent/bridgeWatcher.ts +3 -2
  77. package/packages/core/src/agent/cronManager.ts +2 -2
  78. package/packages/core/src/agent/llmProvider.ts +219 -0
  79. package/packages/core/src/agent/nyxDaemon.ts +104 -0
  80. package/packages/core/src/agent/osAgent.ts +230 -32
  81. package/packages/core/src/agent/reasoning.ts +376 -64
  82. package/packages/core/src/agent/reasoningScratchpad.ts +45 -0
  83. package/packages/core/src/agent/transactionManager.ts +36 -28
  84. package/packages/core/src/agent/web3Agent.ts +291 -40
  85. package/packages/core/src/cognitive/cognitiveManager.ts +65 -10
  86. package/packages/core/src/cognitive/prompts/web3/market-analysis.md +24 -0
  87. package/packages/core/src/cognitive/prompts/web3/portfolio-review.md +29 -0
  88. package/packages/core/src/cognitive/prompts/web3/risk-assessment.md +28 -0
  89. package/packages/core/src/cognitive/prompts/web3/trade-planning.md +30 -0
  90. package/packages/core/src/config/parser.ts +15 -4
  91. package/packages/core/src/gateway/chat.ts +57 -15
  92. package/packages/core/src/gateway/cli.ts +4 -0
  93. package/packages/core/src/gateway/discordAdapter.ts +87 -0
  94. package/packages/core/src/gateway/server.ts +66 -10
  95. package/packages/core/src/gateway/setup-ml.ts +68 -0
  96. package/packages/core/src/gateway/setup.ts +41 -3
  97. package/packages/core/src/gateway/telegram.ts +138 -27
  98. package/packages/core/src/memory/episodic.ts +64 -3
  99. package/packages/core/src/memory/logger.ts +142 -2
  100. package/packages/core/src/memory/promotionEngine.ts +19 -5
  101. package/packages/core/src/system/agentskills.ts +10 -0
  102. package/packages/core/src/system/plugins/GoogleWorkspacePlugin.ts +1 -1
  103. package/packages/core/src/system/plugins/SystemCorePlugin.ts +1 -1
  104. package/packages/core/src/system/plugins/SystemExternalPlugin.ts +1 -1
  105. package/packages/core/src/system/plugins/SystemPluginInstallerPlugin.ts +1 -1
  106. package/packages/core/src/system/plugins/SystemSocialPlugin.ts +1 -1
  107. package/packages/core/src/system/plugins/SystemWebPlugin.ts +1 -1
  108. package/packages/core/src/system/plugins/SystemWorkspacePlugin.ts +3 -3
  109. package/packages/core/src/system/plugins/createSkill.ts +1 -1
  110. package/packages/core/src/system/skillExtractor.ts +16 -5
  111. package/packages/core/src/system/skills/forgetMemory.ts +7 -4
  112. package/packages/core/src/system/skills/scheduleTask.ts +7 -2
  113. package/packages/core/src/utils/contextSummarizer.ts +100 -0
  114. package/packages/core/src/utils/skillManager.ts +1 -1
  115. package/packages/core/src/utils/streamSimulator.ts +88 -0
  116. package/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.ts +30 -11
  117. package/packages/core/src/web3/aggregator/providers/OpBridgeProvider.ts +43 -29
  118. package/packages/core/src/web3/aggregator/routeSelector.ts +6 -1
  119. package/packages/core/src/web3/plugins/Web3DefiPlugin.ts +1 -1
  120. package/packages/core/src/web3/plugins/Web3MarketPlugin.ts +6 -125
  121. package/packages/core/src/web3/plugins/Web3SecurityPlugin.ts +1 -1
  122. package/packages/core/src/web3/plugins/Web3WalletPlugin.ts +1 -1
  123. package/packages/core/src/web3/skills/bridgeToken.ts +21 -7
  124. package/packages/core/src/web3/skills/checkAddress.ts +2 -0
  125. package/packages/core/src/web3/skills/checkPortfolio.ts +23 -2
  126. package/packages/core/src/web3/skills/checkSecurity.ts +2 -0
  127. package/packages/core/src/web3/skills/confirmPendingTx.ts +1 -1
  128. package/packages/core/src/web3/skills/createLimitOrder.ts +17 -2
  129. package/packages/core/src/web3/skills/customTx.ts +3 -0
  130. package/packages/core/src/web3/skills/defiLending.ts +2 -0
  131. package/packages/core/src/web3/skills/getBalance.ts +2 -0
  132. package/packages/core/src/web3/skills/getPrice.ts +134 -30
  133. package/packages/core/src/web3/skills/getTxHistory.ts +2 -0
  134. package/packages/core/src/web3/skills/marketAnalysis.ts +45 -188
  135. package/packages/core/src/web3/skills/mintNft.ts +3 -0
  136. package/packages/core/src/web3/skills/provideLiquidity.ts +16 -10
  137. package/packages/core/src/web3/skills/revokeApprovals.ts +2 -0
  138. package/packages/core/src/web3/skills/swapToken.ts +20 -3
  139. package/packages/core/src/web3/skills/transfer.ts +2 -0
  140. package/packages/core/src/web3/skills/yieldVault.ts +2 -0
  141. package/packages/core/src/web3/utils/chains.ts +12 -0
  142. package/packages/core/src/web3/utils/marketEngine.ts +27 -33
  143. package/packages/dashboard/dist/assets/index-BTfp141V.js +18 -0
  144. package/packages/dashboard/dist/assets/index-DK2sTU47.css +1 -0
  145. package/packages/dashboard/dist/index.html +2 -2
  146. package/packages/dashboard/package.json +10 -10
  147. package/packages/mcp-server/dist/server.js +5 -1
  148. package/packages/mcp-server/package.json +6 -6
  149. package/packages/mcp-server/src/server.ts +11 -7
  150. package/packages/policy/package.json +3 -3
  151. package/packages/signer/package.json +16 -6
  152. package/packages/signer/src/NyxoraSigner.ts +161 -0
  153. package/packages/signer/src/index.ts +1 -0
  154. package/packages/signer/src/server.ts +25 -135
  155. package/dist/packages/core/src/agent/honchoDaemon.js +0 -91
  156. package/dist/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -54
  157. package/dist/packages/core/src/gateway/test.js +0 -15
  158. package/dist/packages/core/src/web3/Web3DefiPlugin.js +0 -28
  159. package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +0 -260
  160. package/dist/packages/core/src/web3/aggregator/aggregatorTestnet.js +0 -119
  161. package/dist/packages/core/src/web3/skills/nativeOpBridge.js +0 -71
  162. package/packages/core/src/agent/honchoDaemon.ts +0 -96
  163. package/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -41
  164. package/packages/core/src/plugin/registry.test.ts +0 -46
  165. package/packages/core/src/utils/formatter.test.ts +0 -41
  166. package/packages/dashboard/dist/assets/index-CSoNa5cx.css +0 -1
  167. package/packages/dashboard/dist/assets/index-DbRWEoSr.js +0 -16
@@ -1,7 +1,7 @@
1
1
  import { Bot, InlineKeyboard } from 'grammy';
2
2
  import { run } from '@grammyjs/runner';
3
3
  import { apiThrottler } from '@grammyjs/transformer-throttler';
4
- import { processUserInput, logger } from '../agent/reasoning';
4
+ import { processUserInput, processUserInputStream, logger } from '../agent/reasoning';
5
5
  import { loadConfig, saveConfig } from '../config/parser';
6
6
  import { txManager } from '../agent/transactionManager';
7
7
  import { executeTransfer } from '../web3/skills/transfer';
@@ -29,17 +29,23 @@ export function formatToTelegramHTML(text: string): string {
29
29
  .replace(/</g, '&lt;')
30
30
  .replace(/>/g, '&gt;');
31
31
 
32
- html = html.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>');
33
- html = html.replace(/(?<!^|\n)\*(?!\s)(.*?)(?<!\s)\*/g, '<i>$1</i>');
34
- html = html.replace(/_(.*?)_/g, '<i>$1</i>');
35
-
36
- html = html.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
37
- html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
38
-
39
32
  html = html.replace(/&lt;thought&gt;[\s\S]*?&lt;\/thought&gt;\n?/g, '');
40
33
  html = html.replace(/<thought>[\s\S]*?<\/thought>\n?/g, '');
41
34
  html = html.replace(/&lt;think&gt;[\s\S]*?&lt;\/think&gt;\n?/g, '');
42
35
  html = html.replace(/<think>[\s\S]*?<\/think>\n?/g, '');
36
+
37
+ html = html.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
38
+ html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
39
+
40
+ html = html.replace(/\*\*(.*?)\*\*/g, (match, p1) => {
41
+ if (p1.includes('<pre>') || p1.includes('<code>')) return match;
42
+ return `<b>${p1}</b>`;
43
+ });
44
+
45
+ html = html.replace(/(?<!^|\n)\*(?!\s)(.*?)(?<!\s)\*/g, (match, p1) => {
46
+ if (p1.includes('<b>') || p1.includes('</b>') || p1.includes('<pre>') || p1.includes('<code>')) return match;
47
+ return `<i>${p1}</i>`;
48
+ });
43
49
 
44
50
  const tableRegex = /(?:\|.*\|(?:\n|$))+/g;
45
51
  html = html.replace(tableRegex, (match) => {
@@ -65,6 +71,19 @@ export function startTelegramBot() {
65
71
  const throttler = apiThrottler();
66
72
  bot.api.config.use(throttler);
67
73
 
74
+ bot.api.config.use(async (prev, method, payload, signal) => {
75
+ try {
76
+ return await prev(method, payload, signal);
77
+ } catch (err: any) {
78
+ if (method === 'getUpdates' && err.message?.includes('ETIMEDOUT')) {
79
+ console.log(pc.yellow('[Telegram] API connection lost (Timeout). Retrying automatically...'));
80
+ } else if (method === 'getUpdates') {
81
+ console.log(pc.yellow(`[Telegram] Failed to fetch updates: ${err.message}`));
82
+ }
83
+ throw err; // Rethrow so the runner can gracefully backoff and retry
84
+ }
85
+ });
86
+
68
87
  const isPaired = !!config.integrations?.telegram?.authorized_chat_id;
69
88
  let generatedPin = '';
70
89
  let pinExpiry = 0;
@@ -122,7 +141,7 @@ export function startTelegramBot() {
122
141
  });
123
142
 
124
143
  bot.command('clear', async (ctx) => {
125
- logger.clear(ctx.chat?.id.toString());
144
+ logger.clear(`telegram_${ctx.chat?.id}`);
126
145
  await ctx.reply("✅ AI memory has been cleared. Let's start a new chat!");
127
146
  });
128
147
 
@@ -131,36 +150,126 @@ export function startTelegramBot() {
131
150
  if (text.startsWith('/')) return;
132
151
 
133
152
  console.log(`[Telegram] Received from ${ctx.from?.first_name || 'User'}: ${text}`);
134
-
135
153
  await ctx.replyWithChatAction('typing');
136
154
 
137
- try {
138
- let progressMsgId: number | null = null;
139
- const onProgress = async (progressText: string) => {
155
+ // No need for a placeholder message anymore! We use ephemeral drafts.
156
+ const draft_id = Math.floor(Math.random() * 100000000) + 1;
157
+ let buffer = '';
158
+ let lastDraftAt = 0;
159
+ let isDrafting = false;
160
+ const THROTTLE_MS = 100; // Native drafts can be streamed incredibly fast
161
+
162
+ const onChunk = async (chunk: string) => {
163
+ buffer += chunk;
164
+ const now = Date.now();
165
+ if (!isDrafting && now - lastDraftAt >= THROTTLE_MS) {
166
+ isDrafting = true;
140
167
  try {
141
- if (!progressMsgId) {
142
- const sent = await ctx.reply(`<i>${progressText.replace(/_/g, '')}</i>`, { parse_mode: 'HTML' });
143
- progressMsgId = sent.message_id;
144
- } else {
145
- await ctx.api.editMessageText(ctx.chat.id, progressMsgId, `<i>${progressText.replace(/_/g, '')}</i>`, { parse_mode: 'HTML' });
146
- }
168
+ await ctx.api.sendMessageDraft(
169
+ ctx.chat.id, draft_id,
170
+ formatToTelegramHTML(buffer),
171
+ { parse_mode: 'HTML' } as any
172
+ );
147
173
  } catch {}
148
- };
174
+ lastDraftAt = Date.now();
175
+ isDrafting = false;
176
+ }
177
+ };
149
178
 
150
- const response = await processUserInput(text, 'user', onProgress, `telegram_${ctx.chat?.id}`);
179
+ const onProgress = async (msg: string) => {
180
+ if (!isDrafting) {
181
+ isDrafting = true;
182
+ try {
183
+ await ctx.api.sendMessageDraft(
184
+ ctx.chat.id, draft_id,
185
+ `<i>${msg.replace(/_/g, '')}</i>`,
186
+ { parse_mode: 'HTML' } as any
187
+ );
188
+ } catch {}
189
+ isDrafting = false;
190
+ }
191
+ };
151
192
 
152
- if (progressMsgId) {
153
- await ctx.api.deleteMessage(ctx.chat.id, progressMsgId).catch(() => {});
193
+ try {
194
+ const response = await processUserInputStream(
195
+ text, onChunk, onProgress, `telegram_${ctx.chat?.id}`
196
+ );
197
+
198
+ // Finalize by sending the permanent message (which replaces the draft)
199
+ let replyMarkup: any = undefined;
200
+ if (/Reply \*\*Yes\*\*/i.test(response) && /\*\*No\*\* to cancel/i.test(response)) {
201
+ replyMarkup = new InlineKeyboard()
202
+ .text('✅ Approve', 'tx_approve')
203
+ .text('❌ Reject', 'tx_reject');
154
204
  }
155
205
 
156
- // Transactions will now use conversational approval (Zero-Latency text prompts) instead of InlineKeyboards.
157
- await ctx.reply(formatToTelegramHTML(response), { parse_mode: 'HTML' });
206
+ await ctx.reply(
207
+ formatToTelegramHTML(response),
208
+ { parse_mode: 'HTML', reply_markup: replyMarkup }
209
+ ).catch((e) => {
210
+ console.error("[Telegram] CRITICAL: ctx.reply failed in text handler:", e.message);
211
+ });
158
212
  } catch (error: any) {
159
213
  console.error('[Telegram] Error processing message:', error);
160
- await ctx.reply('❌ Sorry, I encountered an error while processing your message.');
214
+ await ctx.reply(
215
+ '❌ Sorry, I encountered an error while processing your message.',
216
+ {}
217
+ ).catch(() => {});
218
+ }
219
+ });
220
+
221
+ bot.on('callback_query:data', async (ctx) => {
222
+ const data = ctx.callbackQuery.data;
223
+ if (data === 'tx_approve' || data === 'tx_reject') {
224
+ await ctx.answerCallbackQuery().catch(() => {});
225
+ await ctx.editMessageReplyMarkup(undefined).catch(() => {});
226
+
227
+ const simulatedText = data === 'tx_approve' ? 'yes' : 'no';
228
+ console.log(`[Telegram] User clicked ${simulatedText.toUpperCase()} via Inline Keyboard`);
229
+
230
+ if (!ctx.chat) return;
231
+ await ctx.replyWithChatAction('typing');
232
+
233
+ const draft_id = Math.floor(Math.random() * 100000000) + 1;
234
+ let buffer = '';
235
+ let lastDraftAt = 0;
236
+ let isDrafting = false;
237
+
238
+ const onChunk = async (chunk: string) => {
239
+ buffer += chunk;
240
+ const now = Date.now();
241
+ if (!isDrafting && now - lastDraftAt >= 100) {
242
+ isDrafting = true;
243
+ try { await ctx.api.sendMessageDraft(ctx.chat.id, draft_id, formatToTelegramHTML(buffer), { parse_mode: 'HTML' } as any); } catch {}
244
+ lastDraftAt = Date.now();
245
+ isDrafting = false;
246
+ }
247
+ };
248
+
249
+ const onProgress = async (msg: string) => {
250
+ if (!isDrafting) {
251
+ isDrafting = true;
252
+ try { await ctx.api.sendMessageDraft(ctx.chat.id, draft_id, `<i>${msg.replace(/_/g, '')}</i>`, { parse_mode: 'HTML' } as any); } catch {}
253
+ isDrafting = false;
254
+ }
255
+ };
256
+
257
+ try {
258
+ const response = await processUserInputStream(simulatedText, onChunk, onProgress, `telegram_${ctx.chat.id}`);
259
+ let replyMarkup: any = undefined;
260
+ if (/Reply \*\*Yes\*\*/i.test(response) && /\*\*No\*\* to cancel/i.test(response)) {
261
+ replyMarkup = new InlineKeyboard().text('✅ Approve', 'tx_approve').text('❌ Reject', 'tx_reject');
262
+ }
263
+ await ctx.reply(formatToTelegramHTML(response), { parse_mode: 'HTML', reply_markup: replyMarkup }).catch((e) => {
264
+ console.error("[Telegram] CRITICAL: ctx.reply failed in callback:", e.message);
265
+ });
266
+ } catch (error) {
267
+ await ctx.reply('❌ Sorry, I encountered an error.', {}).catch(() => {});
268
+ }
161
269
  }
162
270
  });
163
271
 
272
+
164
273
  bot.on('message:document', async (ctx) => {
165
274
  const doc = ctx.message.document;
166
275
  const caption = ctx.message.caption || '';
@@ -215,7 +324,9 @@ export function startTelegramBot() {
215
324
  console.error('[Telegram] Grammy error:', err);
216
325
  });
217
326
 
218
- runnerInstance = run(bot);
327
+ runnerInstance = run(bot, {
328
+ runner: { silent: true }
329
+ });
219
330
 
220
331
  if (isPaired) {
221
332
  console.log('🤖 Telegram Bot is running and securely listening for your messages...');
@@ -46,11 +46,17 @@ export class EpisodicMemoryDB {
46
46
  CREATE TABLE IF NOT EXISTS user_personas (
47
47
  id INTEGER PRIMARY KEY AUTOINCREMENT,
48
48
  trait TEXT UNIQUE NOT NULL,
49
+ category TEXT DEFAULT 'general',
49
50
  confidence REAL DEFAULT 0.1,
50
51
  source TEXT,
51
52
  lastUpdated DATETIME DEFAULT CURRENT_TIMESTAMP
52
53
  );
53
54
  `);
55
+
56
+ // Migration: add category column if missing (for older DBs)
57
+ try {
58
+ this.db.prepare('ALTER TABLE user_personas ADD COLUMN category TEXT DEFAULT \'general\'').run();
59
+ } catch {}
54
60
  }
55
61
 
56
62
  public addCandidateFact(fact: string, confidenceScore: number = 0.5, category: string = 'general', ruleType: 'temporary' | 'permanent' | 'observation' = 'observation', keyTopic?: string): void {
@@ -113,6 +119,11 @@ export class EpisodicMemoryDB {
113
119
  stmt.run();
114
120
  }
115
121
 
122
+ public clearAllPersonas(): void {
123
+ const stmt = this.db.prepare('DELETE FROM user_personas');
124
+ stmt.run();
125
+ }
126
+
116
127
  public close(): void {
117
128
  try {
118
129
  this.db.close();
@@ -120,7 +131,12 @@ export class EpisodicMemoryDB {
120
131
  }
121
132
 
122
133
  // --- PERSONA MODELING ---
123
- public updatePersonaTrait(trait: string, confidence: number = 0.5, source: string = 'honcho'): void {
134
+
135
+ /**
136
+ * Legacy method: upsert by exact trait string.
137
+ * Kept for backward compatibility but prefer upsertPersonaByCategory.
138
+ */
139
+ public updatePersonaTrait(trait: string, confidence: number = 0.5, source: string = 'nyx_daemon'): void {
124
140
  const existing = this.db.prepare('SELECT id, confidence FROM user_personas WHERE trait = ?').get(trait) as any;
125
141
 
126
142
  if (existing) {
@@ -128,8 +144,37 @@ export class EpisodicMemoryDB {
128
144
  const stmt = this.db.prepare('UPDATE user_personas SET confidence = ?, source = ?, lastUpdated = CURRENT_TIMESTAMP WHERE id = ?');
129
145
  stmt.run(newConfidence, source, existing.id);
130
146
  } else {
131
- const stmt = this.db.prepare('INSERT INTO user_personas (trait, confidence, source) VALUES (?, ?, ?)');
132
- stmt.run(trait, confidence, source);
147
+ const stmt = this.db.prepare('INSERT INTO user_personas (trait, category, confidence, source) VALUES (?, ?, ?, ?)');
148
+ stmt.run(trait, 'general', confidence, source);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Category-based upsert: each category (language, tone, trading_style, behavior)
154
+ * has exactly ONE row in user_personas, identified by category.
155
+ * This ensures confidence accumulates correctly across audit cycles
156
+ * instead of creating duplicate rows with slightly different phrasing.
157
+ */
158
+ public upsertPersonaByCategory(
159
+ category: string,
160
+ value: string,
161
+ confidence: number = 0.5,
162
+ source: string = 'nyx_daemon'
163
+ ): void {
164
+ if (!value || !value.trim()) return;
165
+ const existing = this.db.prepare(
166
+ 'SELECT id, confidence FROM user_personas WHERE category = ?'
167
+ ).get(category) as any;
168
+
169
+ if (existing) {
170
+ const newConfidence = Math.min(1.0, existing.confidence + (confidence * 0.2));
171
+ this.db.prepare(
172
+ 'UPDATE user_personas SET trait = ?, confidence = ?, source = ?, lastUpdated = CURRENT_TIMESTAMP WHERE id = ?'
173
+ ).run(value.trim(), newConfidence, source, existing.id);
174
+ } else {
175
+ this.db.prepare(
176
+ 'INSERT INTO user_personas (trait, category, confidence, source) VALUES (?, ?, ?, ?)'
177
+ ).run(value.trim(), category, confidence, source);
133
178
  }
134
179
  }
135
180
 
@@ -137,6 +182,22 @@ export class EpisodicMemoryDB {
137
182
  const stmt = this.db.prepare('SELECT * FROM user_personas ORDER BY confidence DESC');
138
183
  return stmt.all() as any[];
139
184
  }
185
+
186
+ /**
187
+ * Get personas filtered by minimum confidence threshold.
188
+ */
189
+ public getStrongPersonas(minConfidence: number = 0.5): any[] {
190
+ const stmt = this.db.prepare(
191
+ 'SELECT * FROM user_personas WHERE confidence >= ? ORDER BY confidence DESC'
192
+ );
193
+ return stmt.all(minConfidence) as any[];
194
+ }
195
+
196
+ public deletePersonaByTrait(keyword: string): number {
197
+ const stmt = this.db.prepare('DELETE FROM user_personas WHERE trait LIKE ?');
198
+ const result = stmt.run(`%${keyword}%`);
199
+ return (result as any).changes || 0;
200
+ }
140
201
  }
141
202
 
142
203
  // Singleton instance
@@ -109,6 +109,34 @@ export class Logger {
109
109
  )
110
110
  `);
111
111
 
112
+ // V4: Transaction Persistence
113
+ this.db.exec(`
114
+ CREATE TABLE IF NOT EXISTS pending_transactions (
115
+ id TEXT PRIMARY KEY,
116
+ type TEXT NOT NULL,
117
+ chain_name TEXT NOT NULL,
118
+ details TEXT NOT NULL,
119
+ status TEXT NOT NULL,
120
+ result TEXT,
121
+ nonce TEXT,
122
+ created_at INTEGER NOT NULL
123
+ )
124
+ `);
125
+
126
+ this.db.exec(`
127
+ CREATE TABLE IF NOT EXISTS pending_withdrawals (
128
+ id TEXT PRIMARY KEY,
129
+ l2_tx_hash TEXT NOT NULL,
130
+ l1_chain TEXT NOT NULL,
131
+ l2_chain TEXT NOT NULL,
132
+ portal_address TEXT NOT NULL,
133
+ user_address TEXT NOT NULL,
134
+ amount TEXT NOT NULL,
135
+ status TEXT NOT NULL,
136
+ created_at INTEGER NOT NULL
137
+ )
138
+ `);
139
+
112
140
 
113
141
  // Ensure session_id exists for older DBs
114
142
  try {
@@ -195,7 +223,7 @@ export class Logger {
195
223
  `).all(term, term);
196
224
  }
197
225
 
198
- public getHistory(sessionId?: string, limit: number = 40): MemoryEntry[] {
226
+ public getHistory(sessionId?: string, limit: number = 70): MemoryEntry[] {
199
227
  let rows;
200
228
  // Phase 2: Sliding Window Algorithm (LLM Context Limit)
201
229
  // Fetch only the last X messages, then order them chronologically
@@ -232,13 +260,42 @@ export class Logger {
232
260
  });
233
261
  }
234
262
 
263
+ /**
264
+ * Returns the N most recent messages across ALL sessions (including Telegram, Discord, and NULL sessions).
265
+ * Used by NyxDaemon to analyze conversation history from every channel.
266
+ */
267
+ public getRecentMessagesAllSessions(limit: number = 30): MemoryEntry[] {
268
+ const rows = this.db.prepare(`
269
+ SELECT * FROM (
270
+ SELECT role, content, name, tool_call_id, tool_calls, session_id, id
271
+ FROM messages
272
+ ORDER BY id DESC LIMIT ?
273
+ ) ORDER BY id ASC
274
+ `).all(limit);
275
+
276
+ return rows.map((row: any) => {
277
+ const entry: MemoryEntry = {
278
+ role: row.role,
279
+ content: row.content,
280
+ };
281
+ if (row.name) entry.name = row.name;
282
+ if (row.tool_call_id) entry.tool_call_id = row.tool_call_id;
283
+ if (row.tool_calls) entry.tool_calls = JSON.parse(row.tool_calls);
284
+ if (row.session_id) entry.session_id = row.session_id;
285
+ return entry;
286
+ });
287
+ }
288
+
289
+
235
290
  public addEntry(entry: MemoryEntry, sessionId?: string) {
236
291
  if (sessionId) {
237
292
  // Auto-create session if it doesn't exist (e.g. for Telegram integration)
238
293
  try {
239
294
  const sessionExists = this.db.prepare('SELECT 1 FROM sessions WHERE id = ?').get(sessionId);
240
295
  if (!sessionExists) {
241
- const title = sessionId.startsWith('telegram_') ? `Telegram Chat` : 'New Session';
296
+ let title = 'New Session';
297
+ if (sessionId.startsWith('telegram_')) title = 'Telegram Chat';
298
+ else if (sessionId.startsWith('discord_')) title = 'Discord Chat';
242
299
  this.db.prepare('INSERT INTO sessions (id, title) VALUES (?, ?)').run(sessionId, title);
243
300
  }
244
301
  } catch {}
@@ -349,6 +406,89 @@ export class Logger {
349
406
  const result = this.db.prepare(`UPDATE limit_orders SET status = 'ACTIVE' WHERE id = ?`).run(orderId);
350
407
  return result.changes > 0;
351
408
  }
409
+
410
+ // V4: Transaction Persistence Methods
411
+ public savePendingTransaction(tx: any) {
412
+ this.db.prepare(`
413
+ INSERT INTO pending_transactions (id, type, chain_name, details, status, result, nonce, created_at)
414
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
415
+ ON CONFLICT(id) DO UPDATE SET
416
+ status = excluded.status,
417
+ result = excluded.result,
418
+ nonce = excluded.nonce
419
+ `).run(
420
+ tx.id,
421
+ tx.type,
422
+ tx.chainName,
423
+ JSON.stringify(tx.details),
424
+ tx.status,
425
+ tx.result || null,
426
+ tx.nonce || null,
427
+ tx.createdAt || Date.now()
428
+ );
429
+ }
430
+
431
+ public getPendingTransactions(): any[] {
432
+ const rows = this.db.prepare(`SELECT * FROM pending_transactions WHERE status = 'pending'`).all() as any[];
433
+ return rows.map(r => ({
434
+ id: r.id,
435
+ type: r.type,
436
+ chainName: r.chain_name,
437
+ details: JSON.parse(r.details),
438
+ status: r.status,
439
+ result: r.result,
440
+ nonce: r.nonce,
441
+ createdAt: r.created_at
442
+ }));
443
+ }
444
+
445
+ public getTransaction(id: string): any {
446
+ const r = this.db.prepare(`SELECT * FROM pending_transactions WHERE id = ?`).get(id) as any;
447
+ if (!r) return undefined;
448
+ return {
449
+ id: r.id,
450
+ type: r.type,
451
+ chainName: r.chain_name,
452
+ details: JSON.parse(r.details),
453
+ status: r.status,
454
+ result: r.result,
455
+ nonce: r.nonce,
456
+ createdAt: r.created_at
457
+ };
458
+ }
459
+
460
+ public savePendingWithdrawal(w: any) {
461
+ this.db.prepare(`
462
+ INSERT INTO pending_withdrawals (id, l2_tx_hash, l1_chain, l2_chain, portal_address, user_address, amount, status, created_at)
463
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
464
+ ON CONFLICT(id) DO UPDATE SET status = excluded.status
465
+ `).run(
466
+ w.id,
467
+ w.l2TxHash,
468
+ w.l1Chain,
469
+ w.l2Chain,
470
+ w.portalAddress,
471
+ w.userAddress,
472
+ w.amount,
473
+ w.status,
474
+ w.createdAt || Date.now()
475
+ );
476
+ }
477
+
478
+ public getPendingWithdrawals(): any[] {
479
+ const rows = this.db.prepare(`SELECT * FROM pending_withdrawals WHERE status != 'COMPLETED'`).all() as any[];
480
+ return rows.map(r => ({
481
+ id: r.id,
482
+ l2TxHash: r.l2_tx_hash,
483
+ l1Chain: r.l1_chain,
484
+ l2Chain: r.l2_chain,
485
+ portalAddress: r.portal_address,
486
+ userAddress: r.user_address,
487
+ amount: r.amount,
488
+ status: r.status,
489
+ createdAt: r.created_at
490
+ }));
491
+ }
352
492
  }
353
493
 
354
494
  export const logger = new Logger();
@@ -39,21 +39,35 @@ export class PromotionEngine {
39
39
  const uniquePermanent = [...new Set(permanentPreferences)];
40
40
  const uniqueRecent = [...new Set(recentObservations)];
41
41
 
42
- // 4. Rewrite user.md (The Golden Profile)
43
- this.rewriteUserProfile(uniquePermanent, uniqueRecent);
42
+ // 4. Fetch Persona Traits
43
+ const personas = episodicDB.getStrongPersonas(0.4);
44
+ const personaStrings: string[] = [];
45
+ for (const p of personas) {
46
+ personaStrings.push(`- [${p.category.toUpperCase()}] ${p.trait}`);
47
+ }
48
+
49
+ // 5. Rewrite user.md (The Golden Profile)
50
+ this.rewriteUserProfile(uniquePermanent, uniqueRecent, personaStrings);
44
51
 
45
52
  } catch (error) {
46
53
  console.error('[PromotionEngine] Error running promotion engine:', error);
47
54
  }
48
55
  }
49
56
 
50
- private static rewriteUserProfile(permanent: string[], recent: string[]): void {
57
+ private static rewriteUserProfile(permanent: string[], recent: string[], personas: string[] = []): void {
51
58
  const userMdPath = getPath('user.md');
52
59
 
53
60
  let newContent = `Write custom instructions, special rules, user profiles, or the persona you want for Nyxora AI in this file.\n\n`;
54
61
  newContent += `<!-- AUTOMANAGED BY PROMOTION ENGINE. MANUAL EDITS MAY BE OVERWRITTEN -->\n\n`;
55
62
 
56
- newContent += `# Permanent Preferences\n`;
63
+ newContent += `# User Persona & Identity\n`;
64
+ if (personas.length === 0) {
65
+ newContent += `*(No specific persona traits identified yet)*\n`;
66
+ } else {
67
+ newContent += personas.join('\n') + '\n';
68
+ }
69
+
70
+ newContent += `\n# Permanent Preferences\n`;
57
71
  if (permanent.length === 0) {
58
72
  newContent += `*(No permanent preferences recorded yet)*\n`;
59
73
  } else {
@@ -68,6 +82,6 @@ export class PromotionEngine {
68
82
  }
69
83
 
70
84
  fs.writeFileSync(userMdPath, newContent, 'utf-8');
71
- console.log(`[PromotionEngine] user.md successfully synchronized with Layer 2 Episodic Memory.`);
85
+ console.log(`[PromotionEngine] user.md successfully synchronized with Layer 2 Episodic Memory and Personas.`);
72
86
  }
73
87
  }
@@ -155,6 +155,16 @@ export class AgentSkills {
155
155
  throw new Error(`Execution script not found for skill '${name}' at path: ${scriptPath}`);
156
156
  }
157
157
 
158
+ // Automatically load .env for the skill if it exists
159
+ const envPath = path.join(this.skillsDir, name, '.env');
160
+ if (fs.existsSync(envPath)) {
161
+ try {
162
+ require('dotenv').config({ path: envPath });
163
+ } catch (e) {
164
+ console.warn(`[AgentSkills] Failed to load .env for skill '${name}':`, e);
165
+ }
166
+ }
167
+
158
168
  try {
159
169
  // Dynamic import of the TS/JS module
160
170
  const module = await import(scriptPath);
@@ -15,7 +15,7 @@ import {
15
15
  export class GoogleWorkspacePlugin implements Plugin {
16
16
  public name = 'GoogleWorkspacePlugin';
17
17
  public description = 'Google Workspace operations including Gmail, Calendar, Docs, and Sheets.';
18
- public version = '1.0.0';
18
+ public version = '1.0.1';
19
19
 
20
20
  public tools = [
21
21
  readGmailInboxToolDefinition,
@@ -9,7 +9,7 @@ import { forgetMemoryToolDefinition, forgetMemory } from '../skills/forgetMemory
9
9
  export class SystemCorePlugin implements Plugin {
10
10
  public name = 'SystemCorePlugin';
11
11
  public description = 'Core system identity, profile, and task scheduling operations.';
12
- public version = '1.0.0';
12
+ public version = '1.0.1';
13
13
 
14
14
  public tools = [
15
15
  updateProfileToolDefinition,
@@ -4,7 +4,7 @@ import { createAgentSkillToolDefinition, createAgentSkill } from '../skills/crea
4
4
  export class SystemExternalPlugin implements Plugin {
5
5
  public name = 'SystemExternalPlugin';
6
6
  public description = 'Provides tools for creating and managing third-party external agent skills.';
7
- public version = '1.0.0';
7
+ public version = '1.0.1';
8
8
 
9
9
  public tools = [
10
10
  createAgentSkillToolDefinition
@@ -22,7 +22,7 @@ const installPluginDefinition = {
22
22
  export class SystemPluginInstallerPlugin implements Plugin {
23
23
  public name = 'SystemPluginInstallerPlugin';
24
24
  public description = 'Autonomous package manager for installing and auto-healing dynamic plugins.';
25
- public version = '1.0.0';
25
+ public version = '1.0.1';
26
26
 
27
27
  public tools = [installPluginDefinition];
28
28
 
@@ -5,7 +5,7 @@ import { notionWorkspaceToolDefinition, manageNotion } from '../skills/notionWor
5
5
  export class SystemSocialPlugin implements Plugin {
6
6
  public name = 'SystemSocialPlugin';
7
7
  public description = 'Social media and external workspace operations (Twitter, Notion).';
8
- public version = '1.0.0';
8
+ public version = '1.0.1';
9
9
 
10
10
  public tools = [
11
11
  xManagerToolDefinition,
@@ -8,7 +8,7 @@ import { audioTranscribeToolDefinition, transcribeAudio } from '../skills/audioT
8
8
  export class SystemWebPlugin implements Plugin {
9
9
  public name = 'SystemWebPlugin';
10
10
  public description = 'Web browsing, searching, and media analysis operations.';
11
- public version = '1.0.0';
11
+ public version = '1.0.1';
12
12
 
13
13
  public tools = [
14
14
  browseWebsiteToolDefinition,
@@ -10,7 +10,7 @@ import { createCognitiveSkillToolDefinition, createCognitiveSkill } from '../ski
10
10
  export class SystemWorkspacePlugin implements Plugin {
11
11
  public name = 'SystemWorkspacePlugin';
12
12
  public description = 'Local system operations including file management, terminal execution, and Git.';
13
- public version = '1.0.0';
13
+ public version = '1.0.1';
14
14
 
15
15
  public tools = [
16
16
  readLocalFileToolDefinition,
@@ -32,8 +32,8 @@ export class SystemWorkspacePlugin implements Plugin {
32
32
  ['edit_local_file']: async (args: any) => {
33
33
  return await editLocalFile(args.filePath, args.searchString, args.replacementString);
34
34
  },
35
- ['generate_excel_report']: async (args: any) => {
36
- return await generateExcelFile(args.data, args.filename);
35
+ ['generate_excel_file']: async (args: any) => {
36
+ return await generateExcelFile(args.data, args.filePath);
37
37
  },
38
38
  ['run_terminal_command']: async (args: any) => {
39
39
  return await runTerminalCommand(args.command);
@@ -4,7 +4,7 @@ import { logger } from '../../agent/reasoning';
4
4
 
5
5
  export class CreateSkillPlugin implements Plugin {
6
6
  public name = 'CreateSkill';
7
- public version = '1.0.0';
7
+ public version = '1.0.1';
8
8
  public description = 'Autonomously extracts and creates skills for Nyxora.';
9
9
 
10
10
  public tools = [