overmind-mcp 2.4.0 → 2.4.2

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.
Files changed (154) hide show
  1. package/README.md +80 -107
  2. package/assets/overmind_mcp_pro_banner_v3.png +0 -0
  3. package/dist/bin/cli.js +7 -438
  4. package/dist/bin/cli.js.map +1 -1
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -3
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/config.d.ts +1 -31
  10. package/dist/lib/config.d.ts.map +1 -1
  11. package/dist/lib/config.js +27 -19
  12. package/dist/lib/config.js.map +1 -1
  13. package/dist/lib/sessions.d.ts +2 -3
  14. package/dist/lib/sessions.d.ts.map +1 -1
  15. package/dist/lib/sessions.js +25 -94
  16. package/dist/lib/sessions.js.map +1 -1
  17. package/dist/memory/MemoryFactory.d.ts +1 -15
  18. package/dist/memory/MemoryFactory.d.ts.map +1 -1
  19. package/dist/memory/MemoryFactory.js +3 -53
  20. package/dist/memory/MemoryFactory.js.map +1 -1
  21. package/dist/memory/PostgresMemoryProvider.d.ts +0 -7
  22. package/dist/memory/PostgresMemoryProvider.d.ts.map +1 -1
  23. package/dist/memory/PostgresMemoryProvider.js +105 -180
  24. package/dist/memory/PostgresMemoryProvider.js.map +1 -1
  25. package/dist/server.d.ts +0 -41
  26. package/dist/server.d.ts.map +1 -1
  27. package/dist/server.js +26 -152
  28. package/dist/server.js.map +1 -1
  29. package/dist/services/AgentManager.d.ts.map +1 -1
  30. package/dist/services/AgentManager.js +11 -67
  31. package/dist/services/AgentManager.js.map +1 -1
  32. package/dist/services/ClaudeRunner.d.ts +0 -8
  33. package/dist/services/ClaudeRunner.d.ts.map +1 -1
  34. package/dist/services/ClaudeRunner.js +213 -514
  35. package/dist/services/ClaudeRunner.js.map +1 -1
  36. package/dist/services/ClineRunner.d.ts +0 -7
  37. package/dist/services/ClineRunner.d.ts.map +1 -1
  38. package/dist/services/ClineRunner.js +11 -80
  39. package/dist/services/ClineRunner.js.map +1 -1
  40. package/dist/services/GeminiRunner.d.ts +0 -6
  41. package/dist/services/GeminiRunner.d.ts.map +1 -1
  42. package/dist/services/GeminiRunner.js +58 -285
  43. package/dist/services/GeminiRunner.js.map +1 -1
  44. package/dist/services/KiloRunner.d.ts +0 -12
  45. package/dist/services/KiloRunner.d.ts.map +1 -1
  46. package/dist/services/KiloRunner.js +57 -441
  47. package/dist/services/KiloRunner.js.map +1 -1
  48. package/dist/services/OpenClawRunner.d.ts +0 -7
  49. package/dist/services/OpenClawRunner.d.ts.map +1 -1
  50. package/dist/services/OpenClawRunner.js +11 -79
  51. package/dist/services/OpenClawRunner.js.map +1 -1
  52. package/dist/services/OpenCodeRunner.d.ts +0 -7
  53. package/dist/services/OpenCodeRunner.d.ts.map +1 -1
  54. package/dist/services/OpenCodeRunner.js +11 -79
  55. package/dist/services/OpenCodeRunner.js.map +1 -1
  56. package/dist/services/QwenRunner.d.ts +19 -0
  57. package/dist/services/QwenRunner.d.ts.map +1 -0
  58. package/dist/services/QwenRunner.js +87 -0
  59. package/dist/services/QwenRunner.js.map +1 -0
  60. package/dist/services/TraeRunner.d.ts +19 -0
  61. package/dist/services/TraeRunner.d.ts.map +1 -0
  62. package/dist/services/TraeRunner.js +88 -0
  63. package/dist/services/TraeRunner.js.map +1 -0
  64. package/dist/tools/config_example.d.ts +4 -7
  65. package/dist/tools/config_example.d.ts.map +1 -1
  66. package/dist/tools/config_example.js +86 -191
  67. package/dist/tools/config_example.js.map +1 -1
  68. package/dist/tools/create_agent.d.ts +9 -15
  69. package/dist/tools/create_agent.d.ts.map +1 -1
  70. package/dist/tools/create_agent.js +9 -8
  71. package/dist/tools/create_agent.js.map +1 -1
  72. package/dist/tools/get_agent_configs.d.ts +4 -10
  73. package/dist/tools/get_agent_configs.d.ts.map +1 -1
  74. package/dist/tools/get_agent_configs.js.map +1 -1
  75. package/dist/tools/manage_agents.d.ts +18 -36
  76. package/dist/tools/manage_agents.d.ts.map +1 -1
  77. package/dist/tools/manage_agents.js +2 -2
  78. package/dist/tools/manage_agents.js.map +1 -1
  79. package/dist/tools/manage_prompts.d.ts +8 -13
  80. package/dist/tools/manage_prompts.d.ts.map +1 -1
  81. package/dist/tools/manage_prompts.js.map +1 -1
  82. package/dist/tools/memory_runs.d.ts +4 -3
  83. package/dist/tools/memory_runs.d.ts.map +1 -1
  84. package/dist/tools/memory_runs.js.map +1 -1
  85. package/dist/tools/memory_search.d.ts +4 -3
  86. package/dist/tools/memory_search.d.ts.map +1 -1
  87. package/dist/tools/memory_search.js.map +1 -1
  88. package/dist/tools/memory_store.d.ts +5 -11
  89. package/dist/tools/memory_store.d.ts.map +1 -1
  90. package/dist/tools/memory_store.js.map +1 -1
  91. package/dist/tools/run_agent.d.ts +16 -15
  92. package/dist/tools/run_agent.d.ts.map +1 -1
  93. package/dist/tools/run_agent.js +160 -128
  94. package/dist/tools/run_agent.js.map +1 -1
  95. package/dist/tools/run_claude.d.ts +3 -8
  96. package/dist/tools/run_claude.d.ts.map +1 -1
  97. package/dist/tools/run_claude.js +41 -59
  98. package/dist/tools/run_claude.js.map +1 -1
  99. package/dist/tools/run_cline.d.ts +4 -13
  100. package/dist/tools/run_cline.d.ts.map +1 -1
  101. package/dist/tools/run_cline.js +43 -55
  102. package/dist/tools/run_cline.js.map +1 -1
  103. package/dist/tools/run_gemini.d.ts +4 -13
  104. package/dist/tools/run_gemini.d.ts.map +1 -1
  105. package/dist/tools/run_gemini.js +33 -53
  106. package/dist/tools/run_gemini.js.map +1 -1
  107. package/dist/tools/run_kilo.d.ts +7 -17
  108. package/dist/tools/run_kilo.d.ts.map +1 -1
  109. package/dist/tools/run_kilo.js +41 -68
  110. package/dist/tools/run_kilo.js.map +1 -1
  111. package/dist/tools/run_openclaw.d.ts +4 -13
  112. package/dist/tools/run_openclaw.d.ts.map +1 -1
  113. package/dist/tools/run_openclaw.js +44 -52
  114. package/dist/tools/run_openclaw.js.map +1 -1
  115. package/dist/tools/run_opencode.d.ts +4 -13
  116. package/dist/tools/run_opencode.d.ts.map +1 -1
  117. package/dist/tools/run_opencode.js +39 -52
  118. package/dist/tools/run_opencode.js.map +1 -1
  119. package/dist/tools/run_qwen.d.ts +15 -0
  120. package/dist/tools/run_qwen.d.ts.map +1 -0
  121. package/dist/tools/run_qwen.js +61 -0
  122. package/dist/tools/run_qwen.js.map +1 -0
  123. package/dist/tools/run_trae.d.ts +15 -0
  124. package/dist/tools/run_trae.d.ts.map +1 -0
  125. package/dist/tools/run_trae.js +66 -0
  126. package/dist/tools/run_trae.js.map +1 -0
  127. package/dist/tools/shell_execute.d.ts +10 -0
  128. package/dist/tools/shell_execute.d.ts.map +1 -0
  129. package/dist/tools/shell_execute.js +24 -0
  130. package/dist/tools/shell_execute.js.map +1 -0
  131. package/package.json +25 -55
  132. package/.mcp.json.example +0 -21
  133. package/assets/overmind.png +0 -0
  134. package/bin/.gitkeep +0 -0
  135. package/bin/README.md +0 -34
  136. package/bin/install-overmind-unix.sh +0 -412
  137. package/bin/install-overmind-windows.bat +0 -407
  138. package/docs/README.md +0 -128
  139. package/docs/agent_control.md +0 -656
  140. package/docs/index.html +0 -493
  141. package/docs/library.html +0 -239
  142. package/docs/prompt.html +0 -1212
  143. package/docs/script.js +0 -428
  144. package/docs/styles.css +0 -2816
  145. package/scripts/auto-changelog.mjs +0 -132
  146. package/scripts/auto-install.mjs +0 -322
  147. package/scripts/install-dependencies.mjs +0 -462
  148. package/scripts/postgres-manager.mjs +0 -219
  149. package/scripts/postinstall.mjs +0 -538
  150. package/scripts/setup-overmind-db.mjs +0 -199
  151. package/scripts/setup-windows.js +0 -266
  152. package/scripts/setup.mjs +0 -397
  153. package/scripts/test-installation.mjs +0 -158
  154. package/scripts/uninstall.mjs +0 -238
