nyxora 26.6.20 → 26.6.22-1
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/README.md +18 -1
- package/bin/nyxora.mjs +46 -2
- package/dist/packages/core/src/agent/cronManager.js +107 -0
- package/dist/packages/core/src/agent/reasoning.js +95 -22
- package/dist/packages/core/src/agent/transactionManager.js +2 -2
- package/dist/packages/core/src/agent/updateIdentity.js +71 -0
- package/dist/packages/core/src/config/parser.js +121 -7
- package/dist/packages/core/src/config/paths.js +5 -20
- package/dist/packages/core/src/gateway/chat.js +120 -0
- package/dist/packages/core/src/gateway/cli.js +82 -19
- package/dist/packages/core/src/gateway/server.js +142 -63
- package/dist/packages/core/src/gateway/setup.js +39 -22
- package/dist/packages/core/src/gateway/telegram.js +43 -0
- package/dist/packages/core/src/gateway/tracker.js +58 -0
- package/dist/packages/core/src/memory/logger.js +1 -1
- package/dist/packages/core/src/system/skills/cancelTask.js +40 -0
- package/dist/packages/core/src/system/skills/editFile.js +5 -0
- package/dist/packages/core/src/system/skills/scheduleTask.js +39 -0
- package/dist/packages/core/src/system/skills/writeFile.js +5 -0
- package/dist/packages/core/src/utils/formatter.test.js +40 -0
- package/dist/packages/core/src/utils/skillManager.js +91 -0
- package/dist/packages/core/src/utils/userWhitelistManager.js +41 -36
- package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +2 -2
- package/dist/packages/core/src/web3/aggregator/defiRouter.js +3 -0
- package/dist/packages/core/src/web3/skills/bridgeToken.js +4 -0
- package/dist/packages/core/src/web3/skills/checkRegistryStatus.js +13 -0
- package/dist/packages/core/src/web3/skills/customTx.js +2 -0
- package/dist/packages/core/src/web3/skills/defiLending.js +2 -0
- package/dist/packages/core/src/web3/skills/getPrice.js +1 -1
- package/dist/packages/core/src/web3/skills/manageCustomTokens.js +18 -32
- package/dist/packages/core/src/web3/skills/marketAnalysis.js +3 -1
- package/dist/packages/core/src/web3/skills/mintNft.js +2 -0
- package/dist/packages/core/src/web3/skills/provideLiquidity.js +2 -0
- package/dist/packages/core/src/web3/skills/revokeApprovals.js +2 -0
- package/dist/packages/core/src/web3/skills/swapToken.js +4 -2
- package/dist/packages/core/src/web3/skills/transfer.js +2 -0
- package/dist/packages/core/src/web3/skills/yieldVault.js +2 -0
- package/dist/packages/core/src/web3/utils/tokens.js +9 -1
- package/dist/packages/policy/src/server.js +1 -1
- package/package.json +5 -1
- package/packages/core/package.json +5 -1
- package/packages/core/src/agent/cronManager.ts +87 -0
- package/packages/core/src/agent/reasoning.ts +102 -21
- package/packages/core/src/agent/transactionManager.ts +2 -1
- package/packages/core/src/agent/updateIdentity.ts +68 -0
- package/packages/core/src/config/parser.ts +119 -9
- package/packages/core/src/config/paths.ts +5 -23
- package/packages/core/src/gateway/chat.ts +124 -0
- package/packages/core/src/gateway/cli.ts +73 -10
- package/packages/core/src/gateway/server.ts +158 -66
- package/packages/core/src/gateway/setup.ts +39 -27
- package/packages/core/src/gateway/telegram.ts +49 -0
- package/packages/core/src/gateway/tracker.ts +55 -0
- package/packages/core/src/memory/logger.ts +1 -1
- package/packages/core/src/system/skills/cancelTask.ts +38 -0
- package/packages/core/src/system/skills/editFile.ts +7 -0
- package/packages/core/src/system/skills/scheduleTask.ts +38 -0
- package/packages/core/src/system/skills/writeFile.ts +7 -0
- package/packages/core/src/utils/formatter.test.ts +41 -0
- package/packages/core/src/utils/skillManager.ts +98 -0
- package/packages/core/src/utils/userWhitelistManager.ts +48 -39
- package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +2 -2
- package/packages/core/src/web3/aggregator/defiRouter.ts +4 -0
- package/packages/core/src/web3/skills/bridgeToken.ts +3 -0
- package/packages/core/src/web3/skills/checkRegistryStatus.ts +13 -0
- package/packages/core/src/web3/skills/customTx.ts +1 -0
- package/packages/core/src/web3/skills/defiLending.ts +1 -0
- package/packages/core/src/web3/skills/getPrice.ts +1 -1
- package/packages/core/src/web3/skills/manageCustomTokens.ts +18 -29
- package/packages/core/src/web3/skills/marketAnalysis.ts +2 -1
- package/packages/core/src/web3/skills/mintNft.ts +1 -0
- package/packages/core/src/web3/skills/provideLiquidity.ts +1 -0
- package/packages/core/src/web3/skills/revokeApprovals.ts +1 -0
- package/packages/core/src/web3/skills/swapToken.ts +3 -2
- package/packages/core/src/web3/skills/transfer.ts +1 -0
- package/packages/core/src/web3/skills/yieldVault.ts +1 -0
- package/packages/core/src/web3/utils/tokens.ts +9 -1
- package/packages/dashboard/dist/assets/index-CjZWf1Ei.css +1 -0
- package/packages/dashboard/dist/assets/index-CmWZofn_.js +16 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/dist/routers/0x.png +0 -0
- package/packages/dashboard/dist/routers/1inch.png +0 -0
- package/packages/dashboard/dist/routers/cmc.png +0 -0
- package/packages/dashboard/dist/routers/kyberswap.png +0 -0
- package/packages/dashboard/dist/routers/lifi.png +0 -0
- package/packages/dashboard/dist/routers/openocean.png +0 -0
- package/packages/dashboard/dist/routers/relay.png +0 -0
- package/packages/dashboard/dist/routers/zerion.png +0 -0
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +1 -1
- package/packages/policy/src/server.ts +1 -1
- package/packages/signer/package.json +1 -1
- package/dist/packages/core/src/agent/limitOrderManager.js +0 -124
- package/dist/packages/core/src/system/pluginManager.js +0 -91
- package/dist/packages/core/src/system/skills/installSkill.js +0 -52
- package/dist/packages/core/src/test-all-routers.js +0 -81
- package/dist/packages/core/src/test-router.js +0 -38
- package/dist/packages/core/src/web3/skills/autonomousDefi.js +0 -191
- package/dist/packages/core/src/web3/skills/createWallet.js +0 -34
- package/dist/packages/core/src/web3/skills/limitOrder.js +0 -106
- package/dist/packages/core/src/web3/utils/protocolRegistry.js +0 -46
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/packages/core/src/__tests__/reasoning.test.ts +0 -81
- package/packages/core/src/__tests__/tokens.test.ts +0 -55
- package/packages/core/src/__tests__/web3.test.ts +0 -50
- package/packages/core/src/agent/reasoning.d.ts.map +0 -1
- package/packages/core/src/config/parser.d.ts.map +0 -1
- package/packages/core/src/gateway/cli.d.ts.map +0 -1
- package/packages/core/src/memory/logger.d.ts.map +0 -1
- package/packages/core/src/test-all-routers.ts +0 -59
- package/packages/core/src/test-router.ts +0 -49
- package/packages/core/src/web3/config.d.ts.map +0 -1
- package/packages/core/src/web3/skills/getBalance.d.ts.map +0 -1
- package/packages/dashboard/dist/assets/index-CQNHWZtN.css +0 -1
- package/packages/dashboard/dist/assets/index-O2m42q4p.js +0 -16
package/README.md
CHANGED
|
@@ -24,6 +24,9 @@ It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human
|
|
|
24
24
|
* **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).
|
|
25
25
|
* **DeFi Configuration BYOK & UI Masking**: All aggregator and provider API keys are strictly isolated via a Bring Your Own Keys (BYOK) architecture into a heavily guarded `~/.nyxora/defi_keys.yaml` file. The local web Dashboard masks these injected secrets using `***********` and `IS_SET` censorship, completely neutralizing malicious browser extensions from exfiltrating your keys.
|
|
26
26
|
* **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.
|
|
27
|
+
* **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.
|
|
28
|
+
* **Human-in-the-Loop Memory Approval**: AI-extracted permanent behavioral rules are strictly quarantined in a `pending` state until explicitly authorized by the user via the Dashboard, neutralizing Persistent Memory Poisoning vectors.
|
|
29
|
+
* **Stateless Policy Engine & DoS Resilience**: The Policy Engine operates as a 100% stateless cryptographic HMAC gatekeeper, hardened with resilient `try...catch` IPC interceptors to withstand Signer-level Denial of Service (Crash) attacks.
|
|
27
30
|
* **Immutable Policy Guardrails**: Transaction limits (e.g. `max_usd_per_tx`) are strictly enforced by the Policy Engine. The LLM has zero write-access to bypass these rules.
|
|
28
31
|
|
|
29
32
|
* **Graceful SQLite WAL Shutdown**: Integrated `SIGTERM`/`SIGINT` interceptors ensure that when the daemon stops, active requests are safely terminated and SQLite Write-Ahead Logs (WAL) are securely flushed, preventing database corruption.
|
|
@@ -133,6 +136,12 @@ nyxora dashboard
|
|
|
133
136
|
```bash
|
|
134
137
|
nyxora clear --force
|
|
135
138
|
```
|
|
139
|
+
|
|
140
|
+
### Utility: Clean Uninstallation
|
|
141
|
+
To completely remove Nyxora, wipe the AI's local memory, and securely delete your Private Key from the OS Keyring before uninstalling the NPM package:
|
|
142
|
+
```bash
|
|
143
|
+
nyxora uninstall
|
|
144
|
+
```
|
|
136
145
|
> **⚠️ IMPORTANT:** Whenever you re-run `nyxora setup` or manually edit the config files, you **must restart the daemon** by running `nyxora restart` for the changes to take effect.
|
|
137
146
|
|
|
138
147
|
### Local Development (From Source)
|
|
@@ -159,6 +168,14 @@ npm start
|
|
|
159
168
|
|
|
160
169
|
---
|
|
161
170
|
|
|
171
|
+
## ⚖️ Terms of Service
|
|
172
|
+
|
|
173
|
+
By downloading, installing, or using the Nyxora AI Agent, you agree to our assumption of risk and liability limitations. Please ensure you review our legal policies before deploying the agent.
|
|
174
|
+
|
|
175
|
+
> **🔗 [Read the Full Terms of Service Here](https://nyxoraai.github.io/Nyxora/terms)**
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
162
179
|
## 📖 Official Documentation
|
|
163
180
|
|
|
164
181
|
For complete technical deep-dives into our Cryptographic Architecture, please visit our official VitePress Documentation Site!
|
|
@@ -172,7 +189,7 @@ For complete technical deep-dives into our Cryptographic Architecture, please vi
|
|
|
172
189
|
**❤️ Support the Project**
|
|
173
190
|
|
|
174
191
|
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:
|
|
175
|
-
- **EVM
|
|
192
|
+
- **EVM :** `0x490717E50D6434C348AA0D2bD5fe682392823708`
|
|
176
193
|
|
|
177
194
|
---
|
|
178
195
|
**License:** MIT License
|
package/bin/nyxora.mjs
CHANGED
|
@@ -86,18 +86,30 @@ async function start() {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
async function stop() {
|
|
89
|
+
async function stop(preserveTracker = false) {
|
|
90
90
|
const pid = await getDaemonPid();
|
|
91
91
|
if (pid) {
|
|
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
|
+
let attempts = 0;
|
|
98
|
+
while (isDaemonRunning(pid.toString()) && attempts < 20) {
|
|
99
|
+
await new Promise(r => setTimeout(r, 100));
|
|
100
|
+
attempts++;
|
|
101
|
+
}
|
|
102
|
+
|
|
95
103
|
console.log('Nyxora stopped gracefully.');
|
|
96
104
|
} catch (e) {
|
|
97
105
|
console.error('Failed to kill process:', e.message);
|
|
98
106
|
}
|
|
99
107
|
try {
|
|
100
108
|
fs.unlinkSync(pidFile);
|
|
109
|
+
if (!preserveTracker) {
|
|
110
|
+
const trackerFile = path.join(appDir, 'run', 'tracker.json');
|
|
111
|
+
if (fs.existsSync(trackerFile)) fs.unlinkSync(trackerFile);
|
|
112
|
+
}
|
|
101
113
|
} catch(e) {}
|
|
102
114
|
} else {
|
|
103
115
|
console.log('Nyxora is not running.');
|
|
@@ -105,7 +117,7 @@ async function stop() {
|
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
async function restart() {
|
|
108
|
-
await stop();
|
|
120
|
+
await stop(true);
|
|
109
121
|
setTimeout(start, 1000);
|
|
110
122
|
}
|
|
111
123
|
|
|
@@ -267,6 +279,34 @@ async function wallet(cliArgs) {
|
|
|
267
279
|
await new Promise(resolve => child.on('close', resolve));
|
|
268
280
|
}
|
|
269
281
|
|
|
282
|
+
async function uninstall(cliArgs) {
|
|
283
|
+
const compiledCli = path.join(projectRoot, 'dist', 'packages/core/src/gateway/cli.js');
|
|
284
|
+
const useCompiled = fs.existsSync(compiledCli);
|
|
285
|
+
const cmd = useCompiled ? 'node' : 'npx';
|
|
286
|
+
const args = useCompiled ? [compiledCli, 'uninstall', ...cliArgs] : ['ts-node', '-T', 'packages/core/src/gateway/cli.ts', 'uninstall', ...cliArgs];
|
|
287
|
+
const child = spawn(cmd, args, {
|
|
288
|
+
cwd: projectRoot,
|
|
289
|
+
stdio: 'inherit',
|
|
290
|
+
env: { ...process.env, TS_NODE_CACHE: 'false' }
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
await new Promise(resolve => child.on('close', resolve));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function chat(cliArgs) {
|
|
297
|
+
const compiledCli = path.join(projectRoot, 'dist', 'packages/core/src/gateway/cli.js');
|
|
298
|
+
const useCompiled = fs.existsSync(compiledCli);
|
|
299
|
+
const cmd = useCompiled ? 'node' : 'npx';
|
|
300
|
+
const args = useCompiled ? [compiledCli, 'chat', ...cliArgs] : ['ts-node', '-T', 'packages/core/src/gateway/cli.ts', 'chat', ...cliArgs];
|
|
301
|
+
const child = spawn(cmd, args, {
|
|
302
|
+
cwd: projectRoot,
|
|
303
|
+
stdio: 'inherit',
|
|
304
|
+
env: { ...process.env, TS_NODE_CACHE: 'false' }
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
await new Promise(resolve => child.on('close', resolve));
|
|
308
|
+
}
|
|
309
|
+
|
|
270
310
|
async function runDoctor() {
|
|
271
311
|
const compiledCli = path.join(projectRoot, 'dist', 'packages/core/src/gateway/doctor.js');
|
|
272
312
|
const useCompiled = fs.existsSync(compiledCli);
|
|
@@ -317,6 +357,8 @@ async function main() {
|
|
|
317
357
|
case 'clear': await clearMemory(process.argv.slice(3)); break;
|
|
318
358
|
case 'set-key': await setKey(process.argv.slice(3)); break;
|
|
319
359
|
case 'wallet': await wallet(process.argv.slice(3)); break;
|
|
360
|
+
case 'chat': await chat(process.argv.slice(3)); break;
|
|
361
|
+
case 'uninstall': await uninstall(process.argv.slice(3)); break;
|
|
320
362
|
case 'start': await start(); break;
|
|
321
363
|
case 'stop': await stop(); break;
|
|
322
364
|
case 'restart': await restart(); break;
|
|
@@ -341,6 +383,7 @@ Commands:
|
|
|
341
383
|
start Start the Nyxora background daemon
|
|
342
384
|
stop Stop the running daemon
|
|
343
385
|
restart Restart the daemon
|
|
386
|
+
chat Chat interactively with the AI in terminal
|
|
344
387
|
setup Run the interactive Setup Wizard
|
|
345
388
|
dashboard Open the dashboard in your browser
|
|
346
389
|
unlock Unlock an inactive dashboard session
|
|
@@ -350,6 +393,7 @@ Commands:
|
|
|
350
393
|
autostart Enable/disable autostart on boot (usage: nyxora autostart enable)
|
|
351
394
|
set-key Securely save API Key (usage: nyxora set-key <provider> <key>)
|
|
352
395
|
wallet Manage your Web3 Wallet (usage: nyxora wallet update)
|
|
396
|
+
uninstall Wipe AI memory, securely delete keys, and remove configuration
|
|
353
397
|
|
|
354
398
|
Options:
|
|
355
399
|
-v, --version Show current version
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.cronManager = void 0;
|
|
40
|
+
const cron = __importStar(require("node-cron"));
|
|
41
|
+
const parser_1 = require("../config/parser");
|
|
42
|
+
const telegram_1 = require("../gateway/telegram");
|
|
43
|
+
const crypto_1 = require("crypto");
|
|
44
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
45
|
+
class CronManager {
|
|
46
|
+
jobs = new Map();
|
|
47
|
+
addJob(expression, prompt) {
|
|
48
|
+
const id = (0, crypto_1.randomUUID)();
|
|
49
|
+
// Validate expression
|
|
50
|
+
if (!cron.validate(expression)) {
|
|
51
|
+
throw new Error(`Invalid cron expression: ${expression}`);
|
|
52
|
+
}
|
|
53
|
+
const task = cron.schedule(expression, async () => {
|
|
54
|
+
console.log(picocolors_1.default.cyan(`[Cron] Executing job ${id}: "${prompt}"`));
|
|
55
|
+
try {
|
|
56
|
+
// Dynamically import processUserInput to avoid circular dependencies
|
|
57
|
+
const { processUserInput } = await Promise.resolve().then(() => __importStar(require('./reasoning')));
|
|
58
|
+
// Execute the prompt as a background system task
|
|
59
|
+
const response = await processUserInput(prompt, 'system', undefined, `cron-${id}`);
|
|
60
|
+
// Push notification to Telegram if configured
|
|
61
|
+
const config = (0, parser_1.loadConfig)();
|
|
62
|
+
if (config.integrations?.telegram?.enabled && config.integrations?.telegram?.authorized_chat_id) {
|
|
63
|
+
const message = `🤖 *AI Scheduled Report*\n\n${response}`;
|
|
64
|
+
await (0, telegram_1.sendPushNotification)(config.integrations.telegram.authorized_chat_id, message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error(picocolors_1.default.red(`[Cron] Failed to execute job ${id}:`), err);
|
|
69
|
+
const config = (0, parser_1.loadConfig)();
|
|
70
|
+
if (config.integrations?.telegram?.enabled && config.integrations?.telegram?.authorized_chat_id) {
|
|
71
|
+
await (0, telegram_1.sendPushNotification)(config.integrations.telegram.authorized_chat_id, `⚠️ *Cron Job Error*\n\nPrompt: ${prompt}\nError: ${err.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
this.jobs.set(id, {
|
|
76
|
+
id,
|
|
77
|
+
expression,
|
|
78
|
+
prompt,
|
|
79
|
+
task,
|
|
80
|
+
createdAt: Date.now()
|
|
81
|
+
});
|
|
82
|
+
console.log(picocolors_1.default.green(`[Cron] Scheduled new job ${id} with expression '${expression}'`));
|
|
83
|
+
return id;
|
|
84
|
+
}
|
|
85
|
+
removeJob(id) {
|
|
86
|
+
const job = this.jobs.get(id);
|
|
87
|
+
if (job) {
|
|
88
|
+
job.task.stop();
|
|
89
|
+
this.jobs.delete(id);
|
|
90
|
+
console.log(picocolors_1.default.yellow(`[Cron] Removed job ${id}`));
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
getJobs() {
|
|
96
|
+
return Array.from(this.jobs.values()).map(job => ({
|
|
97
|
+
id: job.id,
|
|
98
|
+
expression: job.expression,
|
|
99
|
+
prompt: job.prompt,
|
|
100
|
+
createdAt: job.createdAt
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
getActiveJobsCount() {
|
|
104
|
+
return this.jobs.size;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.cronManager = new CronManager();
|
|
@@ -34,6 +34,7 @@ const provideLiquidity_1 = require("../web3/skills/provideLiquidity");
|
|
|
34
34
|
const getTxHistory_1 = require("../web3/skills/getTxHistory");
|
|
35
35
|
const createLimitOrder_1 = require("../web3/skills/createLimitOrder");
|
|
36
36
|
const updateProfile_1 = require("./updateProfile");
|
|
37
|
+
const updateIdentity_1 = require("./updateIdentity");
|
|
37
38
|
const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
|
|
38
39
|
const analyzeDocument_1 = require("../system/skills/analyzeDocument");
|
|
39
40
|
const readFile_1 = require("../system/skills/readFile");
|
|
@@ -48,6 +49,8 @@ const xManager_1 = require("../system/skills/xManager");
|
|
|
48
49
|
const notionWorkspace_1 = require("../system/skills/notionWorkspace");
|
|
49
50
|
const audioTranscribe_1 = require("../system/skills/audioTranscribe");
|
|
50
51
|
const summarizeText_1 = require("../system/skills/summarizeText");
|
|
52
|
+
const scheduleTask_1 = require("../system/skills/scheduleTask");
|
|
53
|
+
const cancelTask_1 = require("../system/skills/cancelTask");
|
|
51
54
|
const googleWorkspace_1 = require("../system/skills/googleWorkspace");
|
|
52
55
|
const paths_1 = require("../config/paths");
|
|
53
56
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
@@ -143,35 +146,62 @@ CRITICAL RULE 11: ADAPTIVE RESPONSE RULE. You must process Web3 data (portfolio,
|
|
|
143
146
|
CRITICAL RULE 13: WALLET CONTEXT CACHING. Portfolio data in chat history is potentially stale. Do not use cached data for transactional planning; refresh the balance via tools first.
|
|
144
147
|
CRITICAL RULE 14: TRANSACTION EXECUTION. For ALL state-changing transactions (swap, bridge, transfer, stake), do NOT ask for verbal confirmation. Execute the tool IMMEDIATELY. The tool itself will trigger a secure popup in the user's dashboard UI for final approval.
|
|
145
148
|
CRITICAL RULE 17: MINIMIZE UNNECESSARY TOOL CALLS. Do not call tools if the answer exists in recent verified context and freshness is not strictly required. Use history to save latency.
|
|
149
|
+
CRITICAL RULE 19: GET_PRICE USAGE. Use get_price ONLY when the user explicitly asks for a simple price check (e.g. 'harga', 'price'). Do NOT use this for 'analysis', 'market analysis', or 'analisis pasar'.
|
|
146
150
|
|
|
147
151
|
[ANTI-HALLUCINATION PROTOCOL]
|
|
148
152
|
CRITICAL RULE 6: NETWORK SAFETY VALIDATION. If a request implies cross-chain or mainnet/testnet mixing, or the token symbol is ambiguous (USDC vs USDC.e), YOU MUST NOT GUESS. Ask for confirmation.
|
|
149
153
|
CRITICAL RULE 7: TOOL CONFIDENCE & HALUCINATION PREVENTION. NEVER fabricate blockchain data. If a tool fails or data is missing, state it explicitly. Do not estimate balances, prices, APY, or gas.
|
|
154
|
+
CRITICAL RULE 18: AMOUNT PRECISION. When displaying crypto amounts, use exactly 6 decimal places for precision, or round to 2 decimals only if the value is significantly large (>$10,000). Never abbreviate unless the number is >$1,000,000.
|
|
150
155
|
CRITICAL RULE 9: DEFI CONFIGURATION FALLBACK. If a tool fails due to Rate Limits, Unauthorized, or Missing API Keys, instruct the user to visit the "DeFi Configuration 🔑" menu in the dashboard.
|
|
151
156
|
CRITICAL RULE 12: SMART SLIPPAGE AWARENESS. For low-liquidity assets, warn the user that default slippage might not be enough. NEVER invent specific slippage percentage numbers.
|
|
152
157
|
CRITICAL RULE 16: CAPABILITY HONESTY. NEVER claim a capability not available through installed tools. If asked for an unsupported action, state honestly that the skill is missing.
|
|
153
|
-
CRITICAL RULE 19: MARKET CONFIDENCE SCORE. When analyzing market data, token security, or preparing trades, you MUST explicitly declare a 'Confidence Score (0-100%)' INSIDE your <think> block. If your score is below 40%, you must firmly WARN the user and advise against the trade in your final response
|
|
154
|
-
|
|
158
|
+
CRITICAL RULE 19: MARKET CONFIDENCE SCORE. When analyzing market data, token security, or preparing trades, you MUST explicitly declare a 'Confidence Score (0-100%)' INSIDE your <think> block. If your score is below 40%, you must firmly WARN the user and advise against the trade in your final response.
|
|
159
|
+
CRITICAL RULE 20: CRON JOBS VS LIMIT ORDERS. STRICT RULE: Do NOT use schedule_task for price-based trading triggers or buying/selling at a specific price level. Use create_limit_order. STRICT RULE: Do NOT use create_limit_order for time-based recurring tasks (e.g. 'every 5 minutes', 'every Monday'). Use schedule_task.
|
|
160
|
+
CRITICAL RULE 21: CONFIGURATION SECURITY. You are STRICTLY FORBIDDEN from modifying config.yaml, rpc_key.yaml, or policy.yaml using OS skills or terminal commands (like sed, echo, nano). If you need to change your name, use the update_identity tool.`;
|
|
161
|
+
const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
|
|
162
|
+
const userMdPath = (0, paths_1.getPath)('user.md');
|
|
163
|
+
let isFirstTime = false;
|
|
155
164
|
try {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
const identityContent = fs_1.default.existsSync(identityMdPath) ? fs_1.default.readFileSync(identityMdPath, 'utf8').trim() : '';
|
|
166
|
+
const userContent = fs_1.default.existsSync(userMdPath) ? fs_1.default.readFileSync(userMdPath, 'utf8').trim() : '';
|
|
167
|
+
// Check if files are empty or contain the default installation text
|
|
168
|
+
const isIdentityDefault = !identityContent || identityContent.includes('You are a Web3 AI assistant named Nyxora.');
|
|
169
|
+
const isUserDefault = !userContent || userContent.includes('Write custom instructions, special rules, user profiles');
|
|
170
|
+
isFirstTime = isIdentityDefault && isUserDefault;
|
|
161
171
|
}
|
|
162
|
-
catch (
|
|
163
|
-
|
|
172
|
+
catch (e) {
|
|
173
|
+
isFirstTime = true;
|
|
164
174
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
if (isFirstTime) {
|
|
176
|
+
basePrompt += `\n\n[ONBOARDING MODE]
|
|
177
|
+
This is your VERY FIRST interaction with the user. You MUST warmly welcome them to Nyxora and ask for 4 things to initialize your setup:
|
|
178
|
+
1. Their Name
|
|
179
|
+
2. What they want to name YOU (the AI Agent)
|
|
180
|
+
3. Their Hobbies or Job (so you can tailor your conversation context)
|
|
181
|
+
4. Your Persona/Character (e.g., professional, sarcastic, JARVIS, anime waifu)
|
|
182
|
+
Do NOT perform any web3 tasks or generic answers until they provide all 4 details. Once they answer, use 'update_profile' to save their name and hobbies/job to user.md, and use 'update_identity' (making sure to provide the 'agentName' parameter!) to save your new name and persona to IDENTITY.md.`;
|
|
172
183
|
}
|
|
173
|
-
|
|
174
|
-
|
|
184
|
+
else {
|
|
185
|
+
// Read IDENTITY.md for core AI persona
|
|
186
|
+
try {
|
|
187
|
+
if (fs_1.default.existsSync(identityMdPath)) {
|
|
188
|
+
const identityInstructions = fs_1.default.readFileSync(identityMdPath, 'utf8');
|
|
189
|
+
basePrompt += `\n\n--- CORE IDENTITY & PERSONA ---\n${identityInstructions}`;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
console.error('Failed to read IDENTITY.md:', error);
|
|
194
|
+
}
|
|
195
|
+
// Read user.md for custom instructions
|
|
196
|
+
try {
|
|
197
|
+
if (fs_1.default.existsSync(userMdPath)) {
|
|
198
|
+
const customInstructions = fs_1.default.readFileSync(userMdPath, 'utf8');
|
|
199
|
+
basePrompt += `\n\n--- CUSTOM USER INSTRUCTIONS ---\n${customInstructions}`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
console.error('Failed to read user.md:', error);
|
|
204
|
+
}
|
|
175
205
|
}
|
|
176
206
|
// Read policy.yaml for NLP security constraints
|
|
177
207
|
try {
|
|
@@ -226,7 +256,7 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
226
256
|
exports.logger.addEntry({ role, content: input }, sessionId);
|
|
227
257
|
const history = exports.logger.getHistory(sessionId);
|
|
228
258
|
// Format messages for OpenAI
|
|
229
|
-
|
|
259
|
+
let messages = [
|
|
230
260
|
{ role: 'system', content: getSystemPrompt() },
|
|
231
261
|
...history
|
|
232
262
|
.filter(m => !(m.role === 'tool' && !m.tool_call_id))
|
|
@@ -244,6 +274,10 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
244
274
|
return msg;
|
|
245
275
|
})
|
|
246
276
|
];
|
|
277
|
+
// Remove orphaned tool responses (truncated by the 40-message limit) at the start of the history window
|
|
278
|
+
while (messages.length > 1 && messages[1].role === 'tool') {
|
|
279
|
+
messages.splice(1, 1);
|
|
280
|
+
}
|
|
247
281
|
try {
|
|
248
282
|
const lowerInput = input.toLowerCase();
|
|
249
283
|
const hasWeb3Keyword = /swap|transfer|price|token|crypto|bridge|wallet|balance|portfolio|buy|sell|send|receive|address|market|limit|mint|nft/i.test(lowerInput);
|
|
@@ -252,7 +286,7 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
252
286
|
if ((0, skillManager_1.isSkillActive)('web3')) {
|
|
253
287
|
tools.push(getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition, bridgeToken_1.bridgeTokenToolDefinition, mintNft_1.mintNftToolDefinition, customTx_1.customTxToolDefinition, checkSecurity_1.checkSecurityToolDefinition, marketAnalysis_1.marketAnalysisToolDefinition, createMarketWatchAgent_1.createMarketWatchAgentToolDefinition, checkPortfolio_1.checkPortfolioToolDefinition, checkAddress_1.checkAddressToolDefinition, getMyAddress_1.getMyAddressToolDefinition, manageCustomTokens_1.manageCustomTokensDefinition, revokeApprovals_1.revokeApprovalToolDefinition, defiLending_1.aaveSupplyToolDefinition, yieldVault_1.vaultDepositToolDefinition, provideLiquidity_1.provideLiquidityToolDefinition, getTxHistory_1.getTxHistoryToolDefinition, createLimitOrder_1.createLimitOrderToolDefinition);
|
|
254
288
|
}
|
|
255
|
-
const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, generateExcel_1.generateExcelToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, editFile_1.editLocalFileToolDefinition, gitManager_1.gitManagerToolDefinition, xManager_1.xManagerToolDefinition, notionWorkspace_1.notionWorkspaceToolDefinition, audioTranscribe_1.audioTranscribeToolDefinition, summarizeText_1.summarizeTextToolDefinition];
|
|
289
|
+
const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateIdentity_1.updateIdentityToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, generateExcel_1.generateExcelToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, editFile_1.editLocalFileToolDefinition, gitManager_1.gitManagerToolDefinition, xManager_1.xManagerToolDefinition, notionWorkspace_1.notionWorkspaceToolDefinition, audioTranscribe_1.audioTranscribeToolDefinition, summarizeText_1.summarizeTextToolDefinition, scheduleTask_1.scheduleTaskDefinition, cancelTask_1.cancelTaskDefinition];
|
|
256
290
|
const GOOGLE_TOOLS = [googleWorkspace_1.readGmailInboxToolDefinition, googleWorkspace_1.listCalendarEventsToolDefinition, googleWorkspace_1.appendRowToSheetsToolDefinition, googleWorkspace_1.readGoogleDocsToolDefinition, googleWorkspace_1.readGoogleFormResponsesToolDefinition];
|
|
257
291
|
let activeTools = [];
|
|
258
292
|
if (hasGoogleKeyword && !hasWeb3Keyword) {
|
|
@@ -315,6 +349,16 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
315
349
|
}, sessionId);
|
|
316
350
|
continue;
|
|
317
351
|
}
|
|
352
|
+
if (!(0, skillManager_1.isSkillActive)(toolName)) {
|
|
353
|
+
console.warn(picocolors_1.default.red(`[Security] Blocked illegal execution of disabled skill: ${toolName}`));
|
|
354
|
+
result = `[System Error] Access denied: Skill '${toolName}' is currently disabled by the user.`;
|
|
355
|
+
exports.logger.addEntry({
|
|
356
|
+
role: "tool",
|
|
357
|
+
tool_call_id: toolCall.id,
|
|
358
|
+
content: result
|
|
359
|
+
}, sessionId);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
318
362
|
try {
|
|
319
363
|
switch (toolName) {
|
|
320
364
|
case 'get_balance': {
|
|
@@ -402,6 +446,10 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
402
446
|
result = (0, updateProfile_1.updateProfile)(args.content, args.mode);
|
|
403
447
|
break;
|
|
404
448
|
}
|
|
449
|
+
case 'update_identity': {
|
|
450
|
+
result = (0, updateIdentity_1.updateIdentity)(args.content, args.mode);
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
405
453
|
case 'update_security_policy': {
|
|
406
454
|
result = await (0, updateSecurityPolicy_1.updateSecurityPolicy)(args.policy, args.action || 'add');
|
|
407
455
|
break;
|
|
@@ -458,6 +506,14 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
458
506
|
result = await (0, searchWeb_1.searchWeb)(args.query, args.depth);
|
|
459
507
|
break;
|
|
460
508
|
}
|
|
509
|
+
case 'schedule_task': {
|
|
510
|
+
result = await (0, scheduleTask_1.executeScheduleTask)(args);
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
case 'cancel_task': {
|
|
514
|
+
result = await (0, cancelTask_1.executeCancelTask)(args);
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
461
517
|
case 'read_gmail_inbox': {
|
|
462
518
|
result = await (0, googleWorkspace_1.readGmailInbox)(args.maxResults);
|
|
463
519
|
break;
|
|
@@ -541,11 +597,28 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
541
597
|
tracker_1.Tracker.addTokens(secondResponse.usage.total_tokens, config.llm.provider);
|
|
542
598
|
}
|
|
543
599
|
tracker_1.Tracker.addEvent('llm.final_response', { provider: config.llm.provider });
|
|
544
|
-
|
|
600
|
+
let finalContent = secondResponse.choices[0].message.content || "";
|
|
601
|
+
// Clean up orphaned <think> blocks that forgot to output </think>
|
|
602
|
+
finalContent = finalContent.replace(/<thought>[\s\S]*?<\/thought>\n?/gi, '');
|
|
603
|
+
finalContent = finalContent.replace(/<think>[\s\S]*?<\/think>\n?/gi, '');
|
|
604
|
+
if (finalContent.includes('<think>')) {
|
|
605
|
+
finalContent = finalContent.replace(/<think>[\s\S]*?\n\n/i, '');
|
|
606
|
+
finalContent = finalContent.replace(/<think>[\s\S]*$/i, '');
|
|
607
|
+
}
|
|
608
|
+
finalContent = finalContent.trim();
|
|
545
609
|
exports.logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
|
|
546
610
|
return finalContent;
|
|
547
611
|
}
|
|
548
|
-
|
|
612
|
+
let finalContent = responseMessage.content || "No response generated.";
|
|
613
|
+
// Clean up orphaned <think> blocks that forgot to output </think>
|
|
614
|
+
finalContent = finalContent.replace(/<thought>[\s\S]*?<\/thought>\n?/gi, '');
|
|
615
|
+
finalContent = finalContent.replace(/<think>[\s\S]*?<\/think>\n?/gi, '');
|
|
616
|
+
if (finalContent.includes('<think>')) {
|
|
617
|
+
finalContent = finalContent.replace(/<think>[\s\S]*?\n\n/i, '');
|
|
618
|
+
finalContent = finalContent.replace(/<think>[\s\S]*$/i, '');
|
|
619
|
+
}
|
|
620
|
+
finalContent = finalContent.trim();
|
|
621
|
+
return finalContent;
|
|
549
622
|
}
|
|
550
623
|
catch (error) {
|
|
551
624
|
console.error("LLM Error:", error);
|
|
@@ -6,13 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.txManager = void 0;
|
|
7
7
|
const crypto_1 = __importDefault(require("crypto"));
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const
|
|
9
|
+
const paths_1 = require("../config/paths");
|
|
10
10
|
class TransactionManager {
|
|
11
11
|
transactions = new Map();
|
|
12
12
|
withdrawals = new Map();
|
|
13
13
|
dbPath;
|
|
14
14
|
constructor() {
|
|
15
|
-
this.dbPath =
|
|
15
|
+
this.dbPath = (0, paths_1.getPath)('.nyxora_withdrawals.json');
|
|
16
16
|
this.loadWithdrawals();
|
|
17
17
|
}
|
|
18
18
|
loadWithdrawals() {
|
|
@@ -0,0 +1,71 @@
|
|
|
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.updateIdentityToolDefinition = void 0;
|
|
7
|
+
exports.updateIdentity = updateIdentity;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const paths_1 = require("../config/paths");
|
|
10
|
+
const parser_1 = require("../config/parser");
|
|
11
|
+
function updateIdentity(content, mode, agentName) {
|
|
12
|
+
try {
|
|
13
|
+
const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
|
|
14
|
+
if (mode === 'replace') {
|
|
15
|
+
fs_1.default.writeFileSync(identityMdPath, content, 'utf8');
|
|
16
|
+
let msg = "Identity replaced successfully. New IDENTITY.md has been saved.";
|
|
17
|
+
if (agentName) {
|
|
18
|
+
const config = (0, parser_1.loadConfig)();
|
|
19
|
+
config.agent.name = agentName;
|
|
20
|
+
(0, parser_1.saveConfig)(config);
|
|
21
|
+
msg += ` Agent Name updated to '${agentName}' in config.`;
|
|
22
|
+
}
|
|
23
|
+
return msg;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
let existingContent = "";
|
|
27
|
+
if (fs_1.default.existsSync(identityMdPath)) {
|
|
28
|
+
existingContent = fs_1.default.readFileSync(identityMdPath, 'utf8');
|
|
29
|
+
}
|
|
30
|
+
const newContent = existingContent + "\n" + content;
|
|
31
|
+
fs_1.default.writeFileSync(identityMdPath, newContent, 'utf8');
|
|
32
|
+
let msg = "Identity appended successfully. New instructions added to IDENTITY.md.";
|
|
33
|
+
if (agentName) {
|
|
34
|
+
const config = (0, parser_1.loadConfig)();
|
|
35
|
+
config.agent.name = agentName;
|
|
36
|
+
(0, parser_1.saveConfig)(config);
|
|
37
|
+
msg += ` Agent Name updated to '${agentName}' in config.`;
|
|
38
|
+
}
|
|
39
|
+
return msg;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return `Failed to update identity: ${error.message}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.updateIdentityToolDefinition = {
|
|
47
|
+
type: "function",
|
|
48
|
+
function: {
|
|
49
|
+
name: "update_identity",
|
|
50
|
+
description: "Updates or rewrites the IDENTITY.md file. Use this when the user sets or changes your AI name, persona, or core character instructions.",
|
|
51
|
+
parameters: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
content: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "The content to write or append to IDENTITY.md",
|
|
57
|
+
},
|
|
58
|
+
mode: {
|
|
59
|
+
type: "string",
|
|
60
|
+
enum: ["append", "replace"],
|
|
61
|
+
description: "Whether to append the content to the existing file or replace the entire file.",
|
|
62
|
+
},
|
|
63
|
+
agentName: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description: "The short display name of the AI agent (e.g. Hinata, Nyxora). MUST be provided if the user assigns you a new name.",
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
required: ["content", "mode"],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|