nyxora 26.7.2-alpha.4 → 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 (70) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +19 -15
  3. package/bin/nyxora.mjs +4 -6
  4. package/dist/launcher.js +44 -8
  5. package/dist/packages/core/src/agent/cronManager.js +2 -2
  6. package/dist/packages/core/src/agent/llmProvider.js +6 -2
  7. package/dist/packages/core/src/agent/nyxDaemon.js +24 -37
  8. package/dist/packages/core/src/agent/osAgent.js +88 -30
  9. package/dist/packages/core/src/agent/reasoning.js +225 -107
  10. package/dist/packages/core/src/agent/reasoningScratchpad.js +47 -0
  11. package/dist/packages/core/src/agent/web3Agent.js +108 -42
  12. package/dist/packages/core/src/cognitive/cognitiveManager.js +63 -10
  13. package/dist/packages/core/src/gateway/cli.js +3 -0
  14. package/dist/packages/core/src/gateway/setup-ml.js +64 -0
  15. package/dist/packages/core/src/gateway/setup.js +21 -1
  16. package/dist/packages/core/src/gateway/telegram.js +19 -8
  17. package/dist/packages/core/src/memory/episodic.js +37 -2
  18. package/dist/packages/core/src/memory/promotionEngine.js +18 -5
  19. package/dist/packages/core/src/system/agentskills.js +10 -0
  20. package/dist/packages/core/src/system/skills/scheduleTask.js +7 -2
  21. package/dist/packages/core/src/utils/contextSummarizer.js +82 -0
  22. package/dist/packages/core/src/utils/skillManager.js +1 -1
  23. package/dist/packages/core/src/utils/streamSimulator.js +18 -8
  24. package/dist/packages/core/src/web3/plugins/Web3MarketPlugin.js +4 -124
  25. package/dist/packages/core/src/web3/skills/checkPortfolio.js +20 -2
  26. package/dist/packages/core/src/web3/skills/confirmPendingTx.js +1 -1
  27. package/dist/packages/core/src/web3/skills/getPrice.js +134 -27
  28. package/dist/packages/core/src/web3/skills/marketAnalysis.js +26 -191
  29. package/dist/packages/core/src/web3/utils/marketEngine.js +26 -33
  30. package/launcher.ts +38 -9
  31. package/package.json +1 -1
  32. package/packages/core/package.json +1 -1
  33. package/packages/core/src/agent/cronManager.ts +2 -2
  34. package/packages/core/src/agent/llmProvider.ts +6 -2
  35. package/packages/core/src/agent/nyxDaemon.ts +24 -41
  36. package/packages/core/src/agent/osAgent.ts +97 -33
  37. package/packages/core/src/agent/reasoning.ts +237 -109
  38. package/packages/core/src/agent/reasoningScratchpad.ts +45 -0
  39. package/packages/core/src/agent/web3Agent.ts +115 -45
  40. package/packages/core/src/cognitive/cognitiveManager.ts +65 -10
  41. package/packages/core/src/cognitive/prompts/web3/market-analysis.md +24 -0
  42. package/packages/core/src/cognitive/prompts/web3/portfolio-review.md +29 -0
  43. package/packages/core/src/cognitive/prompts/web3/risk-assessment.md +28 -0
  44. package/packages/core/src/cognitive/prompts/web3/trade-planning.md +30 -0
  45. package/packages/core/src/gateway/cli.ts +4 -0
  46. package/packages/core/src/gateway/setup-ml.ts +68 -0
  47. package/packages/core/src/gateway/setup.ts +23 -1
  48. package/packages/core/src/gateway/telegram.ts +20 -10
  49. package/packages/core/src/memory/episodic.ts +52 -2
  50. package/packages/core/src/memory/promotionEngine.ts +19 -5
  51. package/packages/core/src/system/agentskills.ts +10 -0
  52. package/packages/core/src/system/skills/scheduleTask.ts +7 -2
  53. package/packages/core/src/utils/contextSummarizer.ts +100 -0
  54. package/packages/core/src/utils/skillManager.ts +1 -1
  55. package/packages/core/src/utils/streamSimulator.ts +21 -7
  56. package/packages/core/src/web3/plugins/Web3MarketPlugin.ts +5 -124
  57. package/packages/core/src/web3/skills/checkPortfolio.ts +21 -2
  58. package/packages/core/src/web3/skills/confirmPendingTx.ts +1 -1
  59. package/packages/core/src/web3/skills/getPrice.ts +134 -30
  60. package/packages/core/src/web3/skills/marketAnalysis.ts +43 -188
  61. package/packages/core/src/web3/utils/marketEngine.ts +27 -33
  62. package/packages/dashboard/dist/assets/{index-B1yTXubl.js → index-BTfp141V.js} +1 -1
  63. package/packages/dashboard/dist/assets/index-DK2sTU47.css +1 -0
  64. package/packages/dashboard/dist/index.html +2 -2
  65. package/packages/dashboard/package.json +1 -1
  66. package/packages/mcp-server/package.json +1 -1
  67. package/packages/policy/package.json +1 -1
  68. package/packages/signer/package.json +1 -1
  69. package/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -41
  70. package/packages/dashboard/dist/assets/index-CLpiTiQH.css +0 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepashangelog.com/en/1.0.0/),
