agent-mp 0.5.38 → 0.5.39

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.
@@ -190,7 +190,7 @@ function detectInstalledClis() {
190
190
  }
191
191
  return installed;
192
192
  }
193
- function detectModels(cliName) {
193
+ async function detectModels(cliName) {
194
194
  try {
195
195
  switch (cliName) {
196
196
  case 'claude': {
@@ -225,17 +225,61 @@ function detectModels(cliName) {
225
225
  case 'agent-impl':
226
226
  case 'agent-rev':
227
227
  case 'agent-explorer': {
228
+ const found = [];
229
+ const add = (m) => { if (m && !found.includes(m))
230
+ found.push(m); };
231
+ // For each sub-agent home dir, read each provider's key file
232
+ // and live-fetch the available models from that provider's API.
228
233
  for (const dir of [`.${cliName}`, '.agent-mp']) {
234
+ const homeDir = path.join(os.homedir(), dir);
235
+ // Qwen / OpenAI-compatible
229
236
  try {
230
- const cfg = JSON.parse(fsSync.readFileSync(path.join(os.homedir(), dir, 'config.json'), 'utf-8'));
231
- if (cfg.availableModels?.length)
232
- return cfg.availableModels;
233
- if (cfg.coordinatorModel)
234
- return [cfg.coordinatorModel];
237
+ const cfg = JSON.parse(fsSync.readFileSync(path.join(homeDir, 'api_key.json'), 'utf-8'));
238
+ if (cfg?.api_key) {
239
+ const remote = await fetchApiKeyModels(cfg);
240
+ if (remote.length)
241
+ remote.forEach(add);
242
+ else
243
+ add(cfg.model);
244
+ }
245
+ }
246
+ catch { }
247
+ // Gemini
248
+ try {
249
+ const cfg = JSON.parse(fsSync.readFileSync(path.join(homeDir, 'gemini_key.json'), 'utf-8'));
250
+ if (cfg?.api_key) {
251
+ const remote = await fetchGeminiModels(cfg);
252
+ if (remote.length)
253
+ remote.forEach(add);
254
+ else
255
+ add(cfg.model);
256
+ }
235
257
  }
236
258
  catch { }
259
+ // DeepSeek
260
+ try {
261
+ const cfg = JSON.parse(fsSync.readFileSync(path.join(homeDir, 'deepseek_key.json'), 'utf-8'));
262
+ if (cfg?.api_key) {
263
+ const remote = await fetchDeepSeekModels(cfg);
264
+ if (remote.length)
265
+ remote.forEach(add);
266
+ else
267
+ add(cfg.model);
268
+ }
269
+ }
270
+ catch { }
271
+ // Legacy fallback fields in config.json
272
+ try {
273
+ const cfg = JSON.parse(fsSync.readFileSync(path.join(homeDir, 'config.json'), 'utf-8'));
274
+ if (Array.isArray(cfg.availableModels))
275
+ cfg.availableModels.forEach(add);
276
+ add(cfg.coordinatorModel);
277
+ }
278
+ catch { }
279
+ if (found.length)
280
+ break;
237
281
  }
238
- return ['coder-model'];
282
+ return found.length ? found : ['coder-model'];
239
283
  }
240
284
  default: return ['default'];
241
285
  }
@@ -411,7 +455,7 @@ async function cmdSetup(rl) {
411
455
  log.cliList(c.name, c.path);
412
456
  console.log(chalk.bold('\n Multi-CLI Configuration'));
413
457
  const cliConfig = await loadCliConfig();
414
- const roles = ['orchestrator', 'implementor', 'reviewer'];
458
+ const roles = ['orchestrator', 'implementor', 'reviewer', 'explorer'];
415
459
  for (const role of roles) {
416
460
  console.log(chalk.yellow(`\n ${role.toUpperCase()}`));
417
461
  const cliName = await selectOption(rl, `CLI for ${role}:`, installed.map((c) => c.name));
@@ -419,22 +463,24 @@ async function cmdSetup(rl) {
419
463
  console.log(chalk.red(` Unknown CLI: ${cliName}`));
420
464
  return;
421
465
  }
422
- const models = detectModels(cliName);
466
+ const models = await detectModels(cliName);
423
467
  const model = await selectOption(rl, `Model for ${cliName}:`, models);
424
468
  cliConfig.roles[role] = { cli: cliName, model };
425
- // Ask about fallback for this role
426
- const fbAnswer = await ask(rl, ` Configure fallback for ${role}? (y/N): `);
427
- if (fbAnswer.toLowerCase() === 'y') {
428
- const otherClis = installed.filter((c) => c.name !== cliName);
429
- if (otherClis.length === 0) {
430
- console.log(chalk.dim(' No other CLIs available for fallback.'));
431
- }
432
- else {
433
- const fbCliName = await selectOption(rl, `Fallback CLI for ${role}:`, otherClis.map((c) => c.name));
434
- const fbModels = detectModels(fbCliName);
435
- const fbModel = await selectOption(rl, `Fallback model for ${fbCliName}:`, fbModels);
436
- cliConfig.roles[role].fallbackCli = fbCliName;
437
- cliConfig.roles[role].fallbackModel = fbModel;
469
+ // Ask about fallback for this role (skip explorer — single CLI is enough)
470
+ if (role !== 'explorer') {
471
+ const fbAnswer = await ask(rl, ` Configure fallback for ${role}? (y/N): `);
472
+ if (fbAnswer.toLowerCase() === 'y') {
473
+ const otherClis = installed.filter((c) => c.name !== cliName);
474
+ if (otherClis.length === 0) {
475
+ console.log(chalk.dim(' No other CLIs available for fallback.'));
476
+ }
477
+ else {
478
+ const fbCliName = await selectOption(rl, `Fallback CLI for ${role}:`, otherClis.map((c) => c.name));
479
+ const fbModels = await detectModels(fbCliName);
480
+ const fbModel = await selectOption(rl, `Fallback model for ${fbCliName}:`, fbModels);
481
+ cliConfig.roles[role].fallbackCli = fbCliName;
482
+ cliConfig.roles[role].fallbackModel = fbModel;
483
+ }
438
484
  }
439
485
  }
440
486
  }
@@ -454,7 +500,7 @@ async function cmdSetup(rl) {
454
500
  const globalFbAnswer = await ask(rl, ' Configurar fallback global? (y/N): ');
455
501
  if (globalFbAnswer.toLowerCase() === 'y') {
456
502
  const fbCliName = await selectOption(rl, 'Fallback global CLI:', installed.map((c) => c.name));
457
- const fbModels = detectModels(fbCliName);
503
+ const fbModels = await detectModels(fbCliName);
458
504
  const fbModel = await selectOption(rl, `Fallback global model for ${fbCliName}:`, fbModels);
459
505
  cliConfig.fallback_global = { cli: fbCliName, model: fbModel };
460
506
  }
@@ -483,6 +529,7 @@ async function cmdSetup(rl) {
483
529
  orchestrator: buildRoleWithFallback('orchestrator'),
484
530
  implementor: buildRoleWithFallback('implementor'),
485
531
  reviewer: buildRoleWithFallback('reviewer'),
532
+ ...(cliConfig.roles.explorer?.cli ? { explorer: { cli: cliConfig.roles.explorer.cli, model: cliConfig.roles.explorer.model, cmd: buildCmd(cliConfig.roles.explorer.cli, cliConfig.roles.explorer.model) } } : {}),
486
533
  },
487
534
  deliberation: cliConfig.deliberation.enabled ? {
488
535
  proposer: { cli: cliConfig.roles.proposer?.cli || '', model: cliConfig.roles.proposer?.model || '', cmd: buildCmd(cliConfig.roles.proposer?.cli || '', cliConfig.roles.proposer?.model || '') },
@@ -526,34 +573,36 @@ async function cmdConfigMulti(rl) {
526
573
  for (const c of installed)
527
574
  log.cliList(c.name, c.path);
528
575
  const cliConfig = await loadCliConfig();
529
- const roles = ['orchestrator', 'implementor', 'reviewer'];
576
+ const roles = ['orchestrator', 'implementor', 'reviewer', 'explorer'];
530
577
  for (const role of roles) {
531
578
  const current = cliConfig.roles[role];
532
579
  const fbInfo = current?.fallbackCli ? ` | fb: ${current.fallbackCli}/${current.fallbackModel}` : '';
533
580
  console.log(chalk.yellow(`\n ${role.toUpperCase()}${current ? ` [current: ${current.cli}/${current.model}${fbInfo}]` : ''}`));
534
581
  const cliName = await selectOption(rl, `CLI for ${role}:`, installed.map((c) => c.name));
535
- const models = detectModels(cliName);
582
+ const models = await detectModels(cliName);
536
583
  const model = await selectOption(rl, `Model for ${cliName}:`, models);
537
584
  cliConfig.roles[role] = { cli: cliName, model };
538
- // Ask about fallback for this role
539
- const fbAnswer = await ask(rl, ` Configure fallback for ${role}? (y/N): `);
540
- if (fbAnswer.toLowerCase() === 'y') {
541
- const otherClis = installed.filter((c) => c.name !== cliName);
542
- if (otherClis.length === 0) {
543
- console.log(chalk.dim(' No other CLIs available for fallback.'));
585
+ // Ask about fallback for this role (skip explorer — single CLI is enough)
586
+ if (role !== 'explorer') {
587
+ const fbAnswer = await ask(rl, ` Configure fallback for ${role}? (y/N): `);
588
+ if (fbAnswer.toLowerCase() === 'y') {
589
+ const otherClis = installed.filter((c) => c.name !== cliName);
590
+ if (otherClis.length === 0) {
591
+ console.log(chalk.dim(' No other CLIs available for fallback.'));
592
+ }
593
+ else {
594
+ const fbCliName = await selectOption(rl, `Fallback CLI for ${role}:`, otherClis.map((c) => c.name));
595
+ const fbModels = await detectModels(fbCliName);
596
+ const fbModel = await selectOption(rl, `Fallback model for ${fbCliName}:`, fbModels);
597
+ cliConfig.roles[role].fallbackCli = fbCliName;
598
+ cliConfig.roles[role].fallbackModel = fbModel;
599
+ }
544
600
  }
545
601
  else {
546
- const fbCliName = await selectOption(rl, `Fallback CLI for ${role}:`, otherClis.map((c) => c.name));
547
- const fbModels = detectModels(fbCliName);
548
- const fbModel = await selectOption(rl, `Fallback model for ${fbCliName}:`, fbModels);
549
- cliConfig.roles[role].fallbackCli = fbCliName;
550
- cliConfig.roles[role].fallbackModel = fbModel;
602
+ delete cliConfig.roles[role].fallbackCli;
603
+ delete cliConfig.roles[role].fallbackModel;
551
604
  }
552
605
  }
553
- else {
554
- delete cliConfig.roles[role].fallbackCli;
555
- delete cliConfig.roles[role].fallbackModel;
556
- }
557
606
  }
558
607
  // Global fallback
559
608
  const currentGlobalFb = cliConfig.fallback_global;
@@ -562,7 +611,7 @@ async function cmdConfigMulti(rl) {
562
611
  const globalFbAnswer = await ask(rl, ' Configurar fallback global? (y/N): ');
563
612
  if (globalFbAnswer.toLowerCase() === 'y') {
564
613
  const fbCliName = await selectOption(rl, 'Fallback global CLI:', installed.map((c) => c.name));
565
- const fbModels = detectModels(fbCliName);
614
+ const fbModels = await detectModels(fbCliName);
566
615
  const fbModel = await selectOption(rl, `Fallback global model for ${fbCliName}:`, fbModels);
567
616
  cliConfig.fallback_global = { cli: fbCliName, model: fbModel };
568
617
  }
@@ -591,6 +640,7 @@ async function cmdConfigMulti(rl) {
591
640
  orchestrator: buildRoleWithFallback('orchestrator'),
592
641
  implementor: buildRoleWithFallback('implementor'),
593
642
  reviewer: buildRoleWithFallback('reviewer'),
643
+ ...(cliConfig.roles.explorer?.cli ? { explorer: { cli: cliConfig.roles.explorer.cli, model: cliConfig.roles.explorer.model, cmd: buildCmd(cliConfig.roles.explorer.cli, cliConfig.roles.explorer.model) } } : {}),
594
644
  },
595
645
  deliberation: cliConfig.deliberation?.enabled ? {
596
646
  proposer: { cli: cliConfig.roles.proposer?.cli || '', model: cliConfig.roles.proposer?.model || '', cmd: buildCmd(cliConfig.roles.proposer?.cli || '', cliConfig.roles.proposer?.model || '') },
@@ -616,7 +666,7 @@ async function cmdConfigOneRole(rl, role) {
616
666
  const current = cliConfig.roles[role];
617
667
  console.log(chalk.yellow(`\n ${role.toUpperCase()}${current ? ` [current: ${current.cli}/${current.model}]` : ''}`));
618
668
  const cliName = await selectOption(rl, `CLI for ${role}:`, installed.map((c) => c.name));
619
- const models = detectModels(cliName);
669
+ const models = await detectModels(cliName);
620
670
  const model = await selectOption(rl, `Model for ${cliName}:`, models);
621
671
  cliConfig.roles[role] = { cli: cliName, model };
622
672
  await saveCliConfig(cliConfig);
@@ -761,15 +811,15 @@ async function cmdModels(roleArg, fi, rl) {
761
811
  console.log(chalk.dim(' Fetching Qwen models...'));
762
812
  models = await fetchQwenModels();
763
813
  if (!models.length)
764
- models = detectModels('qwen');
814
+ models = await detectModels('qwen');
765
815
  }
766
816
  else if (apiKeyCfg?.api_key) {
767
817
  models = await fetchApiKeyModels(apiKeyCfg);
768
818
  if (!models.length)
769
- models = detectModels(activeProvider);
819
+ models = await detectModels(activeProvider);
770
820
  }
771
821
  else {
772
- models = activeProvider ? detectModels(activeProvider) : [];
822
+ models = activeProvider ? await detectModels(activeProvider) : [];
773
823
  }
774
824
  if (!models.length) {
775
825
  resume();
@@ -1148,6 +1198,7 @@ export async function runRepl(resumeSession) {
1148
1198
  orchestrator: `${config.roles.orchestrator.cli}/${config.roles.orchestrator.model}`,
1149
1199
  implementor: `${config.roles.implementor.cli}/${config.roles.implementor.model}`,
1150
1200
  reviewer: `${config.roles.reviewer.cli}/${config.roles.reviewer.model}`,
1201
+ ...(config.roles.explorer?.cli ? { explorer: `${config.roles.explorer.cli}/${config.roles.explorer.model}` } : {}),
1151
1202
  };
1152
1203
  }
1153
1204
  }
@@ -1205,6 +1256,7 @@ export async function runRepl(resumeSession) {
1205
1256
  orchestrator: `${config.roles.orchestrator.cli}/${config.roles.orchestrator.model}`,
1206
1257
  implementor: `${config.roles.implementor.cli}/${config.roles.implementor.model}`,
1207
1258
  reviewer: `${config.roles.reviewer.cli}/${config.roles.reviewer.model}`,
1259
+ ...(config.roles.explorer?.cli ? { explorer: `${config.roles.explorer.cli}/${config.roles.explorer.model}` } : {}),
1208
1260
  };
1209
1261
  }
1210
1262
  }
@@ -25,7 +25,7 @@ function detectInstalledClis() {
25
25
  }
26
26
  return installed;
27
27
  }
28
- function detectModels(cliName) {
28
+ async function detectModels(cliName) {
29
29
  try {
30
30
  switch (cliName) {
31
31
  case 'claude': {
@@ -63,17 +63,58 @@ function detectModels(cliName) {
63
63
  case 'agent-impl':
64
64
  case 'agent-rev':
65
65
  case 'agent-explorer': {
66
+ const { fetchApiKeyModels } = await import('../utils/qwen-auth.js');
67
+ const { fetchGeminiModels } = await import('../utils/gemini.js');
68
+ const { fetchDeepSeekModels } = await import('../utils/deepseek.js');
69
+ const found = [];
70
+ const add = (m) => { if (m && !found.includes(m))
71
+ found.push(m); };
66
72
  for (const dir of [`.${cliName}`, '.agent-mp']) {
73
+ const homeDir = path.join(os.homedir(), dir);
67
74
  try {
68
- const cfg = JSON.parse(fsSync.readFileSync(path.join(os.homedir(), dir, 'config.json'), 'utf-8'));
69
- if (cfg.availableModels?.length)
70
- return cfg.availableModels;
71
- if (cfg.coordinatorModel)
72
- return [cfg.coordinatorModel];
75
+ const cfg = JSON.parse(fsSync.readFileSync(path.join(homeDir, 'api_key.json'), 'utf-8'));
76
+ if (cfg?.api_key) {
77
+ const remote = await fetchApiKeyModels(cfg);
78
+ if (remote.length)
79
+ remote.forEach(add);
80
+ else
81
+ add(cfg.model);
82
+ }
73
83
  }
74
84
  catch { }
85
+ try {
86
+ const cfg = JSON.parse(fsSync.readFileSync(path.join(homeDir, 'gemini_key.json'), 'utf-8'));
87
+ if (cfg?.api_key) {
88
+ const remote = await fetchGeminiModels(cfg);
89
+ if (remote.length)
90
+ remote.forEach(add);
91
+ else
92
+ add(cfg.model);
93
+ }
94
+ }
95
+ catch { }
96
+ try {
97
+ const cfg = JSON.parse(fsSync.readFileSync(path.join(homeDir, 'deepseek_key.json'), 'utf-8'));
98
+ if (cfg?.api_key) {
99
+ const remote = await fetchDeepSeekModels(cfg);
100
+ if (remote.length)
101
+ remote.forEach(add);
102
+ else
103
+ add(cfg.model);
104
+ }
105
+ }
106
+ catch { }
107
+ try {
108
+ const cfg = JSON.parse(fsSync.readFileSync(path.join(homeDir, 'config.json'), 'utf-8'));
109
+ if (Array.isArray(cfg.availableModels))
110
+ cfg.availableModels.forEach(add);
111
+ add(cfg.coordinatorModel);
112
+ }
113
+ catch { }
114
+ if (found.length)
115
+ break;
75
116
  }
76
- return ['coder-model'];
117
+ return found.length ? found : ['coder-model'];
77
118
  }
78
119
  default:
79
120
  return ['default'];
@@ -126,7 +167,7 @@ async function configureOneRole(rl, role, installed, cliConfig) {
126
167
  console.log(chalk.red(` Unknown CLI: ${cliName}`));
127
168
  return false;
128
169
  }
129
- const models = detectModels(cliName);
170
+ const models = await detectModels(cliName);
130
171
  const modelOptions = numberedSelect(`Models for ${cliName}:`, models);
131
172
  const modelChoice = await ask(rl, ` Model: `);
132
173
  cliConfig.roles[role] = {
@@ -244,7 +285,7 @@ export function setupCommand(program) {
244
285
  rl.close();
245
286
  return;
246
287
  }
247
- const models = detectModels(cliName);
288
+ const models = await detectModels(cliName);
248
289
  const modelOptions = numberedSelect(`Models for ${cliName}:`, models);
249
290
  const modelChoice = await ask(rl, ` Model: `);
250
291
  const model = resolveChoice(modelChoice, modelOptions);
@@ -338,7 +379,7 @@ export function setupCommand(program) {
338
379
  continue;
339
380
  }
340
381
  const cliName = resolveChoice(cliChoice, cliOptions);
341
- const models = detectModels(cliName);
382
+ const models = await detectModels(cliName);
342
383
  const modelOptions = numberedSelect(`Models for ${cliName}:`, models);
343
384
  const modelChoice = await ask(rl, ` Model: `);
344
385
  cliConfig.roles[role] = { cli: cliName, model: resolveChoice(modelChoice, modelOptions) };
@@ -40,6 +40,7 @@ export interface WelcomePanelOpts {
40
40
  orchestrator?: string;
41
41
  implementor?: string;
42
42
  reviewer?: string;
43
+ explorer?: string;
43
44
  };
44
45
  }
45
46
  export declare function renderWelcomePanel(opts: WelcomePanelOpts): string;
package/dist/ui/theme.js CHANGED
@@ -90,7 +90,7 @@ export function renderWelcomePanel(opts) {
90
90
  "",
91
91
  ];
92
92
  if (opts.roles &&
93
- (opts.roles.orchestrator || opts.roles.implementor || opts.roles.reviewer)) {
93
+ (opts.roles.orchestrator || opts.roles.implementor || opts.roles.reviewer || opts.roles.explorer)) {
94
94
  right.push(` ${accent("Current roles")}`);
95
95
  right.push(` ${rSep}`);
96
96
  if (opts.roles.orchestrator) {
@@ -102,6 +102,9 @@ export function renderWelcomePanel(opts) {
102
102
  if (opts.roles.reviewer) {
103
103
  right.push(` ${chalk.dim("Rev ")}${chalk.rgb(0, 185, 180)(opts.roles.reviewer)}`);
104
104
  }
105
+ if (opts.roles.explorer) {
106
+ right.push(` ${chalk.dim("Expl ")}${chalk.rgb(0, 185, 180)(opts.roles.explorer)}`);
107
+ }
105
108
  right.push("");
106
109
  }
107
110
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-mp",
3
- "version": "0.5.38",
3
+ "version": "0.5.39",
4
4
  "description": "Deterministic multi-agent CLI orchestrator — plan, code, review",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",