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
@@ -5,15 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.logger = void 0;
7
7
  exports.processUserInput = processUserInput;
8
+ exports.processUserInputStream = processUserInputStream;
8
9
  const fs_1 = __importDefault(require("fs"));
9
10
  const parser_1 = require("../config/parser");
10
11
  const logger_1 = require("../memory/logger");
11
12
  const episodic_1 = require("../memory/episodic");
13
+ const streamSimulator_1 = require("../utils/streamSimulator");
12
14
  const paths_1 = require("../config/paths");
13
15
  const picocolors_1 = __importDefault(require("picocolors"));
14
16
  exports.logger = new logger_1.Logger();
15
17
  const llmUtils_1 = require("../utils/llmUtils");
16
- function getSystemPrompt(context = 'general') {
18
+ const transactionManager_1 = require("./transactionManager");
19
+ async function getSystemPrompt(context = 'general', userInput = '') {
17
20
  const config = (0, parser_1.loadConfig)();
18
21
  const currentDateTime = new Date().toLocaleString('en-US');
19
22
  let basePrompt = "";
@@ -27,7 +30,7 @@ IMPORTANT: The <think> block is strictly for internal monologue. Your final answ
27
30
 
28
31
  [WEB3 EXECUTION WORKFLOW]
29
32
  CRITICAL RULE 1: NEVER expose internal JSON tool calls. Explain the outcome naturally.
30
- CRITICAL RULE 2: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt.
33
+ CRITICAL RULE: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt, UNLESS the Episodic Memories or Cognitive Skills specify a strict language preference.
31
34
  CRITICAL RULE 3: DEFAULT CHAIN HANDLING. Default to: ${config.agent.default_chain} unless specified.
32
35
  CRITICAL RULE 4: CONDITIONAL PARALLEL EXECUTION. Parallel tool execution is ONLY allowed if there are zero data dependencies.
33
36
  CRITICAL RULE 5: TRANSACTION EXECUTION. For ALL state-changing transactions (swap, bridge, transfer), execute IMMEDIATELY. It will trigger a secure popup.
@@ -45,7 +48,7 @@ IMPORTANT: The <think> block is strictly for internal monologue. Your final answ
45
48
 
46
49
  [OS EXECUTION WORKFLOW]
47
50
  CRITICAL RULE 1: NEVER expose internal JSON tool calls. Explain the outcome naturally.
48
- CRITICAL RULE 2: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt.
51
+ CRITICAL RULE: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt, UNLESS the Episodic Memories or Cognitive Skills specify a strict language preference.
49
52
  CRITICAL RULE 3: FILE SYSTEM SAFETY. You are STRICTLY FORBIDDEN from modifying config.yaml, rpc_key.yaml, or policy.yaml using terminal commands like sed or echo.
50
53
  CRITICAL RULE 4: CRON JOBS VS LIMIT ORDERS. Do NOT use schedule_task for price-based trading triggers. Use schedule_task for time-based recurring tasks.
51
54
  CRITICAL RULE 5: TOOL CONFIDENCE. NEVER fabricate file contents or command outputs.`;
@@ -58,7 +61,7 @@ CRITICAL: You MUST use a Chain of Thought approach for every response. Enclose y
58
61
  IMPORTANT: The <think> block is strictly for internal monologue. Your final answer must be OUTSIDE and AFTER the </think> tag.
59
62
 
60
63
  [GENERAL WORKFLOW]
61
- CRITICAL RULE 1: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt.
64
+ CRITICAL RULE 1: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt, UNLESS the Episodic Memories or Cognitive Skills specify a strict language preference.
62
65
  CRITICAL RULE 2: BE HELPFUL AND CONCISE. You do not have Web3 or OS tools in this context. If the user asks for Web3 or OS tasks, politely inform them to rephrase using clear keywords like 'transfer', 'harga', 'file', 'email', etc.`;
63
66
  }
64
67
  const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
@@ -121,46 +124,170 @@ Do NOT perform any web3 tasks or generic answers until they provide all 4 detail
121
124
  catch (error) {
122
125
  console.error('Failed to read policy.yaml:', error);
123
126
  }
124
- // Inject Episodic Memories (Smart Suggestions Context)
127
+ // Inject Episodic Memories via Python RAG
125
128
  try {
126
- const recentMemories = episodic_1.episodicDB.getMemories().slice(0, 10);
127
- if (recentMemories.length > 0) {
128
- basePrompt += `\n\n--- EPISODIC MEMORIES (SMART SUGGESTIONS) ---\nUse these recent observations to proactively suggest or autocomplete parameters (like networks or tokens) without asking the user if they align with the current request:\n`;
129
- recentMemories.forEach(mem => {
130
- basePrompt += `- [${mem.category.toUpperCase()}] ${mem.fact} (Confidence: ${(mem.confidence * 100).toFixed(0)}%)\n`;
131
- });
129
+ const ragRes = await fetch('http://localhost:8000/memory/rag', {
130
+ method: 'POST',
131
+ headers: { 'Content-Type': 'application/json' },
132
+ body: JSON.stringify({ query: userInput, top_k: 5 })
133
+ });
134
+ if (ragRes.ok) {
135
+ const ragData = await ragRes.json();
136
+ if (ragData.memories && ragData.memories.length > 0) {
137
+ basePrompt += `\n\n--- EPISODIC MEMORIES (SMART SUGGESTIONS) ---\n`;
138
+ ragData.memories.forEach((mem) => {
139
+ basePrompt += `- ${mem}\n`;
140
+ });
141
+ }
132
142
  }
133
143
  }
134
- catch { }
144
+ catch (e) {
145
+ // Fallback or ignore if Python ML engine is down
146
+ }
135
147
  // V3: Inject Personalized Risk Profile
136
148
  try {
137
149
  const profile = exports.logger.getUserProfile();
138
- const personas = episodic_1.episodicDB.getPersonas();
139
- if (profile || personas.length > 0) {
150
+ if (profile) {
140
151
  basePrompt += `\n\n--- [USER_PERSONA] RISK PROFILE & PREFERENCES ---\n`;
141
- if (profile) {
142
- basePrompt += `Risk Level: ${profile.risk_level}\n`;
143
- basePrompt += `Max Slippage Tolerance: ${profile.max_slippage}%\n`;
144
- basePrompt += `Avoid Memecoins: ${profile.avoid_memecoins ? 'YES' : 'NO'}\n`;
145
- if (profile.custom_rules) {
146
- basePrompt += `Custom Rules: ${profile.custom_rules}\n`;
147
- }
148
- 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`;
149
- }
150
- if (personas.length > 0) {
151
- basePrompt += `\nObserved Traits (Dialectic Modeling):\n`;
152
- personas.forEach(p => {
153
- basePrompt += `- ${p.trait} (Confidence: ${(p.confidence * 100).toFixed(0)}%)\n`;
154
- });
155
- basePrompt += `Adapt your tone, vocabulary, and suggestions to align with these observed user traits.\n`;
152
+ basePrompt += `Risk Level: ${profile.risk_level}\n`;
153
+ basePrompt += `Max Slippage Tolerance: ${profile.max_slippage}%\n`;
154
+ basePrompt += `Avoid Memecoins: ${profile.avoid_memecoins ? 'YES' : 'NO'}\n`;
155
+ if (profile.custom_rules) {
156
+ basePrompt += `Custom Rules: ${profile.custom_rules}\n`;
156
157
  }
158
+ 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`;
159
+ }
160
+ }
161
+ catch { }
162
+ // HIGHEST PRIORITY: Inject observed user communication style.
163
+ // This section is placed LAST so it overrides any default tone rules above (including IDENTITY.md).
164
+ try {
165
+ // Only use traits with confidence >= 0.5 (confirmed by multiple audit cycles)
166
+ const strongPersonas = episodic_1.episodicDB.getStrongPersonas(0.5);
167
+ if (strongPersonas.length > 0) {
168
+ basePrompt += `\n\n--- ⚡ OVERRIDE: USER COMMUNICATION STYLE (HIGHEST PRIORITY — OVERRIDES ALL RULES ABOVE) ---\n`;
169
+ basePrompt += `CRITICAL: These are CONFIRMED user preferences observed across multiple conversations. You MUST apply them IMMEDIATELY and CONSISTENTLY, overriding any default tone or style instructions above.\n\n`;
170
+ strongPersonas.forEach(p => {
171
+ const label = p.category ? `[${p.category.toUpperCase()}]` : '[PREFERENCE]';
172
+ basePrompt += `${label} ${p.trait}\n`;
173
+ });
174
+ basePrompt += `\nMANDATORY ENFORCEMENT:\n`;
175
+ basePrompt += `- You MUST adapt your tone, language, and style to match the detected user preferences above.\n`;
176
+ basePrompt += `- If the user communicates informally or casually → respond casually. Do NOT be formal or robotic.\n`;
177
+ basePrompt += `- If the user communicates in a specific language or dialect → respond in that same language/style.\n`;
178
+ basePrompt += `- If the user uses slang or informal words → mirror that vocabulary naturally.\n`;
179
+ basePrompt += `- This is a HARD RULE. It overrides any default tone in IDENTITY.md or other instructions above.\n`;
157
180
  }
158
181
  }
159
182
  catch { }
160
183
  return basePrompt;
161
184
  }
185
+ // ──────────────────────────────────────────────────────────────────
186
+ // Module-level routing keyword constants
187
+ // Defined here once and shared by both processUserInput() and processUserInputStream()
188
+ // to ensure consistent routing across sync and streaming paths.
189
+ //
190
+ // NOTE: Arrays are intentionally multilingual. Nyxora targets a global user base.
191
+ // Indonesian terms are included as the primary non-English locale.
192
+ // To add another language, extend these arrays — no other changes required.
193
+ // ──────────────────────────────────────────────────────────────────
194
+ const OS_KEYWORDS = [
195
+ // Files & Documents
196
+ 'excel', 'xlsx', 'spreadsheet', 'generate excel',
197
+ 'file', 'folder', 'directory', 'read file', 'write file', 'pdf', 'word', 'docx', 'document',
198
+ // Terminal & Git
199
+ 'terminal', 'command', 'shell', 'bash', 'script', 'run command',
200
+ 'git', 'commit', 'push', 'pull', 'clone', 'branch', 'merge',
201
+ // Web & Search
202
+ 'search web', 'google', 'browse', 'scrape', 'weather', 'news',
203
+ // Email & Workspace
204
+ 'email', 'gmail', 'google docs', 'google sheets', 'notion', 'calendar',
205
+ // Social & Media
206
+ 'twitter', 'tweet', 'x post', 'transcribe', 'audio',
207
+ // AI Settings
208
+ 'rename agent', 'change persona', 'update profile', 'update identity', 'setting',
209
+ // Summarization
210
+ 'summarize',
211
+ ];
212
+ const WEB3_KEYWORDS = [
213
+ // Transactions
214
+ 'swap', 'bridge', 'transfer', 'send', 'buy', 'sell',
215
+ 'mint', 'stake', 'unstake', 'claim', 'deposit', 'withdraw', 'approve',
216
+ // Assets & Wallets
217
+ 'token', 'crypto', 'coin', 'nft', 'wallet', 'address',
218
+ 'eth', 'bnb', 'usdt', 'usdc', 'sol', 'matic', 'arb', 'op', 'base',
219
+ // DeFi & Market
220
+ 'defi', 'dex', 'liquidity', 'pool', 'aave', 'uniswap', 'apy', 'apr',
221
+ 'price', 'chart', 'market', 'portfolio', 'balance',
222
+ 'gas', 'fee', 'slippage', 'transaction', 'tx',
223
+ // Chains
224
+ 'ethereum', 'polygon', 'arbitrum', 'optimism', 'bsc', 'mainnet', 'testnet',
225
+ 'on-chain', 'blockchain',
226
+ // Fiat & Currency
227
+ 'usd', 'eur', 'gbp', 'jpy', 'aud', 'idr', 'fiat', 'currency', 'convert', 'exchange', 'rate', 'value',
228
+ ];
229
+ // ── P2: Multi-Intent Decomposer ─────────────────────────────────────────────
230
+ /**
231
+ * Detects if the user message contains BOTH web3 AND os intents.
232
+ * Returns 'compound' when both are present so we can route to both agents.
233
+ */
234
+ function detectCompoundIntent(lowerInput, osKeywords, web3Keywords) {
235
+ return {
236
+ hasOs: osKeywords.some(kw => lowerInput.includes(kw)),
237
+ hasWeb3: web3Keywords.some(kw => lowerInput.includes(kw)),
238
+ };
239
+ }
240
+ // ── P7: Task Planner ─────────────────────────────────────────────────────────
241
+ // Trigger keywords for complex requests that benefit from a planning step first.
242
+ // Intentionally multilingual: includes common terms from Indonesian (id), English (en),
243
+ // and universal finance/trading vocabulary to support Nyxora's global user base.
244
+ const PLAN_TRIGGER_KEYWORDS = [
245
+ // Indonesian
246
+ 'buatkan', 'buat rencana', 'rencanakan', 'strategi', 'gimana cara', 'bagaimana cara',
247
+ 'langkah', 'optimasi', 'apa yang harus',
248
+ // English
249
+ 'strategy', 'plan', 'planning', 'step by step', 'breakdown', 'optimize',
250
+ 'rebalance', 'what should i do', 'help me decide', 'how do i',
251
+ // Universal finance terms
252
+ 'roadmap', 'approach', 'framework',
253
+ ];
254
+ function shouldPlan(input) {
255
+ const lower = input.toLowerCase();
256
+ return PLAN_TRIGGER_KEYWORDS.some(kw => lower.includes(kw));
257
+ }
258
+ async function runTaskPlanner(input, context) {
259
+ const config = (0, parser_1.loadConfig)();
260
+ try {
261
+ const planRes = await (0, llmUtils_1.executeWithRetry)(async (client) => client.chat({
262
+ model: config.llm.model,
263
+ temperature: 0.2,
264
+ messages: [
265
+ {
266
+ role: 'system',
267
+ content: `You are a task planning assistant for a crypto AI agent.
268
+ The user has a complex request that needs structured execution.
269
+ Break it down into a clear, ordered execution plan with 3-6 concrete steps.
270
+ Each step should map to a specific action or tool call.
271
+ Be concise. Use bullet points. No fluff.
272
+ Context domain: ${context}`
273
+ },
274
+ { role: 'user', content: `Create an execution plan for: ${input}` }
275
+ ]
276
+ }));
277
+ const plan = planRes.message?.content?.trim() || '';
278
+ if (!plan)
279
+ return '';
280
+ console.log(picocolors_1.default.blue('[TaskPlanner] Plan generated, injecting into agent context.'));
281
+ return `\n\n--- 📋 TASK EXECUTION PLAN (follow this order) ---\n${plan}\n--- END PLAN ---\n`;
282
+ }
283
+ catch {
284
+ return ''; // planning is best-effort, never block the agent
285
+ }
286
+ }
162
287
  const web3Agent_1 = require("./web3Agent");
163
288
  const osAgent_1 = require("./osAgent");
289
+ const web3Agent_2 = require("./web3Agent");
290
+ const osAgent_2 = require("./osAgent");
164
291
  async function processUserInput(input, role = 'user', onProgress, sessionId) {
165
292
  const lowerInput = input.toLowerCase();
166
293
  const config = (0, parser_1.loadConfig)();
@@ -169,46 +296,91 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
169
296
  const textOnlyHistory = history
170
297
  .filter(m => (m.role === 'user' || m.role === 'assistant') && m.content)
171
298
  .map(m => ({ role: m.role === 'system' ? 'user' : m.role, content: m.content || "" }));
172
- const routerPrompt = `You are Nyxora's Semantic Intent Router. Your job is to classify the user's FINAL message into one of three categories: 'web3', 'os', or 'general'.
299
+ // Uses module-level OS_KEYWORDS and WEB3_KEYWORDS constants defined above.
300
+ // Routing logic: deterministic keyword check first (no LLM call needed),
301
+ // with LLM router as fallback for ambiguous inputs.
302
+ let context = 'general';
303
+ let preCheckMatched = false;
304
+ const compound = detectCompoundIntent(lowerInput, OS_KEYWORDS, WEB3_KEYWORDS);
305
+ // P2: Handle compound web3+os intent — run both agents and merge results
306
+ if (compound.hasWeb3 && compound.hasOs) {
307
+ console.log(picocolors_1.default.cyan('[Orchestrator] Compound intent detected (web3 + os). Running both agents sequentially.'));
308
+ exports.logger.addEntry({ role, content: input }, sessionId);
309
+ const [web3Result, osResult] = await Promise.allSettled([
310
+ (0, web3Agent_1.processWeb3Intent)(input, role, onProgress, sessionId),
311
+ (0, osAgent_1.processOsIntent)(input, role, onProgress, sessionId),
312
+ ]);
313
+ const parts = [];
314
+ if (web3Result.status === 'fulfilled' && web3Result.value)
315
+ parts.push(web3Result.value);
316
+ if (osResult.status === 'fulfilled' && osResult.value)
317
+ parts.push(osResult.value);
318
+ return parts.join('\n\n---\n\n') || '⚠️ Both agents returned empty responses.';
319
+ }
320
+ // Single-intent routing
321
+ if (compound.hasOs) {
322
+ context = 'os';
323
+ preCheckMatched = true;
324
+ }
325
+ else if (compound.hasWeb3) {
326
+ context = 'web3';
327
+ preCheckMatched = true;
328
+ }
329
+ if (preCheckMatched) {
330
+ console.log(picocolors_1.default.cyan(`[Orchestrator] Intent pre-classified (keyword match) as: ${context.toUpperCase()}`));
331
+ }
332
+ else {
333
+ // ── Fallback: LLM Router (untuk intent ambigu / percakapan umum) ─────────
334
+ const routerPrompt = `You are Nyxora's Semantic Intent Router. Your job is to classify the user's FINAL message into one of three categories: 'web3', 'os', or 'general'.
173
335
  Rules:
174
336
  1. FOCUS ONLY ON THE FINAL MESSAGE. History is only for context.
175
- 2. The user may speak in ANY language, including casual slang, idioms, or abbreviations (e.g., 'tf', 'wd', 'buy', 'sell'). Translate their core intent logically.
176
- 3. If the core intent involves blockchain, crypto, bridging, swapping, trading, sending/receiving, tokens, wallets, or transactions, reply 'web3'.
177
- 4. If the core intent involves OS automation, web search, weather, emails, files, terminal, or changing AI settings, reply 'os'.
337
+ 2. The user may speak in ANY language, including casual slang, idioms, or abbreviations.
338
+ 3. If the core intent involves blockchain, crypto, bridging, swapping, trading, sending/receiving, tokens, wallets, transactions, OR asking for the price/conversion of ANY asset to fiat, reply 'web3'.
339
+ 4. If the core intent involves OS automation, weather, emails, files, terminal, changing AI settings, OR asking ANY question that requires a web search or real-world factual lookup (e.g., 'who won the game', 'what is the registration date', 'cek info', 'cari tahu'), reply 'os'.
178
340
  5. If it is purely casual conversation, chit-chat, or greetings, reply 'general'.
179
341
  Reply with EXACTLY ONE WORD: web3, os, or general.`;
180
- const routerMessages = [
181
- { role: 'system', content: routerPrompt },
182
- ...textOnlyHistory.slice(-10),
183
- { role: 'user', content: input }
184
- ];
185
- let context = 'general';
186
- try {
187
- const routerResponse = await (0, llmUtils_1.executeWithRetry)(async (client) => {
188
- return await client.chat({
189
- model: config.llm.model,
190
- messages: routerMessages,
191
- temperature: 0.1,
192
- max_tokens: 1000
193
- });
194
- }, 3); // 3 retries for transient 503/429 errors
195
- let contextResponse = (routerResponse.message.content || 'general').toLowerCase().trim();
196
- if (contextResponse.includes('web3'))
197
- context = 'web3';
198
- else if (contextResponse.includes('os'))
199
- context = 'os';
200
- else
342
+ const routerMessages = [
343
+ { role: 'system', content: routerPrompt },
344
+ ...textOnlyHistory.slice(-10),
345
+ { role: 'user', content: input }
346
+ ];
347
+ try {
348
+ const routerResponse = await (0, llmUtils_1.executeWithRetry)(async (client) => {
349
+ return await client.chat({
350
+ model: config.llm.model,
351
+ messages: routerMessages,
352
+ temperature: 0.1,
353
+ max_tokens: 1000
354
+ });
355
+ }, 3); // 3 retries for transient 503/429 errors
356
+ let contextResponse = (routerResponse.message.content || 'general').toLowerCase().trim();
357
+ if (contextResponse.includes('web3'))
358
+ context = 'web3';
359
+ else if (contextResponse.includes('os'))
360
+ context = 'os';
361
+ else
362
+ context = 'general';
363
+ }
364
+ catch (e) {
365
+ console.warn(`[Orchestrator] Router LLM failed, falling back to general. Error:`, e);
201
366
  context = 'general';
367
+ }
368
+ console.log(picocolors_1.default.magenta(`[Orchestrator] Intent classified as: ${context.toUpperCase()}`));
202
369
  }
203
- catch (e) {
204
- console.warn(`[Orchestrator] Router LLM failed, falling back to general. Error:`, e);
205
- context = 'general';
206
- }
207
- console.log(picocolors_1.default.magenta(`[Orchestrator] Intent classified as: ${context.toUpperCase()}`));
208
370
  if (context === 'web3') {
371
+ // P7: Inject task plan for complex requests
372
+ const planInjection = shouldPlan(input) ? await runTaskPlanner(input, 'web3') : '';
373
+ if (planInjection) {
374
+ // Prepend plan as a system note in the input so agent sees it
375
+ return await (0, web3Agent_1.processWeb3Intent)(planInjection + '\n\nUSER REQUEST: ' + input, role, onProgress, sessionId);
376
+ }
209
377
  return await (0, web3Agent_1.processWeb3Intent)(input, role, onProgress, sessionId);
210
378
  }
211
379
  else if (context === 'os') {
380
+ const planInjection = shouldPlan(input) ? await runTaskPlanner(input, 'os') : '';
381
+ if (planInjection) {
382
+ return await (0, osAgent_1.processOsIntent)(planInjection + '\n\nUSER REQUEST: ' + input, role, onProgress, sessionId);
383
+ }
212
384
  return await (0, osAgent_1.processOsIntent)(input, role, onProgress, sessionId);
213
385
  }
214
386
  else {
@@ -217,7 +389,7 @@ Reply with EXACTLY ONE WORD: web3, os, or general.`;
217
389
  // Let's create a dummy general intent processing directly here.
218
390
  exports.logger.addEntry({ role, content: input }, sessionId);
219
391
  const messages = [
220
- { role: 'system', content: getSystemPrompt('general') },
392
+ { role: 'system', content: await getSystemPrompt('general', input) },
221
393
  ...textOnlyHistory,
222
394
  { role: 'user', content: input }
223
395
  ];
@@ -254,3 +426,124 @@ Reply with EXACTLY ONE WORD: web3, os, or general.`;
254
426
  }
255
427
  }
256
428
  }
429
+ /**
430
+ * Streaming variant of processUserInput().
431
+ * Calls onChunk() for each LLM token as it arrives.
432
+ * Falls back to onChunk() with full response if streaming is not supported.
433
+ */
434
+ async function processUserInputStream(input, originalOnChunk, onProgress, sessionId) {
435
+ const smartStream = (0, streamSimulator_1.createSmartStreamWrapper)(originalOnChunk);
436
+ const onChunk = smartStream.onChunk;
437
+ try {
438
+ const lowerInput = input.toLowerCase();
439
+ const config = (0, parser_1.loadConfig)();
440
+ const history = exports.logger.getHistory(sessionId);
441
+ const textOnlyHistory = history
442
+ .filter(m => (m.role === 'user' || m.role === 'assistant') && m.content)
443
+ .map(m => ({ role: m.role === 'system' ? 'user' : m.role, content: m.content || '' }));
444
+ // Use module-level keyword constants for consistent routing across sync and stream paths.
445
+ // detectCompoundIntent and shouldPlan are also shared from the module scope.
446
+ let context = 'general';
447
+ let preCheckMatched = false;
448
+ // P2: Compound intent detection (stream path)
449
+ const streamCompound = detectCompoundIntent(lowerInput, OS_KEYWORDS, WEB3_KEYWORDS);
450
+ const pendingTxs = transactionManager_1.txManager.getPending();
451
+ if (pendingTxs.length > 0) {
452
+ context = 'web3';
453
+ preCheckMatched = true;
454
+ }
455
+ else if (streamCompound.hasWeb3 && streamCompound.hasOs) {
456
+ // Compound: stream both agents and concatenate
457
+ console.log(picocolors_1.default.cyan('[Stream Orchestrator] Compound intent detected (web3 + os).'));
458
+ exports.logger.addEntry({ role: 'user', content: input }, sessionId);
459
+ const [web3R, osR] = await Promise.allSettled([
460
+ (0, web3Agent_2.processWeb3IntentStream)(input, onChunk, onProgress, sessionId),
461
+ (0, osAgent_2.processOsIntentStream)(input, onChunk, onProgress, sessionId),
462
+ ]);
463
+ const parts = [];
464
+ if (web3R.status === 'fulfilled' && web3R.value)
465
+ parts.push(web3R.value);
466
+ if (osR.status === 'fulfilled' && osR.value)
467
+ parts.push(osR.value);
468
+ return parts.join('\n\n---\n\n') || '⚠️ Both agents returned empty responses.';
469
+ }
470
+ else if (streamCompound.hasOs) {
471
+ context = 'os';
472
+ preCheckMatched = true;
473
+ }
474
+ else if (streamCompound.hasWeb3) {
475
+ context = 'web3';
476
+ preCheckMatched = true;
477
+ }
478
+ if (!preCheckMatched) {
479
+ const routerPrompt = `You are Nyxora's Semantic Intent Router. Your job is to classify the user's FINAL message into one of three categories: 'web3', 'os', or 'general'.
480
+ Rules:
481
+ 1. FOCUS ONLY ON THE FINAL MESSAGE. History is only for context.
482
+ 2. The user may speak in ANY language, including casual slang, idioms, or abbreviations.
483
+ 3. If the core intent involves blockchain, crypto, bridging, swapping, trading, sending/receiving, tokens, wallets, transactions, OR asking for the price/conversion of ANY asset to fiat, reply 'web3'.
484
+ 4. If the core intent involves OS automation, weather, emails, files, terminal, changing AI settings, OR asking ANY question that requires a web search or real-world factual lookup (e.g., 'who won the game', 'what is the registration date', 'cek info', 'cari tahu'), reply 'os'.
485
+ 5. If it is purely casual conversation, chit-chat, or greetings, reply 'general'.
486
+ Reply with EXACTLY ONE WORD: web3, os, or general.`;
487
+ const routerMessages = [
488
+ { role: 'system', content: routerPrompt },
489
+ ...textOnlyHistory.slice(-10),
490
+ { role: 'user', content: input }
491
+ ];
492
+ try {
493
+ const routerResponse = await (0, llmUtils_1.executeWithRetry)(async (client) => client.chat({ model: config.llm.model, messages: routerMessages, temperature: 0.1, max_tokens: 1000 }), 3);
494
+ const cr = (routerResponse.message.content || 'general').toLowerCase().trim();
495
+ if (cr.includes('web3'))
496
+ context = 'web3';
497
+ else if (cr.includes('os'))
498
+ context = 'os';
499
+ else
500
+ context = 'general';
501
+ }
502
+ catch {
503
+ context = 'general';
504
+ }
505
+ }
506
+ console.log(picocolors_1.default.cyan(`[Stream Orchestrator] Intent classified as: ${context.toUpperCase()}`));
507
+ let finalResult = '';
508
+ if (context === 'web3') {
509
+ // P7: Inject task plan for complex stream requests
510
+ const planInjection = shouldPlan(input) ? await runTaskPlanner(input, 'web3') : '';
511
+ const streamInput = planInjection ? planInjection + '\n\nUSER REQUEST: ' + input : input;
512
+ finalResult = await (0, web3Agent_2.processWeb3IntentStream)(streamInput, onChunk, onProgress, sessionId);
513
+ }
514
+ else if (context === 'os') {
515
+ const planInjection = shouldPlan(input) ? await runTaskPlanner(input, 'os') : '';
516
+ const streamInput = planInjection ? planInjection + '\n\nUSER REQUEST: ' + input : input;
517
+ finalResult = await (0, osAgent_2.processOsIntentStream)(streamInput, onChunk, onProgress, sessionId);
518
+ }
519
+ else {
520
+ exports.logger.addEntry({ role: 'user', content: input }, sessionId);
521
+ const messages = [
522
+ { role: 'system', content: await getSystemPrompt('general', input) },
523
+ ...textOnlyHistory,
524
+ { role: 'user', content: input }
525
+ ];
526
+ try {
527
+ let streamedContent = '';
528
+ const response = await (0, llmUtils_1.executeWithRetry)(async (client) => client.stream({ model: config.llm.model, messages: messages }, (chunk) => { streamedContent += chunk; onChunk(chunk); }));
529
+ let finalContent = response.message?.content || streamedContent || '';
530
+ finalContent = finalContent
531
+ .replace(/<(think|thought)[\s\S]*?<\/\1>\n?/gi, '')
532
+ .trim();
533
+ if (!finalContent)
534
+ finalContent = '⚠️ The LLM returned an empty response. Please try again.';
535
+ exports.logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
536
+ finalResult = finalContent;
537
+ }
538
+ catch (error) {
539
+ const errorMsg = '⚠️ The system is experiencing LLM API rate limits. Please try again.';
540
+ exports.logger.addEntry({ role: 'assistant', content: errorMsg }, sessionId);
541
+ finalResult = errorMsg;
542
+ }
543
+ }
544
+ return finalResult;
545
+ }
546
+ finally {
547
+ await smartStream.wait();
548
+ }
549
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReasoningScratchpad = void 0;
4
+ /**
5
+ * ReasoningScratchpad — P1: Persistent Reasoning Memory
6
+ *
7
+ * Captures <think> blocks from LLM responses and makes the distilled
8
+ * reasoning available as context for subsequent turns within the same request.
9
+ * This mirrors how Claude/Gemini maintain internal chain-of-thought continuity.
10
+ */
11
+ class ReasoningScratchpad {
12
+ entries = [];
13
+ /**
14
+ * Extract and store the think block from a raw LLM response.
15
+ * Returns the cleaned response (think tags removed) for display.
16
+ */
17
+ capture(rawContent, turn) {
18
+ const thinkRegex = /<(think|thought|thinking|reasoning|analysis|reflection)>([\s\S]*?)<\/\1>/gi;
19
+ let match;
20
+ while ((match = thinkRegex.exec(rawContent)) !== null) {
21
+ const thinkText = match[2].trim();
22
+ if (thinkText.length > 20) {
23
+ // Distil to first 600 chars to avoid bloating the next prompt
24
+ this.entries.push({ turn, summary: thinkText.slice(0, 600) });
25
+ }
26
+ }
27
+ // Return content with think blocks stripped
28
+ return rawContent
29
+ .replace(/<(think|thought|thinking|reasoning|analysis|reflection)>[\s\S]*?<\/\1>\n?/gi, '')
30
+ .trim();
31
+ }
32
+ /**
33
+ * Build an injection string for the next turn's system prompt.
34
+ * Only surfaces the last 2 think blocks to keep token usage lean.
35
+ */
36
+ getInjection() {
37
+ if (this.entries.length === 0)
38
+ return '';
39
+ const recent = this.entries.slice(-2);
40
+ const lines = recent.map(e => `[Turn ${e.turn}] ${e.summary}`).join('\n');
41
+ return `\n\n--- 🧠 REASONING CONTINUITY (your prior internal thoughts) ---\n${lines}\nUse these prior conclusions to avoid re-deriving the same logic.\n`;
42
+ }
43
+ isEmpty() {
44
+ return this.entries.length === 0;
45
+ }
46
+ }
47
+ exports.ReasoningScratchpad = ReasoningScratchpad;