orquesta-cli 0.2.3 → 0.2.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.
@@ -50,6 +50,7 @@ export declare class ConfigManager {
50
50
  updated: number;
51
51
  unchanged: number;
52
52
  }>;
53
+ removeBatutaLegacyEndpoints(): Promise<number>;
53
54
  getLocalOnlyEndpoints(): EndpointConfig[];
54
55
  getOrchestrationConfig(): OrchestrationConfig;
55
56
  getRoleModel(role: OrchestrationRole): string | null;
@@ -333,6 +333,20 @@ export class ConfigManager {
333
333
  await this.updateLastSync();
334
334
  return { added, updated, unchanged };
335
335
  }
336
+ async removeBatutaLegacyEndpoints() {
337
+ const config = this.getConfig();
338
+ const before = config.endpoints.length;
339
+ config.endpoints = config.endpoints.filter((ep) => {
340
+ const looksLikeBatuta = ep.provider === 'batuta' ||
341
+ /^batuta(\s|\b)/i.test(ep.name || '') ||
342
+ (ep.baseUrl || '').endsWith('/api/v1') && /orquesta/i.test(ep.baseUrl || '');
343
+ return !looksLikeBatuta || ep.id === 'batuta-proxy';
344
+ });
345
+ const removed = before - config.endpoints.length;
346
+ if (removed > 0)
347
+ await this.saveConfig();
348
+ return removed;
349
+ }
336
350
  getLocalOnlyEndpoints() {
337
351
  const config = this.getConfig();
338
352
  return config.endpoints;
@@ -424,6 +424,8 @@ export class LLMClient {
424
424
  let finalResponseFailures = 0;
425
425
  const MAX_NO_TOOL_CALL_RETRIES = 3;
426
426
  const MAX_FINAL_RESPONSE_FAILURES = 3;
427
+ const recentToolSignatures = [];
428
+ const LOOP_WINDOW = 5;
427
429
  while (true) {
428
430
  if (this.isInterrupted) {
429
431
  logger.flow('Interrupt detected - stopping tool loop');
@@ -503,6 +505,17 @@ export class LLMClient {
503
505
  for (const toolCall of assistantMessage.tool_calls) {
504
506
  const toolName = toolCall.function.name;
505
507
  let toolArgs;
508
+ const sig = `${toolName}::${toolCall.function.arguments}`;
509
+ recentToolSignatures.push(sig);
510
+ if (recentToolSignatures.length > LOOP_WINDOW)
511
+ recentToolSignatures.shift();
512
+ if (recentToolSignatures.length === LOOP_WINDOW && recentToolSignatures.every(s => s === sig)) {
513
+ const preview = sig.length > 240 ? sig.slice(0, 240) + '…' : sig;
514
+ logger.error('Tool-call loop detected — aborting', new Error(`LOOP_DETECTED: ${preview}`));
515
+ throw new Error(`LOOP_DETECTED: tool '${toolName}' called ${LOOP_WINDOW} times in a row with identical arguments. ` +
516
+ `Common causes: upstream returned a non-progress message (e.g. Claude Max session cap), tool result not being threaded back into context, or a stuck plan. ` +
517
+ `Aborting to protect the session. Last signature: ${preview}`);
518
+ }
506
519
  try {
507
520
  toolArgs = JSON.parse(toolCall.function.arguments);
508
521
  }
@@ -81,6 +81,15 @@ export async function syncOrquestaConfigs() {
81
81
  }
82
82
  const data = (await response.json());
83
83
  const endpoints = data.endpoints || [];
84
+ for (let i = endpoints.length - 1; i >= 0; i--) {
85
+ const e = endpoints[i];
86
+ const isBatuta = e.provider === 'batuta' ||
87
+ e.id === 'batuta-proxy' ||
88
+ e.baseUrl === `${ORQUESTA_API}/api/v1` ||
89
+ /^batuta(\s|\b)/i.test(e.name || '');
90
+ if (isBatuta)
91
+ endpoints.splice(i, 1);
92
+ }
84
93
  const hasBatuta = endpoints.some((e) => e.provider === 'batuta' || e.id === 'batuta-proxy');
85
94
  if (!hasBatuta) {
86
95
  endpoints.push({
@@ -103,7 +112,13 @@ export async function syncOrquestaConfigs() {
103
112
  updatedAt: new Date(),
104
113
  });
105
114
  }
115
+ for (const ep of endpoints) {
116
+ if (ep.provider === 'batuta' || ep.id === 'batuta-proxy') {
117
+ ep.apiKey = orquestaConfig.token;
118
+ }
119
+ }
106
120
  logger.flow('Received endpoints from Orquesta', { count: endpoints.length });
121
+ await configManager.removeBatutaLegacyEndpoints();
107
122
  const result = await configManager.mergeOrquestaEndpoints(endpoints);
108
123
  logger.flow('Sync complete', result);
109
124
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",