nyxora 1.7.3 → 26.6.5

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 CHANGED
@@ -2,10 +2,34 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ The format is based on [Keep a Changelog](https://keepashangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [1.7.3] - Unreleased
8
+ ## [26.6.5] - 2026-06-04 (Hotfix Patch)
9
+ ### Fixed
10
+ - **NPM Monorepo Resolution:** Synced `@inquirer/search` and `duck-duck-scrape` to root `package.json` to prevent `MODULE_NOT_FOUND` and `ERR_CONNECTION_REFUSED` on global installations.
11
+
12
+ ## [26.6.4] - 2026-06-04
13
+
14
+ ### AI Engine Optimizations
15
+ - **Semantic Keyword Router (Zero-Latency)**: Restructured the `reasoning.ts` pipeline to dynamically group tools into specific clusters (`WEB3`, `SYSTEM`, `GOOGLE`). The engine now intercepts the user's prompt using highly optimized Regex keyword-matching. This eliminates "Context Bloat" by only injecting relevant tools into the LLM payload, dramatically increasing LLM responsiveness and minimizing API token consumption.
16
+ - **Zero-LLM Fast Return Expansion**: Expanded the V2 `Fast Return` optimization (which skips the redundant secondary LLM summarization step) to include 7 additional data-heavy read-only tools: `get_price`, `get_my_address`, `analyze_market`, `check_token_security`, `search_web`, `read_gmail_inbox`, and `list_calendar_events`. For these queries, the agent now returns the raw markdown payload instantaneously, cutting response latency by 50-80%.
17
+
18
+ ### Universal LLM Expansion
19
+ - **Dictionary Mapping Refactor**: Completely flattened the massive `if-else` blocks in `reasoning.ts` into a highly dynamic 15-line dictionary map. Adding new LLM providers in the future now only takes a single line of code.
20
+ - **Expanded Provider Ecosystem**: Added native support for **Groq, Mistral AI, xAI (Grok), dan DeepSeek**, seamlessly integrated into the React Dashboard UI's dropdown.
21
+
22
+ ### CLI Enhancements
23
+ - **Searchable Model Prompt**: Replaced the static `@clack/prompts` list with `@inquirer/search` inside `nyxora setup`. Users can now instantly fuzzy-search their desired AI model out of dozens of variants using their keyboard.
24
+ - **2026 Model Roster**: Injected an exhaustive list of the latest frontier models into the CLI (including `gpt-5.5`, `o3-mini`, `gemini-3.1-pro`, `deepseek-reasoner`). A fail-safe `[Tulis Manual / Custom Model]` option is also hardcoded at the bottom of every list.
25
+
26
+ ### Backend Stability (Core Engine)
27
+ - **Zero-Crash SQLite (WAL Mode)**: Enabled `PRAGMA journal_mode = WAL` and `busy_timeout = 5000` on the `node:sqlite` database engine (`logger.ts`). This allows parallel reads and writes without throwing fatal `SQLITE_BUSY` (database locked) errors during high-concurrency operations.
28
+ - **Anti-Zombie LLM Timeout**: Hardcoded a `timeout: 120000` (120 seconds) limit on the core OpenAI SDK instantiation (`reasoning.ts`). If an external AI provider (e.g., local Ollama or OpenRouter) hangs, the system will now correctly severe the connection and trigger Exponential Backoff rather than freezing indefinitely. Disabled internal SDK retries (`maxRetries: 0`) to prevent retry collisions with Nyxora's native retry wrapper.
29
+
30
+ ### UI & Developer Experience
31
+ - **CLI Memory Purge**: Introduced a new developer utility command: `nyxora clear`. It instantly and atomically resets the AI's short-term/long-term memory SQLite database. Includes a mandatory `--force` flag safeguard to prevent accidental data destruction.
32
+ ## [1.7.3] - 2026-06-04
9
33
 
10
34
  ### Web3 Routing & Integrations
11
35
  - **Multi-Router Selection (DeFi)**: Added a dynamic Router dropdown to the Dashboard UI, allowing users to forcefully route transactions through specific protocols natively. Supported routers include `1inch`, `CowSwap (MEV-Protected)`, `Li.Fi`, `Relay`, `Uniswap V2`, `Uniswap V3`, and `PancakeSwap`. This integration heavily relies on deep aggregator proxying (bypassing the need for complex V2/V3 ABI calldata overhead) to ensure 100% smooth, anti-fail execution without requiring additional API keys.
package/README.md CHANGED
@@ -23,6 +23,7 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
23
23
  * **Cryptographically Bound Approval**: Policy changes and transactions requested by the AI are drafted as hashes (`sha256`). Approval via the UI requires a challenge nonce, preventing Man-in-the-Middle (MITM) attacks.
24
24
  * **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.
25
25
  * **Plugin Sandbox VM**: Execute community-built external skills securely inside an airtight Node.js `vm` chamber with zero access to your file system or terminal processes.
26
+ * **Enterprise-Grade Stability**: Runs on a WAL-enabled SQLite backend with resilient anti-zombie connection timeouts to ensure maximum concurrency without database locks.
26
27
 
27
28
  ### 🌐 Web3 Skills (On-Chain)
28
29
  * **Security Scanner**: Nyxora can scan smart contracts via GoPlus Labs to detect Honeypots, Hidden Taxes, and malicious proxy upgrades before you buy.
@@ -40,6 +41,7 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
40
41
  * **Zero-Click Multi-Session**: Instantly create isolated chat sessions with smart auto-naming triggered by your first prompt, exactly like ChatGPT.
41
42
  * **Dynamic Trending Tokens**: Live top 5 crypto assets feed directly injected into the dashboard, completely clickable for instant AI market analysis.
42
43
  * **Premium Utility-Centric UI**: A sleek, dark-themed dashboard built for high readability and professional Web3 execution, featuring Pseudo-Generative UI widgets (`<BalanceWidget>`, `<MarketWidget>`, `<SwapWidget>`).
44
+ * **Massive 2026 Model Roster**: Out-of-the-box support for cutting-edge models via Google Gemini, OpenAI, Groq, Mistral, xAI, DeepSeek, OpenRouter, and local Ollama, equipped with a searchable CLI prompt to instantly find your favorite model.
43
45
  * **Context Overrides Defaults (NLP Intelligence)**: The Dashboard configuration (default chain & router) acts only as a safety net. If you issue an explicit command via Telegram (e.g., *"Swap 10 USDC to USDT on Arbitrum using Li.Fi"*), the NLP engine dynamically bypasses the default settings and executes exactly what you asked for, ensuring maximum flexibility.
44
46
  * **Deep Personalization**: Feed the agent custom rules via `user.md` and define its core persona via `IDENTITY.md`.
45
47
 
@@ -83,6 +85,9 @@ nyxora start
83
85
 
84
86
  # 4. Open the Web Dashboard
85
87
  nyxora dashboard
88
+
89
+ # Utility: Atomically clear the AI's short-term and long-term memory
90
+ nyxora clear --force
86
91
  ```
87
92
  > **⚠️ 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.
88
93
 
package/bin/nyxora.mjs CHANGED
@@ -208,9 +208,20 @@ async function setup() {
208
208
  await new Promise(resolve => child.on('close', resolve));
209
209
  }
210
210
 
211
+ async function clearMemory(args) {
212
+ const child = spawn('npx', ['ts-node', '-T', 'packages/core/src/gateway/cli.ts', 'clear', ...args], {
213
+ cwd: projectRoot,
214
+ stdio: 'inherit',
215
+ env: { ...process.env, TS_NODE_CACHE: 'false' }
216
+ });
217
+
218
+ await new Promise(resolve => child.on('close', resolve));
219
+ }
220
+
211
221
  async function main() {
212
222
  switch (command) {
213
223
  case 'setup': await setup(); break;
224
+ case 'clear': await clearMemory(process.argv.slice(3)); break;
214
225
  case 'start': await start(); break;
215
226
  case 'stop': await stop(); break;
216
227
  case 'restart': await restart(); break;
@@ -234,9 +245,14 @@ Commands:
234
245
  start Start the Nyxora background daemon
235
246
  stop Stop the running daemon
236
247
  restart Restart the daemon
248
+ setup Run the interactive Setup Wizard
237
249
  dashboard Open the dashboard in your browser
250
+ clear Atomically clear the AI's short/long-term memory SQLite database
238
251
  clean-logs Clear the daemon logs
239
252
  autostart Enable/disable autostart on boot (usage: nyxora autostart enable)
253
+
254
+ Options:
255
+ -v, --version Show current version
240
256
  `);