@@ -1,154 +1,79 @@
1
1
  import fs from 'fs';
2
+ import os from 'os';
2
3
  import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { spawn, exec } from 'child_process';
5
- import { CONFIG, resolveConfigPath, getWorkspaceDir } from '../lib/config.js';
4
+ import { spawn } from 'child_process';
5
+ import { CONFIG, resolveConfigPath } from '../lib/config.js';
6
6
  import { getLastSessionId, saveSessionId } from '../lib/sessions.js';
7
- import { interpolateEnvVars } from '../lib/envUtils.js';
8
- import { resolveModel } from '../lib/modelMapping.js';
9
- import { withSpan } from '../lib/telemetry.js';
10
- import { loadEnvQuietly } from '../lib/loadEnv.js';
11
- import { registerProcess, linkSessionToPid, appendOutput, updateProcessStatus, } from '../lib/processRegistry.js';
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = path.dirname(__filename);
14
- // Sur Windows, `child.kill()` ne tue que cmd.exe (le wrapper) — claude.exe
15
- // devient orphelin et garde la session bound au token initial côté provider.
16
- // On utilise `taskkill /F /T /PID` pour propager le kill au sous-arbre,
17
- // puis on attend l'event 'exit' avant de respawn.
18
- const killProcessTree = (child) => {
19
- return new Promise((resolve) => {
20
- if (!child || child.exitCode !== null || child.killed) {
21
- resolve();
22
- return;
23
- }
24
- let settled = false;
25
- const finish = () => {
26
- if (settled)
27
- return;
28
- settled = true;
29
- resolve();
30
- };
31
- child.once('exit', finish);
32
- if (process.platform === 'win32' && child.pid) {
33
- exec(`taskkill /F /T /PID ${child.pid}`, () => {
34
- // taskkill peut échouer si le process est déjà mort — on s'appuie sur 'exit'
35
- });
36
- }
37
- else {
38
- try {
39
- child.kill('SIGTERM');
40
- }
41
- catch {
42
- // Ignored
43
- }
44
- setTimeout(() => {
45
- if (child.exitCode === null && !child.killed) {
46
- try {
47
- child.kill('SIGKILL');
48
- }
49
- catch {
50
- // Ignored
51
- }
52
- }
53
- }, 2000);
54
- }
55
- // Filet de sécurité : si 'exit' ne se déclenche pas (process zombie),
56
- // on débloque le respawn après 5s plutôt que de bloquer indéfiniment.
57
- setTimeout(finish, 5000);
58
- });
59
- };
60
7
  export class ClaudeRunner {
61
8
  config;
62
9
  timeoutMs;
63
10
  constructor() {
64
11
  this.config = CONFIG.CLAUDE;
65
- this.timeoutMs = CONFIG.TIMEOUT_MS || 900000;
12
+ this.timeoutMs = CONFIG.TIMEOUT_MS || 120000;
66
13
  }
67
14
  async runAgent(options) {
68
15
  const { prompt, agentName, autoResume } = options;
69
16
  let { sessionId } = options;
70
17
  const { CORE, PERMISSIONS, PATHS } = this.config;
71
18
  const agentCustomEnv = {};
72
- // Load environment variables FIRST before any processing
73
- const workspaceEnvPath = path.resolve(options.configPath || getWorkspaceDir(), '.env');
74
- loadEnvQuietly(workspaceEnvPath);
75
- // Also load from Workflow directory as fallback
76
- const workflowEnvPath = path.resolve(__dirname, '../../.env');
77
- loadEnvQuietly(workflowEnvPath);
78
- // Debug: check if variables are loaded
79
- if (!options.silent) {
80
- console.error(`[ClaudeRunner] Env check - ANTHROPIC_MODEL_Z present: ${!!process.env.ANTHROPIC_MODEL_Z}`);
81
- console.error(`[ClaudeRunner] Auth tokens present: ${!!process.env.ANTHROPIC_AUTH_TOKEN_Y || !!process.env.ANTHROPIC_AUTH_TOKEN_E}`);
82
- console.error(`[ClaudeRunner] workspaceEnvPath: ${workspaceEnvPath}`);
83
- console.error(`[ClaudeRunner] workflowEnvPath: ${workflowEnvPath}`);
84
- }
85
- if (agentName) {
86
- agentCustomEnv.OVERMIND_AGENT_NAME = agentName;
87
- }
88
19
  // --- Auto Resume ---
89
20
  if (autoResume && agentName && !sessionId) {
90
- const lastId = await getLastSessionId(agentName, options.configPath, 'claude');
21
+ const lastId = await getLastSessionId(agentName);
91
22
  if (lastId) {
92
23
  sessionId = lastId;
93
- if (!options.silent) {
94
- console.log(`[ClaudeRunner] Auto-resume session: ${sessionId}`);
95
- }
96
24
  }
97
25
  }
98
- let settingsPath = resolveConfigPath(PATHS.SETTINGS, options.configPath);
26
+ let settingsPath = resolveConfigPath(PATHS.SETTINGS);
99
27
  if (agentName) {
100
28
  const settingsDir = path.dirname(PATHS.SETTINGS);
101
- const specificSettingsPath = resolveConfigPath(path.join(settingsDir, `settings_${agentName}.json`), options.configPath);
29
+ const specificSettingsPath = resolveConfigPath(path.join(settingsDir, `settings_${agentName}.json`));
102
30
  if (!fs.existsSync(specificSettingsPath)) {
103
31
  return {
104
32
  result: '',
105
- error: `INVALID_AGENT: Agent "${agentName}" non trouvé.`,
33
+ error: `INVALID_AGENT`,
106
34
  };
107
35
  }
108
36
  settingsPath = specificSettingsPath;
109
37
  }
110
- let mcpPath = resolveConfigPath(PATHS.MCP, options.configPath);
38
+ const cwd = process.cwd();
39
+ const relativeSettings = path.relative(cwd, settingsPath);
40
+ if (!relativeSettings.startsWith('..') && !path.isAbsolute(relativeSettings)) {
41
+ settingsPath = relativeSettings.startsWith('./') ? relativeSettings : `./${relativeSettings}`;
42
+ }
43
+ let mcpPath = resolveConfigPath(PATHS.MCP);
111
44
  let tmpMcpPathToDelete = null;
112
- let tmpSettingsPathToDelete = null;
113
45
  let customTimeoutMs = this.timeoutMs;
46
+ // --- Isolation ---
114
47
  if (agentName) {
115
48
  try {
116
- const agentSettingsPath = resolveConfigPath(path.join(path.dirname(PATHS.SETTINGS), `settings_${agentName}.json`), options.configPath);
49
+ const agentSettingsPath = resolveConfigPath(path.join(path.dirname(PATHS.SETTINGS), `settings_${agentName}.json`));
117
50
  if (fs.existsSync(agentSettingsPath)) {
118
- let settings = JSON.parse(fs.readFileSync(agentSettingsPath, 'utf8'));
119
- // Debug: log environment variables
120
- if (!options.silent) {
121
- console.error(`[ClaudeRunner] ANTHROPIC_MODEL_Z present: ${!!process.env.ANTHROPIC_MODEL_Z}`);
122
- console.error(`[ClaudeRunner] Auth tokens present: ${!!process.env.ANTHROPIC_AUTH_TOKEN_Y || !!process.env.ANTHROPIC_AUTH_TOKEN_E}`);
123
- console.error(`[ClaudeRunner] ANTHROPIC_BASE_URL_Z present: ${!!process.env.ANTHROPIC_BASE_URL_Z}`);
124
- }
125
- // --- New interpolation logic ---
126
- settings = interpolateEnvVars(settings);
127
- // Debug: log interpolated values
128
- if (!options.silent) {
129
- console.error(`[ClaudeRunner] Interpolated ANTHROPIC_MODEL = ${settings.env?.ANTHROPIC_MODEL}`);
130
- console.error(`[ClaudeRunner] Interpolated ANTHROPIC_BASE_URL = ${settings.env?.ANTHROPIC_BASE_URL}`);
131
- }
132
- // 1. Create a temporary settings file with interpolated values
133
- const tmpSettingsPath = path.join(path.dirname(agentSettingsPath), `settings_${agentName}_tmp.json`);
134
- fs.writeFileSync(tmpSettingsPath, JSON.stringify(settings, null, 2));
135
- settingsPath = tmpSettingsPath;
136
- tmpSettingsPathToDelete = tmpSettingsPath;
51
+ const settings = JSON.parse(fs.readFileSync(agentSettingsPath, 'utf8'));
137
52
  if (settings.env) {
53
+ // Mémoriser l'env configuré pour l'injection
138
54
  Object.assign(agentCustomEnv, settings.env);
55
+ // --- SMART NICKNAME FALLBACK ---
56
+ const currentModel = settings.env.ANTHROPIC_MODEL;
57
+ const isTechnicalModelId = currentModel && (currentModel.includes('claude') ||
58
+ currentModel.includes('gpt') ||
59
+ currentModel.includes('glm') ||
60
+ currentModel.includes('minimax') ||
61
+ currentModel.includes('deepseek') ||
62
+ currentModel.includes('moonshot'));
63
+ if (currentModel && !isTechnicalModelId) {
64
+ // Si le modèle est un surnom, on l'utilise pour l'affichage mais on remet un vrai ID de modèle pour l'API
65
+ agentCustomEnv.AGENT_NICKNAME = currentModel;
66
+ // On utilise le modèle Sonnet par défaut ou la valeur configurée si elle semble valide
67
+ agentCustomEnv.ANTHROPIC_MODEL = (settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL && settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL.includes('claude'))
68
+ ? settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL
69
+ : 'claude-3-5-sonnet-20241022';
70
+ }
139
71
  if (settings.env.AGENT_TIMEOUT_MS || settings.env.API_TIMEOUT_MS) {
140
72
  const timeoutValue = settings.env.AGENT_TIMEOUT_MS || settings.env.API_TIMEOUT_MS;
141
73
  customTimeoutMs = parseInt(timeoutValue, 10) || customTimeoutMs;
142
74
  }
143
- if (!options.model && settings.env.MODEL) {
144
- agentCustomEnv.ANTHROPIC_MODEL = settings.env.MODEL;
145
- }
146
75
  }
147
- const agentMcpPath = resolveConfigPath(path.join(path.dirname(PATHS.SETTINGS), `.mcp.${agentName}.json`));
148
- if (fs.existsSync(agentMcpPath)) {
149
- mcpPath = agentMcpPath;
150
- }
151
- else if (settings.enableAllProjectMcpServers === false &&
76
+ if (settings.enableAllProjectMcpServers === false &&
152
77
  Array.isArray(settings.enabledMcpjsonServers)) {
153
78
  if (fs.existsSync(mcpPath)) {
154
79
  const fullMcp = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
@@ -166,8 +91,30 @@ export class ClaudeRunner {
166
91
  }
167
92
  }
168
93
  }
94
+ catch (_e) {
95
+ // Warning
96
+ }
97
+ }
98
+ const relativeMcp = path.relative(cwd, mcpPath);
99
+ if (!relativeMcp.startsWith('..') && !path.isAbsolute(relativeMcp)) {
100
+ mcpPath = relativeMcp.startsWith('./') ? relativeMcp : `./${relativeMcp}`;
101
+ }
102
+ let tmpSettingsPathToDelete = null;
103
+ let finalSettingsPath = settingsPath;
104
+ if (agentCustomEnv.AGENT_NICKNAME) {
105
+ try {
106
+ // On crée un fichier settings temporaire pour substituer le surnom par un vrai modèle
107
+ // car le CLI Claude ne valide pas les surnoms dynamiques en interne
108
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
109
+ const tempSettings = JSON.parse(JSON.stringify(settings));
110
+ tempSettings.env.ANTHROPIC_MODEL = agentCustomEnv.ANTHROPIC_MODEL;
111
+ const tmpSettingsPath = path.join(os.tmpdir(), `settings-${agentName || 'agent'}-${Date.now()}.json`);
112
+ fs.writeFileSync(tmpSettingsPath, JSON.stringify(tempSettings, null, 2));
113
+ finalSettingsPath = tmpSettingsPath;
114
+ tmpSettingsPathToDelete = tmpSettingsPath;
115
+ }
169
116
  catch (e) {
170
- console.error(`[ClaudeRunner] [WARN] Error processing agent settings: ${e}`);
117
+ console.error(`[ClaudeRunner] ⚠️ Erreur lors de la création du settings temporaire: ${e}`);
171
118
  }
172
119
  }
173
120
  const argsSpawn = [];
@@ -175,430 +122,182 @@ export class ClaudeRunner {
175
122
  argsSpawn.push(...CORE.split(' ').filter(Boolean));
176
123
  if (PERMISSIONS)
177
124
  argsSpawn.push(...PERMISSIONS.split(' ').filter(Boolean));
178
- argsSpawn.push('--settings', settingsPath);
125
+ // DÉCISION IMPORTANTE: On n'ajoute pas de guillemets manuels ici, spawn s'en occupe
126
+ argsSpawn.push('--settings', finalSettingsPath);
179
127
  argsSpawn.push('--mcp-config', mcpPath);
180
- argsSpawn.push('--output-format', 'json');
181
- let modelUsed = options.model;
182
- if (!modelUsed && agentCustomEnv.ANTHROPIC_MODEL) {
183
- modelUsed = agentCustomEnv.ANTHROPIC_MODEL;
128
+ if (sessionId) {
129
+ argsSpawn.push('--resume', sessionId);
184
130
  }
185
- // Remember original value (nickname or raw model) for display
186
- const originalModel = modelUsed ?? '';
187
- // Resolve nickname → real model ID before calling the API
188
- if (modelUsed) {
189
- modelUsed = resolveModel(modelUsed);
131
+ // --- MODEL & NICKNAME FLAGS ---
132
+ const modelToUse = agentCustomEnv.ANTHROPIC_MODEL || 'claude-3-5-sonnet-20241022';
133
+ console.error(`[ClaudeRunner] 🛠️ Model override: ${modelToUse}`);
134
+ argsSpawn.push('--model', modelToUse);
135
+ if (agentCustomEnv.AGENT_NICKNAME) {
136
+ console.error(`[ClaudeRunner] 👤 Nickname: ${agentCustomEnv.AGENT_NICKNAME}`);
137
+ argsSpawn.push('--name', agentCustomEnv.AGENT_NICKNAME);
190
138
  }
191
- if (sessionId)
192
- argsSpawn.push('--resume', sessionId);
193
- if (modelUsed)
194
- argsSpawn.push('--model', modelUsed);
195
- if (agentName)
139
+ else if (agentName) {
196
140
  argsSpawn.push('--name', agentName);
197
- // ───────────────────────────────────────────────────────────────────────────
198
- // 🔄 FALLBACK TOKEN RETRY LOGIC
199
- //
200
- // Overmind lit les tokens fallback depuis agentCustomEnv (résolus depuis $VAR).
201
- // Si une erreur 401 (auth) survient, on tente chaque fallback séquentiellement :
202
- // AUTH_FALLBACK_1 → AUTH_FALLBACK_2 → AUTH_FALLBACK_3
203
- //
204
- // Settings exemple :
205
- // { "env": { "ANTHROPIC_AUTH_TOKEN": "$ANTHROPIC_AUTH_FALLBACK_1" } }
206
- // ───────────────────────────────────────────────────────────────────────────
207
- const FALLBACK_KEYS = ['AUTH_FALLBACK_1', 'AUTH_FALLBACK_2', 'AUTH_FALLBACK_3'];
208
- const TOKEN_KEYS = ['ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_AUTH_TOKEN_E'];
209
- /**
210
- * Vérifie si une erreur est retryable (fallback recommended).
211
- * 401 = auth error (token invalide/expiré)
212
- * 429 = rate limit / quota exhausted
213
- * 500/502/503 = server error
214
- */
215
- const isRetryableError = (stderr, jsonEnv) => {
216
- const lower = stderr.toLowerCase();
217
- const status = jsonEnv?.api_error_status;
218
- if (status === 401)
219
- return true;
220
- if (status === 429)
221
- return true;
222
- if (status === 500 || status === 502 || status === 503)
223
- return true;
224
- return (lower.includes('401') ||
225
- lower.includes('unauthorized') ||
226
- lower.includes('invalid api key') ||
227
- lower.includes('api key invalid') ||
228
- lower.includes('authentication failed') ||
229
- lower.includes('auth error') ||
230
- lower.includes('invalid authentication') ||
231
- lower.includes('429') ||
232
- lower.includes('rate limit') ||
233
- lower.includes('quota exhausted') ||
234
- lower.includes('limit exhausted') ||
235
- lower.includes('503') ||
236
- lower.includes('service unavailable') ||
237
- lower.includes('500') ||
238
- lower.includes('internal server error'));
239
- };
240
- /**
241
- * Extrait les tokens fallback disponibles depuis agentCustomEnv.
242
- * Retourne un tableau de { key, value } pour chaque fallback non vide.
243
- */
244
- const getAvailableFallbacks = () => {
245
- const fallbacks = [];
246
- for (const key of FALLBACK_KEYS) {
247
- const val = agentCustomEnv[key];
248
- if (val && typeof val === 'string' && val.length > 0) {
249
- fallbacks.push({ key, value: val });
141
+ }
142
+ return new Promise((resolve) => {
143
+ const cleanupTmpFiles = () => {
144
+ if (tmpMcpPathToDelete && fs.existsSync(tmpMcpPathToDelete)) {
145
+ try {
146
+ fs.unlinkSync(tmpMcpPathToDelete);
147
+ }
148
+ catch (_e) { /* intentionally empty */ }
250
149
  }
251
- }
252
- return fallbacks;
253
- };
254
- /**
255
- * Détermine quel token utiliser selon l'index de retry.
256
- * - index 0 = tentative initiale → use primary token (ANTHROPIC_AUTH_TOKEN)
257
- * - index 1+ = retry → skip primary, use fallbacks directly
258
- */
259
- const getTokenForIndex = (index) => {
260
- if (index === 0) {
261
- // Tentative initiale : utiliser le primary token
262
- // NOTE: si la valeur est un $VAR non résolu (interpolateEnvVars n'a pas trouvé
263
- // la variable dans process.env à ce moment), on le passe quand même à spawnWithToken
264
- // qui fera la résolution finale via process.env.
265
- for (const tk of TOKEN_KEYS) {
266
- const val = agentCustomEnv[tk];
267
- if (val && typeof val === 'string' && val.length > 0) {
268
- return { tokenEnvKey: tk, tokenValue: val };
150
+ if (tmpSettingsPathToDelete && fs.existsSync(tmpSettingsPathToDelete)) {
151
+ try {
152
+ fs.unlinkSync(tmpSettingsPathToDelete);
269
153
  }
154
+ catch (_e) { /* intentionally empty */ }
155
+ }
156
+ };
157
+ const isWin = process.platform === 'win32';
158
+ let command = 'claude';
159
+ let spawnArgs;
160
+ // Prepend persona if defined
161
+ let finalPrompt = prompt;
162
+ if (agentName) {
163
+ let agentPromptPath = resolveConfigPath(path.join(path.dirname(PATHS.SETTINGS), 'agents', `${agentName}.md`));
164
+ if (!fs.existsSync(agentPromptPath)) {
165
+ // Fallback: Check agents/ folder at the root level of settingsDir
166
+ agentPromptPath = resolveConfigPath(path.join(path.dirname(path.dirname(PATHS.SETTINGS)), 'agents', `${agentName}.md`));
167
+ }
168
+ if (fs.existsSync(agentPromptPath)) {
169
+ const systemPrompt = fs.readFileSync(agentPromptPath, 'utf8');
170
+ finalPrompt = `${systemPrompt}\n\n[USER QUERY]:\n${prompt}`;
270
171
  }
271
- // Aucun primary token trouvé — retourner null plutôt que de tomber dans les fallbacks
272
- return null;
273
172
  }
274
- // Retry (index >= 1) : skip primary, use fallbacks directly
275
- const fallbacks = getAvailableFallbacks();
276
- const fallbackIndex = index - 1; // index 1 → fallback[0] (AUTH_FALLBACK_1)
277
- if (fallbackIndex < fallbacks.length) {
278
- return { tokenEnvKey: fallbacks[fallbackIndex].key, tokenValue: fallbacks[fallbackIndex].value };
173
+ if (isWin) {
174
+ const claudePath = 'C:\\Users\\Deamon\\AppData\\Roaming\\npm\\claude.ps1';
175
+ if (fs.existsSync(claudePath)) {
176
+ command = 'powershell.exe';
177
+ spawnArgs = [
178
+ '-NoProfile',
179
+ '-ExecutionPolicy',
180
+ 'Bypass',
181
+ '-File',
182
+ claudePath,
183
+ ...argsSpawn,
184
+ '-p',
185
+ finalPrompt,
186
+ ];
187
+ }
188
+ else {
189
+ command = 'cmd.exe';
190
+ spawnArgs = ['/c', 'claude', ...argsSpawn, '-p', finalPrompt];
191
+ }
279
192
  }
280
- return null;
281
- };
282
- // ─── AbortSignal support ─────────────────────────────────────────────────────
283
- let currentChildRef = null;
284
- if (options.signal?.aborted) {
285
- return Promise.reject(new Error('ABORTED'));
286
- }
287
- options.signal?.addEventListener('abort', () => {
288
- if (currentChildRef)
289
- void killProcessTree(currentChildRef);
290
- });
291
- const runImpl = async (span) => {
292
- span.setAttribute('agentName', agentName || '');
293
- span.setAttribute('model', modelUsed || '');
294
- span.setAttribute('runner', 'claude');
295
- return new Promise((resolve) => {
296
- let resolved = false;
297
- let retryCount = 0;
298
- const maxRetries = getAvailableFallbacks().length + 1; // primary + fallbacks
299
- currentChildRef = null;
300
- let currentStderr = '';
301
- let currentStdout = '';
302
- const MAX_BUF = 10 * 1024 * 1024;
303
- let currentSessionId = sessionId;
304
- let earlyExitTriggered = false; // Prevent double-exit on early retry
305
- const safeResolve = (value) => {
306
- if (!resolved) {
307
- resolved = true;
308
- resolve(value);
309
- }
310
- };
311
- const triggerRetry = async (targetRetryCount) => {
312
- if (earlyExitTriggered)
313
- return;
314
- earlyExitTriggered = true;
315
- if (hardTimeoutTimer)
316
- clearTimeout(hardTimeoutTimer);
317
- if (killTimer)
318
- clearTimeout(killTimer);
319
- // Attendre la mort effective de l'arbre (cmd.exe + claude.exe sur Win)
320
- // avant de respawn, sinon le nouveau process tape sur la même session
321
- // encore "vivante" côté provider → 429 fantôme.
322
- if (currentChildRef) {
323
- await killProcessTree(currentChildRef);
324
- }
325
- retryCount = targetRetryCount;
326
- const tokenInfo = getTokenForIndex(retryCount);
327
- if (!options.silent) {
328
- process.stderr.write(`\n\x1b[41m\x1b[37m[ClaudeRunner] 🔄 Retry ${retryCount}/${maxRetries} avec ${tokenInfo?.tokenEnvKey || 'UNKNOWN'}...\x1b[0m\n`);
329
- }
330
- setImmediate(() => spawnWithToken(tokenInfo));
331
- };
332
- const cleanupTmpFiles = () => {
333
- if (tmpMcpPathToDelete && fs.existsSync(tmpMcpPathToDelete)) {
334
- try {
335
- fs.unlinkSync(tmpMcpPathToDelete);
336
- }
337
- catch {
338
- // Ignored
339
- }
193
+ else {
194
+ spawnArgs = [...argsSpawn, '-p', finalPrompt];
195
+ }
196
+ if (agentName) {
197
+ const id = agentCustomEnv.AGENT_NICKNAME || agentName;
198
+ console.error(`[ClaudeRunner] 🚀 Démarrage de l'agent ${id}...`);
199
+ // Debug: Log the prompt size
200
+ console.error(`[ClaudeRunner] 📏 Prompt Size: ${finalPrompt.length} chars`);
201
+ }
202
+ const child = spawn(command, spawnArgs, {
203
+ stdio: ['ignore', 'pipe', 'pipe'],
204
+ cwd: process.cwd(),
205
+ // shell: false explicitly (handled by command selection)
206
+ windowsHide: true,
207
+ env: {
208
+ ...process.env,
209
+ ...agentCustomEnv,
210
+ // Compatibilité Anthropic/Z.ai pour Claude Code standard
211
+ ...(agentCustomEnv.ANTHROPIC_AUTH_TOKEN && !agentCustomEnv.ANTHROPIC_API_KEY
212
+ ? { ANTHROPIC_API_KEY: agentCustomEnv.ANTHROPIC_AUTH_TOKEN }
213
+ : {}),
214
+ ...(agentName ? { OVERMIND_AGENT_NAME: agentName } : {}),
215
+ },
216
+ });
217
+ let stdout = '';
218
+ let stderr = '';
219
+ if (child.stdout) {
220
+ child.stdout.on('data', (d) => {
221
+ const chunk = d.toString();
222
+ stdout += chunk;
223
+ if (agentName) {
224
+ const id = agentCustomEnv.AGENT_NICKNAME || agentName;
225
+ process.stderr.write(`[ClaudeRunner:${id}] ${chunk}`);
340
226
  }
341
- if (tmpSettingsPathToDelete && fs.existsSync(tmpSettingsPathToDelete)) {
342
- try {
343
- fs.unlinkSync(tmpSettingsPathToDelete);
344
- }
345
- catch {
346
- // Ignored
347
- }
227
+ });
228
+ }
229
+ if (child.stderr) {
230
+ child.stderr.on('data', (d) => {
231
+ const chunk = d.toString();
232
+ stderr += chunk;
233
+ if (agentName) {
234
+ const id = agentCustomEnv.AGENT_NICKNAME || agentName;
235
+ process.stderr.write(`[ClaudeRunner:${id}:ERR] ${chunk}`);
348
236
  }
349
- };
350
- let killTimer = null;
351
- let hardTimeoutTimer = null;
352
- /**
353
- * Fonction centrale qui spawn le processus Claude avec le bon token.
354
- * Appelé initialement et après chaque retry.
355
- */
356
- const spawnWithToken = (tokenInfo) => {
357
- // Nettoyer les listeners/timers de la tentative précédente
358
- if (hardTimeoutTimer) {
359
- clearTimeout(hardTimeoutTimer);
360
- hardTimeoutTimer = null;
237
+ });
238
+ }
239
+ const timeout = setTimeout(() => {
240
+ child.kill();
241
+ cleanupTmpFiles();
242
+ resolve({ result: '', error: `TIMEOUT`, rawOutput: stdout });
243
+ }, customTimeoutMs);
244
+ child.on('close', async (code) => {
245
+ clearTimeout(timeout);
246
+ cleanupTmpFiles();
247
+ const detectError = (text) => {
248
+ const lower = text.toLowerCase();
249
+ if (lower.includes('api key') || lower.includes('auth') || lower.includes('401')) {
250
+ return '🔑 Erreur Auth/API Key (Clé invalide ou manquante)';
361
251
  }
362
- if (killTimer) {
363
- clearTimeout(killTimer);
364
- killTimer = null;
252
+ if (lower.includes('quota') || lower.includes('exceeded') || lower.includes('429')) {
253
+ return '📊 Quota dépassé (API Key épuisée)';
365
254
  }
366
- // Construire l'env avec le bon token
367
- // NOTE: Overmind gère la substitution des variables $VAR dans les settings.
368
- // Les fallback tokens (AUTH_FALLBACK_1/2/3) sont résolus ici pour le retry 401.
369
- const spawnEnv = {
370
- ...process.env,
371
- ...agentCustomEnv,
372
- };
373
- if (tokenInfo) {
374
- // Remplacer le token actif par celui du fallback
375
- // NOTE: Les tokens peuvent encore contenir des $VAR non résolus
376
- // (interpolateEnvVars n'a pas trouvé ces vars dans process.env au moment du load).
377
- // On résout ici via process.env (qui a été peuplé par loadEnvQuietly).
378
- for (const tk of TOKEN_KEYS) {
379
- delete spawnEnv[tk];
380
- }
381
- let resolvedToken = tokenInfo.tokenValue;
382
- if (resolvedToken.startsWith('$')) {
383
- const envKey = resolvedToken.slice(1);
384
- resolvedToken = process.env[envKey] || resolvedToken;
385
- }
386
- // Le Claude CLI lit ANTHROPIC_AUTH_TOKEN — on injecte toujours sous ce nom,
387
- // peu importe que tokenInfo vienne du primary ou d'un AUTH_FALLBACK_n.
388
- spawnEnv['ANTHROPIC_AUTH_TOKEN'] = resolvedToken;
255
+ if (lower.includes('rate limit')) {
256
+ return '⏳ Rate limit atteint';
389
257
  }
390
- currentStderr = '';
391
- currentStdout = '';
392
- const command = process.platform === 'win32' ? 'cmd.exe' : 'claude';
393
- const spawnArgs = process.platform === 'win32'
394
- ? ['/c', 'claude', ...argsSpawn, '-p']
395
- : ['claude', ...argsSpawn, '-p'];
396
- if (!options.silent) {
397
- const tokenLabel = tokenInfo ? ` (token: ${tokenInfo.tokenEnvKey})` : '';
398
- process.stderr.write(`\n\x1b[33m[ClaudeRunner]${tokenLabel} Spawning Claude CLI...\x1b[0m\n`);
258
+ if (lower.includes('model') && lower.includes('404')) {
259
+ return '🤖 Modèle introuvable';
399
260
  }
400
- currentChildRef = spawn(command, spawnArgs, {
401
- cwd: options.cwd || process.cwd(),
402
- windowsHide: true,
403
- env: spawnEnv,
404
- shell: false,
405
- signal: options.signal,
261
+ return null;
262
+ };
263
+ if (code !== 0 && !stdout) {
264
+ const specificError = detectError(stderr);
265
+ return resolve({
266
+ result: '',
267
+ error: specificError || `EXIT_CODE_${code}`,
268
+ rawOutput: stderr,
406
269
  });
407
- // Register process immediately after spawn
408
- if (currentChildRef.pid) {
409
- void registerProcess(currentChildRef.pid, {
410
- agentName: agentName || '',
411
- runner: 'claude',
412
- configPath: options.configPath,
413
- });
414
- }
415
- if (currentChildRef.stdout) {
416
- currentChildRef.stdout.on('data', (d) => {
417
- const chunk = d.toString();
418
- if (currentChildRef && currentChildRef.pid && chunk) {
419
- void appendOutput(currentChildRef.pid, chunk, options.configPath);
420
- }
421
- if (currentStdout.length + chunk.length > MAX_BUF)
422
- currentStdout = currentStdout.slice(-MAX_BUF);
423
- else
424
- currentStdout += chunk;
425
- if (agentName && !options.silent) {
426
- process.stderr.write(`[ClaudeRunner:${agentName}] ${chunk}`);
427
- }
428
- });
429
- }
430
- if (currentChildRef.stderr) {
431
- currentChildRef.stderr.on('data', (d) => {
432
- const chunk = d.toString();
433
- if (currentChildRef && currentChildRef.pid && chunk) {
434
- void appendOutput(currentChildRef.pid, chunk, options.configPath);
435
- }
436
- if (currentStderr.length + chunk.length > MAX_BUF)
437
- currentStderr = currentStderr.slice(-MAX_BUF);
438
- else
439
- currentStderr += chunk;
440
- if (agentName && !options.silent) {
441
- process.stderr.write(`[ClaudeRunner:${agentName}:ERR] ${chunk}`);
442
- }
443
- });
270
+ }
271
+ try {
272
+ let jsonStr = stdout.trim();
273
+ const jsonStartIndex = jsonStr.indexOf('{');
274
+ const jsonLastIndex = jsonStr.lastIndexOf('}');
275
+ if (jsonStartIndex >= 0 && jsonLastIndex > jsonStartIndex) {
276
+ jsonStr = jsonStr.substring(jsonStartIndex, jsonLastIndex + 1);
444
277
  }
445
- if (currentChildRef.stdin) {
446
- currentChildRef.stdin.write(prompt);
447
- currentChildRef.stdin.end();
278
+ const response = JSON.parse(jsonStr || '{}');
279
+ if (agentName && response.session_id) {
280
+ await saveSessionId(agentName, response.session_id);
448
281
  }
449
- const timeout = setTimeout(() => {
450
- if (currentChildRef && currentChildRef.stdin && !currentChildRef.stdin.destroyed) {
451
- try {
452
- currentChildRef.stdin.write('\n');
453
- if (!options.silent) {
454
- process.stderr.write(`\n\x1b[33m[ClaudeRunner] [WARN] Agent stagnant (${customTimeoutMs}ms). Envoi d'un keep-alive (\\n)...\x1b[0m\n`);
455
- }
456
- }
457
- catch (_e) {
458
- // Ignore
459
- }
460
- }
461
- const hardTimeoutDelay = CONFIG.HARD_TIMEOUT_MS || 60000;
462
- hardTimeoutTimer = setTimeout(() => {
463
- if (currentChildRef)
464
- void killProcessTree(currentChildRef);
465
- cleanupTmpFiles();
466
- safeResolve({
467
- result: '',
468
- error: 'HARD_TIMEOUT',
469
- rawOutput: currentStdout + currentStderr,
470
- });
471
- }, hardTimeoutDelay);
472
- }, customTimeoutMs);
473
- currentChildRef.on('error', (err) => {
474
- clearTimeout(timeout);
475
- if (hardTimeoutTimer)
476
- clearTimeout(hardTimeoutTimer);
477
- cleanupTmpFiles();
478
- safeResolve({ result: '', error: `SPAWN_ERROR: ${err.message}`, rawOutput: '' });
282
+ resolve({
283
+ result: response.result || JSON.stringify(response),
284
+ sessionId: response.session_id,
285
+ rawOutput: stdout,
479
286
  });
480
- currentChildRef.on('close', async (code) => {
481
- clearTimeout(timeout);
482
- if (hardTimeoutTimer)
483
- clearTimeout(hardTimeoutTimer);
484
- const fullRaw = currentStdout + (currentStderr ? `\n\n--- STDERR ---\n${currentStderr}` : '');
485
- // ─── Parser le JSON en premier (pour extraire api_error_status) ───
486
- let jsonEnvelope = null;
487
- const trimmedStdout = currentStdout.trim();
488
- try {
489
- jsonEnvelope = JSON.parse(trimmedStdout);
490
- }
491
- catch {
492
- const lastBrace = trimmedStdout.lastIndexOf('}');
493
- const firstBrace = trimmedStdout.lastIndexOf('{', lastBrace);
494
- if (firstBrace !== -1 && lastBrace !== -1) {
495
- try {
496
- jsonEnvelope = JSON.parse(trimmedStdout.substring(firstBrace, lastBrace + 1));
497
- }
498
- catch {
499
- // Ignored
500
- }
501
- }
502
- }
503
- // ─── Fallback retry ─────────────────────────────────────────────────
504
- // Sur 401/429/5xx, on tue l'arbre du process courant via killProcessTree
505
- // (taskkill /F /T sur Windows pour ne pas laisser claude.exe orphelin),
506
- // puis on respawn un nouveau noeud avec --resume + le token fallback
507
- // suivant (AUTH_FALLBACK_1 → 2 → 3).
508
- // ───────────────────────────────────────────────────────────────────
509
- const FALLBACK_RETRY_ENABLED = true;
510
- const isRetryable = isRetryableError(currentStderr, jsonEnvelope);
511
- const hasRetryableStatus = jsonEnvelope !== null &&
512
- (jsonEnvelope.api_error_status === 401 ||
513
- jsonEnvelope.api_error_status === 429 ||
514
- jsonEnvelope.api_error_status === 500 ||
515
- jsonEnvelope.api_error_status === 502 ||
516
- jsonEnvelope.api_error_status === 503);
517
- const isFailure = FALLBACK_RETRY_ENABLED && ((code !== 0 && isRetryable) || hasRetryableStatus);
518
- if (isFailure) {
519
- if (retryCount < maxRetries) {
520
- triggerRetry(retryCount + 1);
521
- return;
522
- }
523
- else {
524
- if (!options.silent) {
525
- process.stderr.write(`\n\x1b[41m\x1b[37m[ClaudeRunner] ❌ Tous les tokens fallback épuisés. Error retryable finale.\x1b[0m\n`);
526
- }
527
- if (currentChildRef?.pid) {
528
- void updateProcessStatus(currentChildRef.pid, 'failed', code, options.configPath);
529
- }
530
- cleanupTmpFiles();
531
- safeResolve({
532
- result: '',
533
- error: 'RETRYABLE_ERROR_ALL_FALLBACKS_EXHAUSTED',
534
- rawOutput: fullRaw,
535
- });
536
- return;
537
- }
538
- }
539
- cleanupTmpFiles();
540
- try {
541
- if (jsonEnvelope) {
542
- let foundSessionId = currentSessionId;
543
- if (jsonEnvelope.session_id && agentName) {
544
- foundSessionId = jsonEnvelope.session_id;
545
- currentSessionId = foundSessionId;
546
- await saveSessionId(agentName, jsonEnvelope.session_id, options.configPath, 'claude');
547
- if (currentChildRef?.pid) {
548
- void linkSessionToPid(jsonEnvelope.session_id, currentChildRef.pid, options.configPath);
549
- }
550
- }
551
- if (currentChildRef?.pid) {
552
- void updateProcessStatus(currentChildRef.pid, code === 0 ? 'done' : 'failed', code ?? null, options.configPath);
553
- }
554
- return safeResolve({
555
- result: jsonEnvelope.reply ||
556
- jsonEnvelope.result ||
557
- currentStdout.trim(),
558
- sessionId: foundSessionId,
559
- rawOutput: currentStdout,
560
- model: modelUsed ?? undefined,
561
- nickname: originalModel !== modelUsed ? originalModel : undefined,
562
- });
563
- }
564
- if (code === 0) {
565
- if (currentChildRef?.pid) {
566
- void updateProcessStatus(currentChildRef.pid, 'done', code, options.configPath);
567
- }
568
- return safeResolve({
569
- result: currentStdout.trim(),
570
- sessionId: currentSessionId,
571
- rawOutput: currentStdout,
572
- model: modelUsed ?? undefined,
573
- nickname: originalModel !== modelUsed ? originalModel : undefined,
574
- });
575
- }
576
- if (currentChildRef?.pid) {
577
- void updateProcessStatus(currentChildRef.pid, 'failed', code, options.configPath);
578
- }
579
- safeResolve({
580
- result: '',
581
- error: code !== 0 ? `EXIT_CODE_${code}` : 'JSON_PARSE_ERROR',
582
- rawOutput: fullRaw,
583
- });
584
- }
585
- catch (error) {
586
- safeResolve({
587
- result: '',
588
- error: `INTERNAL_ERROR: ${error instanceof Error ? error.message : String(error)}`,
589
- rawOutput: fullRaw,
590
- });
591
- }
287
+ }
288
+ catch (_error) {
289
+ const specificError = detectError(stdout) || detectError(stderr);
290
+ resolve({
291
+ result: '',
292
+ error: specificError || 'JSON_PARSE_ERROR',
293
+ rawOutput: stdout,
592
294
  });
593
- };
594
- // ─── Démarrage initial avec le primary token ───
595
- spawnWithToken(getTokenForIndex(0));
295
+ }
296
+ });
297
+ child.on('error', (err) => {
298
+ cleanupTmpFiles();
299
+ resolve({ result: '', error: err.message, rawOutput: '' });
596
300
  });
597
- };
598
- return withSpan('claude.runAgent', runImpl, {
599
- agentName: agentName || '',
600
- model: modelUsed || '',
601
- runner: 'claude',
602
301
  });
603
302
  }
604
303
  }