orquesta-cli 0.2.4 → 0.2.6
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.
|
@@ -322,11 +322,22 @@ export class ConfigManager {
|
|
|
322
322
|
added++;
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
325
|
+
const currentExists = !!config.currentEndpoint && config.endpoints.some((e) => e.id === config.currentEndpoint);
|
|
326
|
+
if (!currentExists && config.endpoints.length > 0) {
|
|
327
|
+
const fallback = config.endpoints.find((e) => e.provider === 'batuta' || e.id === 'batuta-proxy') ||
|
|
328
|
+
config.endpoints[0];
|
|
329
|
+
config.currentEndpoint = fallback.id;
|
|
330
|
+
const enabledModel = fallback.models.find((m) => m.enabled) || fallback.models[0];
|
|
331
|
+
if (enabledModel)
|
|
332
|
+
config.currentModel = enabledModel.id;
|
|
333
|
+
}
|
|
334
|
+
else if (currentExists) {
|
|
335
|
+
const ep = config.endpoints.find((e) => e.id === config.currentEndpoint);
|
|
336
|
+
const modelExists = ep.models.some((m) => m.id === config.currentModel);
|
|
337
|
+
if (!modelExists) {
|
|
338
|
+
const enabledModel = ep.models.find((m) => m.enabled) || ep.models[0];
|
|
339
|
+
if (enabledModel)
|
|
340
|
+
config.currentModel = enabledModel.id;
|
|
330
341
|
}
|
|
331
342
|
}
|
|
332
343
|
await this.saveConfig();
|
|
@@ -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
|
}
|