AUDIT / src /lib /agentLoop /executionPlanBlock.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 3)
95eb75a verified
Raw
History Blame Contribute Delete
4.11 kB
/**
* executionPlanBlock.ts — S495: extracted from agentLoop.ts
*
* Wrapper per il blocco Execution Plan (S414/S421).
* "Pensa prima di ogni azione" a livello macro — genera un piano strutturato
* (auth → db → api → ui con checkpoint) PRIMA del primo LLM call.
*
* Trigger: _isCodeTask=true + ≥2 requisiti strutturati rilevati.
* Output: { plan, planShort, planFull, epOrder } o null.
* Mai blocca: tutto wrapped in try/catch (best-effort).
*
* @module executionPlanBlock
*/
import type { AgentStep } from "../storage";
import type { ExecutionPlan, ExecutionStep } from "../executionPlanner"; // S554: tipi espliciti
export interface ExecutionPlanResult {
plan: ExecutionPlan; // S554: was any — ExecutionPlan è esportato da executionPlanner
planShort: string;
planFull: string;
epOrder: string[];
}
/**
* Tenta di generare un piano strutturato per il task di codice.
* Restituisce null se il task è troppo semplice (< 2 requisiti) o in caso di errore.
*
* @param task — testo del task/messaggio utente
* @param opts — opzioni: onStatus, onSteps, steps (per aggiungere __plan__ step)
* @returns — ExecutionPlanResult o null
*/
export async function runExecutionPlanIfNeeded(
task: string,
opts?: {
onStatus?: (msg: string) => void;
onSteps?: (steps: AgentStep[]) => void;
steps?: AgentStep[];
},
): Promise<ExecutionPlanResult | null> {
try {
const { decomposeGoal } = await import("../requirementDecomposer");
const { planArchitecture } = await import("../architecturePlanner");
const { buildExecutionPlan, formatExecutionPlan,
formatExecutionPlanCompact } = await import("../executionPlanner");
const _epSpec = decomposeGoal(task);
if (_epSpec.requirements.length < 2) return null;
// GAP-3: recupera i file rilevanti dal VFS PRIMA della pianificazione architetturale.
// Permette a planArchitecture di rilevare lo stack tecnologico già in uso e pianificare
// in modo coerente con il codebase esistente (es. non suggerire di aggiungere React
// se .tsx già presenti, non suggerire un nuovo backend se .py già ci sono).
// topK=8 / minScore=0.06: rete larga — vogliamo vedere l'architettura generale, non solo i file
// più rilevanti per la query specifica.
let _existingFiles: Array<{ path: string; lang: string }> = [];
try {
const { getRelevantFiles } = await import("../context/repoMap");
const _rf = await getRelevantFiles(task, 8, 0.06);
_existingFiles = _rf.map(f => ({ path: f.path, lang: f.lang }));
} catch { /* non-blocking — planner funziona anche senza contesto file */ }
const _epArch = planArchitecture(task, _epSpec.requirements, _existingFiles);
const _epPlan = buildExecutionPlan(task, _epSpec, _epArch);
if (_epPlan.isMinimal) return null;
const planFull = formatExecutionPlan(_epPlan);
const planShort = formatExecutionPlanCompact(_epPlan);
// Step visibile in UI: piano compatto con ordine moduli
if (opts?.steps && opts.onSteps) {
opts.steps.push({
tool: "__plan__",
args: {},
result: planShort,
status: "done",
at: Date.now(),
} as AgentStep);
opts.onSteps([...opts.steps]);
}
opts?.onStatus?.(`Ho un piano, parto con ${_epPlan.steps[0]?.name ?? "l'analisi"}…`);
// Fix 7 (S421): sincronizza ordine con GraphOrchestrator
// S554: usa filesToCreate — il campo corretto di ExecutionStep (bug fix)
const epOrder: string[] = _epPlan.steps.flatMap((s: ExecutionStep) => s.filesToCreate ?? []);
if (epOrder.length > 0) {
try {
const { setEpPlanOrder } = await import("../agent/GraphOrchestrator");
setEpPlanOrder(epOrder);
} catch { /* non-blocking */ }
}
return { plan: _epPlan, planShort, planFull, epOrder };
} catch {
// Non-blocking — piano non disponibile, loop prosegue senza
return null;
}
}