File size: 2,717 Bytes
95eb75a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
 * execReinjectBlock.ts — S496: extracted from agentLoop.ts
 *
 * S83: Exec loop closure gate — quando il tool di esecuzione dell'iterazione
 * precedente ha prodotto output (exec/run_code/shell), riinietta l'output nel
 * contesto per far analizzare al modello il risultato prima di procedere.
 *
 * Limita il numero di re-inject a MAX_EXEC_REINJECT per evitare loop infiniti
 * su task di debug che non convergono.
 *
 * Restituisce { shouldContinue, newReinjectCount }:
 *   - shouldContinue=true  → caller deve fare `continue` nel loop
 *   - shouldContinue=false → caller procede normalmente
 *
 * Muta loopMessages direttamente (by reference).
 *
 * @module execReinjectBlock
 */

import type { ApiMsg } from "./networkTools";
import { MAX_EXEC_REINJECT } from "./toolGate";
import { buildExecReinjectMessage } from "./execLoopGate"; // S501: usa modulo estratto invece di inline

export interface ExecReinjectCtx {
  lastExecResultIter: number;
  execReinjectCount:  number;
  iter:               number;
  candidateText:      string;
  lastExecTool:       string;
  lastExecOutput:     string;
  loopMessages:       ApiMsg[];
  onStatus:           (msg: string) => void;
  /** eventRuntime.emit — passato come callback per evitare import circolare */
  emitEvent:          (evt: Record<string, unknown>) => void;
}

export interface ExecReinjectResult {
  shouldContinue:  boolean;
  newReinjectCount: number;
}

export function processExecReinject(ctx: ExecReinjectCtx): ExecReinjectResult {
  const {
    lastExecResultIter, execReinjectCount, iter, candidateText,
    lastExecTool, lastExecOutput, loopMessages, onStatus, emitEvent,
  } = ctx;

  // Gate: solo se il tool di exec è stato chiamato nell'iterazione precedente
  if (
    lastExecResultIter !== iter - 1 ||
    execReinjectCount >= MAX_EXEC_REINJECT ||
    candidateText.includes("RISULTATO FINALE") ||
    candidateText.toLowerCase().includes("task completat") ||
    candidateText.toLowerCase().includes("compito completat")
  ) {
    return { shouldContinue: false, newReinjectCount: execReinjectCount };
  }

  const newCount = execReinjectCount + 1;
  onStatus(`Analisi output esecuzione... (loop ${newCount}/${MAX_EXEC_REINJECT})`);

  // S501: usa buildExecReinjectMessage da execLoopGate.ts (modulo estratto, ora wired)
  loopMessages.push({ role: "user", content: buildExecReinjectMessage(lastExecTool, lastExecOutput) } as ApiMsg);

  emitEvent({
    kind:          "exec_loop_reinject",
    taskId:        `loop-${iter}-${newCount}`,
    tool:          lastExecTool,
    reinjectCount: newCount,
    outputSnippet: lastExecOutput.slice(0, 120),
  });

  return { shouldContinue: true, newReinjectCount: newCount };
}