Spaces:
Sleeping
Sleeping
File size: 4,107 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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | /**
* 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;
}
}
|