Spaces:
Running
Running
sync: 166 file da Baida98/AI@2d40c46b (2026-08-21 15:53 UTC) [deploy-all] (#49)
Browse files- sync: 166 file da Baida98/AI@2d40c46b (2026-08-21 15:53 UTC) [deploy-all] (521e6701c7f153c466db18c8f8e61faf14345874)
- benchmark-extended.mjs +257 -52
benchmark-extended.mjs
CHANGED
|
@@ -80,7 +80,8 @@ const G="\x1b[32m",R="\x1b[31m",Y="\x1b[33m",B="\x1b[34m",
|
|
| 80 |
const _baseUrlArg = _A.find(a=>a.startsWith("--base-url="));
|
| 81 |
const BASE_URL = (_baseUrlArg ? _baseUrlArg.slice("--base-url=".length) : (process.env.BENCHMARK_BASE_URL ?? process.env.BACKEND_URL ?? "https://baida07-terminal.hf.space")).replace(/\/+$/, "");
|
| 82 |
const TASK_DIR = "/tmp/bench-ext/tasks";
|
| 83 |
-
const
|
|
|
|
| 84 |
const HF_GSM8K = 1319; // openai/gsm8k test split size
|
| 85 |
const HF_BBH = 250; // lukaemon/bbh logical_deduction size
|
| 86 |
const HF_SCIQ = 1000; // allenai/sciq test split size
|
|
@@ -259,7 +260,12 @@ FSH.security =
|
|
| 259 |
"1. **VulnerabilitΓ ** (tipo + CWE)\n" +
|
| 260 |
"2. **Codice critico**: snippet vulnerabile\n" +
|
| 261 |
"3. **Fix** (TypeScript sicuro, compilabile)\n" +
|
| 262 |
-
"4. **Prevenzione futura**: pattern / libreria"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
|
| 264 |
FSH.reasoning =
|
| 265 |
"RAGIONAMENTO STEP-BY-STEP:\n" +
|
|
@@ -314,13 +320,25 @@ FSH.autonomy =
|
|
| 314 |
// e ritenta 1 volta se la risposta Γ¨ troppo corta (cold start / timeout transitorio)
|
| 315 |
async function callAgentWithRetry(task, timeoutMs) {
|
| 316 |
const hint = FSH[task.category];
|
|
|
|
|
|
|
| 317 |
// Append FSH hint so the agent knows the expected output format.
|
| 318 |
const goal = hint
|
| 319 |
? `${task.prompt}\n\n---\nπ FORMATO RISPOSTA ATTESO:\n${hint}`
|
| 320 |
: task.prompt;
|
| 321 |
-
const a = await callAgent(goal,
|
| 322 |
-
//
|
| 323 |
-
if (a.failed)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
|
| 325 |
// Reasoning retry: one hidden-contract recalculation before the final attempt.
|
| 326 |
const reasoningFailure = reasoningRetryFailure(task, a.output);
|
|
@@ -328,7 +346,7 @@ async function callAgentWithRetry(task, timeoutMs) {
|
|
| 328 |
const repairGoal = `${goal}\n\n---\nπ CONTROLLO DI CALCOLO:\nLa risposta numerica finale non Γ¨ stata accettata dal controllo deterministico (${reasoningFailure}). Ricalcola il problema indipendentemente, verifica ogni passaggio e restituisci una sola risposta finale nel formato #### N. Non assumere il risultato precedente.`;
|
| 329 |
if(!F_JSON) process.stdout.write(` β³ retry reasoning (${reasoningFailure})... `);
|
| 330 |
await new Promise(r => setTimeout(r, 1000));
|
| 331 |
-
const a2 = await callAgent(repairGoal,
|
| 332 |
if (!F_JSON) process.stdout.write("ok\\n");
|
| 333 |
const secondFailure = reasoningRetryFailure(task, a2.output);
|
| 334 |
if (!a2.failed && !secondFailure) return a2;
|
|
@@ -339,15 +357,94 @@ async function callAgentWithRetry(task, timeoutMs) {
|
|
| 339 |
if ((a.output||"").length < 40 && !(a.output||"").includes("TIMEOUT")) {
|
| 340 |
if(!F_JSON) process.stdout.write(" β³ retry (risposta vuota)... ");
|
| 341 |
await new Promise(r => setTimeout(r, 4000));
|
| 342 |
-
const a2 = await callAgent(goal,
|
| 343 |
if (!F_JSON) process.stdout.write("ok\\n");
|
| 344 |
return (a2.output||"").length > (a.output||"").length ? a2 : a;
|
| 345 |
}
|
| 346 |
-
|
| 347 |
}
|
| 348 |
|
| 349 |
-
|
| 350 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
FSH.devops =
|
| 352 |
"OUTPUT STRUTTURATO β due sezioni obbligatorie:\n" +
|
| 353 |
"1. **Configurazione** (Dockerfile/YAML/bash compilabile, nessun placeholder)\n" +
|
|
@@ -544,15 +641,83 @@ async function runCmd(cmd,args=[],cwd="/tmp",ms=20000){
|
|
| 544 |
p.on("error",ev=>res({exitCode:1,stdout:"",stderr:ev.message}));
|
| 545 |
});
|
| 546 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
function extractCode(out,langs){
|
| 548 |
-
|
| 549 |
-
//
|
| 550 |
-
const
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
}
|
| 555 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
}
|
| 557 |
|
| 558 |
// MMLU parser canonico β mantenere sincronizzato con backend/benchmarks/validators.py.
|
|
@@ -624,11 +789,14 @@ async function tscRun(dir,codeFile,code,testMjs){
|
|
| 624 |
writeFileSync(join(dir,codeFile),c);
|
| 625 |
const tsconf={compilerOptions:{target:"ES2022",module:"NodeNext",moduleResolution:"NodeNext",
|
| 626 |
strict:true,noImplicitAny:true,skipLibCheck:true,noEmit:false,outDir:"dist",
|
| 627 |
-
lib:["ES2022"]
|
| 628 |
writeFileSync(join(dir,"tsconfig.json"),JSON.stringify(tsconf,null,2));
|
| 629 |
const bR=await runCmd(TSC_BIN,["-p","tsconfig.json"],dir,15000);
|
| 630 |
-
const
|
| 631 |
-
|
|
|
|
|
|
|
|
|
|
| 632 |
writeFileSync(join(dir,"test.mjs"),testMjs);
|
| 633 |
const tR=await runCmd("node",["test.mjs"],dir,15000);
|
| 634 |
return{buildPassed:true,testsPassed:tR.exitCode===0,
|
|
@@ -691,45 +859,59 @@ async function fetchSciQ(seed){
|
|
| 691 |
}
|
| 692 |
|
| 693 |
// ββ callAgent βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 694 |
-
async function callAgent(goal,timeoutMs=90000){
|
| 695 |
-
const t0=Date.now(); let out="",engine="?",ttfa=null,toolCalls=0,done=false,failed=false,failureReason="",taskId="";
|
|
|
|
| 696 |
const internalToken=process.env.INTERNAL_TOKEN||"";
|
| 697 |
-
if(!internalToken)return{ok:false,output:"",engine,
|
| 698 |
const ctrl=new AbortController();
|
| 699 |
const timer=setTimeout(()=>ctrl.abort(),timeoutMs);
|
| 700 |
const headers={"Content-Type":"application/json","X-Internal-Token":internalToken};
|
| 701 |
try{
|
| 702 |
const created=await fetch(`${BASE_URL}/api/agent/tasks`,{
|
| 703 |
method:"POST",headers,
|
| 704 |
-
body:JSON.stringify({goal,context:[],max_steps:16,
|
| 705 |
session_id:`bext5_${Date.now()}_${Math.random().toString(36).slice(2,5)}`}),
|
| 706 |
signal:ctrl.signal});
|
| 707 |
-
if(!created.ok)return{ok:false,output:"",engine,
|
| 708 |
const createdBody=await created.json();
|
| 709 |
taskId=String(createdBody.taskId||"");
|
| 710 |
-
if(!taskId)return{ok:false,output:"",engine,
|
| 711 |
|
| 712 |
const res=await fetch(`${BASE_URL}/api/agent/tasks/${encodeURIComponent(taskId)}/stream`,{
|
| 713 |
method:"GET",headers:{"X-Internal-Token":internalToken},signal:ctrl.signal});
|
| 714 |
-
if(!res.ok)return{ok:false,output:"",engine,
|
| 715 |
-
if(!res.body)return{ok:false,output:"",engine,
|
| 716 |
const dec=new TextDecoder(); let buf="";
|
| 717 |
const rd=res.body.getReader();
|
| 718 |
let stopStream=false;
|
|
|
|
|
|
|
| 719 |
const processSSEData=(raw)=>{
|
| 720 |
if(!raw||raw==="[DONE]")return raw==="[DONE]";
|
| 721 |
try{
|
| 722 |
const ev=JSON.parse(raw),now=Date.now()-t0;
|
| 723 |
-
|
| 724 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 725 |
if(eventType==="tool_use"){toolCalls++;ttfa=ttfa??now;}
|
| 726 |
else if(eventType==="step_done"){ttfa=ttfa??now;}
|
| 727 |
else if(eventType==="text_chunk"&&!done){
|
| 728 |
-
const value=
|
| 729 |
-
if(value!==undefined&&value!==null){
|
| 730 |
const chunk=String(value),trimmed=chunk.trim();
|
|
|
|
| 731 |
if(!(trimmed.startsWith("{")&&trimmed.endsWith("}")))out+=chunk;
|
| 732 |
ttfa=ttfa??now;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 733 |
}
|
| 734 |
} else if(eventType==="task_error"){
|
| 735 |
failed=true;failureReason=String(ev.error||"task_error");done=true;return true;
|
|
@@ -739,7 +921,9 @@ async function callAgent(goal,timeoutMs=90000){
|
|
| 739 |
if(ev.success===false||result.startsWith("[LLM_UNAVAILABLE]")||result.includes("tutti i provider configurati sono falliti")){
|
| 740 |
failed=true;failureReason=result||"provider_unavailable";engine=ev.engine??engine;done=true;return true;
|
| 741 |
}
|
| 742 |
-
|
|
|
|
|
|
|
| 743 |
engine=ev.engine??engine;done=true;return true;
|
| 744 |
}
|
| 745 |
}catch{}
|
|
@@ -761,13 +945,23 @@ async function callAgent(goal,timeoutMs=90000){
|
|
| 761 |
// A proxy may close after a final unterminated data line.
|
| 762 |
if(!stopStream&&buf.trim().startsWith("data:"))processSSEData(buf.trim().slice(5).trim());
|
| 763 |
rd.cancel?.();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
}catch(e){
|
| 765 |
if(taskId){
|
| 766 |
try{await fetch(`${BASE_URL}/api/agent/tasks/${encodeURIComponent(taskId)}`,{method:"DELETE",headers:{"X-Internal-Token":internalToken}});}catch{}
|
| 767 |
}
|
| 768 |
-
|
|
|
|
|
|
|
| 769 |
}finally{clearTimeout(timer);}
|
| 770 |
-
|
|
|
|
| 771 |
}
|
| 772 |
|
| 773 |
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -893,7 +1087,7 @@ function makeBugFix(rng, ghSnippets={}){
|
|
| 893 |
];
|
| 894 |
const cfg=rng.pick(cfgs);
|
| 895 |
return{id:"BF",category:"bug_fix",label:cfg.lbl,targetMs:45000,ref:REF.bug_fix,
|
| 896 |
-
prompt:`Identifica e correggi i bug TypeScript
|
| 897 |
verify:async(o)=>{const r=cfg.chk(o);return{buildPassed:r.bp,testsPassed:r.tp,detail:`bp:${r.bp} tp:${r.tp}`};},isCoding:true};
|
| 898 |
}
|
| 899 |
|
|
@@ -919,7 +1113,7 @@ function makeRefactor(rng){
|
|
| 919 |
code:`function p<T>(d:any[],f:(x:any)=>boolean,g:(x:any)=>T,n:number):T[]{const r:T[]=[]; for(const x of d){if(f(x)){r.push(g(x));if(r.length>=n)break}}return r}\nfunction m(a:any,b:any){return{...a,...b}}\nfunction v(s:string){return s.length>0&&s.includes('@')}\nexport{p,m,v}`,
|
| 920 |
chk:(o)=>{const c=extractCode(o,["typescript","ts"]);if(!c)return{bp:false,tp:false};
|
| 921 |
const noSingle=!/\bfunction [pmvr]\b/.test(c);
|
| 922 |
-
const typed=c.
|
| 923 |
return{bp:noSingle&&typed,tp:noSingle};}},
|
| 924 |
|
| 925 |
{lbl:"God class β singola responsabilitΓ SRP",
|
|
@@ -981,11 +1175,11 @@ function makeFeature(rng){
|
|
| 981 |
];
|
| 982 |
const cfg=rng.pick(cfgs);
|
| 983 |
const prompts={
|
| 984 |
-
"Rate limiter sliding window":`Implementa
|
| 985 |
-
"CRUD Express 5 con Zod validation":`Implementa CRUD REST per ${cfg.entity} con Express 5 + TypeScript + Zod.
|
| 986 |
-
"Event system tipizzato con error isolation":`Implementa event system TypeScript per ${cfg.event}.
|
| 987 |
-
"Middleware chain Express-like con error propagation":`Implementa middleware chain Express-like TypeScript.
|
| 988 |
-
"Observable store con selector e subscription tipizzato":`Implementa observable store TypeScript generico.
|
| 989 |
};
|
| 990 |
return{id:"FT",category:"feature",label:cfg.lbl,targetMs:55000,ref:REF.feature,
|
| 991 |
prompt:prompts[cfg.lbl]??`Implementa ${cfg.lbl} in TypeScript con interfacce esplicite e gestione errori. Scrivi in \`\`\`typescript.`,
|
|
@@ -1044,8 +1238,8 @@ function makeSecurity(rng, ghAdvisory=null){
|
|
| 1044 |
];
|
| 1045 |
const cfg=rng.pick(cfgs);
|
| 1046 |
return{id:"SC",category:"security",label:cfg.lbl,targetMs:50000,ref:REF.security,
|
| 1047 |
-
prompt:`Identifica vulnerabilitΓ [SEVERITY] Titolo: desc.\n\n\`\`\`typescript\n${cfg.code}\n\`\`\`\n\nScrivi il codice corretto in \`\`\`typescript.`,
|
| 1048 |
-
verify:async(o)=>{const r=cfg.chk(o);return{buildPassed:r.bp,testsPassed:r.tp,detail:`bp:${r.bp} tp:${r.tp} sev:${
|
| 1049 |
}
|
| 1050 |
|
| 1051 |
function makePerformance(rng){
|
|
@@ -1617,13 +1811,13 @@ async function makeResearchSynthesis(rng,seed){
|
|
| 1617 |
let data,prompt;
|
| 1618 |
if(cfg.lbl.startsWith("Compare")){
|
| 1619 |
data=rng.pick(cfg.pairs);
|
| 1620 |
-
prompt=`
|
| 1621 |
} else if(cfg.lbl.startsWith("Analisi")){
|
| 1622 |
data=rng.pick(cfg.patterns);
|
| 1623 |
-
prompt=`
|
| 1624 |
} else {
|
| 1625 |
data=sciQ;
|
| 1626 |
-
prompt=`Domanda: **${sciQ.question}**
|
| 1627 |
}
|
| 1628 |
return{id:"RY",category:"research_synthesis",label:cfg.lbl.slice(0,55),
|
| 1629 |
hfSource:sciQ&&cfg.lbl.startsWith("SciQ")?sciQ.source:"local-structured",targetMs:60000,ref:REF.research_synthesis,
|
|
@@ -2121,7 +2315,18 @@ async function runOneSeed(seed,opts={}){
|
|
| 2121 |
const t0=Date.now();
|
| 2122 |
// Il target resta una metrica di punteggio; non Γ¨ un hard-stop di trasporto.
|
| 2123 |
// I fallback gratuiti possono richiedere piΓΉ tempo per il primo chunk su task coding.
|
| 2124 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2125 |
const agentMs=Date.now()-t0;
|
| 2126 |
if(agent.failed){
|
| 2127 |
const reason=String(agent.failureReason||"errore sconosciuto").slice(0,240);
|
|
@@ -2132,8 +2337,8 @@ async function runOneSeed(seed,opts={}){
|
|
| 2132 |
hfSource:task.hfSource||"local",hfOffset:task.hfOffset??null,
|
| 2133 |
score:null,ref:task.ref,buildPassed:null,testsPassed:null,
|
| 2134 |
planScore:null,executionScore:null,recoveryScore:null,autonomyScore:null,
|
| 2135 |
-
agentMs,ttfa:agent.ttfa??9999,toolCalls:agent.toolCalls??0,tokEstimate:0,
|
| 2136 |
-
engine:agent.engine,breakdown:null,detail:`non valutabile: ${reason}`,unavailable:true,failureReason:reason});
|
| 2137 |
continue;
|
| 2138 |
}
|
| 2139 |
|
|
@@ -2171,8 +2376,8 @@ async function runOneSeed(seed,opts={}){
|
|
| 2171 |
hfSource:task.hfSource||"local",hfOffset:task.hfOffset??null,
|
| 2172 |
score,ref,buildPassed:vr.buildPassed,testsPassed:vr.testsPassed,
|
| 2173 |
planScore:vr.planScore,executionScore:vr.executionScore,recoveryScore:vr.recoveryScore,
|
| 2174 |
-
agentMs,ttfa:agent.ttfa??9999,toolCalls:agent.toolCalls??0,tokEstimate:tokEst,
|
| 2175 |
-
engine:agent.engine,breakdown,detail:(vr.detail||"").slice(0,200)});
|
| 2176 |
}
|
| 2177 |
|
| 2178 |
// ββ Summary ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 80 |
const _baseUrlArg = _A.find(a=>a.startsWith("--base-url="));
|
| 81 |
const BASE_URL = (_baseUrlArg ? _baseUrlArg.slice("--base-url=".length) : (process.env.BENCHMARK_BASE_URL ?? process.env.BACKEND_URL ?? "https://baida07-terminal.hf.space")).replace(/\/+$/, "");
|
| 82 |
const TASK_DIR = "/tmp/bench-ext/tasks";
|
| 83 |
+
const LOCAL_TSC = join(process.cwd(),"node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsc");
|
| 84 |
+
const TSC_BIN = process.env.TSC_BIN || (existsSync(LOCAL_TSC)?LOCAL_TSC:"tsc");
|
| 85 |
const HF_GSM8K = 1319; // openai/gsm8k test split size
|
| 86 |
const HF_BBH = 250; // lukaemon/bbh logical_deduction size
|
| 87 |
const HF_SCIQ = 1000; // allenai/sciq test split size
|
|
|
|
| 260 |
"1. **VulnerabilitΓ ** (tipo + CWE)\n" +
|
| 261 |
"2. **Codice critico**: snippet vulnerabile\n" +
|
| 262 |
"3. **Fix** (TypeScript sicuro, compilabile)\n" +
|
| 263 |
+
"4. **Prevenzione futura**: pattern / libreria\n\n" +
|
| 264 |
+
"CHECKLIST OBBLIGATORIA NEL CODICE:\n" +
|
| 265 |
+
"- Proteggi la rotta admin con un middleware nominato `requireAuth` o `verifyToken` e restituisci 401/403 prima di leggere i dati.\n" +
|
| 266 |
+
"- Applica `rateLimit` o `limiter` a `/api/login` prima dellβhandler.\n" +
|
| 267 |
+
"- Il codice deve contenere entrambe le protezioni, non solo descriverle.\n" +
|
| 268 |
+
"Esempio di forma accettata: `app.get('/api/admin', requireAuth, handler)` e `app.post('/api/login', rateLimit, handler)`.";
|
| 269 |
|
| 270 |
FSH.reasoning =
|
| 271 |
"RAGIONAMENTO STEP-BY-STEP:\n" +
|
|
|
|
| 320 |
// e ritenta 1 volta se la risposta Γ¨ troppo corta (cold start / timeout transitorio)
|
| 321 |
async function callAgentWithRetry(task, timeoutMs) {
|
| 322 |
const hint = FSH[task.category];
|
| 323 |
+
const taskOptions = task.category === "feature" ? {maxSteps:6,earlyComplete:"typescript"} : task.category === "research_synthesis" ? {maxSteps:8} : task.category === "bug_fix" ? {maxSteps:8} : {};
|
| 324 |
+
const effectiveTimeout = (task.category === "feature" || task.category === "research_synthesis") ? Math.min(timeoutMs,150000) : timeoutMs;
|
| 325 |
// Append FSH hint so the agent knows the expected output format.
|
| 326 |
const goal = hint
|
| 327 |
? `${task.prompt}\n\n---\nπ FORMATO RISPOSTA ATTESO:\n${hint}`
|
| 328 |
: task.prompt;
|
| 329 |
+
const a = await callAgent(goal, effectiveTimeout, taskOptions);
|
| 330 |
+
// Retry una sola volta gli errori di rete transitori: non sono risposte del modello.
|
| 331 |
+
if (a.failed) {
|
| 332 |
+
const transient = /fetch failed|network|socket|ECONNRESET|HTTP 5\\d\\d|stream HTTP 5\\d\\d|NO_OUTPUT/i.test(String(a.failureReason||""));
|
| 333 |
+
if (transient) {
|
| 334 |
+
if(!F_JSON) process.stdout.write(` β³ retry network (${String(a.failureReason).slice(0,50)})... `);
|
| 335 |
+
await new Promise(r => setTimeout(r, 1500));
|
| 336 |
+
const a2 = await callAgent(goal, effectiveTimeout, taskOptions);
|
| 337 |
+
if (!F_JSON) process.stdout.write("ok\\n");
|
| 338 |
+
return a2.failed ? a : a2;
|
| 339 |
+
}
|
| 340 |
+
return a;
|
| 341 |
+
}
|
| 342 |
|
| 343 |
// Reasoning retry: one hidden-contract recalculation before the final attempt.
|
| 344 |
const reasoningFailure = reasoningRetryFailure(task, a.output);
|
|
|
|
| 346 |
const repairGoal = `${goal}\n\n---\nπ CONTROLLO DI CALCOLO:\nLa risposta numerica finale non Γ¨ stata accettata dal controllo deterministico (${reasoningFailure}). Ricalcola il problema indipendentemente, verifica ogni passaggio e restituisci una sola risposta finale nel formato #### N. Non assumere il risultato precedente.`;
|
| 347 |
if(!F_JSON) process.stdout.write(` β³ retry reasoning (${reasoningFailure})... `);
|
| 348 |
await new Promise(r => setTimeout(r, 1000));
|
| 349 |
+
const a2 = await callAgent(repairGoal, effectiveTimeout, taskOptions);
|
| 350 |
if (!F_JSON) process.stdout.write("ok\\n");
|
| 351 |
const secondFailure = reasoningRetryFailure(task, a2.output);
|
| 352 |
if (!a2.failed && !secondFailure) return a2;
|
|
|
|
| 357 |
if ((a.output||"").length < 40 && !(a.output||"").includes("TIMEOUT")) {
|
| 358 |
if(!F_JSON) process.stdout.write(" β³ retry (risposta vuota)... ");
|
| 359 |
await new Promise(r => setTimeout(r, 4000));
|
| 360 |
+
const a2 = await callAgent(goal, effectiveTimeout, taskOptions);
|
| 361 |
if (!F_JSON) process.stdout.write("ok\\n");
|
| 362 |
return (a2.output||"").length > (a.output||"").length ? a2 : a;
|
| 363 |
}
|
| 364 |
+
return a;
|
| 365 |
}
|
| 366 |
|
| 367 |
+
// Repair retry security: si attiva solo quando il validator rileva auth o rate mancanti.
|
| 368 |
+
// Non modifica i criteri di scoring: sostituisce la risposta solo se il retry passa entrambi i controlli.
|
| 369 |
+
async function repairSecurityIfNeeded(task, agent, timeoutMs) {
|
| 370 |
+
if (task.category !== "security" || agent.failed) return {agent, retried:false, reason:null};
|
| 371 |
+
let first;
|
| 372 |
+
try { first = await task.verify(agent.output || ""); } catch { return {agent, retried:false, reason:null}; }
|
| 373 |
+
const detail = String(first.detail || "");
|
| 374 |
+
const needsAuth = detail.includes("auth:false");
|
| 375 |
+
const needsRate = detail.includes("rate:false");
|
| 376 |
+
if (!needsAuth && !needsRate) return {agent, retried:false, reason:null};
|
| 377 |
+
const missing = [needsAuth && "autenticazione admin", needsRate && "rate limiting login"].filter(Boolean).join(" e ");
|
| 378 |
+
const repairGoal = `${task.prompt}\n\n---\nπ§ SECURITY REPAIR OBBLIGATORIO\nLa risposta precedente non ha superato il controllo: manca ${missing}.\nRestituisci una RISPOSTA COMPLETA sostitutiva in TypeScript, non una spiegazione e non una patch parziale.\nDeve contenere esplicitamente entrambe le forme: app.get('/api/admin', requireAuth, handler) oppure middleware equivalente con verifica token; e app.post('/api/login', rateLimit, handler) oppure limiter equivalente applicato prima dell'handler.\nMantieni 401/403 per utenti non autenticati, non esporre dati admin e includi severitΓ HIGH/MEDIUM/LOW.\nIl codice deve compilare.`;
|
| 379 |
+
if (!F_JSON) process.stdout.write(` β³ repair security (${missing})... `);
|
| 380 |
+
const repaired = await callAgent(repairGoal, timeoutMs);
|
| 381 |
+
let checked = null;
|
| 382 |
+
if (!repaired.failed) {
|
| 383 |
+
try { checked = await task.verify(repaired.output || ""); } catch {}
|
| 384 |
+
}
|
| 385 |
+
if (!F_JSON) process.stdout.write(`${checked?.testsPassed ? "pass" : "fail"}\\n`);
|
| 386 |
+
if (!repaired.failed && checked?.testsPassed) return {agent:repaired, retried:true, reason:missing};
|
| 387 |
+
return {agent, retried:true, reason:missing};
|
| 388 |
+
}
|
| 389 |
+
async function repairCodeCorrectIfNeeded(task, agent, timeoutMs) {
|
| 390 |
+
if (task.category !== "code_correct" || agent.failed) return {agent, retried:false, reason:null};
|
| 391 |
+
let first; try { first=await task.verify(agent.output||""); } catch { return {agent,retried:false,reason:null}; }
|
| 392 |
+
if(first.buildPassed && first.testsPassed) return {agent,retried:false,reason:null};
|
| 393 |
+
const reason=!extractCode(agent.output||"",["typescript","ts"]) ? "no TS extracted" : "build/test failed";
|
| 394 |
+
const repairGoal=`${task.prompt}\n\n---\nCODE-CORRECT REPAIR OBBLIGATORIO\nLa risposta precedente ha fallito: ${reason}. Restituisci esclusivamente un singolo blocco TypeScript delimitato da tre backtick, senza testo prima o dopo. Usa esattamente la firma richiesta, export named e codice TypeScript compilabile. Non usare placeholder, ellissi o spiegazioni.`;
|
| 395 |
+
if(!F_JSON) process.stdout.write(` β³ repair code_correct (${reason})... `);
|
| 396 |
+
const repaired=await callAgent(repairGoal,Math.min(timeoutMs,120000),{maxSteps:6,earlyComplete:"typescript",debugRaw:true});
|
| 397 |
+
const repairedOutput=normalizeAgentOutput(repaired.output||"");
|
| 398 |
+
repaired.output=repairedOutput;
|
| 399 |
+
const repairFailureReason=String(repaired.failureReason||"")||(!repairedOutput.trim()?"NO_OUTPUT":"UNVALIDATED_OUTPUT");
|
| 400 |
+
let checked=null; if(!repaired.failed&&repairedOutput.trim()){try{checked=await task.verify(repairedOutput)}catch{}}
|
| 401 |
+
const extractedLength=extractCode(repairedOutput,["typescript","ts"]).length;
|
| 402 |
+
try{writeFileSync("/tmp/code_correct_candidate_debug.json",JSON.stringify({length:repairedOutput.length,hasFence:/```(?:typescript|ts)/i.test(repairedOutput),extractedLength,preview:repairedOutput.slice(0,1200),verify:checked,repairFailed:!!repaired.failed,failureReason:repairFailureReason,fallbackEmpty:!repairedOutput.trim(),originalOutputLength:String(agent.output||"").length},null,2));}catch{}
|
| 403 |
+
if(!F_JSON) process.stdout.write(`${checked?.testsPassed?"pass":"fail"}\\n`);
|
| 404 |
+
if(!repaired.failed&&checked?.buildPassed&&checked?.testsPassed)return{agent:repaired,retried:true,reason};
|
| 405 |
+
return{agent:{...agent,repairFailureReason,repairFallbackEmpty:!repairedOutput.trim()},retried:true,reason:`${reason}:${repairFailureReason}`};
|
| 406 |
+
}
|
| 407 |
+
async function repairFeatureIfNeeded(task, agent, timeoutMs) {
|
| 408 |
+
if (task.category !== "feature" || agent.failed) return {agent, retried:false, reason:null};
|
| 409 |
+
let first; try { first=await task.verify(agent.output||""); } catch { return {agent,retried:false,reason:null}; }
|
| 410 |
+
if(first.buildPassed && first.testsPassed) return {agent,retried:false,reason:null};
|
| 411 |
+
const repairGoal=`${task.prompt}\n\n---\nFEATURE REPAIR OBBLIGATORIO\nLa risposta precedente non ha superato build/test. Restituisci solo un blocco TypeScript completo, compilabile e autosufficiente, senza markdown fuori dal blocco. Mantieni tutte le interfacce richieste, includi async/await e gestione errori dove previsto. Massimo 100 righe.`;
|
| 412 |
+
if(!F_JSON) process.stdout.write(" β³ repair feature... ");
|
| 413 |
+
const repaired=await callAgent(repairGoal,Math.min(timeoutMs,120000),{maxSteps:6,earlyComplete:"typescript"});
|
| 414 |
+
let checked=null; if(!repaired.failed){try{checked=await task.verify(repaired.output||"")}catch{}}
|
| 415 |
+
if(!F_JSON) process.stdout.write(`${checked?.testsPassed?"pass":"fail"}\\n`);
|
| 416 |
+
if(!repaired.failed&&checked?.buildPassed&&checked?.testsPassed)return{agent:repaired,retried:true,reason:"build/test"};
|
| 417 |
+
return{agent,retried:true,reason:"build/test"};
|
| 418 |
+
}
|
| 419 |
+
async function repairBugFixIfNeeded(task, agent, timeoutMs) {
|
| 420 |
+
if (task.category !== "bug_fix" || agent.failed) return {agent, retried:false, reason:null};
|
| 421 |
+
let first;
|
| 422 |
+
try { first = await task.verify(agent.output || ""); } catch { return {agent, retried:false, reason:null}; }
|
| 423 |
+
if (first.buildPassed && first.testsPassed) return {agent, retried:false, reason:null};
|
| 424 |
+
const missing = [!first.buildPassed && "compilazione/estrazione TypeScript", !first.testsPassed && "test del fix"] .filter(Boolean).join(" e ");
|
| 425 |
+
const repairGoal = `${task.prompt}\n\n---\nπ§ BUG-FIX REPAIR OBBLIGATORIO\nLa risposta precedente non ha superato: ${missing}. Restituisci una risposta completa sostitutiva in un solo blocco TypeScript, senza spiegazioni. Mantieni la struttura e correggi il bug specifico. Per race condition React includi sia una guardia mounted/cancelled/ignore o AbortController sia il cleanup/abort nel return di useEffect. Il codice deve contenere tutti i simboli necessari per superare il test.`;
|
| 426 |
+
if (!F_JSON) process.stdout.write(` β³ repair bug_fix (${missing})... `);
|
| 427 |
+
const repaired = await callAgent(repairGoal, timeoutMs, {maxSteps:8});
|
| 428 |
+
let checked = null;
|
| 429 |
+
if (!repaired.failed) { try { checked = await task.verify(repaired.output || ""); } catch {} }
|
| 430 |
+
if (!F_JSON) process.stdout.write(`${checked?.testsPassed ? "pass" : "fail"}\\n`);
|
| 431 |
+
if (!repaired.failed && checked?.buildPassed && checked?.testsPassed) return {agent:repaired, retried:true, reason:missing};
|
| 432 |
+
return {agent, retried:true, reason:missing};
|
| 433 |
+
}
|
| 434 |
+
async function repairRefactorIfNeeded(task, agent, timeoutMs) {
|
| 435 |
+
if (task.category !== "refactor" || agent.failed) return {agent, retried:false, reason:null};
|
| 436 |
+
let first;
|
| 437 |
+
try { first = await task.verify(agent.output || ""); } catch { return {agent, retried:false, reason:null}; }
|
| 438 |
+
if (first.buildPassed && first.testsPassed) return {agent, retried:false, reason:null};
|
| 439 |
+
const repairGoal = `${task.prompt}\n\n---\nREFACTOR REPAIR OBBLIGATORIO\nLa risposta precedente ha superato solo parzialmente il controllo (${first.detail || "build/test"}). Restituisci esclusivamente un singolo blocco TypeScript completo, senza spiegazioni. Mantieni esattamente il comportamento e le firme pubbliche richieste, sostituisci tutti i nomi generici a una lettera con nomi semantici, aggiungi interfacce o tipi espliciti per gli input e gli output e mantieni export named. Non usare any, placeholder o ellissi.`;
|
| 440 |
+
if (!F_JSON) process.stdout.write(` β³ repair refactor... `);
|
| 441 |
+
const repaired = await callAgent(repairGoal, Math.min(timeoutMs,120000), {maxSteps:8, earlyComplete:"typescript"});
|
| 442 |
+
let checked = null;
|
| 443 |
+
if (!repaired.failed) { try { checked = await task.verify(repaired.output || ""); } catch {} }
|
| 444 |
+
if (!F_JSON) process.stdout.write(`${checked?.testsPassed ? "pass" : "fail"}\\n`);
|
| 445 |
+
if (!repaired.failed && checked?.buildPassed && checked?.testsPassed) return {agent:repaired,retried:true,reason:"build/test"};
|
| 446 |
+
return {agent,retried:true,reason:"build/test"};
|
| 447 |
+
}
|
| 448 |
FSH.devops =
|
| 449 |
"OUTPUT STRUTTURATO β due sezioni obbligatorie:\n" +
|
| 450 |
"1. **Configurazione** (Dockerfile/YAML/bash compilabile, nessun placeholder)\n" +
|
|
|
|
| 641 |
p.on("error",ev=>res({exitCode:1,stdout:"",stderr:ev.message}));
|
| 642 |
});
|
| 643 |
}
|
| 644 |
+
function normalizeAgentOutput(value){
|
| 645 |
+
let s=String(value??"").replace(/^\uFEFF/,"");
|
| 646 |
+
for(let i=0;i<2;i++){
|
| 647 |
+
const t=s.trim();
|
| 648 |
+
if(t.startsWith("{")&&t.endsWith("}")){
|
| 649 |
+
try{const v=JSON.parse(t);const next=v.content??v.text??v.output??v.message??null;if(next!==null&&String(next)!==s){s=String(next);continue;}}catch{}
|
| 650 |
+
}
|
| 651 |
+
break;
|
| 652 |
+
}
|
| 653 |
+
if(s.includes("```")&&!s.includes("\n"))s=s.replace(/\\n/g,"\n").replace(/\\r/g,"\r");
|
| 654 |
+
return s;
|
| 655 |
+
}
|
| 656 |
+
function normalizeSSEEvent(ev){
|
| 657 |
+
const type=String(ev?.type||ev?.event||ev?.kind||"").toLowerCase();
|
| 658 |
+
const choices=Array.isArray(ev?.choices)?ev.choices:[];
|
| 659 |
+
const delta=choices[0]?.delta||choices[0]?.message||{};
|
| 660 |
+
const text=ev?.token??ev?.content??ev?.text??ev?.delta??delta.content??delta.text??"";
|
| 661 |
+
const normalizedType=type==="step_done"||type==="step_start"?"step_done":type==="task_done"||type==="task_complete"||ev?.done===true?"task_done":type.includes("error")?"task_error":type.includes("tool")?"tool_use":text!==""?"text_chunk":type;
|
| 662 |
+
return {type:normalizedType,text:String(text??""),rawType:type,provider:ev?.provider??ev?.providerName??ev?.meta?.provider??null,model:ev?.model??ev?.modelName??ev?.meta?.model??null};
|
| 663 |
+
}
|
| 664 |
+
function redactSSEPayload(ev){
|
| 665 |
+
const copy={...ev};
|
| 666 |
+
// Il contenuto dei text_chunk serve al debug e non Γ¨ una credenziale: viene
|
| 667 |
+
// conservato localmente ma limitato per dimensione. Redigiamo solo header,
|
| 668 |
+
// chiavi e campi esplicitamente sensibili.
|
| 669 |
+
for(const k of ["content","text","delta"]){if(typeof copy[k]==="string")copy[k]=copy[k].slice(0,4000)}
|
| 670 |
+
if(typeof copy.token==="string")copy.token=copy.token.slice(0,4000);
|
| 671 |
+
for(const k of ["authorization","apiKey","api_key","secret","password"]){if(k in copy)copy[k]="[REDACTED]"}
|
| 672 |
+
return copy;
|
| 673 |
+
}
|
| 674 |
function extractCode(out,langs){
|
| 675 |
+
const wanted=new Set((langs||[]).map(x=>String(x).toLowerCase()));
|
| 676 |
+
const isWanted=(lang)=>!wanted.size||wanted.has("*")||wanted.has(String(lang||"").trim().toLowerCase())||((wanted.has("ts")||wanted.has("typescript"))&&(!lang||/^(ts|typescript|tsx)$/i.test(String(lang).trim())));
|
| 677 |
+
const unwrap=(value,depth=0)=>{
|
| 678 |
+
if(depth>3)return String(value??"");
|
| 679 |
+
let source=String(value??"").replace(/^\uFEFF/,"").trim();
|
| 680 |
+
for(let i=0;i<2;i++){
|
| 681 |
+
if(!source.startsWith("{")||!source.endsWith("}"))break;
|
| 682 |
+
try{
|
| 683 |
+
const obj=JSON.parse(source);
|
| 684 |
+
const next=obj.content??obj.text??obj.output??obj.response??obj.answer??obj.code??obj.message;
|
| 685 |
+
if(next===undefined||String(next)===source)break;
|
| 686 |
+
source=String(next).trim();
|
| 687 |
+
}catch{break;}
|
| 688 |
+
}
|
| 689 |
+
if(source.includes("\\n")&&!source.includes("\n"))source=source.replace(/\\r/g,"\r").replace(/\\n/g,"\n").replace(/\\t/g,"\t");
|
| 690 |
+
return source.replace(/^<code[^>]*>/i,"").replace(/<\/code>$/i,"").trim();
|
| 691 |
+
};
|
| 692 |
+
const source=unwrap(out);
|
| 693 |
+
let best="";
|
| 694 |
+
// Markdown backticks, tilde fences, optional language and arbitrary spacing.
|
| 695 |
+
const fenceRe=/(?:^|\n)[ \t]*(```|~~~)[ \t]*([A-Za-z0-9_+#.-]*)[^\n]*\n([\s\S]*?)[ \t]*\1[ \t]*(?=\n|$)/g;
|
| 696 |
+
for(const m of source.matchAll(fenceRe)){
|
| 697 |
+
const lang=String(m[2]||"").toLowerCase();
|
| 698 |
+
const code=String(m[3]||"").trim();
|
| 699 |
+
if(isWanted(lang)&&code.length>best.length)best=code;
|
| 700 |
}
|
| 701 |
+
// Inline fence form: ```typescript code ``` or [typescript]...[/typescript].
|
| 702 |
+
if(!best){
|
| 703 |
+
const inlineRe=/(```|~~~)[ \t]*([A-Za-z0-9_+#.-]*)[ \t]+([\s\S]*?)\1/g;
|
| 704 |
+
for(const m of source.matchAll(inlineRe)){const lang=String(m[2]||"").toLowerCase(),code=String(m[3]||"").trim();if(isWanted(lang)&&code.length>best.length)best=code;}
|
| 705 |
+
}
|
| 706 |
+
if(!best){
|
| 707 |
+
const tagRe=/\[(typescript|ts)\][\s\S]*?\[\/\1\]/ig;
|
| 708 |
+
for(const m of source.matchAll(tagRe)){const code=m[0].replace(/^\[[^\]]+\]/,"").replace(/\[\/[^\]]+\]$/i,"").trim();if(code.length>best.length)best=code;}
|
| 709 |
+
}
|
| 710 |
+
if(best)return best.replace(/^```[^\n]*\n?/i,"").replace(/```\s*$/i,"").trim();
|
| 711 |
+
// Controlled raw-code fallback: require a declaration/export, balanced-ish
|
| 712 |
+
// code markers and enough length; ordinary prose is rejected.
|
| 713 |
+
const raw=source.trim();
|
| 714 |
+
const codeStart=raw.search(/(?:^|\n)\s*(?:export\s+)?(?:async\s+)?(?:function|const|let|class|interface|type)\b/m);
|
| 715 |
+
const tsLike=(langs||[]).some(x=>/^(ts|typescript|tsx|\*)$/i.test(String(x)));
|
| 716 |
+
if(tsLike&&codeStart>=0){
|
| 717 |
+
const candidate=raw.slice(codeStart).replace(/\n(?:Explanation|Spiegazione|Note|Notes|Here is|Ecco)[:\s][\\s\\S]*$/i,"").trim();
|
| 718 |
+
if(candidate.length>40&&/[{}();=]/.test(candidate)&&(!/^(?:I|The|This|Ecco|Here)\b/m.test(candidate)))return candidate;
|
| 719 |
+
}
|
| 720 |
+
return "";
|
| 721 |
}
|
| 722 |
|
| 723 |
// MMLU parser canonico β mantenere sincronizzato con backend/benchmarks/validators.py.
|
|
|
|
| 789 |
writeFileSync(join(dir,codeFile),c);
|
| 790 |
const tsconf={compilerOptions:{target:"ES2022",module:"NodeNext",moduleResolution:"NodeNext",
|
| 791 |
strict:true,noImplicitAny:true,skipLibCheck:true,noEmit:false,outDir:"dist",
|
| 792 |
+
lib:["ES2022"]},include:[codeFile]};
|
| 793 |
writeFileSync(join(dir,"tsconfig.json"),JSON.stringify(tsconf,null,2));
|
| 794 |
const bR=await runCmd(TSC_BIN,["-p","tsconfig.json"],dir,15000);
|
| 795 |
+
const diagnostics=(bR.stdout+bR.stderr).split("\n").filter(l=>l.includes("error TS")&&!l.includes("TS2869")&&!l.includes("TS2688"));
|
| 796 |
+
const hasOnlyMissingNodeTypes=bR.exitCode!==0&&/TS2688/.test(bR.stdout+bR.stderr);
|
| 797 |
+
const compiledPath=join(dir,"dist",codeFile.replace(/\.ts$/,".js"));
|
| 798 |
+
if((bR.exitCode!==0&&!hasOnlyMissingNodeTypes)||diagnostics.length)return{buildPassed:false,testsPassed:false,detail:(diagnostics.slice(0,3).join("|")||bR.stderr||"tsc failed").slice(0,200)};
|
| 799 |
+
if(!existsSync(compiledPath))return{buildPassed:false,testsPassed:false,detail:`compiled module missing: ${compiledPath}`.slice(0,200)};
|
| 800 |
writeFileSync(join(dir,"test.mjs"),testMjs);
|
| 801 |
const tR=await runCmd("node",["test.mjs"],dir,15000);
|
| 802 |
return{buildPassed:true,testsPassed:tR.exitCode===0,
|
|
|
|
| 859 |
}
|
| 860 |
|
| 861 |
// ββ callAgent βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 862 |
+
async function callAgent(goal,timeoutMs=90000,options={}){
|
| 863 |
+
const t0=Date.now(); let out="",engine="?",provider="?",model="?",ttfa=null,toolCalls=0,done=false,failed=false,failureReason="",taskId="",lastEvent="",lastEventAt=null;
|
| 864 |
+
const telemetry=()=>({provider:provider||"?",model:model||engine||"?",ttfaMs:ttfa??9999,lastEvent:lastEvent||null,lastEventAt});
|
| 865 |
const internalToken=process.env.INTERNAL_TOKEN||"";
|
| 866 |
+
if(!internalToken)return{ok:false,output:"",engine,...telemetry(),toolCalls,durationMs:0,failed:true,failureReason:"INTERNAL_TOKEN mancante"};
|
| 867 |
const ctrl=new AbortController();
|
| 868 |
const timer=setTimeout(()=>ctrl.abort(),timeoutMs);
|
| 869 |
const headers={"Content-Type":"application/json","X-Internal-Token":internalToken};
|
| 870 |
try{
|
| 871 |
const created=await fetch(`${BASE_URL}/api/agent/tasks`,{
|
| 872 |
method:"POST",headers,
|
| 873 |
+
body:JSON.stringify({goal,context:[],max_steps:options.maxSteps??16,
|
| 874 |
session_id:`bext5_${Date.now()}_${Math.random().toString(36).slice(2,5)}`}),
|
| 875 |
signal:ctrl.signal});
|
| 876 |
+
if(!created.ok)return{ok:false,output:"",engine,...telemetry(),toolCalls,durationMs:Date.now()-t0,failed:true,failureReason:`create task HTTP ${created.status}`};
|
| 877 |
const createdBody=await created.json();
|
| 878 |
taskId=String(createdBody.taskId||"");
|
| 879 |
+
if(!taskId)return{ok:false,output:"",engine,...telemetry(),toolCalls,durationMs:Date.now()-t0,failed:true,failureReason:"taskId mancante"};
|
| 880 |
|
| 881 |
const res=await fetch(`${BASE_URL}/api/agent/tasks/${encodeURIComponent(taskId)}/stream`,{
|
| 882 |
method:"GET",headers:{"X-Internal-Token":internalToken},signal:ctrl.signal});
|
| 883 |
+
if(!res.ok)return{ok:false,output:"",engine,...telemetry(),toolCalls,durationMs:Date.now()-t0,failed:true,failureReason:`stream HTTP ${res.status}`};
|
| 884 |
+
if(!res.body)return{ok:false,output:"",engine,...telemetry(),toolCalls,durationMs:Date.now()-t0,failed:true,failureReason:"stream body mancante"};
|
| 885 |
const dec=new TextDecoder(); let buf="";
|
| 886 |
const rd=res.body.getReader();
|
| 887 |
let stopStream=false;
|
| 888 |
+
let sawText=false;
|
| 889 |
+
const rawPayloadLog=[];
|
| 890 |
const processSSEData=(raw)=>{
|
| 891 |
if(!raw||raw==="[DONE]")return raw==="[DONE]";
|
| 892 |
try{
|
| 893 |
const ev=JSON.parse(raw),now=Date.now()-t0;
|
| 894 |
+
const normalized=normalizeSSEEvent(ev);
|
| 895 |
+
lastEvent=normalized.type||null; lastEventAt=now;
|
| 896 |
+
provider=String(ev.provider??ev.providerName??ev.meta?.provider??provider??"?");
|
| 897 |
+
model=String(ev.model??ev.modelName??ev.meta?.model??model??"?");
|
| 898 |
+
if(normalized.text&&ttfa===null)ttfa=now;
|
| 899 |
+
if(options.debugRaw) rawPayloadLog.push({at:now,event:normalized,raw:redactSSEPayload(ev)});
|
| 900 |
+
const eventType=normalized.type;
|
| 901 |
if(eventType==="tool_use"){toolCalls++;ttfa=ttfa??now;}
|
| 902 |
else if(eventType==="step_done"){ttfa=ttfa??now;}
|
| 903 |
else if(eventType==="text_chunk"&&!done){
|
| 904 |
+
const value=normalized.text;
|
| 905 |
+
if(value!==undefined&&value!==null&&String(value)!==""){
|
| 906 |
const chunk=String(value),trimmed=chunk.trim();
|
| 907 |
+
sawText=true;
|
| 908 |
if(!(trimmed.startsWith("{")&&trimmed.endsWith("}")))out+=chunk;
|
| 909 |
ttfa=ttfa??now;
|
| 910 |
+
// Per feature il validator lavora sul blocco TS: non attendere task_done
|
| 911 |
+
// quando il modello ha giΓ prodotto un blocco completo e sufficientemente lungo.
|
| 912 |
+
if(options.earlyComplete === "typescript" && /```(?:typescript|ts)\s*[\\s\\S]{80,}?```/i.test(out)){
|
| 913 |
+
done=true; return true;
|
| 914 |
+
}
|
| 915 |
}
|
| 916 |
} else if(eventType==="task_error"){
|
| 917 |
failed=true;failureReason=String(ev.error||"task_error");done=true;return true;
|
|
|
|
| 921 |
if(ev.success===false||result.startsWith("[LLM_UNAVAILABLE]")||result.includes("tutti i provider configurati sono falliti")){
|
| 922 |
failed=true;failureReason=result||"provider_unavailable";engine=ev.engine??engine;done=true;return true;
|
| 923 |
}
|
| 924 |
+
// Il backend puΓ² inviare un result stale o non coerente al termine dello stream.
|
| 925 |
+
// Se abbiamo ricevuto text_chunk, il buffer SSE Γ¨ la fonte autorevole.
|
| 926 |
+
if(result&&(!sawText||result.length>(out||"").length)&&!sawText)out=result;
|
| 927 |
engine=ev.engine??engine;done=true;return true;
|
| 928 |
}
|
| 929 |
}catch{}
|
|
|
|
| 945 |
// A proxy may close after a final unterminated data line.
|
| 946 |
if(!stopStream&&buf.trim().startsWith("data:"))processSSEData(buf.trim().slice(5).trim());
|
| 947 |
rd.cancel?.();
|
| 948 |
+
if(options.debugRaw){try{writeFileSync("/tmp/code_correct_retry_sse.log",JSON.stringify({timestamp:new Date().toISOString(),sawText,outputLength:out.length,events:rawPayloadLog},null,2));}catch{}}
|
| 949 |
+
if(done && !sawText && !out){
|
| 950 |
+
return{ok:false,output:"",engine,...telemetry(),toolCalls,durationMs:Date.now()-t0,failed:true,failureReason:"NO_OUTPUT"};
|
| 951 |
+
}
|
| 952 |
+
if(done && options.earlyComplete && taskId){
|
| 953 |
+
try{await fetch(`${BASE_URL}/api/agent/tasks/${encodeURIComponent(taskId)}`,{method:"DELETE",headers:{"X-Internal-Token":internalToken}});}catch{}
|
| 954 |
+
}
|
| 955 |
}catch(e){
|
| 956 |
if(taskId){
|
| 957 |
try{await fetch(`${BASE_URL}/api/agent/tasks/${encodeURIComponent(taskId)}`,{method:"DELETE",headers:{"X-Internal-Token":internalToken}});}catch{}
|
| 958 |
}
|
| 959 |
+
const partial = normalizeAgentOutput(out || "");
|
| 960 |
+
const salvageFeature = options.earlyComplete === "typescript" && partial.length >= 120 && /(?:interface|type|class|function|const)\b/.test(partial) && /(?:subscribe|getState|async|await|try|catch)/.test(partial);
|
| 961 |
+
return{ok:salvageFeature,output:partial,engine,...telemetry(),toolCalls,durationMs:Date.now()-t0,failed:!salvageFeature,failureReason:e.name==="AbortError"?(salvageFeature?"PARTIAL_TIMEOUT":"TIMEOUT"):String(e.message||e),partial:salvageFeature};
|
| 962 |
}finally{clearTimeout(timer);}
|
| 963 |
+
const finalOutput=normalizeAgentOutput(out);
|
| 964 |
+
return{ok:finalOutput.length>30&&!failed,output:finalOutput,engine,...telemetry(),toolCalls,durationMs:Date.now()-t0,failed,failureReason};
|
| 965 |
}
|
| 966 |
|
| 967 |
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 1087 |
];
|
| 1088 |
const cfg=rng.pick(cfgs);
|
| 1089 |
return{id:"BF",category:"bug_fix",label:cfg.lbl,targetMs:45000,ref:REF.bug_fix,
|
| 1090 |
+
prompt:`Identifica e correggi i bug TypeScript senza riscrivere la struttura. Restituisci un solo blocco TypeScript, senza spiegazioni. Per effetti asincroni React devi includere sia una guardia di smontaggio (mounted/cancelled/ignore/isMounted oppure AbortController) sia il cleanup restituito da useEffect (o abort()).\n\n\`\`\`typescript\n${cfg.code}\n\`\`\``,
|
| 1091 |
verify:async(o)=>{const r=cfg.chk(o);return{buildPassed:r.bp,testsPassed:r.tp,detail:`bp:${r.bp} tp:${r.tp}`};},isCoding:true};
|
| 1092 |
}
|
| 1093 |
|
|
|
|
| 1113 |
code:`function p<T>(d:any[],f:(x:any)=>boolean,g:(x:any)=>T,n:number):T[]{const r:T[]=[]; for(const x of d){if(f(x)){r.push(g(x));if(r.length>=n)break}}return r}\nfunction m(a:any,b:any){return{...a,...b}}\nfunction v(s:string){return s.length>0&&s.includes('@')}\nexport{p,m,v}`,
|
| 1114 |
chk:(o)=>{const c=extractCode(o,["typescript","ts"]);if(!c)return{bp:false,tp:false};
|
| 1115 |
const noSingle=!/\bfunction [pmvr]\b/.test(c);
|
| 1116 |
+
const typed=/\binterface\s+\w+/.test(c)||/\btype\s+\w+\s*=/.test(c)||/<T(?:\s|>|,)/.test(c)||/:\s*(?:string|number|boolean|unknown|any|\w+(?:\[\])?)/.test(c);
|
| 1117 |
return{bp:noSingle&&typed,tp:noSingle};}},
|
| 1118 |
|
| 1119 |
{lbl:"God class β singola responsabilitΓ SRP",
|
|
|
|
| 1175 |
];
|
| 1176 |
const cfg=rng.pick(cfgs);
|
| 1177 |
const prompts={
|
| 1178 |
+
"Rate limiter sliding window":`Implementa in un solo blocco TypeScript un rate limiter sliding-window: ${cfg.rate} richieste per ${cfg.win}ms. Interface: \`isAllowed(key: string): boolean\`. Massimo 100 righe, nessuna dipendenza, nessun testo fuori dal blocco.`,
|
| 1179 |
+
"CRUD Express 5 con Zod validation":`Implementa CRUD REST minimale per ${cfg.entity} con Express 5 + TypeScript + Zod. Includi GET/POST/PUT/DELETE e handler tipizzati. Un solo blocco TypeScript, massimo 160 righe, nessuna spiegazione.`,
|
| 1180 |
+
"Event system tipizzato con error isolation":`Implementa un event system TypeScript minimale per ${cfg.event}. Handler asincroni indipendenti con isolamento errori tramite try/catch o Promise.allSettled. Un solo blocco TypeScript, massimo 140 righe, nessun testo fuori dal codice.`,
|
| 1181 |
+
"Middleware chain Express-like con error propagation":`Implementa una middleware chain Express-like TypeScript minimale. Interface: \`use(fn: Middleware): void\`, \`compose(): RequestHandler\`. Propaga gli errori a ErrorMiddleware. Un solo blocco TypeScript, massimo 140 righe, nessuna spiegazione.`,
|
| 1182 |
+
"Observable store con selector e subscription tipizzato":`Implementa un observable store TypeScript generico senza dipendenze. Interface: \`getState(): S\`, \`select<T>(fn: (s:S)=>T): T\`, \`subscribe(fn: ()=>void): ()=>void\`. Un solo blocco TypeScript, massimo 120 righe, nessuna spiegazione.`,
|
| 1183 |
};
|
| 1184 |
return{id:"FT",category:"feature",label:cfg.lbl,targetMs:55000,ref:REF.feature,
|
| 1185 |
prompt:prompts[cfg.lbl]??`Implementa ${cfg.lbl} in TypeScript con interfacce esplicite e gestione errori. Scrivi in \`\`\`typescript.`,
|
|
|
|
| 1238 |
];
|
| 1239 |
const cfg=rng.pick(cfgs);
|
| 1240 |
return{id:"SC",category:"security",label:cfg.lbl,targetMs:50000,ref:REF.security,
|
| 1241 |
+
prompt:`Identifica vulnerabilitΓ [SEVERITY] Titolo: desc.\n\n\`\`\`typescript\n${cfg.code}\n\`\`\`\n\nScrivi il codice corretto in \`\`\`typescript.\n\nRequisiti obbligatori per questo scenario:\n1. Proteggi /api/admin con middleware di autenticazione/verifica token (per esempio requireAuth, authenticate, verifyToken o equivalente middleware esplicito).\n2. Applica un rate limiter a /api/login (per esempio rateLimit, limiter, rate.limit o equivalente), configurato prima dellβhandler.\n3. Mantieni una risposta 401/403 per richieste non autorizzate e non esporre dati admin senza autenticazione.\n4. Includi nel testo una severitΓ esplicita HIGH, MEDIUM o LOW e una breve motivazione.\nIl codice deve compilare e implementare entrambi i requisiti, non solo descriverli.`,
|
| 1242 |
+
verify:async(o)=>{const r=cfg.chk(o);const c=extractCode(o,["typescript","ts"])||"";const auth=/requireAuth|authenticate|middleware|verifyToken/.test(c);const rate=/rateLimit|limiter|rate.limit/.test(c);const sev=/CRITICAL|HIGH|MEDIUM|LOW/.test(o);return{buildPassed:r.bp,testsPassed:r.tp,detail:`bp:${r.bp} tp:${r.tp} auth:${auth} rate:${rate} sev:${sev}`};},isCoding:true};
|
| 1243 |
}
|
| 1244 |
|
| 1245 |
function makePerformance(rng){
|
|
|
|
| 1811 |
let data,prompt;
|
| 1812 |
if(cfg.lbl.startsWith("Compare")){
|
| 1813 |
data=rng.pick(cfg.pairs);
|
| 1814 |
+
prompt=`Confronta **${data.a}** vs **${data.b}** per **${data.ctx}**. Markdown compatto, massimo 500 parole: tabella sui criteri ${data.keys.join(", ")}; pro e contro; raccomandazione finale con condizioni. Niente introduzione generica.`;
|
| 1815 |
} else if(cfg.lbl.startsWith("Analisi")){
|
| 1816 |
data=rng.pick(cfg.patterns);
|
| 1817 |
+
prompt=`Analizza il tradeoff **${data.p}** nei microservizi. Massimo 500 parole e Markdown compatto: problema; 3 vantaggi; 2 svantaggi; quando usarlo; alternative. Usa le parole chiave ${data.keys.join(", ")}. Evita ripetizioni.`;
|
| 1818 |
} else {
|
| 1819 |
data=sciQ;
|
| 1820 |
+
prompt=`Domanda: **${sciQ.question}**. Massimo 350 parole: risposta diretta, meccanismo e perchΓ© le alternative sono errate. Usa solo questo contesto: "${sciQ.support.slice(0,220)}"`;
|
| 1821 |
}
|
| 1822 |
return{id:"RY",category:"research_synthesis",label:cfg.lbl.slice(0,55),
|
| 1823 |
hfSource:sciQ&&cfg.lbl.startsWith("SciQ")?sciQ.source:"local-structured",targetMs:60000,ref:REF.research_synthesis,
|
|
|
|
| 2315 |
const t0=Date.now();
|
| 2316 |
// Il target resta una metrica di punteggio; non Γ¨ un hard-stop di trasporto.
|
| 2317 |
// I fallback gratuiti possono richiedere piΓΉ tempo per il primo chunk su task coding.
|
| 2318 |
+
const transportTimeout = (task.category === "feature" || task.category === "research_synthesis") ? 150000 : Math.max(task.targetMs||65000,180000);
|
| 2319 |
+
let agent=await callAgentWithRetry(task,transportTimeout);
|
| 2320 |
+
const repair=await repairSecurityIfNeeded(task,agent,Math.min(transportTimeout,120000));
|
| 2321 |
+
agent=repair.agent;
|
| 2322 |
+
const featureRepair=await repairFeatureIfNeeded(task,agent,transportTimeout);
|
| 2323 |
+
agent=featureRepair.agent;
|
| 2324 |
+
const codeRepair=await repairCodeCorrectIfNeeded(task,agent,transportTimeout);
|
| 2325 |
+
agent=codeRepair.agent;
|
| 2326 |
+
const bugRepair=await repairBugFixIfNeeded(task,agent,transportTimeout);
|
| 2327 |
+
agent=bugRepair.agent;
|
| 2328 |
+
const refactorRepair=await repairRefactorIfNeeded(task,agent,transportTimeout);
|
| 2329 |
+
agent=refactorRepair.agent;
|
| 2330 |
const agentMs=Date.now()-t0;
|
| 2331 |
if(agent.failed){
|
| 2332 |
const reason=String(agent.failureReason||"errore sconosciuto").slice(0,240);
|
|
|
|
| 2337 |
hfSource:task.hfSource||"local",hfOffset:task.hfOffset??null,
|
| 2338 |
score:null,ref:task.ref,buildPassed:null,testsPassed:null,
|
| 2339 |
planScore:null,executionScore:null,recoveryScore:null,autonomyScore:null,
|
| 2340 |
+
agentMs,ttfa:agent.ttfa??9999,ttfaMs:agent.ttfaMs??agent.ttfa??9999,toolCalls:agent.toolCalls??0,tokEstimate:0,
|
| 2341 |
+
engine:agent.engine,provider:agent.provider??"?",model:agent.model??agent.engine??"?",lastEvent:agent.lastEvent??null,lastEventAt:agent.lastEventAt??null,breakdown:null,detail:`non valutabile: ${reason}`,unavailable:true,failureReason:reason});
|
| 2342 |
continue;
|
| 2343 |
}
|
| 2344 |
|
|
|
|
| 2376 |
hfSource:task.hfSource||"local",hfOffset:task.hfOffset??null,
|
| 2377 |
score,ref,buildPassed:vr.buildPassed,testsPassed:vr.testsPassed,
|
| 2378 |
planScore:vr.planScore,executionScore:vr.executionScore,recoveryScore:vr.recoveryScore,
|
| 2379 |
+
agentMs,ttfa:agent.ttfa??9999,ttfaMs:agent.ttfaMs??agent.ttfa??9999,toolCalls:agent.toolCalls??0,tokEstimate:tokEst,
|
| 2380 |
+
engine:agent.engine,provider:agent.provider??"?",model:agent.model??agent.engine??"?",lastEvent:agent.lastEvent??null,lastEventAt:agent.lastEventAt??null,breakdown,detail:(vr.detail||"").slice(0,200),failureReason:agent.failureReason||null});
|
| 2381 |
}
|
| 2382 |
|
| 2383 |
// ββ Summary ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|