nyxora 26.7.2-alpha.1 → 26.7.2-alpha.4
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 +21 -1
- package/README.md +4 -4
- package/dist/packages/core/src/agent/bridgeWatcher.js +3 -2
- package/dist/packages/core/src/agent/llmProvider.js +238 -0
- package/dist/packages/core/src/agent/nyxDaemon.js +110 -0
- package/dist/packages/core/src/agent/osAgent.js +116 -0
- package/dist/packages/core/src/agent/reasoning.js +203 -28
- package/dist/packages/core/src/agent/transactionManager.js +36 -31
- package/dist/packages/core/src/agent/web3Agent.js +116 -0
- package/dist/packages/core/src/config/parser.js +10 -4
- package/dist/packages/core/src/gateway/chat.js +56 -13
- package/dist/packages/core/src/gateway/discordAdapter.js +81 -0
- package/dist/packages/core/src/gateway/server.js +58 -9
- package/dist/packages/core/src/gateway/setup.js +18 -2
- package/dist/packages/core/src/gateway/telegram.js +103 -18
- package/dist/packages/core/src/memory/episodic.js +10 -1
- package/dist/packages/core/src/memory/logger.js +120 -2
- package/dist/packages/core/src/system/plugins/GoogleWorkspacePlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemCorePlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemExternalPlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemPluginInstallerPlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemSocialPlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemWebPlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemWorkspacePlugin.js +3 -3
- package/dist/packages/core/src/system/plugins/createSkill.js +1 -1
- package/dist/packages/core/src/system/skills/forgetMemory.js +6 -4
- package/dist/packages/core/src/utils/streamSimulator.js +75 -0
- package/dist/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.js +28 -11
- package/dist/packages/core/src/web3/aggregator/providers/OpBridgeProvider.js +41 -27
- package/dist/packages/core/src/web3/aggregator/providers/TestnetSwapProvider.js +65 -0
- package/dist/packages/core/src/web3/plugins/Web3DefiPlugin.js +1 -1
- package/dist/packages/core/src/web3/plugins/Web3MarketPlugin.js +1 -1
- package/dist/packages/core/src/web3/plugins/Web3SecurityPlugin.js +1 -1
- package/dist/packages/core/src/web3/plugins/Web3WalletPlugin.js +1 -1
- package/dist/packages/signer/src/NyxoraSigner.js +181 -0
- package/dist/packages/signer/src/index.js +17 -0
- package/dist/packages/signer/src/server.js +25 -161
- package/package.json +6 -2
- package/packages/core/package.json +11 -10
- package/packages/core/src/agent/bridgeWatcher.ts +3 -2
- package/packages/core/src/agent/llmProvider.ts +215 -0
- package/packages/core/src/agent/nyxDaemon.ts +121 -0
- package/packages/core/src/agent/osAgent.ts +134 -0
- package/packages/core/src/agent/reasoning.ts +216 -32
- package/packages/core/src/agent/transactionManager.ts +36 -28
- package/packages/core/src/agent/web3Agent.ts +134 -0
- package/packages/core/src/config/parser.ts +15 -4
- package/packages/core/src/gateway/chat.ts +57 -15
- package/packages/core/src/gateway/discordAdapter.ts +87 -0
- package/packages/core/src/gateway/server.ts +66 -10
- package/packages/core/src/gateway/setup.ts +18 -2
- package/packages/core/src/gateway/telegram.ts +120 -19
- package/packages/core/src/memory/episodic.ts +12 -1
- package/packages/core/src/memory/logger.ts +142 -2
- package/packages/core/src/system/plugins/GoogleWorkspacePlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemCorePlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemExternalPlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemPluginInstallerPlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemSocialPlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemWebPlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemWorkspacePlugin.ts +3 -3
- package/packages/core/src/system/plugins/createSkill.ts +1 -1
- package/packages/core/src/system/skills/forgetMemory.ts +7 -4
- package/packages/core/src/utils/streamSimulator.ts +74 -0
- package/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.ts +30 -11
- package/packages/core/src/web3/aggregator/providers/OpBridgeProvider.ts +43 -29
- package/packages/core/src/web3/plugins/Web3DefiPlugin.ts +1 -1
- package/packages/core/src/web3/plugins/Web3MarketPlugin.ts +1 -1
- package/packages/core/src/web3/plugins/Web3SecurityPlugin.ts +1 -1
- package/packages/core/src/web3/plugins/Web3WalletPlugin.ts +1 -1
- package/packages/dashboard/dist/assets/index-B1yTXubl.js +18 -0
- package/packages/dashboard/dist/assets/{index-BrLPedT0.css → index-CLpiTiQH.css} +1 -1
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package.json +10 -10
- package/packages/mcp-server/dist/server.js +5 -1
- package/packages/mcp-server/package.json +6 -6
- package/packages/mcp-server/src/server.ts +11 -7
- package/packages/policy/package.json +3 -3
- package/packages/signer/package.json +16 -6
- package/packages/signer/src/NyxoraSigner.ts +161 -0
- package/packages/signer/src/index.ts +1 -0
- package/packages/signer/src/server.ts +25 -135
- package/dist/packages/core/src/agent/honchoDaemon.js +0 -91
- package/dist/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -54
- package/dist/packages/core/src/gateway/test.js +0 -15
- package/dist/packages/core/src/web3/Web3DefiPlugin.js +0 -28
- package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +0 -260
- package/dist/packages/core/src/web3/aggregator/aggregatorTestnet.js +0 -119
- package/dist/packages/core/src/web3/skills/nativeOpBridge.js +0 -71
- package/packages/core/src/agent/honchoDaemon.ts +0 -96
- package/packages/dashboard/dist/assets/index-JRseMFEX.js +0 -16
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,27 @@
|
|
|
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
|
-
|
|
6
|
+
## [26.7.2-alpha.4]
|
|
7
|
+
### Features & Platform Integrations
|
|
8
|
+
- **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.
|
|
9
|
+
|
|
10
|
+
### Bug Fixes & Stability
|
|
11
|
+
- **Telegram Polling Timeout Silence**: Mitigated an issue where sudden network disconnects or API timeouts (`ETIMEDOUT`) would cause the `@grammyjs/runner` to spam the console with massive stack traces. The system now seamlessly intercepts these failures via a custom API config transformer, suppresses the default runner logs, and gracefully emits a clean, single-line reconnection warning.
|
|
12
|
+
|
|
13
|
+
## [26.7.2-alpha.3]
|
|
14
|
+
### Hotfixes
|
|
15
|
+
- **Daemon Crash & Missing Dependency**: Resolved a critical crash preventing `nyxora start` from booting the daemon by properly injecting the missing `discord.js` dependency into the core package requirements.
|
|
16
|
+
- **MCP Server TypeScript Rigidity**: Fixed a severe TypeScript compilation failure on the MCP Server caused by an un-asserted `type: "text"` field within the JSON-RPC return shape, ensuring `alpha.3` builds securely and passes strict compilation checks before deployment.
|
|
17
|
+
|
|
18
|
+
## [26.7.2-alpha.2]
|
|
19
|
+
- **Core Stability & Graceful Shutdown**: Engineered a robust `Graceful Shutdown` hook in the Gateway API (`server.ts`) to actively track floating Web3 transaction promises. Nyxora now intelligently waits up to 10 seconds for on-chain transactions to finalize before shutting down, completely eradicating dangling transactions and fund loss during SIGINT/SIGTERM.
|
|
20
|
+
- **SQLite Transaction Persistence**: Overhauled `transactionManager.ts` to migrate away from volatile RAM Maps and JSON files (`.nyxora_withdrawals.json`). All pending transactions and L2 withdrawals are now persistently written to `memory.db` via `logger.ts`, guaranteeing 100% state recovery and ACID compliance across sudden power losses or daemon reboots.
|
|
21
|
+
- **Atomic File Operations**: Fortified configuration write operations (`config.yaml` and Google Credentials) in `parser.ts` using OS-level atomic renames (`fs.renameSync`). This mechanically eliminates the possibility of 0-byte file corruption during sudden server crashes.
|
|
22
|
+
- **Unified Message Bus (Multi-Platform Integration)**: Radically expanded Nyxora's gateway architecture beyond Telegram and the Web Dashboard. Successfully engineered and deployed a unified multi-platform message bus:
|
|
23
|
+
- **Discord Integration**: Engineered `discordAdapter.ts` using `discord.js` to allow Nyxora to natively join Discord servers, intercept mentions, and stream Markdown-rich responses via WebSockets in real-time.
|
|
24
|
+
- **Multi-Identity Tracking**: Overhauled the core `logger.ts` memory architecture to persistently track user dialects across multiple platforms by dynamically segmenting SQLite session IDs (`discord_<id>`, `telegram_<id>`).
|
|
25
|
+
- **Light Theme Login Readability**: Resolved a severe contrast issue on the Dashboard Login screen. In Light Mode, the dark text was practically invisible against the hardcoded dark card. Appended strict `body.light-theme` overrides in `Login.css` to seamlessly transition the card into a readable "glass" background without polluting or breaking the existing Dark Mode aesthetics.
|
|
26
|
+
- **Background Daemon Identity Integration**: Officially formalized the naming convention of the Dialectic User Modeling background process to **Nyx Daemon**. Realigned all core TypeScript files (`nyxDaemon.ts`), internal system logging prefixes, and public documentation to reflect this unified system identity, ensuring a seamless aesthetic and conceptual integration with the broader Nyxora ecosystem.
|
|
7
27
|
|
|
8
28
|
## [26.7.2-alpha.1]
|
|
9
29
|
### Security & Architecture
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
Nyxora is a **secure, non-custodial runtime infrastructure for autonomous onchain agents** built with a robust Monorepo architecture (Node.js & React). Designed for autonomous workflows with a premium Utility-Centric dark-themed UI and strict client-side key isolation.
|
|
15
15
|
|
|
16
|
-
**Nyxora now natively supports the Model Context Protocol (MCP)**. You can transform your external AI agents (like Claude Desktop and Cursor) into secure Web3 actors that execute swaps and fetch balances using Nyxora's secure signer vault. [View the MCP Integration Guide](https://nyxoraai.github.io/Nyxora/
|
|
16
|
+
**Nyxora now natively supports the Model Context Protocol (MCP)**. You can transform your external AI agents (like Claude Desktop and Cursor) into secure Web3 actors that execute swaps and fetch balances using Nyxora's secure signer vault. [View the MCP Integration Guide](https://nyxoraai.github.io/Nyxora/mcp/)
|
|
17
17
|
|
|
18
18
|
It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human-in-the-Loop** execution model, ensuring that Remote AIs (LLMs) never have unilateral access to your funds.
|
|
19
19
|
|
|
@@ -67,7 +67,7 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
|
|
|
67
67
|
## 🔥 Key Features
|
|
68
68
|
|
|
69
69
|
### Advanced Security Architecture
|
|
70
|
-
* **🛡️ 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/
|
|
70
|
+
* **🛡️ 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
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).
|
|
72
72
|
* **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
73
|
* **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.
|
|
@@ -100,7 +100,7 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
|
|
|
100
100
|
### 🧠 The Masterpiece Memory Architecture
|
|
101
101
|
* **4-Layer Air-Gapped Vault**: Nyxora features a god-tier memory system that completely isolates conversational habits from the OS Keyring. The AI can dynamically learn your behaviors without ever having physical read-paths to your private keys.
|
|
102
102
|
* **Hard-Coded Anti-Injection Shield**: We enforce a Zero-Trust memory paradigm. Before any user habit is saved to the local SQLite database, it must pass a strict RegExp-based validation layer that autonomously annihilates Private Keys, BIP-39 Seed Phrases, and Prompt Injection attempts.
|
|
103
|
-
* **Dialectic User Modeling (
|
|
103
|
+
* **Dialectic User Modeling (Nyx Daemon)**: Nyxora continuously runs an asynchronous background daemon that quietly audits your conversational history. It extracts your behavioral traits, trading style, and risk tolerance, saving them securely to `episodic.db` and injecting them dynamically into the AI's reasoning engine.
|
|
104
104
|
* **Smart Suggestion Engine**: Nyxora actively queries its Layer-2 Episodic Database to seamlessly autocomplete your repetitive Web3 routines. If you always swap on Arbitrum using USDC, the AI will proactively suggest it, slashing human-in-the-loop latency by up to 90%.
|
|
105
105
|
|
|
106
106
|
### AI & UI Customization
|
|
@@ -138,7 +138,7 @@ Within the AI Brain, the Web3 codebase is strictly divided to prevent the LLM fr
|
|
|
138
138
|
|
|
139
139
|
## 🛡️ Advanced Security & Threat Model
|
|
140
140
|
|
|
141
|
-
To dive deeper into the technical details of our Zero-Knowledge security architecture, please visit the [Nyxora Security](https://nyxoraai.github.io/Nyxora/).
|
|
141
|
+
To dive deeper into the technical details of our Zero-Knowledge security architecture, please visit the [Nyxora Security](https://nyxoraai.github.io/Nyxora/architecture).
|
|
142
142
|
|
|
143
143
|
---
|
|
144
144
|
|
|
@@ -8,7 +8,8 @@ const parser_1 = require("../config/parser");
|
|
|
8
8
|
// to fetch the Merkle Proof and call proveWithdrawalTransaction on L1.
|
|
9
9
|
// For the scope of this architecture prototype, we simulate the Challenge Period
|
|
10
10
|
// watcher by using a time-delay, representing the exact asynchronous behavior.
|
|
11
|
-
|
|
11
|
+
// Actual 7-day challenge period for OP Stack and Arbitrum withdrawals
|
|
12
|
+
const CHALLENGE_PERIOD_MS = 7 * 24 * 60 * 60 * 1000;
|
|
12
13
|
function startBridgeWatcher() {
|
|
13
14
|
console.log('[Bridge Watcher] Started background daemon for asynchronous L2 withdrawals');
|
|
14
15
|
setInterval(async () => {
|
|
@@ -30,5 +31,5 @@ function startBridgeWatcher() {
|
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
},
|
|
34
|
+
}, 12 * 60 * 60 * 1000); // Check every 12 hours
|
|
34
35
|
}
|
|
@@ -16,6 +16,46 @@ class OpenAIAdapter {
|
|
|
16
16
|
usage: response.usage ? { total_tokens: response.usage.total_tokens } : undefined
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
+
async stream(request, onChunk) {
|
|
20
|
+
try {
|
|
21
|
+
const streamRes = await this.client.chat.completions.create({ ...request, stream: true });
|
|
22
|
+
let fullContent = '';
|
|
23
|
+
const toolCallsMap = {};
|
|
24
|
+
for await (const chunk of streamRes) {
|
|
25
|
+
const delta = chunk.choices[0]?.delta;
|
|
26
|
+
if (delta?.content) {
|
|
27
|
+
fullContent += delta.content;
|
|
28
|
+
onChunk(delta.content);
|
|
29
|
+
}
|
|
30
|
+
if (delta?.tool_calls) {
|
|
31
|
+
for (const tc of delta.tool_calls) {
|
|
32
|
+
if (!toolCallsMap[tc.index]) {
|
|
33
|
+
toolCallsMap[tc.index] = { id: tc.id || '', type: 'function', function: { name: tc.function?.name || '', arguments: tc.function?.arguments || '' } };
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
if (tc.id)
|
|
37
|
+
toolCallsMap[tc.index].id = tc.id;
|
|
38
|
+
if (tc.function?.name)
|
|
39
|
+
toolCallsMap[tc.index].function.name += tc.function.name;
|
|
40
|
+
if (tc.function?.arguments)
|
|
41
|
+
toolCallsMap[tc.index].function.arguments += tc.function.arguments;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const toolCalls = Object.values(toolCallsMap);
|
|
47
|
+
return {
|
|
48
|
+
message: {
|
|
49
|
+
content: fullContent || null,
|
|
50
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : undefined
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Fallback to non-streaming if streaming fails
|
|
56
|
+
return this.chat(request);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
19
59
|
}
|
|
20
60
|
exports.OpenAIAdapter = OpenAIAdapter;
|
|
21
61
|
class AnthropicAdapter {
|
|
@@ -129,6 +169,84 @@ class AnthropicAdapter {
|
|
|
129
169
|
usage: response.usage ? { total_tokens: response.usage.input_tokens + response.usage.output_tokens } : undefined
|
|
130
170
|
};
|
|
131
171
|
}
|
|
172
|
+
async stream(request, onChunk) {
|
|
173
|
+
try {
|
|
174
|
+
// Build the same message format as chat()
|
|
175
|
+
let systemPrompt = '';
|
|
176
|
+
const anthropicMessages = [];
|
|
177
|
+
for (const m of request.messages) {
|
|
178
|
+
if (m.role === 'system') {
|
|
179
|
+
systemPrompt = m.content;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (m.role === 'user') {
|
|
183
|
+
anthropicMessages.push({ role: 'user', content: m.content });
|
|
184
|
+
}
|
|
185
|
+
else if (m.role === 'assistant') {
|
|
186
|
+
const blocks = [];
|
|
187
|
+
if (m.content)
|
|
188
|
+
blocks.push({ type: 'text', text: m.content });
|
|
189
|
+
if (m.tool_calls)
|
|
190
|
+
m.tool_calls.forEach((tc) => {
|
|
191
|
+
try {
|
|
192
|
+
blocks.push({ type: 'tool_use', id: tc.id, name: tc.function.name, input: JSON.parse(tc.function.arguments) });
|
|
193
|
+
}
|
|
194
|
+
catch { }
|
|
195
|
+
});
|
|
196
|
+
anthropicMessages.push({ role: 'assistant', content: blocks.length > 0 ? blocks : m.content });
|
|
197
|
+
}
|
|
198
|
+
else if (m.role === 'tool') {
|
|
199
|
+
anthropicMessages.push({ role: 'user', content: [{ type: 'tool_result', tool_use_id: m.tool_call_id, content: m.content }] });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const mergedAnthropic = [];
|
|
203
|
+
for (const m of anthropicMessages) {
|
|
204
|
+
const last = mergedAnthropic[mergedAnthropic.length - 1];
|
|
205
|
+
if (last && last.role === m.role) {
|
|
206
|
+
if (Array.isArray(last.content) && Array.isArray(m.content))
|
|
207
|
+
last.content.push(...m.content);
|
|
208
|
+
else if (typeof last.content === 'string' && typeof m.content === 'string')
|
|
209
|
+
last.content += '\n\n' + m.content;
|
|
210
|
+
else if (Array.isArray(last.content) && typeof m.content === 'string')
|
|
211
|
+
last.content.push({ type: 'text', text: m.content });
|
|
212
|
+
else
|
|
213
|
+
last.content = [{ type: 'text', text: typeof last.content === 'string' ? last.content : '' }, ...m.content];
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
mergedAnthropic.push(m);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const anthropicTools = request.tools?.map(t => ({ name: t.function.name, description: t.function.description, input_schema: t.function.parameters }));
|
|
220
|
+
const stream = this.client.messages.stream({
|
|
221
|
+
model: request.model,
|
|
222
|
+
system: systemPrompt,
|
|
223
|
+
messages: mergedAnthropic,
|
|
224
|
+
tools: anthropicTools,
|
|
225
|
+
temperature: request.temperature,
|
|
226
|
+
max_tokens: request.max_tokens || 4096
|
|
227
|
+
});
|
|
228
|
+
let fullContent = '';
|
|
229
|
+
const toolCalls = [];
|
|
230
|
+
for await (const event of stream) {
|
|
231
|
+
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
|
|
232
|
+
fullContent += event.delta.text;
|
|
233
|
+
onChunk(event.delta.text);
|
|
234
|
+
}
|
|
235
|
+
if (event.type === 'content_block_start' && event.content_block.type === 'tool_use') {
|
|
236
|
+
toolCalls.push({ id: event.content_block.id, type: 'function', function: { name: event.content_block.name, arguments: '' } });
|
|
237
|
+
}
|
|
238
|
+
if (event.type === 'content_block_delta' && event.delta.type === 'input_json_delta') {
|
|
239
|
+
const last = toolCalls[toolCalls.length - 1];
|
|
240
|
+
if (last)
|
|
241
|
+
last.function.arguments += event.delta.partial_json;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { message: { content: fullContent || null, tool_calls: toolCalls.length > 0 ? toolCalls : undefined } };
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return this.chat(request);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
132
250
|
}
|
|
133
251
|
exports.AnthropicAdapter = AnthropicAdapter;
|
|
134
252
|
class GeminiAdapter {
|
|
@@ -268,5 +386,125 @@ class GeminiAdapter {
|
|
|
268
386
|
usage: totalTokens > 0 ? { total_tokens: totalTokens } : undefined
|
|
269
387
|
};
|
|
270
388
|
}
|
|
389
|
+
async stream(request, onChunk) {
|
|
390
|
+
let systemInstruction = '';
|
|
391
|
+
const contents = [];
|
|
392
|
+
for (const m of request.messages) {
|
|
393
|
+
if (m.role === 'system') {
|
|
394
|
+
systemInstruction = m.content;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (m.role === 'user') {
|
|
398
|
+
contents.push({ role: 'user', parts: [{ text: m.content }] });
|
|
399
|
+
}
|
|
400
|
+
else if (m.role === 'assistant') {
|
|
401
|
+
const parts = [];
|
|
402
|
+
if (m.content)
|
|
403
|
+
parts.push({ text: m.content });
|
|
404
|
+
if (m.tool_calls) {
|
|
405
|
+
m.tool_calls.forEach((tc) => {
|
|
406
|
+
try {
|
|
407
|
+
parts.push({ functionCall: { name: tc.function.name, args: JSON.parse(tc.function.arguments) } });
|
|
408
|
+
}
|
|
409
|
+
catch { }
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (parts.length > 0)
|
|
413
|
+
contents.push({ role: 'model', parts });
|
|
414
|
+
}
|
|
415
|
+
else if (m.role === 'tool') {
|
|
416
|
+
contents.push({ role: 'user', parts: [{ functionResponse: { name: m.name || 'unknown_tool', response: { result: m.content } } }] });
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const mergedContents = [];
|
|
420
|
+
for (const m of contents) {
|
|
421
|
+
const last = mergedContents[mergedContents.length - 1];
|
|
422
|
+
if (last && last.role === m.role)
|
|
423
|
+
last.parts.push(...m.parts);
|
|
424
|
+
else
|
|
425
|
+
mergedContents.push(m);
|
|
426
|
+
}
|
|
427
|
+
let tools = undefined;
|
|
428
|
+
if (request.tools && request.tools.length > 0) {
|
|
429
|
+
tools = [{ functionDeclarations: request.tools.map(t => ({ name: t.function.name, description: t.function.description, parameters: t.function.parameters })) }];
|
|
430
|
+
}
|
|
431
|
+
const payload = {
|
|
432
|
+
contents: mergedContents,
|
|
433
|
+
generationConfig: { temperature: request.temperature || 0.7 },
|
|
434
|
+
safetySettings: [
|
|
435
|
+
{ category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_NONE' },
|
|
436
|
+
{ category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_NONE' },
|
|
437
|
+
{ category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
|
|
438
|
+
{ category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', threshold: 'BLOCK_NONE' }
|
|
439
|
+
]
|
|
440
|
+
};
|
|
441
|
+
if (systemInstruction)
|
|
442
|
+
payload.systemInstruction = { parts: [{ text: systemInstruction }] };
|
|
443
|
+
if (tools)
|
|
444
|
+
payload.tools = tools;
|
|
445
|
+
try {
|
|
446
|
+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${request.model}:streamGenerateContent?alt=sse&key=${this.apiKey}`, {
|
|
447
|
+
method: 'POST',
|
|
448
|
+
headers: { 'Content-Type': 'application/json' },
|
|
449
|
+
body: JSON.stringify(payload)
|
|
450
|
+
});
|
|
451
|
+
if (!response.ok)
|
|
452
|
+
return this.chat(request);
|
|
453
|
+
let contentStr = '';
|
|
454
|
+
const toolCalls = [];
|
|
455
|
+
let totalTokens = 0;
|
|
456
|
+
const reader = response.body?.getReader();
|
|
457
|
+
const decoder = new TextDecoder();
|
|
458
|
+
if (reader) {
|
|
459
|
+
let buffer = '';
|
|
460
|
+
while (true) {
|
|
461
|
+
const { done, value } = await reader.read();
|
|
462
|
+
if (done)
|
|
463
|
+
break;
|
|
464
|
+
buffer += decoder.decode(value, { stream: true });
|
|
465
|
+
const lines = buffer.split('\n');
|
|
466
|
+
buffer = lines.pop() || '';
|
|
467
|
+
for (const line of lines) {
|
|
468
|
+
if (!line.startsWith('data: '))
|
|
469
|
+
continue;
|
|
470
|
+
const raw = line.slice(6).trim();
|
|
471
|
+
if (!raw || raw === '[DONE]')
|
|
472
|
+
continue;
|
|
473
|
+
try {
|
|
474
|
+
const data = JSON.parse(raw);
|
|
475
|
+
if (data.candidates && data.candidates.length > 0) {
|
|
476
|
+
const candidate = data.candidates[0];
|
|
477
|
+
if (candidate.content && candidate.content.parts) {
|
|
478
|
+
for (const part of candidate.content.parts) {
|
|
479
|
+
if (part.text) {
|
|
480
|
+
contentStr += part.text;
|
|
481
|
+
onChunk(part.text);
|
|
482
|
+
}
|
|
483
|
+
else if (part.functionCall) {
|
|
484
|
+
toolCalls.push({
|
|
485
|
+
id: `call_${Math.random().toString(36).substring(7)}`,
|
|
486
|
+
type: 'function',
|
|
487
|
+
function: { name: part.functionCall.name, arguments: JSON.stringify(part.functionCall.args || {}) }
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (data.usageMetadata?.totalTokenCount)
|
|
494
|
+
totalTokens = data.usageMetadata.totalTokenCount;
|
|
495
|
+
}
|
|
496
|
+
catch { }
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
message: { content: contentStr || null, tool_calls: toolCalls.length > 0 ? toolCalls : undefined },
|
|
502
|
+
usage: totalTokens > 0 ? { total_tokens: totalTokens } : undefined
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
catch {
|
|
506
|
+
return this.chat(request);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
271
509
|
}
|
|
272
510
|
exports.GeminiAdapter = GeminiAdapter;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.nyxDaemon = exports.NyxDaemon = void 0;
|
|
7
|
+
const parser_1 = require("../config/parser");
|
|
8
|
+
const llmUtils_1 = require("../utils/llmUtils");
|
|
9
|
+
const episodic_1 = require("../memory/episodic");
|
|
10
|
+
const reasoning_1 = require("./reasoning");
|
|
11
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
12
|
+
class NyxDaemon {
|
|
13
|
+
isProcessing = false;
|
|
14
|
+
interval = null;
|
|
15
|
+
initialTimeout = null;
|
|
16
|
+
start() {
|
|
17
|
+
if (this.interval)
|
|
18
|
+
return;
|
|
19
|
+
// Initial run after 5 minutes
|
|
20
|
+
this.initialTimeout = setTimeout(() => {
|
|
21
|
+
this.runAudit();
|
|
22
|
+
}, 5 * 60 * 1000);
|
|
23
|
+
// Audit memory every 30 minutes
|
|
24
|
+
this.interval = setInterval(() => {
|
|
25
|
+
this.runAudit();
|
|
26
|
+
}, 30 * 60 * 1000);
|
|
27
|
+
console.log(picocolors_1.default.magenta('[Nyx] Dialectic User Modeling daemon started.'));
|
|
28
|
+
}
|
|
29
|
+
stop() {
|
|
30
|
+
if (this.initialTimeout) {
|
|
31
|
+
clearTimeout(this.initialTimeout);
|
|
32
|
+
this.initialTimeout = null;
|
|
33
|
+
}
|
|
34
|
+
if (this.interval) {
|
|
35
|
+
clearInterval(this.interval);
|
|
36
|
+
this.interval = null;
|
|
37
|
+
console.log(picocolors_1.default.magenta('[Nyx] Daemon stopped.'));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async runAudit() {
|
|
41
|
+
if (this.isProcessing)
|
|
42
|
+
return;
|
|
43
|
+
this.isProcessing = true;
|
|
44
|
+
try {
|
|
45
|
+
console.log(picocolors_1.default.magenta('[Nyx] Running dialectic user modeling...'));
|
|
46
|
+
// Fix A: Use getRecentMessagesAllSessions so Telegram/Discord history is included.
|
|
47
|
+
// Previously getHistory(undefined) only read session_id IS NULL — Telegram sessions have a session_id.
|
|
48
|
+
const history = reasoning_1.logger.getRecentMessagesAllSessions(30);
|
|
49
|
+
// Fix B: Filter noise — only send plain user & assistant text messages to the LLM.
|
|
50
|
+
// Skip tool messages, system messages, and assistant entries that only contain tool_calls.
|
|
51
|
+
const conversationOnly = history.filter(m => (m.role === 'user' || m.role === 'assistant') &&
|
|
52
|
+
m.content &&
|
|
53
|
+
m.content.trim().length > 0 &&
|
|
54
|
+
!m.tool_calls);
|
|
55
|
+
if (conversationOnly.length < 5) {
|
|
56
|
+
console.log(picocolors_1.default.magenta('[Nyx] Not enough conversation messages to analyze, skipping.'));
|
|
57
|
+
this.isProcessing = false;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
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
|
+
});
|
|
82
|
+
});
|
|
83
|
+
let content = response.message.content || '[]';
|
|
84
|
+
// Clean markdown if any
|
|
85
|
+
content = content.replace(/\`\`\`json/g, '').replace(/\`\`\`/g, '').trim();
|
|
86
|
+
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}`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
console.error(picocolors_1.default.red('[Nyx] Failed to parse JSON traits'), content);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
console.error(picocolors_1.default.red(`[Nyx] Analysis failed: ${e.message}`));
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
this.isProcessing = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.NyxDaemon = NyxDaemon;
|
|
110
|
+
exports.nyxDaemon = new NyxDaemon();
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.logger = void 0;
|
|
7
7
|
exports.processOsIntent = processOsIntent;
|
|
8
|
+
exports.processOsIntentStream = processOsIntentStream;
|
|
8
9
|
const parser_1 = require("../config/parser");
|
|
9
10
|
const logger_1 = require("../memory/logger");
|
|
10
11
|
const tracker_1 = require("../gateway/tracker");
|
|
@@ -229,3 +230,118 @@ async function processOsIntent(input, role = 'user', onProgress, sessionId) {
|
|
|
229
230
|
return errorMsg;
|
|
230
231
|
}
|
|
231
232
|
}
|
|
233
|
+
async function processOsIntentStream(input, onChunk, onProgress, sessionId) {
|
|
234
|
+
const config = (0, parser_1.loadConfig)();
|
|
235
|
+
exports.logger.addEntry({ role: 'user', content: input }, sessionId);
|
|
236
|
+
const pluginTools = registry_1.pluginManager.getAllToolDefinitions();
|
|
237
|
+
let activeTools = [...pluginTools].filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
|
|
238
|
+
const { sanitizeHistoryForLLM } = require('../utils/historySanitizer');
|
|
239
|
+
try {
|
|
240
|
+
let turnCount = 0;
|
|
241
|
+
const MAX_TURNS = 10;
|
|
242
|
+
let fullResponse = '';
|
|
243
|
+
while (turnCount < MAX_TURNS) {
|
|
244
|
+
turnCount++;
|
|
245
|
+
const currentHistory = exports.logger.getHistory(sessionId);
|
|
246
|
+
const sanitizedHistory = sanitizeHistoryForLLM(currentHistory, activeTools, config.llm.provider);
|
|
247
|
+
const messages = [
|
|
248
|
+
{ role: 'system', content: getSystemPrompt('os', input) },
|
|
249
|
+
...sanitizedHistory
|
|
250
|
+
];
|
|
251
|
+
let streamedContent = '';
|
|
252
|
+
const response = await (0, llmUtils_1.executeWithRetry)(async (client) => {
|
|
253
|
+
return await client.stream({ model: config.llm.model, temperature: config.llm.temperature, messages, tools: activeTools }, (chunk) => {
|
|
254
|
+
streamedContent += chunk;
|
|
255
|
+
onChunk(chunk);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
const responseMessage = response.message;
|
|
259
|
+
if (turnCount === 1)
|
|
260
|
+
tracker_1.Tracker.addMessage();
|
|
261
|
+
if (response.usage?.total_tokens)
|
|
262
|
+
tracker_1.Tracker.addTokens(response.usage.total_tokens, config.llm.provider);
|
|
263
|
+
tracker_1.Tracker.addEvent('llm.response', { provider: config.llm.provider, tool_calls: responseMessage.tool_calls?.length || 0 });
|
|
264
|
+
exports.logger.addEntry({
|
|
265
|
+
role: 'assistant',
|
|
266
|
+
content: responseMessage.content || '',
|
|
267
|
+
tool_calls: responseMessage.tool_calls,
|
|
268
|
+
}, sessionId);
|
|
269
|
+
if (!responseMessage.tool_calls || responseMessage.tool_calls.length === 0) {
|
|
270
|
+
let finalContent = responseMessage.content || 'No response generated.';
|
|
271
|
+
finalContent = finalContent.replace(/<(think|thought|thinking|reasoning|analysis|reflection)[\s\S]*?<\/\1>\n?/gi, '').trim();
|
|
272
|
+
fullResponse = finalContent;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
// Tool calls detected — pause stream visually and execute tools
|
|
276
|
+
const fastReturnTools = ['transfer_token', 'transfer_native', 'swap_token', 'bridge_token', 'mint_nft', 'custom_tx', 'revoke_approval', 'supply_aave', 'deposit_yield_vault', 'provide_liquidity_v3'];
|
|
277
|
+
let canFastReturnAll = true;
|
|
278
|
+
const accumulatedResults = [];
|
|
279
|
+
for (const _toolCall of responseMessage.tool_calls) {
|
|
280
|
+
const toolCall = _toolCall;
|
|
281
|
+
const toolName = toolCall.function.name;
|
|
282
|
+
let result = '';
|
|
283
|
+
let args = {};
|
|
284
|
+
console.log(picocolors_1.default.yellow(`[⚡ Tool Execution] AI is calling ${toolName}...`));
|
|
285
|
+
if (onProgress)
|
|
286
|
+
onProgress(`_⚡ Running tool: ${toolName}..._`);
|
|
287
|
+
try {
|
|
288
|
+
let argStr = toolCall.function.arguments;
|
|
289
|
+
if (argStr && !argStr.trim().endsWith('}'))
|
|
290
|
+
argStr += '}';
|
|
291
|
+
args = JSON.parse(argStr);
|
|
292
|
+
}
|
|
293
|
+
catch (parseError) {
|
|
294
|
+
result = `[System Error] Arguments for ${toolName} must be valid JSON. Error: ${parseError.message}`;
|
|
295
|
+
exports.logger.addEntry({ role: 'tool', tool_call_id: toolCall.id, content: result }, sessionId);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (!(0, skillManager_1.isSkillActive)(toolName)) {
|
|
299
|
+
result = `[System Error] Access denied: Skill '${toolName}' is currently disabled.`;
|
|
300
|
+
exports.logger.addEntry({ role: 'tool', tool_call_id: toolCall.id, content: result }, sessionId);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
const pluginResult = await registry_1.pluginManager.executeTool(toolName, args, { sessionId });
|
|
305
|
+
result = pluginResult !== null ? pluginResult : `Error: Tool ${toolName} is not implemented.`;
|
|
306
|
+
if (result.includes('[Security Blocked]') || result.startsWith('Error:')) {
|
|
307
|
+
console.log(picocolors_1.default.red(`[❌ Failed] Tool ${toolName} returned an error or was blocked.`));
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
console.log(picocolors_1.default.green(`[✅ Success] Tool ${toolName} executed successfully.`));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch (toolError) {
|
|
314
|
+
result = `Error executing ${toolName}: ${toolError.message}`;
|
|
315
|
+
console.error(picocolors_1.default.red(`[❌ Error Crash] Execution of ${toolName} failed: ${toolError.message}`));
|
|
316
|
+
}
|
|
317
|
+
exports.logger.addEntry({ role: 'tool', tool_call_id: toolCall.id, name: toolName, content: result }, sessionId);
|
|
318
|
+
accumulatedResults.push(result);
|
|
319
|
+
if (!fastReturnTools.includes(toolName))
|
|
320
|
+
canFastReturnAll = false;
|
|
321
|
+
}
|
|
322
|
+
if (canFastReturnAll && accumulatedResults.length > 0) {
|
|
323
|
+
const finalContent = accumulatedResults.join('\n\n---\n\n');
|
|
324
|
+
exports.logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
|
|
325
|
+
onChunk(finalContent);
|
|
326
|
+
fullResponse = finalContent;
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (!fullResponse) {
|
|
331
|
+
const maxTurnMsg = '⚠️ Reached maximum interaction limit (10 turns). Please be more specific.';
|
|
332
|
+
exports.logger.addEntry({ role: 'assistant', content: maxTurnMsg }, sessionId);
|
|
333
|
+
fullResponse = maxTurnMsg;
|
|
334
|
+
}
|
|
335
|
+
return fullResponse;
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
console.error('LLM Stream Error:', error);
|
|
339
|
+
const status = error?.status || error?.response?.status;
|
|
340
|
+
let errorMsg = '⚠️ All models are temporarily rate-limited. Please try again in a few minutes.';
|
|
341
|
+
if (status === 400 || (error.message && error.message.toLowerCase().includes('invalid'))) {
|
|
342
|
+
errorMsg = '⚠️ Failed to parse instruction. Please describe your command more specifically.';
|
|
343
|
+
}
|
|
344
|
+
exports.logger.addEntry({ role: 'assistant', content: errorMsg }, sessionId);
|
|
345
|
+
return errorMsg;
|
|
346
|
+
}
|
|
347
|
+
}
|