overmind-mcp 2.8.24 → 2.8.26

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.
@@ -5,15 +5,22 @@
5
5
  Chaque variable suit cette priorite (la premiere gagne):
6
6
 
7
7
  ```
8
- HERMES_HOME/.env (C:\Users\Deamon\AppData\Local\hermes\.env)
9
-
10
- settings_[agent].json → env (apres interpolation $VAR par process.env)
8
+ process.env (env du parent, herite par le spawn)
11
9
 
12
10
  Workflow/.env (C:\Users\Deamon\Desktop\Backup\Serveur MCP\Workflow\.env)
13
11
 
14
- process.env (env du parent, herite par le spawn)
12
+ settings_[agent].json → env (apres interpolation $VAR par process.env)
13
+
14
+ HERMES_HOME/.env (C:\Users\Deamon\AppData\Local\hermes\.env) ← DERNIER MOT
15
15
  ```
16
16
 
17
+ **Detail du code** (`NousHermesRunner.ts` l.268-456):
18
+ 1. `agentCustomEnv = { ...process.env, ...settings.env }` (line 268 puis 384)
19
+ 2. Puis lit `overmindHermesSubPath/.env` (le `.hermes/.env` de l'agent) et fait `agentCustomEnv[key] = value` (line 437-456) — ce qui OVERRIDE tout ce qui precede.
20
+ 3. Finalement ecrit ce `agentCustomEnv` dans le `.env` final de l'agent (line 722-732) avec dedup sur les cles `*api_key` / `*auth_token`.
21
+
22
+ Donc le `.hermes/.env` de l'agent a **toujours le dernier mot**. Si tu mets `MINIMAX_API_KEY=*** dans le .hermes/.env, il ecrase `MINIMAX_API_KEY` du process.env et du settings.
23
+
17
24
  **MAIS** pour `OPENROUTER_API_KEY` uniquement:
18
25
  - Hermes lit d'abord `HERMES_HOME/.env` puis `os.environ`
19
26
  - Si present dans `auth.json` avec status `exhausted` → skip
@@ -108,7 +115,7 @@ Pour qu'un agent utilise le token Y (secondary) au lieu de E (primary), le setti
108
115
  | Provider ID | `minimax-cn` | config.yaml ou settings |
109
116
  | API Key | `MINIMAX_CN_API_KEY` **uniquement** | `HERMES_HOME/.env` → `os.environ` |
110
117
  | Base URL | `https://api.minimaxi.com/anthropic` | hardcoded dans ProviderConfig |
111
- | Model | `MiniMax-M2.7` | settings `env.ANTHROPIC_MODEL` |
118
+ | Model | `MiniMax-M3` | settings `env.ANTHROPIC_MODEL` |
112
119
 
113
120
  ```json
114
121
  // settings_[agent].json — MiniMax correct
@@ -116,18 +123,42 @@ Pour qu'un agent utilise le token Y (secondary) au lieu de E (primary), le setti
116
123
  "env": {
117
124
  "ANTHROPIC_AUTH_TOKEN": "$MINIMAX_CN_API_KEY",
118
125
  "ANTHROPIC_BASE_URL": "https://api.minimaxi.com/anthropic",
119
- "ANTHROPIC_MODEL": "MiniMax-M2.7"
126
+ "ANTHROPIC_MODEL": "MiniMax-M3"
120
127
  }
121
128
  }
122
129
  ```
123
130
 
124
131
  ```bash
125
- # .env minimal pour MiniMax
132
+ # .env minimal pour MiniMax CN
126
133
  MINIMAX_CN_API_KEY=ton_token_minimax_ici
127
134
  ```
128
135
 
129
136
  **auth.json** stocke sous `MINIMAX_CN_API_KEY`.
130
137
 
138
+ #### MiniMax CN par défaut (convention)
139
+
140
+ Le préfixe `sk-cp-` est partagé entre MiniMax GLOBAL (`api.minimax.com`) et
141
+ MiniMax CN (`api.minimaxi.com`). L'URL est le seul signal qui disambiguïse.
142
+ Pour les setups où TOUS les tokens MiniMax sont CN (cas le plus commun),
143
+ le runner a un fallback par défaut : quand un token `sk-cp-*` est détecté
144
+ sans URL explicite, on bascule sur CN.
145
+
146
+ Controlable via env var :
147
+
148
+ ```bash
149
+ # Défaut (recommandé pour les setups CN-only)
150
+ OVERMIND_MINIMAX_DEFAULT=cn # sk-cp-* sans URL → minimax-cn
151
+
152
+ # Pour les setups GLOBAL-only
153
+ OVERMIND_MINIMAX_DEFAULT=global # sk-cp-* sans URL → minimax
154
+
155
+ # Strict mode (jamais d'inference, URL obligatoire)
156
+ OVERMIND_MINIMAX_DEFAULT=auto # sk-cp-* sans URL → minimax (no override)
157
+ ```
158
+
159
+ Si une URL explicite est présente dans `ANTHROPIC_BASE_URL`, elle gagne
160
+ toujours sur `OVERMIND_MINIMAX_DEFAULT`.
161
+
131
162
  ---
132
163
 
133
164
  ### OpenRouter — BANNIR pour LLM
@@ -145,8 +176,63 @@ MINIMAX_CN_API_KEY=ton_token_minimax_ici
145
176
 
146
177
  ---
147
178
 
179
+ ## HERMES_HOME resolution (multi-OS, multi-install)
180
+
181
+ Chaque agent Hermes a son propre HERMES_HOME. Le path est resolu de maniere
182
+ deterministe par `getAgentHermesHome(agentName)` dans `src/lib/config.ts`.
183
+
184
+ Ordre de resolution (la premiere gagne):
185
+
186
+ ```
187
+ OVERMIND_AGENT_HOME (env var, set par l'install ou systemd)
188
+
189
+ <workspace>/.overmind/hermes/agent_<name>/.hermes (legacy, si deja existant)
190
+
191
+ $HOME/.overmind/hermes/agent_<name>/.hermes (Linux/Mac sudo npm -g)
192
+ %LOCALAPPDATA%\overmind\hermes\agent_<name>\.hermes (Windows npm -g)
193
+ %USERPROFILE%\overmind\hermes\agent_<name>\.hermes (Windows fallback)
194
+ ```
195
+
196
+ Pour un agent nomme `sniperbot_analyst`:
197
+ - Dev local (workspace = `C:\Users\Deamon\Desktop\Backup\Serveur MCP\Workflow`):
198
+ `Workflow\.overmind\hermes\agent_sniperbot_analyst\.hermes` (si deja cree)
199
+ - Prod Linux: `~/.overmind/hermes/agent_sniperbot_analyst/.hermes`
200
+ - Prod Windows: `%LOCALAPPDATA%\overmind\hermes\agent_sniperbot_analyst\.hermes`
201
+
202
+ **Pourquoi c'est important**: avant, le runner Overmind calculait HERMES_HOME
203
+ depuis `process.cwd()`. Si le sniper etait lance depuis `C:\Users\Deamon\Desktop\Backup\Serveur MCP\`
204
+ et le runner depuis `Workflow\`, ils lisaient des `.hermes/.env` differents
205
+ et ecrivaient dans des `auth.json` differents. Maintenant, c'est cwd-independent.
206
+
207
+ Pour forcer un chemin explicite (ex: deploy Docker), set:
208
+ ```bash
209
+ export OVERMIND_AGENT_HOME=/var/lib/overmind/hermes
210
+ ```
211
+
212
+
148
213
  ## Comment Hermes decide quel provider utiliser
149
214
 
215
+ Il y a DEUX niveaux de decision:
216
+
217
+ ### Niveau 1: Overmind runner (avant spawn)
218
+
219
+ `NousHermesRunner.writeAuthJson()` (l.782-918) vote entre 3 signaux pour determiner le `effectiveProvider` qui sera seed dans `auth.json` et le `.env` de l'agent:
220
+
221
+ 1. **Token prefix** (le plus fiable) — `detectTokenProvider()` reconnait:
222
+ - `sk-cp-...` ou `sk-mm-...` → `minimax`
223
+ - `32hex.32hex` ou 32-char hex → `zai`
224
+ - `sk-ant-...` → `anthropic`
225
+ - `sk-or-...` → `openrouter` (mais BLOQUE pour LLM par la suite)
226
+ - `sk-...` (autre) → `openai`
227
+ 2. **BASE_URL** (tres fiable, plus specifique que le token pour CN vs GLOBAL) — `api.minimaxi.com` → `minimax-cn`, `api.minimax.com` → `minimax`, `api.z.ai/api/coding/paas/v4` → `zai`, `anthropic.com` → `anthropic`, `openai.com` → `openai`.
228
+ 3. **ANTHROPIC_PROVIDER** hint du settings (le moins fiable).
229
+
230
+ **Cas special CN vs GLOBAL**: `sk-cp-` est ambigu entre `minimax` et `minimax-cn` (meme prefix). Si l'URL dit `minimaxi` (avec le `i`), c'est CN. Si `minimax` (sans le `i`), c'est GLOBAL. **L'URL gagne dans ce cas precis** parce qu'elle est la seule a desambiguïser.
231
+
232
+ ### Niveau 2: Hermes upstream (apres spawn)
233
+
234
+ Hermes relit son `auth.json` + `HERMES_HOME/.env` + `os.environ` avec sa propre logique (dans `hermes_cli/auth.py`):
235
+
150
236
  ```
151
237
  1. Si `provider` explicite dans config.yaml → utilise ce provider
152
238
  2. Sinon si `ANTHROPIC_BASE_URL` contient openrouter → "openrouter"
@@ -156,7 +242,7 @@ MINIMAX_CN_API_KEY=ton_token_minimax_ici
156
242
  4. Sinon fallback: model.default dans config.yaml
157
243
  ```
158
244
 
159
- Le model resolve le provider. Donc `ANTHROPIC_MODEL=glm-5.1` sans provider explicite `zai`. `ANTHROPIC_MODEL=MiniMax-M2.7` `minimax-cn`.
245
+ **Le runner a deja vote au Niveau 1** — donc ce que tu mets dans `auth.json` (via `writeAuthJson()`) determine ce qu'Hermes verra au Niveau 2. Si le runner a seed `minimax-cn` mais que `config.yaml` dit `provider: minimax`, Hermes va probablement se plaindre. **Garde les deux alignes** ou laisse le runner ecrire le `config.yaml` (ce qu'il fait deja a chaque run).
160
246
 
161
247
  ---
162
248
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overmind-mcp",
3
- "version": "2.8.24",
3
+ "version": "2.8.26",
4
4
  "description": "Orchestrateur universel agents IA multi-modeles via MCP. Inclut le protocole 'Custom-Nickname' pour identifier vos agents avec des surnoms originaux (The Chaos Prophet, Shadow Sniper, etc.), l'isolation mémoire (Private Memory Context) et le support pour QwenCli et Nous Hermes. Installation automatique des dépendances Docker (PostgreSQL, pgvector) inclus.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * migrate-hermes-home.js — One-shot migration for HERMES_HOME path change.
4
+ *
5
+ * Background: HERMES_HOME was previously computed from process.cwd() which
6
+ * produced different paths depending on where the runner was launched from.
7
+ * Starting with Overmind 2.8.27, HERMES_HOME is resolved from
8
+ * OVERMIND_AGENT_HOME or $HOME/.overmind/hermes/agent_<name>/.hermes.
9
+ *
10
+ * This script migrates any existing state from the legacy workspace-relative
11
+ * paths (e.g. <workflow>/.overmind/hermes/agent_<name>/) to the new canonical
12
+ * HOME-based path. Safe to run multiple times.
13
+ *
14
+ * Usage: node scripts/migrate-hermes-home.js [--dry-run]
15
+ */
16
+
17
+ import fs from 'fs';
18
+ import path from 'path';
19
+ import os from 'os';
20
+
21
+ const args = process.argv.slice(2);
22
+ const dryRun = args.includes('--dry-run');
23
+
24
+ function log(msg) {
25
+ process.stdout.write(`[migrate-hermes-home] ${msg}\n`);
26
+ }
27
+
28
+ function findLegacyBases(workspaceDir) {
29
+ const legacy = [];
30
+ for (const sub of ['.overmind/hermes', '.overmind/hermes/agent_*']) {
31
+ const full = path.join(workspaceDir, sub);
32
+ if (fs.existsSync(full)) {
33
+ try {
34
+ const entries = fs.readdirSync(full, { withFileTypes: true });
35
+ for (const e of entries) {
36
+ if (e.isDirectory() && e.name.startsWith('agent_')) {
37
+ legacy.push(path.join(full, e.name));
38
+ } else if (e.isDirectory() && e.name === 'central') {
39
+ legacy.push(path.join(full, e.name));
40
+ }
41
+ }
42
+ } catch { /* ignore */ }
43
+ }
44
+ }
45
+ return legacy;
46
+ }
47
+
48
+ function getCanonicalBase(platform) {
49
+ const homeBase = process.env.LOCALAPPDATA
50
+ || process.env.USERPROFILE
51
+ || os.homedir();
52
+ return platform === 'win32'
53
+ ? path.join(homeBase, 'overmind', 'hermes')
54
+ : path.join(homeBase, '.overmind', 'hermes');
55
+ }
56
+
57
+ function migrateOne(src, dst) {
58
+ // src = legacy agent dir (e.g. <workspace>/.overmind/hermes/agent_foo)
59
+ // dst = canonical agent dir (e.g. ~/.overmind/hermes/agent_foo)
60
+ if (fs.existsSync(dst)) {
61
+ log(` SKIP: destination already exists: ${dst}`);
62
+ return false;
63
+ }
64
+ if (dryRun) {
65
+ log(` DRY-RUN: would move ${src} -> ${dst}`);
66
+ return true;
67
+ }
68
+ // Make parent if missing
69
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
70
+ // Use rename for atomicity (works on same-filesystem; cross-fs would need copy+delete)
71
+ try {
72
+ fs.renameSync(src, dst);
73
+ log(` MOVED: ${src} -> ${dst}`);
74
+ return true;
75
+ } catch (e) {
76
+ if (e.code === 'EXDEV') {
77
+ // Cross-device: copy + delete
78
+ log(` CROSS-FS: copying ${src} -> ${dst}`);
79
+ copyDirSync(src, dst);
80
+ fs.rmSync(src, { recursive: true, force: true });
81
+ return true;
82
+ }
83
+ throw e;
84
+ }
85
+ }
86
+
87
+ function copyDirSync(src, dst) {
88
+ fs.mkdirSync(dst, { recursive: true });
89
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
90
+ const s = path.join(src, entry.name);
91
+ const d = path.join(dst, entry.name);
92
+ if (entry.isDirectory()) {
93
+ copyDirSync(s, d);
94
+ } else if (entry.isFile()) {
95
+ fs.copyFileSync(s, d);
96
+ }
97
+ }
98
+ }
99
+
100
+ function main() {
101
+ const workspaceDir = process.env.OVERMIND_WORKSPACE
102
+ || process.cwd();
103
+ const platform = process.platform;
104
+ const canonicalBase = getCanonicalBase(platform);
105
+
106
+ log(`workspace: ${workspaceDir}`);
107
+ log(`platform: ${platform}`);
108
+ log(`canonical: ${canonicalBase}`);
109
+ log(`mode: ${dryRun ? 'DRY-RUN' : 'LIVE'}`);
110
+ log('');
111
+
112
+ const legacy = findLegacyBases(workspaceDir);
113
+ if (legacy.length === 0) {
114
+ log('No legacy .overmind directories found. Nothing to migrate.');
115
+ return;
116
+ }
117
+
118
+ let moved = 0;
119
+ for (const src of legacy) {
120
+ const name = path.basename(src);
121
+ const dst = path.join(canonicalBase, name);
122
+ log(`Migrating ${name}:`);
123
+ if (migrateOne(src, dst)) moved++;
124
+ }
125
+
126
+ log('');
127
+ log(`Done. ${moved}/${legacy.length} agent directories migrated.`);
128
+ if (dryRun) {
129
+ log('Re-run without --dry-run to actually perform the migration.');
130
+ }
131
+ }
132
+
133
+ main();