241
257
  }
242
258
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nyxora",
3
- "version": "1.7.3",
3
+ "version": "26.6.5",
4
4
  "license": "MIT",
5
5
  "workspaces": [
6
6
  "packages/*"
@@ -9,7 +9,7 @@
9
9
  "nyxora": "bin/nyxora.mjs"
10
10
  },
11
11
  "scripts": {
12
- "postinstall": "npm run build --workspace=dashboard || echo '⚠️ Build failed – dashboard UI may be missing'",
12
+ "prepare": "npm run build --workspace=dashboard || echo '⚠️ Build failed – dashboard UI may be missing'",
13
13
  "dev": "concurrently -n \"BACKEND,FRONTEND\" -c \"blue,green\" \"npx ts-node -T launcher.ts\" \"npm run dev --workspace=dashboard\"",
14
14
  "start": "node ./bin/nyxora.mjs start",
15
15
  "stop": "node ./bin/nyxora.mjs stop",
@@ -21,11 +21,13 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@clack/prompts": "^1.4.0",
24
+ "@inquirer/search": "^4.2.1",
24
25
  "@modelcontextprotocol/sdk": "^1.5.0",
25
26
  "@napi-rs/keyring": "^1.3.0",
26
27
  "concurrently": "^9.2.1",
27
28
  "cors": "^2.8.6",
28
29
  "dotenv": "^17.4.2",
30
+ "duck-duck-scrape": "^2.2.7",
29
31
  "express": "^5.2.1",
30
32
  "express-rate-limit": "^7.5.0",
31
33
  "helmet": "^8.0.0",
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "nyxora-agent-core",
3
- "version": "1.7.3",
3
+ "version": "26.6.5",
4
4
  "private": true,
5
5
  "main": "src/gateway/server.ts",
6
6
  "dependencies": {
7
7
  "@clack/prompts": "^1.4.0",
8
+ "@inquirer/search": "^4.2.1",
8
9
  "cors": "^2.8.6",
9
10
  "duck-duck-scrape": "^2.2.7",
10
11
  "express": "^5.2.1",
@@ -48,66 +48,58 @@ export const logger = new Logger();
48
48
 
49
49
  let currentKeyIndex = 0;
50
50
 
51
+ const PROVIDER_CONFIGS: Record<string, { baseURL?: string; requiresApiKey: boolean }> = {
52
+ ollama: { baseURL: process.env.OLLAMA_BASE_URL ? `${process.env.OLLAMA_BASE_URL}/v1` : 'http://localhost:11434/v1', requiresApiKey: false },
53
+ gemini: { baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/', requiresApiKey: true },
54
+ openrouter: { baseURL: 'https://openrouter.ai/api/v1', requiresApiKey: true },
55
+ groq: { baseURL: 'https://api.groq.com/openai/v1', requiresApiKey: true },
56
+ mistral: { baseURL: 'https://api.mistral.ai/v1', requiresApiKey: true },
57
+ xai: { baseURL: 'https://api.x.ai/v1', requiresApiKey: true },
58
+ deepseek: { baseURL: 'https://api.deepseek.com', requiresApiKey: true },
59
+ openai: { requiresApiKey: true }
60
+ };
61
+
51
62
  async function getOpenAI(): Promise<OpenAI> {
52
63
  const config = loadConfig();
53
64
  const vaultKeys = await loadApiKeys();
54
-
55
- if (config.llm.provider === 'ollama') {
56
- return new OpenAI({
57
- baseURL: process.env.OLLAMA_BASE_URL ? `${process.env.OLLAMA_BASE_URL}/v1` : 'http://localhost:11434/v1',
58
- apiKey: 'ollama', // API key is not required for local Ollama
59
- });
60
- }
65
+ const providerName = config.llm.provider || 'openai';
66
+ const providerConf = PROVIDER_CONFIGS[providerName] || PROVIDER_CONFIGS['openai'];
61
67
 
62
- // Get API key from config (UI) or fallback to .env
63
- let apiKey = '';
64
-
65
- let configuredKeys = config.llm.api_keys;
66
- if (typeof configuredKeys === 'string') {
67
- configuredKeys = [configuredKeys];
68
- }
69
-
70
- if (Array.isArray(configuredKeys) && configuredKeys.length > 0) {
71
- // Filter out empty keys
72
- const keys = configuredKeys.filter(k => typeof k === 'string' && k.trim() !== '');
73
- if (keys.length > 0) {
74
- currentKeyIndex = currentKeyIndex % keys.length;
75
- apiKey = keys[currentKeyIndex];
76
- console.log(`[LLM] Using rotated API Key (${currentKeyIndex + 1}/${keys.length}): ${apiKey.substring(0, 4)}...`);
77
- currentKeyIndex++; // Increment for next request
68
+ let apiKey = 'local';
69
+ if (providerConf.requiresApiKey) {
70
+ apiKey = '';
71
+ let configuredKeys = config.llm.api_keys;
72
+ if (typeof configuredKeys === 'string') {
73
+ configuredKeys = [configuredKeys];
78
74
  }
79
- }
80
-
81
- // Fallbacks if no valid keys found in config.llm.api_keys
82
- if (!apiKey) {
83
- if (config.llm.provider === 'gemini') {
84
- apiKey = vaultKeys.gemini_key || config.credentials?.gemini_key || '';
85
- } else if (config.llm.provider === 'openrouter') {
86
- apiKey = vaultKeys.openrouter_key || config.credentials?.openrouter_key || '';
87
- } else {
88
- apiKey = vaultKeys.openai_key || config.credentials?.openai_key || '';
75
+
76
+ if (Array.isArray(configuredKeys) && configuredKeys.length > 0) {
77
+ const keys = configuredKeys.filter(k => typeof k === 'string' && k.trim() !== '');
78
+ if (keys.length > 0) {
79
+ currentKeyIndex = currentKeyIndex % keys.length;
80
+ apiKey = keys[currentKeyIndex];
81
+ console.log(`[LLM] Using rotated API Key (${currentKeyIndex + 1}/${keys.length}): ${apiKey.substring(0, 4)}...`);
82
+ currentKeyIndex++;
83
+ }
89
84
  }
85
+
90
86
  if (!apiKey) {
91
- throw new Error(`No API Key found for ${config.llm.provider}. Please run 'nyxora setup' to configure it.`);
87
+ const fallbackKeyName = `${providerName}_key`;
88
+ apiKey = vaultKeys[fallbackKeyName] || config.credentials?.[fallbackKeyName] || '';
89
+
90
+ if (!apiKey) {
91
+ throw new Error(`No API Key found for ${providerName}. Please run 'nyxora setup' to configure it.`);
92
+ }
93
+ console.log(`[LLM] Using API Key from secure vault`);
92
94
  }
93
- console.log(`[LLM] Using API Key from secure vault`);
94
95
  }
95
96
 
96
- if (config.llm.provider === 'gemini') {
97
- return new OpenAI({
98
- baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
99
- apiKey: apiKey,
100
- });
101
- } else if (config.llm.provider === 'openrouter') {
102
- return new OpenAI({
103
- baseURL: 'https://openrouter.ai/api/v1',
104
- apiKey: apiKey,
105
- });
106
- } else {
107
- return new OpenAI({
108
- apiKey: apiKey,
109
- });
110
- }
97
+ return new OpenAI({
98
+ baseURL: providerConf.baseURL,
99
+ apiKey: apiKey,
100
+ timeout: 120 * 1000,
101
+ maxRetries: 0
102
+ });
111
103
  }
112
104
 
113
105
  async function executeWithRetry(
@@ -234,45 +226,33 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
234
226
  return `Provider ${config.llm.provider} is configured, but currently only OpenAI, OpenRouter, Ollama, and Gemini adapters are implemented.`;
235
227
  }
236
228
 
229
+ // --- v1.7.4 Semantic Keyword Router ---
230
+ const lowerInput = input.toLowerCase();
231
+ const hasWeb3Keyword = /swap|transfer|price|token|crypto|bridge|wallet|balance|portfolio|buy|sell|send|receive|address|market|limit|mint|nft/i.test(lowerInput);
232
+ const hasGoogleKeyword = /email|gmail|calendar|sheet|doc|form|event/i.test(lowerInput);
233
+
234
+ const WEB3_TOOLS = [getBalanceToolDefinition, transferToolDefinition, getPriceToolDefinition, swapTokenToolDefinition, bridgeTokenToolDefinition, mintNftToolDefinition, customTxToolDefinition, createWalletToolDefinition, checkSecurityToolDefinition, marketAnalysisToolDefinition, checkPortfolioToolDefinition, checkAddressToolDefinition, getMyAddressToolDefinition, createLimitOrderToolDefinition, listLimitOrdersToolDefinition, cancelLimitOrderToolDefinition];
235
+ const SYSTEM_TOOLS = [updateProfileToolDefinition, updateSecurityPolicyToolDefinition, analyzeDocumentToolDefinition, readLocalFileToolDefinition, writeLocalFileToolDefinition, runTerminalCommandToolDefinition, browseWebsiteToolDefinition, searchWebToolDefinition, installExternalSkillToolDefinition];
236
+ const GOOGLE_TOOLS = [readGmailInboxToolDefinition, listCalendarEventsToolDefinition, appendRowToSheetsToolDefinition, readGoogleDocsToolDefinition, readGoogleFormResponsesToolDefinition];
237
+
238
+ let activeTools: any[] = [];
239
+ if (hasGoogleKeyword && !hasWeb3Keyword) {
240
+ activeTools = [...GOOGLE_TOOLS, ...SYSTEM_TOOLS, ...pluginManager.getToolDefinitions()];
241
+ } else if (hasWeb3Keyword && !hasGoogleKeyword) {
242
+ activeTools = [...WEB3_TOOLS, ...SYSTEM_TOOLS, ...pluginManager.getToolDefinitions()];
243
+ } else {
244
+ activeTools = [...WEB3_TOOLS, ...SYSTEM_TOOLS, ...GOOGLE_TOOLS, ...pluginManager.getToolDefinitions()];
245
+ }
246
+ activeTools = activeTools.filter(t => isSkillActive(t.function.name));
247
+ // ----------------------------------------
248
+
237
249
  const response = await executeWithRetry(async (client) => {
238
250
  return await client.chat.completions.create({
239
- model: config.llm.model,
240
- temperature: config.llm.temperature,
241
- messages: messages,
242
- tools: [
243
- getBalanceToolDefinition as any,
244
- transferToolDefinition as any,
245
- getPriceToolDefinition as any,
246
- swapTokenToolDefinition as any,
247
- bridgeTokenToolDefinition as any,
248
- mintNftToolDefinition as any,
249
- customTxToolDefinition as any,
250
- createWalletToolDefinition as any,
251
- checkSecurityToolDefinition as any,
252
- marketAnalysisToolDefinition as any,
253
- checkPortfolioToolDefinition as any,
254
- checkAddressToolDefinition as any,
255
- getMyAddressToolDefinition as any,
256
- createLimitOrderToolDefinition as any,
257
- listLimitOrdersToolDefinition as any,
258
- cancelLimitOrderToolDefinition as any,
259
- updateProfileToolDefinition as any,
260
- updateSecurityPolicyToolDefinition as any,
261
- analyzeDocumentToolDefinition as any,
262
- readLocalFileToolDefinition as any,
263
- writeLocalFileToolDefinition as any,
264
- runTerminalCommandToolDefinition as any,
265
- browseWebsiteToolDefinition as any,
266
- searchWebToolDefinition as any,
267
- installExternalSkillToolDefinition as any,
268
- readGmailInboxToolDefinition as any,
269
- listCalendarEventsToolDefinition as any,
270
- appendRowToSheetsToolDefinition as any,
271
- readGoogleDocsToolDefinition as any,
272
- readGoogleFormResponsesToolDefinition as any,
273
- ...pluginManager.getToolDefinitions()
274
- ].filter(t => isSkillActive(t.function.name)),
275
- tool_choice: "auto",
251
+ model: config.llm.model,
252
+ temperature: config.llm.temperature,
253
+ messages: messages,
254
+ tools: activeTools,
255
+ tool_choice: "auto",
276
256
  });
277
257
  });
278
258
 
@@ -484,9 +464,13 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
484
464
  content: result,
485
465
  }, sessionId);
486
466
 
487
- // V2 Optimization: Zero-LLM Fast Return for data-heavy tools
467
+ // V2 Optimization (Expanded in v1.7.4): Zero-LLM Fast Return for data-heavy and read-only tools
488
468
  // If the tool already returns perfectly formatted markdown, skip the second LLM call to save 5-10s latency and tokens!
489
- if (toolName === 'check_portfolio' || toolName === 'check_address') {
469
+ const fastReturnTools = [
470
+ 'check_portfolio', 'check_address', 'get_price', 'get_my_address',
471
+ 'analyze_market', 'check_token_security', 'search_web', 'read_gmail_inbox', 'list_calendar_events'
472
+ ];
473
+ if (fastReturnTools.includes(toolName)) {
490
474
  logger.addEntry({ role: 'assistant', content: result }, sessionId);
491
475
  return result;
492
476
  }
@@ -44,7 +44,7 @@ export interface NyxoraConfig {
44
44
  default_router?: string;
45
45
  };
46
46
  llm: {
47
- provider: 'openai' | 'anthropic' | 'ollama' | 'gemini' | 'openrouter';
47
+ provider: string;
48
48
  model: string;
49
49
  temperature: number;
50
50
  api_keys?: string[];
@@ -31,6 +31,20 @@ console.log(`================================`);
31
31
  process.exit(0);
32
32
  }
33
33
 
34
+ // Check for memory clear command
35
+ if (process.argv.includes('clear')) {
36
+ if (process.argv.includes('--force') || process.argv.includes('-y')) {
37
+ const { Logger } = require('../memory/logger');
38
+ const logger = new Logger();
39
+ logger.clear();
40
+ console.log(pc.green('✅ Memory cleared successfully.'));
41
+ process.exit(0);
42
+ } else {
43
+ console.log(pc.yellow('⚠️ Warning: This will wipe all AI memory. Run "nyxora clear --force" to confirm.'));
44
+ process.exit(1);
45
+ }
46
+ }
47
+
34
48
  // Check for set-key shortcut
35
49
  if (process.argv.includes('set-key')) {
36
50
  const setKeyIndex = process.argv.indexOf('set-key');
@@ -1,4 +1,5 @@
1
1
  import { intro, outro, confirm, select, text, isCancel, cancel, note, password, spinner } from '@clack/prompts';
2
+ import search from '@inquirer/search';
2
3
  import pc from 'picocolors';
3
4
  import fs from 'fs';
4
5
  import path from 'path';
@@ -102,47 +103,98 @@ Provider: ${config.llm.provider}`;
102
103
  { value: 'gemini', label: 'Google Gemini' },
103
104
  { value: 'openrouter', label: 'OpenRouter (Many Models)' },
104
105
  { value: 'ollama', label: 'Ollama (Local)' },
106
+ { value: 'groq', label: 'Groq (Ultra-fast)' },
107
+ { value: 'mistral', label: 'Mistral AI' },
108
+ { value: 'xai', label: 'xAI (Grok)' },
109
+ { value: 'deepseek', label: 'DeepSeek' },
105
110
  ],
106
111
  });
107
112
  if (isCancel(provider)) return process.exit(0);
108
113
 
109
114
  // 2. Model Name
110
- let modelOptions: { value: string, label: string }[] = [];
115
+ let modelOptions: { value: string, name: string }[] = [];
111
116
  if (provider === 'gemini') {
112
117
  modelOptions = [
113
- { value: 'gemini-2.5-flash', label: 'gemini-2.5-flash (Fast & Cheap)' },
114
- { value: 'gemini-2.5-pro', label: 'gemini-2.5-pro (Advanced Reasoning)' },
115
- { value: 'gemini-1.5-pro', label: 'gemini-1.5-pro' },
118
+ { value: 'gemini-3.1-pro', name: 'gemini-3.1-pro' },
119
+ { value: 'gemini-3.1-flash-lite', name: 'gemini-3.1-flash-lite' },
120
+ { value: 'gemini-2.5-flash', name: 'gemini-2.5-flash' },
121
+ { value: 'gemini-2.5-pro', name: 'gemini-2.5-pro' },
122
+ { value: 'gemini-1.5-pro', name: 'gemini-1.5-pro' },
123
+ { value: 'gemini-1.5-flash', name: 'gemini-1.5-flash' },
116
124
  ];
117
125
  } else if (provider === 'openai') {
118
126
  modelOptions = [
119
- { value: 'gpt-4o', label: 'gpt-4o (Powerful)' },
120
- { value: 'gpt-4o-mini', label: 'gpt-4o-mini (Fast)' },
121
- { value: 'o1-preview', label: 'o1-preview (Reasoning)' },
122
- { value: 'o1-mini', label: 'o1-mini' },
127
+ { value: 'gpt-5.5-pro', name: 'gpt-5.5-pro' },
128
+ { value: 'gpt-5.5', name: 'gpt-5.5' },
129
+ { value: 'gpt-5.4-mini', name: 'gpt-5.4-mini' },
130
+ { value: 'gpt-5.4-nano', name: 'gpt-5.4-nano' },
131
+ { value: 'gpt-4o', name: 'gpt-4o' },
132
+ { value: 'o3-mini', name: 'o3-mini' },
123
133
  ];
124
134
  } else if (provider === 'openrouter') {
125
135
  modelOptions = [
126
- { value: 'anthropic/claude-3.5-sonnet', label: 'Claude 3.5 Sonnet' },
127
- { value: 'meta-llama/llama-3.1-70b-instruct', label: 'Llama 3.1 70B' },
128
- { value: 'liquid/lfm-40b', label: 'Liquid LFM 40B' },
129
- { value: 'google/gemini-pro-1.5', label: 'Gemini Pro 1.5' },
136
+ { value: 'anthropic/claude-3.5-sonnet', name: 'Claude 3.5 Sonnet' },
137
+ { value: 'meta-llama/llama-3.3-70b-instruct', name: 'Llama 3.3 70B' },
138
+ { value: 'google/gemini-3.1-pro', name: 'Gemini 3.1 Pro' },
139
+ { value: 'openai/gpt-5.5', name: 'GPT-5.5' },
140
+ { value: 'x-ai/grok-2', name: 'Grok 2' },
141
+ { value: 'mistralai/mistral-large', name: 'Mistral Large' },
142
+ ];
143
+ } else if (provider === 'groq') {
144
+ modelOptions = [
145
+ { value: 'llama-3.3-70b-versatile', name: 'llama-3.3-70b-versatile' },
146
+ { value: 'llama-3.1-8b-instant', name: 'llama-3.1-8b-instant' },
147
+ { value: 'mixtral-8x7b-32768', name: 'mixtral-8x7b-32768' },
148
+ ];
149
+ } else if (provider === 'mistral') {
150
+ modelOptions = [
151
+ { value: 'mistral-large-latest', name: 'mistral-large-latest' },
152
+ { value: 'mistral-small-latest', name: 'mistral-small-latest' },
153
+ { value: 'open-mistral-nemo', name: 'open-mistral-nemo' },
154
+ ];
155
+ } else if (provider === 'xai') {
156
+ modelOptions = [
157
+ { value: 'grok-4.3', name: 'grok-4.3' },
158
+ { value: 'grok-2-latest', name: 'grok-2-latest' },
159
+ { value: 'grok-beta', name: 'grok-beta' },
160
+ ];
161
+ } else if (provider === 'deepseek') {
162
+ modelOptions = [
163
+ { value: 'deepseek-chat', name: 'deepseek-chat (V3)' },
164
+ { value: 'deepseek-reasoner', name: 'deepseek-reasoner (R1)' },
130
165
  ];
131
166
  } else {
132
167
  modelOptions = [
133
- { value: 'llama3', label: 'Llama 3 (8B)' },
134
- { value: 'qwen2', label: 'Qwen 2' },
135
- { value: 'phi3', label: 'Phi-3' },
168
+ { value: 'llama3.2', name: 'llama3.2' },
169
+ { value: 'llama3.1', name: 'llama3.1' },
170
+ { value: 'qwen2.5', name: 'qwen2.5' },
171
+ { value: 'phi4', name: 'phi4' },
172
+ { value: 'mistral', name: 'mistral' },
136
173
  ];
137
174
  }
138
175
 
139
- modelOptions.push({ value: 'custom', label: 'Type manually (Custom Model)' });
140
-
141
- let model = (await select({
142
- message: 'Select AI Model:',
143
- options: modelOptions,
144
- })) as string;
145
- if (isCancel(model)) return process.exit(0);
176
+ modelOptions.push({ value: 'custom', name: '[Tulis Manual / Custom Model]' });
177
+
178
+ let model = '';
179
+ try {
180
+ model = await search({
181
+ message: 'Select AI Model (Type to search):',
182
+ source: async (input: string | undefined) => {
183
+ if (!input) {
184
+ return modelOptions;
185
+ }
186
+ return modelOptions.filter((opt) =>
187
+ opt.name.toLowerCase().includes(input.toLowerCase()) ||
188
+ opt.value.toLowerCase().includes(input.toLowerCase())
189
+ );
190
+ }
191
+ });
192
+ } catch (err: any) {
193
+ if (err.name === 'ExitPromptError') {
194
+ return process.exit(0);
195
+ }
196
+ throw err;
197
+ }
146
198
 
147
199
  if (model === 'custom') {
148
200
  model = (await text({
@@ -282,9 +334,7 @@ Provider: ${config.llm.provider}`;
282
334
 
283
335
  const newApiKeys: Record<string, string> = {};
284
336
  if (apiKey) {
285
- if (provider === 'openai') newApiKeys.openai_key = apiKey;
286
- if (provider === 'gemini') newApiKeys.gemini_key = apiKey;
287
- if (provider === 'openrouter') newApiKeys.openrouter_key = apiKey;
337
+ newApiKeys[`${provider}_key`] = apiKey;
288
338
  }
289
339
 
290
340
  if (!config.web_search) config.web_search = { provider: 'mesh', enabled: true };
@@ -42,6 +42,9 @@ export class Logger {
42
42
  }
43
43
 
44
44
  private initDb() {
45
+ this.db.exec('PRAGMA journal_mode = WAL;');
46
+ this.db.exec('PRAGMA synchronous = NORMAL;');
47
+ this.db.exec('PRAGMA busy_timeout = 5000;');
45
48
  this.db.exec(`
46
49
  CREATE TABLE IF NOT EXISTS sessions (
47
50
  id TEXT PRIMARY KEY,