6
+
7
+ ## [26.7.3]
8
+ ### Bug Fixes & Improvements
9
+ - **Daemon Graceful Shutdown (Port 8000)**: Improved the `npm run stop` behavior by injecting a `forceKill` (`SIGKILL`) method within `launcher.ts`, explicitly terminating detached ML Engine processes (`uvicorn`) and `ts-node` instances that were previously hanging and preventing clean reboots.
10
+ - **Telegram Connectivity Timeout**: Resolved a persistent `Network request for 'getUpdates' failed!` issue in the Telegram integration. Enforced `dns.setDefaultResultOrder('ipv4first')` in `cli.ts` to bypass dual-stack IPv6 conflicts and ensure stable grammatical API fetching.
11
+ ## [26.7.2-alpha.5]
12
+ ### Features & Architecture (Python ML Engine)
13
+ - **Local Python ML Engine Integration**: Successfully integrated a local Python-based Machine Learning Engine (FastAPI + LangChain + Pandas) alongside the core Node.js gateway. This massively enhances Nyxora's analytical and cognitive capabilities.
14
+ - **Cognitive Memory & RAG**: Shifted Persona Dialectic Reasoning and Episodic Memory Semantic Search (RAG) to the new Python Engine. Integrated `langchain_huggingface` using the local `all-MiniLM-L6-v2` embedding model for ultra-fast, offline vector processing without API costs.
15
+ - **Market Intelligence Delegation**: Completely refactored `marketPlugin.ts` (Node.js) to delegate deep market analysis and momentum calculations directly to the Python ML Engine (`/web3/analyze`), significantly reducing redundant API calls and code overlap.
16
+
6
17
  ## [26.7.2-alpha.4]
7
18
  ### Features & Platform Integrations
8
19
  - **Telegram Native Streaming (Bot API 9.3+)**: Radically overhauled the Telegram bot integration to completely bypass the standard 1-second `editMessageText` API rate limit. The engine now natively implements the modern `sendMessageDraft` method, streaming ephemeral hardware-accelerated "Typing..." animations directly to the Telegram client at 100ms intervals. This fully resolves UI stuttering and achieves ultra-smooth, real-time typewriter effects matching premium web interfaces.
package/README.md CHANGED
@@ -55,9 +55,13 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
55
55
 
56
56
  **💻 Core Technologies**
57
57
  <p align="center">
58
+ <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/Node.js-43853D?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js"></a>
58
59
  <a href="https://react.dev/"><img src="https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB" alt="React"></a>
59
60
  <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript"></a>
60
61
  <a href="https://viem.sh/"><img src="https://img.shields.io/badge/Viem-1E1E2E?style=for-the-badge&logo=v&logoColor=white" alt="Viem"></a>
62
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white" alt="Python"></a>
63
+ <a href="https://fastapi.tiangolo.com/"><img src="https://img.shields.io/badge/FastAPI-009688?style=for-the-badge&logo=fastapi&logoColor=white" alt="FastAPI"></a>
64
+ <a href="https://www.langchain.com/"><img src="https://img.shields.io/badge/LangChain-1C3C3C?style=for-the-badge&logo=langchain&logoColor=white" alt="LangChain"></a>
61
65
  </p>
62
66
 
63
67
  <br/>
@@ -68,7 +72,7 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
68
72
 
69
73
  ### Advanced Security Architecture
70
74
  * **🛡️ 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/smart-contract)
71
- * **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).
75
+ * **4-Tier IPC Architecture**: Nyxora is split into isolated processes: **Core** (Node.js LLM Runtime), **ML Engine** (Python Cognitive Sidecar on port 8000), **Policy Engine** (Guardrails on port 3001), and **Signer Vault** (Isolated Key Manager on Unix Sockets).
72
76
  * **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.
73
77
  * **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.
74
78
  * **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.
@@ -117,14 +121,15 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
117
121
 
118
122
  ## 📐 Architecture Workflow
119
123
 
120
- The following diagram illustrates Nyxora's **3-Tier Monorepo Architecture**, showing the isolated communication channels (REST API and Unix Socket).
124
+ The following diagram illustrates Nyxora's **4-Tier Hybrid Architecture**, showing the isolated communication channels (REST API and Unix Socket).
121
125
 
