neoagent 2.1.7 → 2.1.8

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 CHANGED
@@ -10,7 +10,7 @@
10
10
  [![License](https://img.shields.io/badge/License-MIT-a855f7?style=flat-square)](LICENSE)
11
11
 
12
12
  A self-hosted, proactive AI agent with a Flutter client for web and Android.
13
- Connects to OpenAI, xAI, Google, and local Ollama.
13
+ Connects to OpenAI, xAI, Google, MiniMax Code, and local Ollama.
14
14
  Runs tasks on a schedule, controls a browser, manages files, and talks to you over Telegram, Discord, or WhatsApp.
15
15
 
16
16
  ```bash
@@ -25,6 +25,7 @@ At least one API key is required. The active provider and model are configured i
25
25
  | `OPENAI_API_KEY` | GPT-4o / Whisper (OpenAI) |
26
26
  | `XAI_API_KEY` | Grok (xAI) |
27
27
  | `GOOGLE_AI_KEY` | Gemini (Google) |
28
+ | `MINIMAX_API_KEY` | MiniMax Code (Coding Plan / Token Plan for `MiniMax-M2.7`) |
28
29
  | `BRAVE_SEARCH_API_KEY` | Brave Search API for the native `web_search` tool |
29
30
  | `DEEPGRAM_API_KEY` | Recordings transcription with Deepgram Nova-3 multilingual |
30
31
  | `DEEPGRAM_MODEL` | Deepgram speech model override (defaults to `nova-3`) |
package/lib/manager.js CHANGED
@@ -268,6 +268,7 @@ async function cmdSetup() {
268
268
  const openai = await ask('OpenAI API key', current.OPENAI_API_KEY || '');
269
269
  const xai = await ask('xAI API key', current.XAI_API_KEY || '');
270
270
  const google = await ask('Google API key', current.GOOGLE_AI_KEY || '');
271
+ const minimax = await ask('MiniMax Code key', current.MINIMAX_API_KEY || '');
271
272
  const brave = await ask('Brave Search API key', current.BRAVE_SEARCH_API_KEY || '');
272
273
  const ollama = await ask('Ollama URL', current.OLLAMA_URL || 'http://localhost:11434');
273
274
  const origins = await ask('Allowed CORS origins', current.ALLOWED_ORIGINS || '');
@@ -280,6 +281,7 @@ async function cmdSetup() {
280
281
  openai ? `OPENAI_API_KEY=${openai}` : '',
281
282
  xai ? `XAI_API_KEY=${xai}` : '',
282
283
  google ? `GOOGLE_AI_KEY=${google}` : '',
284
+ minimax ? `MINIMAX_API_KEY=${minimax}` : '',
283
285
  brave ? `BRAVE_SEARCH_API_KEY=${brave}` : '',
284
286
  ollama ? `OLLAMA_URL=${ollama}` : '',
285
287
  origins ? `ALLOWED_ORIGINS=${origins}` : ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.1.7",
3
+ "version": "2.1.8",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"052f31d115eceda8cbff1b3481fcde4330c4ae
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "204310170" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "2922699188" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
@@ -45,6 +45,12 @@ const STATIC_MODELS = [
45
45
  provider: 'google',
46
46
  purpose: 'general'
47
47
  },
48
+ {
49
+ id: 'MiniMax-M2.7',
50
+ label: 'MiniMax M2.7 (Coding Plan)',
51
+ provider: 'minimax',
52
+ purpose: 'coding'
53
+ },
48
54
  {
49
55
  id: 'qwen3.5:4b',
50
56
  label: 'Qwen 3.5 4B (Local / Ollama)',
@@ -210,6 +216,8 @@ function createProviderInstance(providerStr, userId = null, configOverrides = {}
210
216
  return new AnthropicProvider({ apiKey: runtime.apiKey, baseUrl: runtime.baseUrl, ...configOverrides });
211
217
  } else if (providerStr === 'google') {
212
218
  return new GoogleProvider({ apiKey: runtime.apiKey, ...configOverrides });
219
+ } else if (providerStr === 'minimax') {
220
+ return new AnthropicProvider({ apiKey: runtime.apiKey, baseUrl: runtime.baseUrl, ...configOverrides });
213
221
  } else if (providerStr === 'ollama') {
214
222
  return new OllamaProvider({ baseUrl: runtime.baseUrl, ...configOverrides });
215
223
  }
@@ -9,13 +9,15 @@ class AnthropicProvider extends BaseProvider {
9
9
  'claude-sonnet-4-20250514',
10
10
  'claude-3-5-sonnet-20241022',
11
11
  'claude-3-5-haiku-20241022',
12
- 'claude-3-opus-20240229'
12
+ 'claude-3-opus-20240229',
13
+ 'MiniMax-M2.7'
13
14
  ];
14
15
  this.contextWindows = {
15
16
  'claude-sonnet-4-20250514': 200000,
16
17
  'claude-3-5-sonnet-20241022': 200000,
17
18
  'claude-3-5-haiku-20241022': 200000,
18
- 'claude-3-opus-20240229': 200000
19
+ 'claude-3-opus-20240229': 200000,
20
+ 'MiniMax-M2.7': 204800
19
21
  };
20
22
  this.client = new Anthropic({
21
23
  apiKey: config.apiKey || process.env.ANTHROPIC_API_KEY,
@@ -41,6 +41,16 @@ const AI_PROVIDER_DEFINITIONS = Object.freeze({
41
41
  defaultEnabled: true,
42
42
  defaultBaseUrl: 'https://api.x.ai/v1'
43
43
  },
44
+ minimax: {
45
+ id: 'minimax',
46
+ label: 'MiniMax Code',
47
+ description: 'MiniMax Coding Plan for MiniMax-M2.7 through the Anthropic-compatible endpoint.',
48
+ envKey: 'MINIMAX_API_KEY',
49
+ supportsApiKey: true,
50
+ supportsBaseUrl: true,
51
+ defaultEnabled: false,
52
+ defaultBaseUrl: 'https://api.minimax.io/anthropic'
53
+ },
44
54
  ollama: {
45
55
  id: 'ollama',
46
56
  label: 'Ollama',
@@ -456,6 +456,20 @@ function updateIniValue(content, key, value) {
456
456
  return `${content.replace(/\s*$/, '')}\n${line}\n`;
457
457
  }
458
458
 
459
+ function readIniValue(content, key) {
460
+ const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
461
+ const match = content.match(new RegExp(`^${escapedKey}=(.*)$`, 'm'));
462
+ return match ? String(match[1] || '').trim() : null;
463
+ }
464
+
465
+ function systemImagePackageToRelativeDir(packageName) {
466
+ const parts = String(packageName || '').split(';').filter(Boolean);
467
+ if (parts.length !== 4 || parts[0] !== 'system-images') {
468
+ return null;
469
+ }
470
+ return `${parts.join('/')}/`;
471
+ }
472
+
459
473
  function sanitizeUiXml(raw) {
460
474
  const text = String(raw || '');
461
475
  const start = text.indexOf('<?xml');
@@ -564,10 +578,10 @@ class AndroidController {
564
578
  const preferredInstalled =
565
579
  chooseConfiguredSystemImage(installedImages) ||
566
580
  chooseLatestSystemImage(installedImages);
567
- if (
568
- preferredInstalled &&
569
- preferredInstalled.packageName !== state.systemImage
570
- ) {
581
+ if (!preferredInstalled) {
582
+ throw new Error(formatSystemImageError(installedImages));
583
+ }
584
+ if (preferredInstalled.packageName !== state.systemImage) {
571
585
  appendState({
572
586
  bootstrapped: true,
573
587
  systemImage: preferredInstalled.packageName,
@@ -655,7 +669,18 @@ class AndroidController {
655
669
  const pkg = state.systemImage;
656
670
  if (!pkg) throw new Error('Android system image not installed');
657
671
  const avdExists = list.includes(`Name: ${this.avdName}`);
658
- const avdNeedsRecreate = avdExists && (!state.avdSystemImage || state.avdSystemImage !== pkg);
672
+ let avdNeedsRecreate = avdExists && (!state.avdSystemImage || state.avdSystemImage !== pkg);
673
+ const configPath = path.join(AVD_HOME, `${this.avdName}.avd`, 'config.ini');
674
+ if (avdExists && fs.existsSync(configPath)) {
675
+ try {
676
+ const config = fs.readFileSync(configPath, 'utf8');
677
+ const currentImageDir = readIniValue(config, 'image.sysdir.1');
678
+ const expectedImageDir = systemImagePackageToRelativeDir(pkg);
679
+ if (expectedImageDir && currentImageDir && currentImageDir !== expectedImageDir) {
680
+ avdNeedsRecreate = true;
681
+ }
682
+ } catch {}
683
+ }
659
684
 
660
685
  if (avdNeedsRecreate) {
661
686
  await this.stopEmulator().catch(() => {});