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.
- package/CHANGELOG.md +11 -0
- package/README.md +19 -15
- package/bin/nyxora.mjs +4 -6
- package/dist/launcher.js +44 -8
- package/dist/packages/core/src/agent/cronManager.js +2 -2
- package/dist/packages/core/src/agent/llmProvider.js +6 -2
- package/dist/packages/core/src/agent/nyxDaemon.js +24 -37
- package/dist/packages/core/src/agent/osAgent.js +88 -30
- package/dist/packages/core/src/agent/reasoning.js +225 -107
- package/dist/packages/core/src/agent/reasoningScratchpad.js +47 -0
- package/dist/packages/core/src/agent/web3Agent.js +108 -42
- package/dist/packages/core/src/cognitive/cognitiveManager.js +63 -10
- package/dist/packages/core/src/gateway/cli.js +3 -0
- package/dist/packages/core/src/gateway/setup-ml.js +64 -0
- package/dist/packages/core/src/gateway/setup.js +21 -1
- package/dist/packages/core/src/gateway/telegram.js +19 -8
- package/dist/packages/core/src/memory/episodic.js +37 -2
- package/dist/packages/core/src/memory/promotionEngine.js +18 -5
- package/dist/packages/core/src/system/agentskills.js +10 -0
- package/dist/packages/core/src/system/skills/scheduleTask.js +7 -2
- package/dist/packages/core/src/utils/contextSummarizer.js +82 -0
- package/dist/packages/core/src/utils/skillManager.js +1 -1
- package/dist/packages/core/src/utils/streamSimulator.js +18 -8
- package/dist/packages/core/src/web3/plugins/Web3MarketPlugin.js +4 -124
- package/dist/packages/core/src/web3/skills/checkPortfolio.js +20 -2
- package/dist/packages/core/src/web3/skills/confirmPendingTx.js +1 -1
- package/dist/packages/core/src/web3/skills/getPrice.js +134 -27
- package/dist/packages/core/src/web3/skills/marketAnalysis.js +26 -191
- package/dist/packages/core/src/web3/utils/marketEngine.js +26 -33
- package/launcher.ts +38 -9
- package/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/agent/cronManager.ts +2 -2
- package/packages/core/src/agent/llmProvider.ts +6 -2
- package/packages/core/src/agent/nyxDaemon.ts +24 -41
- package/packages/core/src/agent/osAgent.ts +97 -33
- package/packages/core/src/agent/reasoning.ts +237 -109
- package/packages/core/src/agent/reasoningScratchpad.ts +45 -0
- package/packages/core/src/agent/web3Agent.ts +115 -45
- package/packages/core/src/cognitive/cognitiveManager.ts +65 -10
- package/packages/core/src/cognitive/prompts/web3/market-analysis.md +24 -0
- package/packages/core/src/cognitive/prompts/web3/portfolio-review.md +29 -0
- package/packages/core/src/cognitive/prompts/web3/risk-assessment.md +28 -0
- package/packages/core/src/cognitive/prompts/web3/trade-planning.md +30 -0
- package/packages/core/src/gateway/cli.ts +4 -0
- package/packages/core/src/gateway/setup-ml.ts +68 -0
- package/packages/core/src/gateway/setup.ts +23 -1
- package/packages/core/src/gateway/telegram.ts +20 -10
- package/packages/core/src/memory/episodic.ts +52 -2
- package/packages/core/src/memory/promotionEngine.ts +19 -5
- package/packages/core/src/system/agentskills.ts +10 -0
- package/packages/core/src/system/skills/scheduleTask.ts +7 -2
- package/packages/core/src/utils/contextSummarizer.ts +100 -0
- package/packages/core/src/utils/skillManager.ts +1 -1
- package/packages/core/src/utils/streamSimulator.ts +21 -7
- package/packages/core/src/web3/plugins/Web3MarketPlugin.ts +5 -124
- package/packages/core/src/web3/skills/checkPortfolio.ts +21 -2
- package/packages/core/src/web3/skills/confirmPendingTx.ts +1 -1
- package/packages/core/src/web3/skills/getPrice.ts +134 -30
- package/packages/core/src/web3/skills/marketAnalysis.ts +43 -188
- package/packages/core/src/web3/utils/marketEngine.ts +27 -33
- package/packages/dashboard/dist/assets/{index-B1yTXubl.js → index-BTfp141V.js} +1 -1
- package/packages/dashboard/dist/assets/index-DK2sTU47.css +1 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +1 -1
- package/packages/signer/package.json +1 -1
- package/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -41
- 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
|
-
* **
|
|
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 **
|
|
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
|

|
|
123
127
|
|
|
124
|
-
*Nyxora separates its duties into
|
|
125
|
-
1. **🧠 Core (The AI Brain)**: The intelligent assistant that strategizes and plans transactions, but **never** holds your funds.
|
|
126
|
-
2.
|
|
127
|
-
3.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
|
17
|
+
// Initial run after 30 seconds (faster persona availability for new sessions)
|
|
20
18
|
this.initialTimeout = setTimeout(() => {
|
|
21
19
|
this.runAudit();
|
|
22
|
-
},
|
|
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
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
for (const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
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
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
//
|
|
210
|
-
|
|
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 = '';
|