122
126
  ![Architecture Workflow](https://raw.githubusercontent.com/perasyudha/Nyxora/main/assets/architecture.svg)
123
127
 
124
- *Nyxora separates its duties into 3 independent layers for absolute security:*
125
- 1. **🧠 Core (The AI Brain)**: The intelligent assistant that strategizes and plans transactions, but **never** holds your funds.
126
- 2. **🛡️ Policy Engine (The Guard)**: The security guard that verifies the Brain's plans. If the AI attempts to send funds exceeding your set limits, this engine automatically blocks it.
127
- 3. **🔒 Signer Vault (The Safe)**: The offline vault where your Private Keys **and highly sensitive 3rd-party tokens (e.g., Google Workspace OAuth)** are securely locked natively in your OS Keyring (GNOME Keyring / macOS Keychain / Windows Credential Manager). It only signs transactions after they pass all rigorous security checks.
128
+ *Nyxora separates its duties into 4 independent layers for absolute security and cognitive depth:*
129
+ 1. **🧠 Core (The AI Brain)**: The Node.js intelligent assistant that strategizes and plans transactions, but **never** holds your funds.
130
+ 2. **🧬 ML Engine (Cognitive Sidecar)**: A local Python/FastAPI sidecar running LangChain and HuggingFace models for hyper-fast Semantic RAG memory and Pandas-based technical market analysis.
131
+ 3. **🛡️ Policy Engine (The Guard)**: The security guard that verifies the Brain's plans. If the AI attempts to send funds exceeding your set limits, this engine automatically blocks it.
132
+ 4. **🔒 Signer Vault (The Safe)**: The offline vault where your Private Keys **and highly sensitive 3rd-party tokens (e.g., Google Workspace OAuth)** are securely locked natively in your OS Keyring. It only signs transactions after they pass all rigorous security checks.
128
133
 
129
134
  ### Web3 Separation of Concerns (Zero-Trust Routing)
130
135
  Within the AI Brain, the Web3 codebase is strictly divided to prevent the LLM from hallucinating or maliciously manipulating low-level routing paths:
@@ -144,8 +149,11 @@ To dive deeper into the technical details of our Zero-Knowledge security archite
144
149
 
145
150
  ## 🚀 Quick Start & Installation
146
151
 
152
+ ### Prerequisites
153
+ Nyxora requires **Node.js 18+** and **Python 3.10+** (for the ML Cognitive Engine) to be installed on your system.
154
+
147
155
  ### Option 1: One-Line Installation (Recommended)
148
- The fastest way to install Nyxora is via our smart installation wrapper. This script automatically prepares Node.js (if missing) and securely fetches the Nyxora daemon directly from the NPM Registry.
156
+ The fastest way to install Nyxora is via our smart installation wrapper. This script automatically prepares Node.js (if missing) and securely fetches the Nyxora daemon directly from the NPM Registry. *(Note: You must have Python 3.10+ pre-installed on your system, as this script only handles Node.js dependencies).*
149
157
 
150
158
  **Linux & macOS:**
151
159
  ```bash
@@ -164,7 +172,8 @@ If you already have Node.js installed, you can natively install Nyxora globally
164
172
  # Install globally
165
173
  npm install -g nyxora
166
174
 
167
- # Run the interactive setup wizard (API Keys, Wallet, Telegram)
175
+ # Run the interactive setup wizard
176
+ # (Automatically validates Node.js & Python 3.10+ requirements, configures API Keys, Wallet, and ML Environment)
168
177
  nyxora setup
169
178
 
170
179
  # Start the background daemon
@@ -187,10 +196,10 @@ npm install
187
196
  # 2. Build the Core, MCP Server, and Dashboard UI
188
197
  npm run build
189
198
 
190
- # 3. Interactive Setup Wizard
199
+ # 3. Interactive Setup Wizard (Will also install Python ML requirements via pip)
191
200
  npm run setup
192
201
 
193
- # 4. Start the Application
202
+ # 4. Start the Application (Spawns Node.js Core and Python FastAPI sidecar)
194
203
  npm start
195
204
  ```
196
205
 
@@ -233,11 +242,6 @@ For complete technical deep-dives into our Cryptographic Architecture, please vi
233
242
 
234
243
  ---
235
244
 
236
- **❤️ Support the Project**
237
-
238
- 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:
239
- - **EVM :** `0x18a30D5DB50D287dbA669c5672CD71246CC4c4c6`
240
-
241
245
  ---
242
246
  **License:** MIT License
243
247
 
package/bin/nyxora.mjs CHANGED
@@ -78,7 +78,7 @@ async function start() {
78
78
  });
79
79
 
80
80
  child.unref();
