orquesta-cli 0.2.9 → 0.2.11

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.
@@ -425,7 +425,11 @@ export class LLMClient {
425
425
  const MAX_NO_TOOL_CALL_RETRIES = 3;
426
426
  const MAX_FINAL_RESPONSE_FAILURES = 3;
427
427
  const recentToolSignatures = [];
428
+ const recentNormalizedSignatures = [];
428
429
  const LOOP_WINDOW = 5;
430
+ const SEMANTIC_WINDOW = 8;
431
+ const SEMANTIC_DISTINCT = 2;
432
+ const normalizeSig = (s) => s.toLowerCase().replace(/['"`]/g, '').replace(/\d+/g, 'N').replace(/\s+/g, ' ').trim();
429
433
  while (true) {
430
434
  if (this.isInterrupted) {
431
435
  logger.flow('Interrupt detected - stopping tool loop');
@@ -506,15 +510,28 @@ export class LLMClient {
506
510
  const toolName = toolCall.function.name;
507
511
  let toolArgs;
508
512
  const sig = `${toolName}::${toolCall.function.arguments}`;
513
+ const normSig = normalizeSig(sig);
509
514
  recentToolSignatures.push(sig);
515
+ recentNormalizedSignatures.push(normSig);
510
516
  if (recentToolSignatures.length > LOOP_WINDOW)
511
517
  recentToolSignatures.shift();
512
- if (recentToolSignatures.length === LOOP_WINDOW && recentToolSignatures.every(s => s === sig)) {
518
+ if (recentNormalizedSignatures.length > SEMANTIC_WINDOW)
519
+ recentNormalizedSignatures.shift();
520
+ const fail = (detail) => {
513
521
  const preview = sig.length > 240 ? sig.slice(0, 240) + '…' : sig;
514
522
  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. ` +
523
+ throw new Error(`LOOP_DETECTED: ${detail} ` +
516
524
  `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
525
  `Aborting to protect the session. Last signature: ${preview}`);
526
+ };
527
+ if (recentToolSignatures.length === LOOP_WINDOW && recentToolSignatures.every(s => s === sig)) {
528
+ fail(`tool '${toolName}' called ${LOOP_WINDOW} times in a row with identical arguments.`);
529
+ }
530
+ if (recentNormalizedSignatures.length === SEMANTIC_WINDOW) {
531
+ const distinct = new Set(recentNormalizedSignatures).size;
532
+ if (distinct <= SEMANTIC_DISTINCT) {
533
+ fail(`the last ${SEMANTIC_WINDOW} tool calls cycled only ${distinct} distinct command(s) without progress (e.g. re-running the same checks).`);
534
+ }
518
535
  }
519
536
  try {
520
537
  toolArgs = JSON.parse(toolCall.function.arguments);
@@ -1158,14 +1158,27 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
1158
1158
  }
1159
1159
  case 'tool_result':
1160
1160
  return null;
1161
- case 'shell_result':
1162
- return (React.createElement(Box, { key: entry.id, marginLeft: 2 },
1163
- React.createElement(Text, { color: "gray" }, "\u23BF "),
1164
- React.createElement(Text, { color: entry.success ? 'white' : 'red' }, entry.content)));
1161
+ case 'shell_result': {
1162
+ const SHELL_MAX_LINES = 8;
1163
+ const allLines = (entry.content || '').split('\n');
1164
+ const shown = allLines.slice(0, SHELL_MAX_LINES).join('\n');
1165
+ const extra = allLines.length - SHELL_MAX_LINES;
1166
+ return (React.createElement(Box, { key: entry.id, flexDirection: "column", marginLeft: 2 },
1167
+ React.createElement(Box, null,
1168
+ React.createElement(Text, { color: "gray" }, "\u23BF "),
1169
+ React.createElement(Text, { color: entry.success ? 'white' : 'red', dimColor: true }, shown)),
1170
+ extra > 0 && (React.createElement(Text, { color: "gray", dimColor: true },
1171
+ ' ',
1172
+ "\u2026 +",
1173
+ extra,
1174
+ " more line",
1175
+ extra > 1 ? 's' : ''))));
1176
+ }
1165
1177
  case 'tell_user':
1166
- return (React.createElement(Box, { key: entry.id, marginTop: 1 },
1167
- React.createElement(Text, { color: "yellow", bold: true }, "\u25CF "),
1168
- React.createElement(Text, null, entry.content)));
1178
+ return (React.createElement(Box, { key: entry.id, flexDirection: "column", marginTop: 1, marginBottom: 1 },
1179
+ React.createElement(Text, { color: "green", bold: true }, "\u25CF Response"),
1180
+ React.createElement(Box, { paddingLeft: 2 },
1181
+ React.createElement(MarkdownRenderer, { content: entry.content }))));
1169
1182
  case 'plan_created':
1170
1183
  return (React.createElement(Box, { key: entry.id, flexDirection: "column", marginTop: 1 },
1171
1184
  React.createElement(Text, { color: "magenta", bold: true },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",