nyxora 26.6.24 → 26.6.26

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 (120) hide show
  1. package/README.md +20 -49
  2. package/dist/packages/core/src/agent/llmProvider.js +48 -33
  3. package/dist/packages/core/src/agent/osAgent.js +25 -205
  4. package/dist/packages/core/src/agent/reasoning.js +15 -117
  5. package/dist/packages/core/src/agent/web3Agent.js +26 -208
  6. package/dist/packages/core/src/config/marketConfigManager.js +43 -0
  7. package/dist/packages/core/src/config/parser.js +14 -1
  8. package/dist/packages/core/src/gateway/cli.js +1 -1
  9. package/dist/packages/core/src/gateway/server.js +90 -91
  10. package/dist/packages/core/src/gateway/telegram.js +5 -5
  11. package/dist/packages/core/src/memory/logger.js +11 -0
  12. package/dist/packages/core/src/memory/reflection.js +2 -2
  13. package/dist/packages/core/src/plugin/PluginManager.js +39 -0
  14. package/dist/packages/core/src/plugin/registry.js +81 -0
  15. package/dist/packages/core/src/plugin/registry.test.js +38 -0
  16. package/dist/packages/core/src/plugin/types.js +2 -0
  17. package/dist/packages/core/src/system/plugins/GoogleWorkspacePlugin.js +34 -0
  18. package/dist/packages/core/src/system/plugins/SystemCorePlugin.js +38 -0
  19. package/dist/packages/core/src/system/plugins/SystemPluginInstallerPlugin.js +85 -0
  20. package/dist/packages/core/src/system/plugins/SystemSocialPlugin.js +23 -0
  21. package/dist/packages/core/src/system/plugins/SystemWebPlugin.js +38 -0
  22. package/dist/packages/core/src/system/plugins/SystemWorkspacePlugin.js +43 -0
  23. package/dist/packages/core/src/system/skills/audioTranscribe.js +2 -2
  24. package/dist/packages/core/src/system/skills/summarizeText.js +2 -2
  25. package/dist/packages/core/src/utils/historySanitizer.js +27 -17
  26. package/dist/packages/core/src/utils/llmUtils.js +126 -0
  27. package/dist/packages/core/src/web3/Web3DefiPlugin.js +28 -0
  28. package/dist/packages/core/src/web3/aggregator/defiRouter.js +12 -12
  29. package/dist/packages/core/src/web3/aggregator/providerHealthService.js +47 -0
  30. package/dist/packages/core/src/web3/aggregator/providerRegistry.js +118 -0
  31. package/dist/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.js +62 -0
  32. package/dist/packages/core/src/web3/aggregator/providers/KyberSwapProvider.js +87 -0
  33. package/dist/packages/core/src/web3/aggregator/providers/LifiProvider.js +80 -0
  34. package/dist/packages/core/src/web3/aggregator/providers/OneInchProvider.js +85 -0
  35. package/dist/packages/core/src/web3/aggregator/providers/OpBridgeProvider.js +72 -0
  36. package/dist/packages/core/src/web3/aggregator/providers/OpenOceanProvider.js +32 -0
  37. package/dist/packages/core/src/web3/aggregator/providers/RelayProvider.js +90 -0
  38. package/dist/packages/core/src/web3/aggregator/providers/ZeroXProvider.js +80 -0
  39. package/dist/packages/core/src/web3/aggregator/quoteValidator.js +36 -0
  40. package/dist/packages/core/src/web3/aggregator/routeScorer.js +31 -0
  41. package/dist/packages/core/src/web3/aggregator/routeSelector.js +79 -0
  42. package/dist/packages/core/src/web3/aggregator/types.js +2 -0
  43. package/dist/packages/core/src/web3/plugins/Web3DefiPlugin.js +68 -0
  44. package/dist/packages/core/src/web3/plugins/Web3SecurityPlugin.js +34 -0
  45. package/dist/packages/core/src/web3/plugins/Web3WalletPlugin.js +51 -0
  46. package/dist/packages/core/src/web3/skills/bridgeToken.js +4 -4
  47. package/dist/packages/core/src/web3/skills/getPrice.js +7 -2
  48. package/dist/packages/core/src/web3/skills/installDefiProvider.js +76 -0
  49. package/dist/packages/core/src/web3/skills/marketAnalysis.js +7 -2
  50. package/dist/packages/core/src/web3/skills/swapToken.js +5 -5
  51. package/dist/packages/core/src/web3/utils/marketEngine.js +32 -27
  52. package/dist/packages/core/src/web3/utils/vaultClient.js +2 -1
  53. package/dist/packages/policy/src/server.js +31 -26
  54. package/package.json +1 -2
  55. package/packages/core/package.json +1 -2
  56. package/packages/core/src/agent/llmProvider.ts +60 -39
  57. package/packages/core/src/agent/osAgent.ts +24 -227
  58. package/packages/core/src/agent/reasoning.ts +16 -127
  59. package/packages/core/src/agent/web3Agent.ts +26 -259
  60. package/packages/core/src/config/marketConfigManager.ts +39 -0
  61. package/packages/core/src/config/parser.ts +22 -7
  62. package/packages/core/src/gateway/cli.ts +1 -1
  63. package/packages/core/src/gateway/server.ts +97 -98
  64. package/packages/core/src/gateway/telegram.ts +5 -5
  65. package/packages/core/src/memory/logger.ts +11 -0
  66. package/packages/core/src/memory/reflection.ts +1 -1
  67. package/packages/core/src/plugin/PluginManager.ts +42 -0
  68. package/packages/core/src/plugin/registry.test.ts +46 -0
  69. package/packages/core/src/plugin/registry.ts +46 -0
  70. package/packages/core/src/plugin/types.ts +13 -0
  71. package/packages/core/src/system/plugins/GoogleWorkspacePlugin.ts +45 -0
  72. package/packages/core/src/system/plugins/SystemCorePlugin.ts +38 -0
  73. package/packages/core/src/system/plugins/SystemPluginInstallerPlugin.ts +88 -0
  74. package/packages/core/src/system/plugins/SystemSocialPlugin.ts +23 -0
  75. package/packages/core/src/system/plugins/SystemWebPlugin.ts +38 -0
  76. package/packages/core/src/system/plugins/SystemWorkspacePlugin.ts +43 -0
  77. package/packages/core/src/system/skills/audioTranscribe.ts +1 -1
  78. package/packages/core/src/system/skills/summarizeText.ts +1 -1
  79. package/packages/core/src/utils/historySanitizer.ts +24 -16
  80. package/packages/core/src/utils/llmUtils.ts +139 -0
  81. package/packages/core/src/web3/aggregator/defiRouter.ts +14 -12
  82. package/packages/core/src/web3/aggregator/providerHealthService.ts +51 -0
  83. package/packages/core/src/web3/aggregator/providerRegistry.ts +90 -0
  84. package/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.ts +64 -0
  85. package/packages/core/src/web3/aggregator/providers/KyberSwapProvider.ts +88 -0
  86. package/packages/core/src/web3/aggregator/providers/LifiProvider.ts +81 -0
  87. package/packages/core/src/web3/aggregator/providers/OneInchProvider.ts +89 -0
  88. package/packages/core/src/web3/aggregator/providers/OpBridgeProvider.ts +76 -0
  89. package/packages/core/src/web3/aggregator/providers/OpenOceanProvider.ts +34 -0
  90. package/packages/core/src/web3/aggregator/providers/RelayProvider.ts +94 -0
  91. package/packages/core/src/web3/aggregator/providers/ZeroXProvider.ts +80 -0
  92. package/packages/core/src/web3/aggregator/quoteValidator.ts +42 -0
  93. package/packages/core/src/web3/aggregator/routeScorer.ts +34 -0
  94. package/packages/core/src/web3/aggregator/routeSelector.ts +95 -0
  95. package/packages/core/src/web3/aggregator/types.ts +98 -0
  96. package/packages/core/src/web3/plugins/Web3DefiPlugin.ts +76 -0
  97. package/packages/core/src/web3/plugins/Web3SecurityPlugin.ts +34 -0
  98. package/packages/core/src/web3/plugins/Web3WalletPlugin.ts +51 -0
  99. package/packages/core/src/web3/skills/bridgeToken.ts +4 -4
  100. package/packages/core/src/web3/skills/getPrice.ts +9 -2
  101. package/packages/core/src/web3/skills/installDefiProvider.ts +83 -0
  102. package/packages/core/src/web3/skills/marketAnalysis.ts +9 -2
  103. package/packages/core/src/web3/skills/swapToken.ts +5 -5
  104. package/packages/core/src/web3/utils/marketEngine.ts +44 -38
  105. package/packages/core/src/web3/utils/vaultClient.ts +4 -1
  106. package/packages/dashboard/dist/assets/index-C_WmWSch.js +16 -0
  107. package/packages/dashboard/dist/index.html +1 -1
  108. package/packages/dashboard/dist/routers/coingecko.png +0 -0
  109. package/packages/dashboard/package.json +3 -2
  110. package/packages/mcp-server/package.json +5 -3
  111. package/packages/policy/package.json +4 -2
  112. package/packages/policy/src/server.ts +27 -29
  113. package/packages/signer/package.json +2 -1
  114. package/packages/signer/src/server.ts +0 -1
  115. package/packages/core/src/gateway/test.ts +0 -16
  116. package/packages/core/src/test_security.ts +0 -45
  117. package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +0 -293
  118. package/packages/core/src/web3/aggregator/aggregatorTestnet.ts +0 -146
  119. package/packages/core/src/web3/skills/nativeOpBridge.ts +0 -84
  120. package/packages/dashboard/dist/assets/index-BLMS9VtQ.js +0 -16
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
  [![Status: Alpha](https://img.shields.io/badge/Status-Alpha-red.svg)](#)
6
- [![Built on Arbitrum](https://img.shields.io/badge/Built_on-Arbitrum-28A0F0?style=flat&logo=arbitrum&logoColor=white)](https://arbitrum.io/)
6
+ [![Built on Base](https://img.shields.io/badge/Built_on-Base-0052FF?style=flat&logo=base&logoColor=white)](https://base.org/)
7
7
  [![MCP Supported](https://img.shields.io/badge/MCP-Supported-blue.svg)](#)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
9
  [![Security: Defense-in-Depth](https://img.shields.io/badge/Security-Defense--in--Depth-blue.svg)](#️-advanced-security-threat-model)
@@ -21,9 +21,9 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
21
21
  ## 🔥 Key Features
22
22
 
23
23
  ### Advanced Security Architecture
24
- * **🛡️ On-Chain AI Kill-Switch**: Nyxora is governed by an Arbitrum Smart Contract (`NyxoraAgentRegistry`). Users have absolute cryptographic power to instantly paralyze the AI's on-chain execution if compromised, solving the Web3 AI safety dilemma. [Read more about our Arbitrum Architecture](https://nyxoraai.github.io/Nyxora/security/smart-contract)
24
+ * **🛡️ On-Chain AI Kill-Switch**: Nyxora is governed by a Base Smart Contract (`NyxoraAgentRegistry`). Users have absolute cryptographic power to instantly paralyze the AI's on-chain execution if compromised, solving the Web3 AI safety dilemma. [Read more about our Base Architecture](https://nyxoraai.github.io/Nyxora/security/smart-contract)
25
25
  * **3-Tier IPC Architecture**: Nyxora is split into isolated processes: **Core** (LLM Runtime), **Policy Engine** (Guardrails on port 3001), and **Signer Vault** (Isolated Key Manager on Unix Sockets).
26
- * **DeFi Configuration BYOK & UI Masking**: All aggregator and provider API keys are strictly isolated via a Bring Your Own Keys (BYOK) architecture into a heavily guarded `~/.nyxora/defi_keys.yaml` file. The local web Dashboard masks these injected secrets using `***********` and `IS_SET` censorship, completely neutralizing malicious browser extensions from exfiltrating your keys.
26
+ * **DeFi & Market Configuration BYOK & UI Masking**: All aggregator, provider, and oracle API keys are strictly isolated via a Bring Your Own Keys (BYOK) architecture into heavily guarded `~/.nyxora/defi_keys.yaml` and `~/.nyxora/market_keys.yaml` files. The local web Dashboard masks these injected secrets using `***********` and `IS_SET` censorship, completely neutralizing malicious browser extensions from exfiltrating your keys.
27
27
  * **Approval Replay Protection (Nonce Guard)**: Transactions requested by the AI are drafted as hashes and signed with a randomized 16-byte Nonce. The `/api/transactions/:id/approve` endpoint strictly enforces Nonce matching to completely eliminate double-spending and Replay Attacks.
28
28
  * **Native Asset Parameter Tampering Protection**: The internal cryptographic HMAC signature rigorously binds `toAddress`, `txData`, and `valueWei`, rendering the system mathematically immune to Native Token (ETH/BNB) destination or amount hijacking via Indirect Prompt Injections.
29
29
  * **Human-in-the-Loop Memory Approval**: AI-extracted permanent behavioral rules are strictly quarantined in a `pending` state until explicitly authorized by the user via the Dashboard, neutralizing Persistent Memory Poisoning vectors.
@@ -35,10 +35,10 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
35
35
  ### 🌐 Web3 Skills (On-Chain)
36
36
  * **Security Scanner**: Nyxora can scan smart contracts via GoPlus Labs to detect Honeypots, Hidden Taxes, and malicious proxy upgrades before you buy.
37
37
  * **Advanced DeFi Optimization**: Autonomously supply assets to Aave V3, deposit into Beefy/Yearn Auto-Compounder Vaults, manage Uniswap V3 Liquidity (LP), and instantly revoke infinite approvals to secure your wallet. Features intelligent Transaction Chaining to auto-approve allowances prior to execution.
38
- * **6-Engine Meta-Aggregator & Anti-MEV**: The core engine interfaces with a powerful 6-Engine Meta-Aggregator (**1inch, 0x, LI.FI, Relay, OpenOcean, and KyberSwap**) to route tokens cross-chain, ensuring absolute maximum liquidity depth.
38
+ * **Extensible 8-Engine Meta-Aggregator & Anti-MEV**: The core engine interfaces with a powerful, extensible Meta-Aggregator (**1inch, 0x, LI.FI, Relay, OpenOcean, KyberSwap, ArbitrumBridge, and OpBridge**) via a dynamic Provider Registry to route tokens cross-chain, ensuring absolute maximum liquidity depth.
39
39
  * **Adaptive Auto Slippage Protection**: Nyxora enforces a dynamic and adaptive **'auto' slippage** by default to leverage dynamic MEV-protection from these industry-standard aggregators. However, the user retains absolute control to override this dynamically—either globally via the Dashboard Settings or on a per-transaction basis through NLP chat commands (e.g., *"Swap 1 ETH to PEPE with 10% slippage"*).
40
40
 
41
- * **Dual-Routing Market Intelligence Engine**: Real-time asset tracking utilizing a sophisticated API Waterfall. Symbol queries are routed to CoinGecko/CEXs for global FDV, while Contract Addresses trigger DexScreener for live on-chain liquidity metrics across all networks.
41
+ * **Dual-Routing Market Intelligence & Smart Fallback Engine**: Real-time asset tracking utilizing a sophisticated API Waterfall. Symbol queries are routed to CoinGecko/CoinMarketCap Pro endpoints (if BYOK is configured) or gracefully fall back to public endpoints. Contract Addresses seamlessly trigger DexScreener for live on-chain liquidity metrics across all networks, guaranteeing robust discovery even for unlisted memecoins.
42
42
  * **Asynchronous Watchdog Agents**: Seamlessly spawn detached background instances for long-running monitoring tasks (e.g., *"Notify me when $ETH drops below $2500"*), leaving your primary chat session free for other operations.
43
43
  * **"Lean Degen" Auto-Whitelist**: Automatically intercepts Contract Addresses (CAs) whenever you check balances or swap tokens, saving them to your localized `user_whitelist.json` for future tracking.
44
44
  * **Dynamic Portfolio Engine**: Merges standard tokens, your custom Degen CAs, and CoinGecko's daily trending list into a single hyper-fast Multicall scan to deliver a clean, spam-free PnL portfolio report in under 1 second.
@@ -95,58 +95,27 @@ To dive deeper into the technical details of our Zero-Knowledge security archite
95
95
 
96
96
  ---
97
97
 
98
- ## 1. 🚀 Quick Start & Installation
98
+ ## 🚀 Quick Start & Installation
99
99
 
100
- ### Global Installation via NPM (Recommended)
101
- The easiest and fastest way to use Nyxora is to install it globally via NPM. This ensures you get the latest version and can run Nyxora from anywhere on your machine.
100
+ ### Option 1: Global Installation (Recommended)
101
+ Nyxora can be installed globally via NPM, allowing you to use the `nyxora` CLI command from anywhere on your machine.
102
102
 
103
- The fastest way to install Nyxora is via our automated installation script:
104
-
105
- **For Linux & macOS (Bash):**
106
103
  ```bash
107
- curl -fsSL https://nyxoraai.github.io/Nyxora/install.sh | bash
108
- ```
109
-
110
- **For Windows (PowerShell):**
111
- ```powershell
112
- iwr https://nyxoraai.github.io/Nyxora/install.ps1 -useb | iex
113
- ```
114
-
115
- Alternatively, you can install it manually on any operating system using NPM:
104
+ # Install globally
105
+ npm install -g nyxora
116
106
 
117
- ```bash
118
- npm install -g nyxora@latest
119
- ```
120
-
121
- ### 2. Run the Interactive Setup Wizard (API Keys, Wallet, Telegram)
122
- ```bash
107
+ # Run the interactive setup wizard (API Keys, Wallet, Telegram)
123
108
  nyxora setup
124
- ```
125
109
 
126
- ### 3. Start the Nyxora background daemon
127
- ```bash
110
+ # Start the background daemon
128
111
  nyxora start
129
- ```
130
112
 
131
- ### 4. Open the Web Dashboard
132
- ```bash
113
+ # Open the interactive UI dashboard
133
114
  nyxora dashboard
134
115
  ```
135
116
 
136
- ### Utility: Atomically clear the AI's short-term and long-term memory
137
- ```bash
138
- nyxora clear --force
139
- ```
140
-
141
- ### Utility: Clean Uninstallation
142
- To completely remove Nyxora, wipe the AI's local memory, and securely delete your Private Key from the OS Keyring before uninstalling the NPM package:
143
- ```bash
144
- nyxora uninstall
145
- ```
146
- > **⚠️ IMPORTANT:** Whenever you re-run `nyxora setup` or manually edit the config files, you **must restart the daemon** by running `nyxora restart` for the changes to take effect.
147
-
148
- ### Local Development (From Source)
149
- If you wish to modify the code or run from source, you can use the Monorepo architecture.
117
+ ### Option 2: Local Development (Source Code)
118
+ Nyxora operates on a Monorepo architecture. To run it locally from the source code, modify its behaviors, or contribute to the repository, follow these steps:
150
119
 
151
120
  ```bash
152
121
  git clone https://github.com/nyxoraAI/Nyxora.git
@@ -158,14 +127,16 @@ npm install
158
127
  # 2. Build the Dashboard UI
159
128
  npm run build
160
129
 
161
- # 3. Interactive Setup Wizard (API Keys, Wallet, Telegram)
130
+ # 3. Interactive Setup Wizard
162
131
  npm run setup
163
132
 
164
133
  # 4. Start the Application
165
134
  npm start
166
135
  ```
136
+
167
137
  *(If you are actively developing and modifying the source code, use `npm run dev` to enable hot-reloading for the frontend and backend).*
168
- > **⚠️ IMPORTANT:** Whenever you re-run `npm run setup` or manually edit the config files, you **must restart the dev server** for the changes to take effect.
138
+
139
+ > **⚠️ IMPORTANT:** Whenever you re-run `nyxora setup` or manually edit the config files, you **must restart the server** for the changes to take effect.
169
140
 
170
141
  ---
171
142
 
@@ -188,7 +159,7 @@ For complete technical deep-dives into our Cryptographic Architecture, please vi
188
159
  **❤️ Support the Project**
189
160
 
190
161
  Building and maintaining a highly secure, zero-trust architecture takes significant time and resources. If you love what we are building, you can help us keep Nyxora open, secure, and constantly evolving by sending a coffee our way:
191
- - **EVM :** `0x490717E50D6434C348AA0D2bD5fe682392823708`
162
+ - **EVM :** `0xF5726f0F185d6304f30c9BAE95c477471E29F14f`
192
163
 
193
164
  ---
194
165
  **License:** MIT License
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GeminiAdapter = exports.AnthropicAdapter = exports.OpenAIAdapter = void 0;
4
- const genai_1 = require("@google/genai");
5
4
  class OpenAIAdapter {
6
5
  client;
7
6
  constructor(client) {
@@ -131,20 +130,20 @@ class AnthropicAdapter {
131
130
  }
132
131
  exports.AnthropicAdapter = AnthropicAdapter;
133
132
  class GeminiAdapter {
134
- client;
135
- constructor(client) {
136
- this.client = client;
133
+ apiKey;
134
+ constructor(apiKey) {
135
+ this.apiKey = apiKey;
137
136
  }
138
137
  async chat(request) {
139
138
  let systemInstruction = '';
140
- const rawGemini = [];
139
+ const contents = [];
141
140
  for (const m of request.messages) {
142
141
  if (m.role === 'system') {
143
142
  systemInstruction = m.content;
144
143
  continue;
145
144
  }
146
145
  if (m.role === 'user') {
147
- rawGemini.push({ role: 'user', parts: [{ text: m.content }] });
146
+ contents.push({ role: 'user', parts: [{ text: m.content }] });
148
147
  }
149
148
  else if (m.role === 'assistant') {
150
149
  const parts = [];
@@ -163,10 +162,12 @@ class GeminiAdapter {
163
162
  catch (e) { }
164
163
  });
165
164
  }
166
- rawGemini.push({ role: 'model', parts: parts });
165
+ if (parts.length > 0) {
166
+ contents.push({ role: 'model', parts: parts });
167
+ }
167
168
  }
168
169
  else if (m.role === 'tool') {
169
- rawGemini.push({
170
+ contents.push({
170
171
  role: 'user',
171
172
  parts: [{
172
173
  functionResponse: {
@@ -177,18 +178,19 @@ class GeminiAdapter {
177
178
  });
178
179
  }
179
180
  }
180
- const geminiMessages = [];
181
- for (const m of rawGemini) {
182
- const last = geminiMessages[geminiMessages.length - 1];
181
+ // Merge adjacent messages of the same role
182
+ const mergedContents = [];
183
+ for (const m of contents) {
184
+ const last = mergedContents[mergedContents.length - 1];
183
185
  if (last && last.role === m.role) {
184
186
  last.parts.push(...m.parts);
185
187
  }
186
188
  else {
187
- geminiMessages.push(m);
189
+ mergedContents.push(m);
188
190
  }
189
191
  }
190
192
  let tools = undefined;
191
- if (request.tools) {
193
+ if (request.tools && request.tools.length > 0) {
192
194
  tools = [{
193
195
  functionDeclarations: request.tools.map(t => ({
194
196
  name: t.function.name,
@@ -197,32 +199,45 @@ class GeminiAdapter {
197
199
  }))
198
200
  }];
199
201
  }
200
- const response = await this.client.models.generateContent({
201
- model: request.model,
202
- contents: geminiMessages,
203
- config: {
204
- systemInstruction: systemInstruction,
205
- tools: tools,
206
- temperature: request.temperature,
207
- safetySettings: [
208
- { category: genai_1.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: genai_1.HarmBlockThreshold.BLOCK_NONE },
209
- { category: genai_1.HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: genai_1.HarmBlockThreshold.BLOCK_NONE },
210
- { category: genai_1.HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: genai_1.HarmBlockThreshold.BLOCK_NONE },
211
- { category: genai_1.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: genai_1.HarmBlockThreshold.BLOCK_NONE }
212
- ]
213
- }
202
+ const payload = {
203
+ contents: mergedContents,
204
+ generationConfig: {
205
+ temperature: request.temperature || 0.7,
206
+ },
207
+ safetySettings: [
208
+ { category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_NONE' },
209
+ { category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_NONE' },
210
+ { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
211
+ { category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', threshold: 'BLOCK_NONE' }
212
+ ]
213
+ };
214
+ if (systemInstruction) {
215
+ payload.systemInstruction = { parts: [{ text: systemInstruction }] };
216
+ }
217
+ if (tools) {
218
+ payload.tools = tools;
219
+ }
220
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${request.model}:generateContent?key=${this.apiKey}`, {
221
+ method: 'POST',
222
+ headers: {
223
+ 'Content-Type': 'application/json'
224
+ },
225
+ body: JSON.stringify(payload)
214
226
  });
227
+ if (!response.ok) {
228
+ const errText = await response.text();
229
+ throw new Error(`Gemini API Error: ${response.status} ${response.statusText} - ${errText}`);
230
+ }
231
+ const data = await response.json();
215
232
  let contentStr = null;
216
233
  let toolCalls = [];
217
- if (response.candidates && response.candidates.length > 0) {
218
- const candidate = response.candidates[0];
219
- // Log finish reason for debugging safety blocks
234
+ if (data.candidates && data.candidates.length > 0) {
235
+ const candidate = data.candidates[0];
220
236
  if (candidate.finishReason && candidate.finishReason !== 'STOP') {
221
237
  console.warn(`[LLM] Gemini API returned finishReason: ${candidate.finishReason}`);
222
238
  }
223
239
  if (candidate.content && candidate.content.parts) {
224
- const parts = candidate.content.parts;
225
- for (const part of parts) {
240
+ for (const part of candidate.content.parts) {
226
241
  if (part.text) {
227
242
  contentStr = (contentStr || '') + part.text;
228
243
  }
@@ -232,7 +247,7 @@ class GeminiAdapter {
232
247
  type: 'function',
233
248
  function: {
234
249
  name: part.functionCall.name,
235
- arguments: JSON.stringify(part.functionCall.args)
250
+ arguments: JSON.stringify(part.functionCall.args || {})
236
251
  }
237
252
  });
238
253
  }
@@ -4,102 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.logger = void 0;
7
- exports.getOpenAI = getOpenAI;
8
7
  exports.processOsIntent = processOsIntent;
9
- const openai_1 = require("openai");
10
8
  const parser_1 = require("../config/parser");
11
9
  const logger_1 = require("../memory/logger");
12
10
  const tracker_1 = require("../gateway/tracker");
13
11
  const episodic_1 = require("../memory/episodic");
14
12
  const skillManager_1 = require("../utils/skillManager");
15
- const updateProfile_1 = require("./updateProfile");
16
- const updateIdentity_1 = require("./updateIdentity");
17
- const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
18
- const analyzeDocument_1 = require("../system/skills/analyzeDocument");
19
- const readFile_1 = require("../system/skills/readFile");
20
- const writeFile_1 = require("../system/skills/writeFile");
21
- const generateExcel_1 = require("../system/skills/generateExcel");
22
- const executeShell_1 = require("../system/skills/executeShell");
23
- const browseWeb_1 = require("../system/skills/browseWeb");
24
- const searchWeb_1 = require("../system/skills/searchWeb");
25
- const editFile_1 = require("../system/skills/editFile");
26
- const gitManager_1 = require("../system/skills/gitManager");
27
- const xManager_1 = require("../system/skills/xManager");
28
- const notionWorkspace_1 = require("../system/skills/notionWorkspace");
29
- const audioTranscribe_1 = require("../system/skills/audioTranscribe");
30
- const summarizeText_1 = require("../system/skills/summarizeText");
31
- const scheduleTask_1 = require("../system/skills/scheduleTask");
32
- const cancelTask_1 = require("../system/skills/cancelTask");
33
- const googleWorkspace_1 = require("../system/skills/googleWorkspace");
13
+ const registry_1 = require("../plugin/registry");
34
14
  const picocolors_1 = __importDefault(require("picocolors"));
35
15
  exports.logger = new logger_1.Logger();
36
- const PROVIDER_CONFIGS = {
37
- ollama: { baseURL: process.env.OLLAMA_BASE_URL ? `${process.env.OLLAMA_BASE_URL}/v1` : 'http://localhost:11434/v1', requiresApiKey: false },
38
- gemini: { baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/', requiresApiKey: true },
39
- openrouter: { baseURL: 'https://openrouter.ai/api/v1', requiresApiKey: true },
40
- groq: { baseURL: 'https://api.groq.com/openai/v1', requiresApiKey: true },
41
- mistral: { baseURL: 'https://api.mistral.ai/v1', requiresApiKey: true },
42
- xai: { baseURL: 'https://api.x.ai/v1', requiresApiKey: true },
43
- deepseek: { baseURL: 'https://api.deepseek.com', requiresApiKey: true },
44
- openai: { requiresApiKey: true }
45
- };
46
- async function getOpenAI() {
47
- const config = (0, parser_1.loadConfig)();
48
- const vaultKeys = await (0, parser_1.loadApiKeys)();
49
- const providerName = config.llm.provider || 'openai';
50
- const providerConf = PROVIDER_CONFIGS[providerName] || PROVIDER_CONFIGS['openai'];
51
- let apiKey = 'local';
52
- if (providerConf.requiresApiKey) {
53
- apiKey = '';
54
- const keyName = `${providerName}_key`;
55
- apiKey = vaultKeys[keyName] || config.credentials?.[keyName] || '';
56
- if (!apiKey) {
57
- throw new Error(`[Security] No API Key found for ${providerName} in OS Keyring. Please run 'nyxora set-key ${providerName} <key>' or 'nyxora setup'.`);
58
- }
59
- console.log(`[LLM] Using API Key securely unlocked from OS Keyring vault.`);
60
- }
61
- return new openai_1.OpenAI({
62
- baseURL: providerConf.baseURL,
63
- apiKey: apiKey,
64
- timeout: 120 * 1000,
65
- maxRetries: 0
66
- });
67
- }
68
- async function executeWithRetry(requestBuilder, maxRetries = 3) {
69
- let retries = 0;
70
- while (retries <= maxRetries) {
71
- try {
72
- const client = await getOpenAI();
73
- return await requestBuilder(client);
74
- }
75
- catch (error) {
76
- const status = error?.status || error?.response?.status;
77
- // 401 Unauthorized or 400 Bad Request - don't retry, it's fatal
78
- if (status === 401 || status === 400) {
79
- console.error(`[LLM] Fatal Error ${status}: ${error.message}. Aborting.`);
80
- throw error;
81
- }
82
- // 429 Rate Limit - rotate provider/key immediately and retry
83
- if (status === 429) {
84
- console.warn(`[LLM] Rate Limit (429) hit. Rotating key...`);
85
- // getOpenAI() automatically rotates to next key if available
86
- retries++;
87
- if (retries > maxRetries)
88
- throw error;
89
- continue; // Try next key immediately
90
- }
91
- // 500, 502, 503, Timeout, Network error - Exponential Backoff
92
- retries++;
93
- if (retries > maxRetries) {
94
- console.error(`[LLM] Max retries reached.`);
95
- throw error;
96
- }
97
- const delayMs = Math.pow(2, retries) * 1000; // 2s, 4s, 8s
98
- console.warn(`[LLM] API Error (${status || error.message}). Retrying in ${delayMs}ms...`);
99
- await new Promise(resolve => setTimeout(resolve, delayMs));
100
- }
101
- }
102
- }
16
+ const llmUtils_1 = require("../utils/llmUtils");
103
17
  function getSystemPrompt(context = 'os') {
104
18
  const config = (0, parser_1.loadConfig)();
105
19
  const currentDateTime = new Date().toLocaleString('id-ID', { timeZone: 'Asia/Jakarta' });
@@ -137,32 +51,24 @@ async function processOsIntent(input, role = 'user', onProgress, sessionId) {
137
51
  exports.logger.addEntry({ role, content: input }, sessionId);
138
52
  const history = exports.logger.getHistory(sessionId);
139
53
  // Format messages for OpenAI
140
- let activeTools = [];
141
- const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateIdentity_1.updateIdentityToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, generateExcel_1.generateExcelToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, editFile_1.editLocalFileToolDefinition, gitManager_1.gitManagerToolDefinition, xManager_1.xManagerToolDefinition, notionWorkspace_1.notionWorkspaceToolDefinition, audioTranscribe_1.audioTranscribeToolDefinition, summarizeText_1.summarizeTextToolDefinition, scheduleTask_1.scheduleTaskDefinition, cancelTask_1.cancelTaskDefinition];
142
- const GOOGLE_TOOLS = [googleWorkspace_1.readGmailInboxToolDefinition, googleWorkspace_1.listCalendarEventsToolDefinition, googleWorkspace_1.appendRowToSheetsToolDefinition, googleWorkspace_1.readGoogleDocsToolDefinition, googleWorkspace_1.readGoogleFormResponsesToolDefinition];
143
- activeTools = [...SYSTEM_TOOLS, ...GOOGLE_TOOLS].filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
54
+ let activeTools = [...registry_1.pluginManager.getAllToolDefinitions()];
55
+ activeTools = activeTools.filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
144
56
  const { sanitizeHistoryForLLM } = require('../utils/historySanitizer');
145
- const sanitizedHistory = sanitizeHistoryForLLM(history, activeTools);
57
+ const sanitizedHistory = sanitizeHistoryForLLM(history, activeTools, config.llm.provider);
146
58
  let messages = [
147
59
  { role: 'system', content: getSystemPrompt('os') },
148
60
  ...sanitizedHistory
149
61
  ];
150
62
  try {
151
- const context = 'os';
152
- const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateIdentity_1.updateIdentityToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, generateExcel_1.generateExcelToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, editFile_1.editLocalFileToolDefinition, gitManager_1.gitManagerToolDefinition, xManager_1.xManagerToolDefinition, notionWorkspace_1.notionWorkspaceToolDefinition, audioTranscribe_1.audioTranscribeToolDefinition, summarizeText_1.summarizeTextToolDefinition, scheduleTask_1.scheduleTaskDefinition, cancelTask_1.cancelTaskDefinition];
153
- const GOOGLE_TOOLS = [googleWorkspace_1.readGmailInboxToolDefinition, googleWorkspace_1.listCalendarEventsToolDefinition, googleWorkspace_1.appendRowToSheetsToolDefinition, googleWorkspace_1.readGoogleDocsToolDefinition, googleWorkspace_1.readGoogleFormResponsesToolDefinition];
154
- let activeTools = [...SYSTEM_TOOLS, ...GOOGLE_TOOLS];
155
- activeTools = activeTools.filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
156
- const response = await executeWithRetry(async (client) => {
157
- return await client.chat.completions.create({
63
+ const response = await (0, llmUtils_1.executeWithRetry)(async (client) => {
64
+ return await client.chat({
158
65
  model: config.llm.model,
159
66
  temperature: config.llm.temperature,
160
67
  messages: messages,
161
- tools: activeTools,
162
- tool_choice: "auto",
68
+ tools: activeTools
163
69
  });
164
70
  });
165
- const responseMessage = response.choices[0].message;
71
+ const responseMessage = response.message;
166
72
  tracker_1.Tracker.addMessage();
167
73
  if (response.usage?.total_tokens) {
168
74
  tracker_1.Tracker.addTokens(response.usage.total_tokens, config.llm.provider);
@@ -214,103 +120,13 @@ async function processOsIntent(input, role = 'user', onProgress, sessionId) {
214
120
  continue;
215
121
  }
216
122
  try {
217
- switch (toolName) {
218
- case 'update_profile': {
219
- result = (0, updateProfile_1.updateProfile)(args.content, args.mode);
220
- break;
221
- }
222
- case 'update_identity': {
223
- result = (0, updateIdentity_1.updateIdentity)(args.content, args.mode);
224
- break;
225
- }
226
- case 'update_security_policy': {
227
- result = await (0, updateSecurityPolicy_1.updateSecurityPolicy)(args.policy, args.action || 'add');
228
- break;
229
- }
230
- case 'analyze_document': {
231
- result = await (0, analyzeDocument_1.analyzeDocument)(args.filePath);
232
- break;
233
- }
234
- case 'read_local_file': {
235
- result = (0, readFile_1.readLocalFile)(args.filePath, args.startLine, args.endLine);
236
- break;
237
- }
238
- case 'edit_local_file': {
239
- result = (0, editFile_1.editLocalFile)(args.filePath, args.searchString, args.replacementString);
240
- break;
241
- }
242
- case 'execute_git_command': {
243
- result = await (0, gitManager_1.executeGitCommand)(args.action, args.commitMessage);
244
- break;
245
- }
246
- case 'manage_twitter': {
247
- result = await (0, xManager_1.manageTwitter)(args.action, args.content, args.username);
248
- break;
249
- }
250
- case 'manage_notion': {
251
- result = await (0, notionWorkspace_1.manageNotion)(args.action, args.pageId, args.text);
252
- break;
253
- }
254
- case 'transcribe_audio': {
255
- result = await (0, audioTranscribe_1.transcribeAudio)(args.filePath);
256
- break;
257
- }
258
- case 'summarize_text': {
259
- result = await (0, summarizeText_1.summarizeText)(args.text, args.focus);
260
- break;
261
- }
262
- case 'write_local_file': {
263
- result = (0, writeFile_1.writeLocalFile)(args.filePath, args.content);
264
- break;
265
- }
266
- case 'generate_excel_file': {
267
- result = await (0, generateExcel_1.generateExcelFile)(args.data, args.filePath);
268
- break;
269
- }
270
- case 'run_terminal_command': {
271
- result = await (0, executeShell_1.runTerminalCommand)(args.command);
272
- break;
273
- }
274
- case 'browse_website': {
275
- result = await (0, browseWeb_1.browseWebsite)(args.url);
276
- break;
277
- }
278
- case 'search_web': {
279
- result = await (0, searchWeb_1.searchWeb)(args.query, args.depth);
280
- break;
281
- }
282
- case 'schedule_task': {
283
- result = await (0, scheduleTask_1.executeScheduleTask)(args);
284
- break;
285
- }
286
- case 'cancel_task': {
287
- result = await (0, cancelTask_1.executeCancelTask)(args);
288
- break;
289
- }
290
- case 'read_gmail_inbox': {
291
- result = await (0, googleWorkspace_1.readGmailInbox)(args.maxResults);
292
- break;
293
- }
294
- case 'list_calendar_events': {
295
- result = await (0, googleWorkspace_1.listCalendarEvents)(args.maxResults);
296
- break;
297
- }
298
- case 'append_row_to_sheets': {
299
- result = await (0, googleWorkspace_1.appendRowToSheets)(args.spreadsheetId, args.range, args.values);
300
- break;
301
- }
302
- case 'read_google_docs': {
303
- result = await (0, googleWorkspace_1.readGoogleDocs)(args.documentId);
304
- break;
305
- }
306
- case 'read_google_form_responses': {
307
- result = await (0, googleWorkspace_1.readGoogleFormResponses)(args.formId);
308
- break;
309
- }
310
- default: {
311
- result = `Error: Tool ${toolName} is not implemented.`;
312
- break;
313
- }
123
+ // 1. Execute via PluginManager
124
+ const pluginResult = await registry_1.pluginManager.executeTool(toolName, args, { sessionId });
125
+ if (pluginResult !== null) {
126
+ result = pluginResult;
127
+ }
128
+ else {
129
+ result = `Error: Tool ${toolName} is not implemented.`;
314
130
  }
315
131
  if (result.includes('[Security Blocked]') || result.startsWith('Error:')) {
316
132
  console.log(picocolors_1.default.red(`[❌ Failed] Tool ${toolName} returned an error or was blocked.`));
@@ -342,13 +158,13 @@ async function processOsIntent(input, role = 'user', onProgress, sessionId) {
342
158
  return finalContent;
343
159
  }
344
160
  // Second call to get the final answer after tool execution
345
- const secondSanitized = sanitizeHistoryForLLM(exports.logger.getHistory(sessionId), activeTools);
161
+ const secondSanitized = sanitizeHistoryForLLM(exports.logger.getHistory(sessionId), activeTools, config.llm.provider);
346
162
  const secondMessages = [
347
163
  { role: 'system', content: getSystemPrompt('os') },
348
164
  ...secondSanitized
349
165
  ];
350
- const secondResponse = await executeWithRetry(async (client) => {
351
- return await client.chat.completions.create({
166
+ const secondResponse = await (0, llmUtils_1.executeWithRetry)(async (client) => {
167
+ return await client.chat({
352
168
  model: config.llm.model,
353
169
  messages: secondMessages,
354
170
  });
@@ -357,7 +173,7 @@ async function processOsIntent(input, role = 'user', onProgress, sessionId) {
357
173
  tracker_1.Tracker.addTokens(secondResponse.usage.total_tokens, config.llm.provider);
358
174
  }
359
175
  tracker_1.Tracker.addEvent('llm.final_response', { provider: config.llm.provider });
360
- let finalContent = secondResponse.choices[0].message.content || "";
176
+ let finalContent = secondResponse.message.content || "";
361
177
  // Clean up orphaned <think> blocks that forgot to output </think>
362
178
  finalContent = finalContent.replace(/<thought>[\s\S]*?<\/thought>\n?/gi, '');
363
179
  finalContent = finalContent.replace(/<think>[\s\S]*?<\/think>\n?/gi, '');
@@ -382,7 +198,11 @@ async function processOsIntent(input, role = 'user', onProgress, sessionId) {
382
198
  }
383
199
  catch (error) {
384
200
  console.error("LLM Error:", error);
385
- const errorMsg = '⚠️ All models are temporarily rate-limited. Please try again in a few minutes.';
201
+ const status = error?.status || error?.response?.status;
202
+ let errorMsg = '⚠️ All models are temporarily rate-limited. Please try again in a few minutes.';
203
+ if (status === 400 || (error.message && error.message.toLowerCase().includes('invalid'))) {
204
+ errorMsg = '⚠️ Terjadi kesalahan pemahaman instruksi. LLM kesulitan menentukan format alat (skill) yang cocok. Silakan coba deskripsikan perintah Anda dengan lebih spesifik.';
205
+ }
386
206
  exports.logger.addEntry({ role: 'assistant', content: errorMsg }, sessionId);
387
207
  return errorMsg;
388
208
  }