81
-
81
+
82
82
  if (child.pid) {
83
83
  fs.writeFileSync(pidFile, child.pid.toString());
84
84
  console.log(`Nyxora daemon started (PID: ${child.pid}).`);
@@ -92,20 +92,18 @@ async function stop(preserveTracker = false) {
92
92
  console.log(`Stopping Nyxora daemon (PID: ${pid})...`);
93
93
  try {
94
94
  process.kill(-pid, 'SIGTERM');
95
-
96
- // Wait for process to exit to avoid race condition with flushState
97
95
  let attempts = 0;
98
96
  while (isDaemonRunning(pid.toString()) && attempts < 20) {
99
97
  await new Promise(r => setTimeout(r, 100));
100
98
  attempts++;
101
99
  }
102
-
103
100
  console.log('Nyxora stopped gracefully.');
104
101
  } catch (e) {
105
- console.error('Failed to kill process:', e.message);
102
+ console.error('Failed to kill Nyxora process:', e.message);
106
103
  }
104
+
107
105
  try {
108
- fs.unlinkSync(pidFile);
106
+ if (fs.existsSync(pidFile)) fs.unlinkSync(pidFile);
109
107
  if (!preserveTracker) {
110
108
  const trackerFile = path.join(appDir, 'run', 'tracker.json');
111
109
  if (fs.existsSync(trackerFile)) fs.unlinkSync(trackerFile);
package/dist/launcher.js CHANGED
@@ -26,20 +26,28 @@ const env = {
26
26
  ...process.env,
27
27
  INTERNAL_AUTH_TOKEN,
28
28
  SIGNER_SOCKET_PATH: '/tmp/nyxora-signer.sock',
29
- TS_NODE_CACHE: 'false'
29
+ TS_NODE_CACHE: 'false',
30
+ PYTHONUNBUFFERED: '1'
30
31
  };
31
- const spawnService = (name, command, args, env, inheritStdio = false) => {
32
+ const spawnService = (name, command, args, env, inheritStdio = false, cwd) => {
32
33
  let child;
33
34
  let crashCount = 0;
34
35
  let crashWindowStart = Date.now();
35
36
  let isShuttingDown = false;
36
37
  const startProcess = () => {
37
- child = (0, child_process_1.spawn)(command, args, { env, stdio: inheritStdio ? 'inherit' : 'pipe' });
38
+ const spawnOpts = { env, stdio: inheritStdio ? 'inherit' : 'pipe' };
39
+ if (cwd)
40
+ spawnOpts.cwd = cwd;
41
+ child = (0, child_process_1.spawn)(command, args, spawnOpts);
42
+ child.on('error', (err) => {
43
+ console.error(`[Launcher] Failed to spawn ${name}:`, err.message);
44
+ isShuttingDown = true; // Prevent retry loop if spawn fails
45
+ });
38
46
  if (!inheritStdio) {
39
47
  child.stdout?.on('data', (data) => process.stdout.write(`[${name}] ${data}`));
40
48
  child.stderr?.on('data', (data) => {
41
49
  const msg = data.toString();
42
- if (msg.toLowerCase().includes('warn')) {
50
+ if (msg.toLowerCase().includes('warn') || msg.toLowerCase().includes('info')) {
43
51
  process.stderr.write(`[${name}] ${msg}`);
44
52
  }
45
53
  else {
@@ -98,6 +106,14 @@ const spawnService = (name, command, args, env, inheritStdio = false) => {
98
106
  }
99
107
  catch (e) { }
100
108
  }
109
+ },
110
+ forceKill: () => {
111
+ if (child && !child.killed && child.pid) {
112
+ try {
113
+ process.kill(child.pid, 'SIGKILL');
114
+ }
115
+ catch (e) { }
116
+ }
101
117
  }
102
118
  };
103
119
  };
@@ -122,10 +138,23 @@ setTimeout(() => {
122
138
  const policy = spawnService('Policy', cmd, [...baseArgs, policyPath], env);
123
139
  children.push(policy);
124
140
  setTimeout(() => {
125
- const corePath = path_1.default.join(__dirnameResolved, `packages/core/src/gateway/cli${ext}`);
126
- const args = process.argv.slice(2);
127
- const core = spawnService('Core', cmd, [...baseArgs, corePath, ...args], env, true);
128
- children.push(core);
141
+ // Spawn ML Engine (Python Sidecar)
142
+ const pythonPath = path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', '.nyxora', 'ml-engine', 'venv', 'bin', 'python');
143
+ if (fs_1.default.existsSync(pythonPath)) {
144
+ const mlDir = path_1.default.join(__dirnameResolved, 'packages', 'ml-engine');
145
+ const mlArgs = ['-m', 'uvicorn', 'main:app', '--host', '127.0.0.1', '--port', '8000'];
146
+ const mlEngine = spawnService('ML Engine', pythonPath, mlArgs, env, false, mlDir);
147
+ children.push(mlEngine);
148
+ }
149
+ else {
150
+ console.warn('[Launcher] Warning: Python virtual environment not found. Did you run setup?');
151
+ }
152
+ setTimeout(() => {
153
+ const corePath = path_1.default.join(__dirnameResolved, `packages/core/src/gateway/cli${ext}`);
154
+ const args = process.argv.slice(2);
155
+ const core = spawnService('Core', cmd, [...baseArgs, corePath, ...args], env, true);
156
+ children.push(core);
157
+ }, 1000);
129
158
  }, 1000);
130
159
  }, 1000);
131
160
  // Ensure all child processes are killed when launcher exits
@@ -138,8 +167,15 @@ const cleanup = () => {
138
167
  children.forEach(c => c.kill());
139
168
  // Give them a moment to cleanup
140
169
  setTimeout(() => {
170
+ children.forEach(c => {
171
+ try {
172
+ c.forceKill();
173
+ }
174
+ catch (e) { }
175
+ });
141
176
  try {
142
177
  require('child_process').execSync('pkill -f ts-node');
178
+ require('child_process').execSync('pkill -f uvicorn');
143
179
  }
144
180
  catch (e) { }
145
181
  process.exit(0);
@@ -44,7 +44,7 @@ const crypto_1 = require("crypto");
44
44
  const picocolors_1 = __importDefault(require("picocolors"));
45
45
  class CronManager {
46
46
  jobs = new Map();
47
- addJob(expression, prompt) {
47
+ addJob(expression, prompt, sessionId) {
48
48
  const id = (0, crypto_1.randomUUID)();
49
49
  // Validate expression
50
50
  try {
@@ -59,7 +59,7 @@ class CronManager {
59
59
  // Dynamically import processUserInput to avoid circular dependencies
60
60
  const { processUserInput } = await Promise.resolve().then(() => __importStar(require('./reasoning')));
61
61
  // Execute the prompt as a background system task
62
- const response = await processUserInput(prompt, 'system', undefined, `cron-${id}`);
62
+ const response = await processUserInput(prompt, 'system', undefined, sessionId || `cron-${id}`);
63
63
  // Push notification to Telegram if configured
64
64
  const config = (0, parser_1.loadConfig)();
65
65
  if (config.integrations?.telegram?.enabled && config.integrations?.telegram?.authorized_chat_id) {
@@ -51,9 +51,13 @@ class OpenAIAdapter {
51
51
  }
52
52
  };
53
53
  }
54
- catch {
54
+ catch (e) {
55
55
  // Fallback to non-streaming if streaming fails
56
- return this.chat(request);
56
+ const chatRes = await this.chat(request);
57
+ if (chatRes.message.content) {
58
+ onChunk(chatRes.message.content);
59
+ }
60
+ return chatRes;
57
61
  }
58
62
  }
59
63
  }
@@ -4,8 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.nyxDaemon = exports.NyxDaemon = void 0;
7
- const parser_1 = require("../config/parser");
8
- const llmUtils_1 = require("../utils/llmUtils");
9
7
  const episodic_1 = require("../memory/episodic");
10
8
  const reasoning_1 = require("./reasoning");
11
9
  const picocolors_1 = __importDefault(require("picocolors"));
@@ -16,10 +14,10 @@ class NyxDaemon {
16
14
  start() {
17
15
  if (this.interval)
18
16
  return;
19
- // Initial run after 5 minutes
17
+ // Initial run after 30 seconds (faster persona availability for new sessions)
20
18
  this.initialTimeout = setTimeout(() => {
21
19
  this.runAudit();
22
- }, 5 * 60 * 1000);
20
+ }, 30 * 1000);
23
21
  // Audit memory every 30 minutes
24
22
  this.interval = setInterval(() => {
25
23
  this.runAudit();
@@ -57,45 +55,34 @@ class NyxDaemon {
57
55
  this.isProcessing = false;
58
56
  return;
59
57
  }
60
- const config = (0, parser_1.loadConfig)();
61
- const prompt = `You are Nyx, Nyxora's background Persona Auditor.
62
- Analyze the following recent conversation between the USER and Nyxora.
63
- Identify any persistent user traits, behavioral preferences, trading styles, AND language preferences.
64
- Output your findings AS A STRICT JSON ARRAY of strings. If no strong traits are found, output an empty array [].
65
- Examples of valid traits:
66
- - Behavior: "Prefers concise answers", "Aggressive trader", "Risk-averse", "Polite", "Often trades on Arbitrum"
67
- - Language: "Primarily speaks Indonesian", "Uses English for technical terms", "Communicates in casual/informal tone", "Mixes Indonesian and English (code-switching)"
68
- IMPORTANT: Always include a language preference trait if the user's language or communication style is identifiable.
69
- DO NOT output markdown, just the JSON array.`;
70
- const messages = conversationOnly
71
- .map((m) => `${m.role === 'user' ? 'USER' : 'NYXORA'}: ${m.content}`)
72
- .join('\n');
73
- const response = await (0, llmUtils_1.executeWithRetry)(async (client) => {
74
- return await client.chat({
75
- model: config.llm.model,
76
- messages: [
77
- { role: 'system', content: prompt },
78
- { role: 'user', content: messages }
79
- ],
80
- temperature: 0.2
81
- });
58
+ // Kirim riwayat percakapan ke Python ML Engine untuk diproses oleh LangChain
59
+ const res = await fetch('http://localhost:8000/cognitive/reason', {
60
+ method: 'POST',
61
+ headers: { 'Content-Type': 'application/json' },
62
+ body: JSON.stringify({ messages: conversationOnly })
82
63
  });
83
- let content = response.message.content || '[]';
84
- // Clean markdown if any
85
- content = content.replace(/\`\`\`json/g, '').replace(/\`\`\`/g, '').trim();
64
+ if (!res.ok) {
65
+ throw new Error(`Python ML Engine returned ${res.status}: ${await res.text()}`);
66
+ }
67
+ const traits = await res.json();
86
68
  try {
87
- const traits = JSON.parse(content);
88
- if (Array.isArray(traits) && traits.length > 0) {
89
- for (const trait of traits) {
90
- // Fix C: Start confidence at 0.4 (not 0.8) so traits build up gradually across audits.
91
- // updatePersonaTrait has upsert logic: repeated traits gain +0.4 * 0.2 per audit cycle.
92
- episodic_1.episodicDB.updatePersonaTrait(trait, 0.4, 'nyx_daemon');
93
- console.log(picocolors_1.default.magenta(`[Nyx] Discovered new trait: ${trait}`));
69
+ if (traits && typeof traits === 'object' && !Array.isArray(traits)) {
70
+ const categories = ['language', 'tone', 'trading_style', 'behavior'];
71
+ for (const cat of categories) {
72
+ const value = traits[cat];
73
+ if (value && typeof value === 'string' && value.trim()) {
74
+ // Category-based upsert: confidence accumulates correctly per category
75
+ episodic_1.episodicDB.upsertPersonaByCategory(cat, value.trim(), 0.5, 'nyx_daemon');
76
+ console.log(picocolors_1.default.magenta(`[Nyx] Updated persona [${cat}]: ${value.trim()}`));
77
+ }
94
78
  }
95
79
  }
80
+ else {
81
+ console.log(picocolors_1.default.magenta('[Nyx] No strong traits found in this audit cycle.'));
82
+ }
96
83
  }
97
84
  catch (e) {
98
- console.error(picocolors_1.default.red('[Nyx] Failed to parse JSON traits'), content);
85
+ console.error(picocolors_1.default.red('[Nyx] Failed to process traits from Python'), traits);
99
86
  }
100
87
  }
101
88
  catch (e) {
@@ -6,12 +6,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.logger = void 0;
7
7
  exports.processOsIntent = processOsIntent;
8
8
  exports.processOsIntentStream = processOsIntentStream;
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 tracker_1 = require("../gateway/tracker");
12
13
  const episodic_1 = require("../memory/episodic");
13
14
  const skillManager_1 = require("../utils/skillManager");
14
15
  const cognitiveManager_1 = require("../cognitive/cognitiveManager");
16
+ const reasoningScratchpad_1 = require("./reasoningScratchpad");
17
+ const contextSummarizer_1 = require("../utils/contextSummarizer");
15
18
  const EXECUTION_DISCIPLINE = `
16
19
  <tool_persistence>
17
20
  Use tools whenever they can increase the accuracy, completeness, or factual correctness of your response.
@@ -37,10 +40,11 @@ NEVER fabricate, hallucinate, or forge tool outputs.
37
40
  </task_completion>
38
41
  `;
39
42
  const registry_1 = require("../plugin/registry");
43
+ const paths_1 = require("../config/paths");
40
44
  const picocolors_1 = __importDefault(require("picocolors"));
41
45
  exports.logger = new logger_1.Logger();
42
46
  const llmUtils_1 = require("../utils/llmUtils");
43
- function getSystemPrompt(context = 'os', userInput = '') {
47
+ async function getSystemPrompt(context = 'os', userInput = '') {
44
48
  const config = (0, parser_1.loadConfig)();
45
49
  const currentDateTime = new Date().toLocaleString('en-US');
46
50
  let basePrompt = `You are Nyxora's OS Agent (System & Automation Specialist).
@@ -50,7 +54,7 @@ Reason internally. Never reveal private reasoning. Provide only concise conclusi
50
54
 
51
55
  [OS EXECUTION WORKFLOW]
52
56
  CRITICAL RULE 1: NEVER expose internal JSON tool calls. Explain the outcome naturally.
53
- CRITICAL RULE 2: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt.
57
+ CRITICAL RULE 2: 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.
54
58
  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.
55
59
  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.
56
60
  CRITICAL RULE 5: TOOL CONFIDENCE. NEVER fabricate file contents or command outputs.
@@ -62,46 +66,83 @@ ${EXECUTION_DISCIPLINE}
62
66
  if (activeSOP) {
63
67
  basePrompt += `\n\n[ACTIVE COGNITIVE SKILLS]\n${activeSOP}\n`;
64
68
  }
65
- // Inject Episodic Memories
69
+ // Inject Episodic Memories via Python RAG
66
70
  try {
67
- const recentMemories = episodic_1.episodicDB.getMemories().slice(0, 10);
68
- if (recentMemories.length > 0) {
69
- basePrompt += `
70
-
71
- --- EPISODIC MEMORIES (SMART SUGGESTIONS) ---
72
- `;
73
- recentMemories.forEach(mem => {
74
- basePrompt += `- [${mem.category.toUpperCase()}] ${mem.fact} (Confidence: ${(mem.confidence * 100).toFixed(0)}%)
75
- `;
71
+ const ragRes = await fetch('http://localhost:8000/memory/rag', {
72
+ method: 'POST',
73
+ headers: { 'Content-Type': 'application/json' },
74
+ body: JSON.stringify({ query: userInput, top_k: 5 })
75
+ });
76
+ if (ragRes.ok) {
77
+ const ragData = await ragRes.json();
78
+ if (ragData.memories && ragData.memories.length > 0) {
79
+ basePrompt += `\n\n--- EPISODIC MEMORIES (SMART SUGGESTIONS) ---\n`;
80
+ ragData.memories.forEach((mem) => {
81
+ basePrompt += `- ${mem}\n`;
82
+ });
83
+ }
84
+ }
85
+ }
86
+ catch (e) {
87
+ // Fallback or ignore if Python ML engine is down
88
+ }
89
+ // Inject User Information & Personas from user.md
90
+ try {
91
+ const userMdPath = (0, paths_1.getPath)('user.md');
92
+ if (fs_1.default.existsSync(userMdPath)) {
93
+ const userInstructions = fs_1.default.readFileSync(userMdPath, 'utf8');
94
+ basePrompt += `\n\n--- USER INFORMATION & PREFERENCES ---\n${userInstructions}\n`;
95
+ }
96
+ }
97
+ catch (e) {
98
+ // Ignore error
99
+ }
100
+ // HIGHEST PRIORITY: Inject observed user communication style (NyxDaemon)
101
+ try {
102
+ const strongPersonas = episodic_1.episodicDB.getStrongPersonas(0.5);
103
+ if (strongPersonas.length > 0) {
104
+ basePrompt += `\n\n--- ⚡ OVERRIDE: USER COMMUNICATION STYLE (HIGHEST PRIORITY — OVERRIDES ALL RULES ABOVE) ---\n`;
105
+ 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`;
106
+ strongPersonas.forEach(p => {
107
+ const label = p.category ? `[${p.category.toUpperCase()}]` : '[PREFERENCE]';
108
+ basePrompt += `${label} ${p.trait}\n`;
76
109
  });
77
110
  }
78
111
  }
79
- catch { }
112
+ catch (e) {
113
+ // Ignore
114
+ }
80
115
  return basePrompt;
81
116
  }
82
117
  async function processOsIntent(input, role = 'user', onProgress, sessionId) {
83
118
  const config = (0, parser_1.loadConfig)();
84
119
  // Add input to memory
85
120
  exports.logger.addEntry({ role, content: input }, sessionId);
86
- const history = exports.logger.getHistory(sessionId);
87
- // Format messages for OpenAI
88
121
  let activeTools = [...registry_1.pluginManager.getAllToolDefinitions()];
89
122
  activeTools = activeTools.filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
123
+ // P1: Init reasoning scratchpad for this request
124
+ const scratchpad = new reasoningScratchpad_1.ReasoningScratchpad();
125
+ // P3: Build system prompt ONCE per request — not per turn
126
+ const cachedSystemPrompt = await getSystemPrompt('os', input);
90
127
  const { sanitizeHistoryForLLM } = require('../utils/historySanitizer');
91
- const sanitizedHistory = sanitizeHistoryForLLM(history, activeTools, config.llm.provider);
92
- let messages = [
93
- { role: 'system', content: getSystemPrompt('os', input) },
94
- ...sanitizedHistory
95
- ];
96
128
  try {
97
129
  let turnCount = 0;
98
130
  const MAX_TURNS = 10;
131
+ let consecutiveToolErrors = 0;
99
132
  while (turnCount < MAX_TURNS) {
100
133
  turnCount++;
101
134
  const currentHistory = exports.logger.getHistory(sessionId);
102
- const sanitizedHistory = sanitizeHistoryForLLM(currentHistory, activeTools, config.llm.provider);
135
+ // P6: Compress history if conversation is too long
136
+ const historyToUse = (0, contextSummarizer_1.needsCompression)(currentHistory)
137
+ ? await (0, contextSummarizer_1.compressHistory)(currentHistory)
138
+ : currentHistory;
139
+ const sanitizedHistory = sanitizeHistoryForLLM(historyToUse, activeTools, config.llm.provider);
140
+ // P1: Inject scratchpad into system prompt for turns > 1
141
+ const sysPrompt = turnCount === 1
142
+ ? cachedSystemPrompt
143
+ : cachedSystemPrompt + scratchpad.getInjection();
103
144
  const messages = [
104
- { role: 'system', content: getSystemPrompt('os', input) },
145
+ { role: 'system', content: sysPrompt },
105
146
  ...sanitizedHistory
106
147
  ];
107
148
  const response = await (0, llmUtils_1.executeWithRetry)(async (client) => {
@@ -120,16 +161,15 @@ async function processOsIntent(input, role = 'user', onProgress, sessionId) {
120
161
  tracker_1.Tracker.addTokens(response.usage.total_tokens, config.llm.provider);
121
162
  }
122
163
  tracker_1.Tracker.addEvent('llm.response', { provider: config.llm.provider, tool_calls: responseMessage.tool_calls?.length || 0 });
164
+ // P1: Capture <think> blocks for scratchpad, get clean content
165
+ const cleanedContent = scratchpad.capture(responseMessage.content || '', turnCount);
123
166
  exports.logger.addEntry({
124
167
  role: 'assistant',
125
- content: responseMessage.content || "",
168
+ content: cleanedContent || '',
126
169
  tool_calls: responseMessage.tool_calls,
127
170
  }, sessionId);
128
171
  if (!responseMessage.tool_calls || responseMessage.tool_calls.length === 0) {
129
- let finalContent = responseMessage.content || "No response generated.";
130
- finalContent = finalContent.replace(/<(think|thought|thinking|reasoning|analysis|reflection)>[\s\S]*?<\/\1>\n?/gi, '');
131
- finalContent = finalContent.trim();
132
- return finalContent;
172
+ return cleanedContent || 'No response generated.';
133
173
  }
134
174
  let canFastReturnAll = true;
135
175
  let accumulatedResults = [];
@@ -206,8 +246,26 @@ async function processOsIntent(input, role = 'user', onProgress, sessionId) {
206
246
  canFastReturnAll = false;
207
247
  }
208
248
  }
209
- // V2 Optimization (Expanded in v1.7.4): Zero-LLM Fast Return for data-heavy and read-only tools
210
- // If all tools already return perfectly formatted markdown, skip the second LLM call to save 5-10s latency!
249
+ // P4: Self-reflection if ALL tools failed, inject reflection before next turn
250
+ const allFailed = accumulatedResults.length > 0 && accumulatedResults.every(r => r.startsWith('Error') || r.includes('[System Error]') || r.includes('[Security Blocked]'));
251
+ if (allFailed) {
252
+ consecutiveToolErrors++;
253
+ const reflection = `[SELF-REFLECTION] All ${accumulatedResults.length} tool call(s) failed (attempt ${consecutiveToolErrors}). ` +
254
+ `Errors: ${accumulatedResults.join(' | ')}. ` +
255
+ `Analyze WHY each failed. Options: (1) retry with corrected params, (2) use alternative tool, (3) inform user clearly. ` +
256
+ `Do NOT repeat the exact same failed call.`;
257
+ exports.logger.addEntry({ role: 'system', content: reflection }, sessionId);
258
+ console.log(picocolors_1.default.magenta(`[Self-Reflection] Turn ${turnCount}: all tools failed, injecting reflection.`));
259
+ if (consecutiveToolErrors >= 2) {
260
+ const errorSummary = `⚠️ Unable to complete this request after multiple attempts.\n${accumulatedResults.join('\n')}`;
261
+ exports.logger.addEntry({ role: 'assistant', content: errorSummary }, sessionId);
262
+ return errorSummary;
263
+ }
264
+ }
265
+ else {
266
+ consecutiveToolErrors = 0;
267
+ }
268
+ // V2 Optimization: Zero-LLM Fast Return for transaction tools
211
269
  if (canFastReturnAll && accumulatedResults.length > 0) {
212
270
  const finalContent = accumulatedResults.join('\n\n---\n\n');
213
271
  exports.logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
@@ -245,7 +303,7 @@ async function processOsIntentStream(input, onChunk, onProgress, sessionId) {
245
303
  const currentHistory = exports.logger.getHistory(sessionId);
246
304
  const sanitizedHistory = sanitizeHistoryForLLM(currentHistory, activeTools, config.llm.provider);
247
305
  const messages = [
248
- { role: 'system', content: getSystemPrompt('os', input) },
306
+ { role: 'system', content: await getSystemPrompt('os', input) },
249
307
  ...sanitizedHistory
250
308
  ];
251
309
  let streamedContent = '';