nyxora 26.7.2-alpha.3 → 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 +12 -0
- package/README.md +3 -3
- package/dist/packages/core/src/agent/llmProvider.js +238 -0
- package/dist/packages/core/src/agent/nyxDaemon.js +22 -6
- 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/web3Agent.js +116 -0
- package/dist/packages/core/src/gateway/chat.js +56 -13
- package/dist/packages/core/src/gateway/server.js +44 -3
- package/dist/packages/core/src/gateway/telegram.js +103 -18
- package/dist/packages/core/src/memory/episodic.js +9 -0
- package/dist/packages/core/src/memory/logger.js +28 -0
- package/dist/packages/core/src/system/plugins/SystemWorkspacePlugin.js +2 -2
- package/dist/packages/core/src/system/skills/forgetMemory.js +6 -4
- package/dist/packages/core/src/utils/streamSimulator.js +75 -0
- package/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/agent/llmProvider.ts +215 -0
- package/packages/core/src/agent/nyxDaemon.ts +27 -6
- package/packages/core/src/agent/osAgent.ts +134 -0
- package/packages/core/src/agent/reasoning.ts +216 -32
- package/packages/core/src/agent/web3Agent.ts +134 -0
- package/packages/core/src/gateway/chat.ts +57 -15
- package/packages/core/src/gateway/server.ts +49 -4
- package/packages/core/src/gateway/telegram.ts +120 -19
- package/packages/core/src/memory/episodic.ts +11 -0
- package/packages/core/src/memory/logger.ts +27 -0
- package/packages/core/src/system/plugins/SystemWorkspacePlugin.ts +2 -2
- package/packages/core/src/system/skills/forgetMemory.ts +7 -4
- package/packages/core/src/utils/streamSimulator.ts +74 -0
- package/packages/dashboard/dist/assets/index-B1yTXubl.js +18 -0
- package/packages/dashboard/dist/index.html +1 -1
- 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/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/gateway/twitterAdapter.js +0 -103
- 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/utils/safeLogger.js +0 -59
- package/packages/dashboard/dist/assets/index-BT1Oq9PW.js +0 -16
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
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
|
+
## [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
|
+
|
|
6
18
|
## [26.7.2-alpha.2]
|
|
7
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.
|
|
8
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.
|
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.
|
|
@@ -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
|
|
|
@@ -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;
|
|
@@ -43,19 +43,33 @@ class NyxDaemon {
|
|
|
43
43
|
this.isProcessing = true;
|
|
44
44
|
try {
|
|
45
45
|
console.log(picocolors_1.default.magenta('[Nyx] Running dialectic user modeling...'));
|
|
46
|
-
|
|
47
|
-
|
|
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.'));
|
|
48
57
|
this.isProcessing = false;
|
|
49
58
|
return;
|
|
50
59
|
}
|
|
51
60
|
const config = (0, parser_1.loadConfig)();
|
|
52
61
|
const prompt = `You are Nyx, Nyxora's background Persona Auditor.
|
|
53
62
|
Analyze the following recent conversation between the USER and Nyxora.
|
|
54
|
-
Identify any persistent user traits, behavioral preferences,
|
|
63
|
+
Identify any persistent user traits, behavioral preferences, trading styles, AND language preferences.
|
|
55
64
|
Output your findings AS A STRICT JSON ARRAY of strings. If no strong traits are found, output an empty array [].
|
|
56
|
-
Examples of valid traits:
|
|
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.
|
|
57
69
|
DO NOT output markdown, just the JSON array.`;
|
|
58
|
-
const messages =
|
|
70
|
+
const messages = conversationOnly
|
|
71
|
+
.map((m) => `${m.role === 'user' ? 'USER' : 'NYXORA'}: ${m.content}`)
|
|
72
|
+
.join('\n');
|
|
59
73
|
const response = await (0, llmUtils_1.executeWithRetry)(async (client) => {
|
|
60
74
|
return await client.chat({
|
|
61
75
|
model: config.llm.model,
|
|
@@ -73,7 +87,9 @@ DO NOT output markdown, just the JSON array.`;
|
|
|
73
87
|
const traits = JSON.parse(content);
|
|
74
88
|
if (Array.isArray(traits) && traits.length > 0) {
|
|
75
89
|
for (const trait of traits) {
|
|
76
|
-
|
|
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');
|
|
77
93
|
console.log(picocolors_1.default.magenta(`[Nyx] Discovered new trait: ${trait}`));
|
|
78
94
|
}
|
|
79
95
|
}
|
|
@@ -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
|
+
}
|