Spaces:
Sleeping
Sleeping
| // S450: extracted from agentLoop.ts — circuit breaker factory (per-call state) | |
| // States: CLOSED (normal) → OPEN (tripped, skip tool) → HALF-OPEN (probe) → CLOSED | |
| interface BreakerState { | |
| consecutiveFails: number; | |
| totalFails: number; | |
| openSince: number; | |
| lastError: string; | |
| } | |
| export const CB_ALT: Record<string, string> = { | |
| web_search: "fetch_url o read_page", | |
| read_page: "fetch_url o web_search", | |
| fetch_url: "read_page o web_search", | |
| run_code: "execute_shell", | |
| execute_shell: "run_code", | |
| git_push: "git_push_multiple", | |
| git_push_multiple: "git_push", | |
| // S656: CB_ALT mancava di entries per tool aggiunti in S648-S655. | |
| // Senza entry, cbSkipMsg() non suggerisce alternative → LLM rimane bloccato | |
| // invece di provare un fallback. Aggiunta alternativa per ogni tool nuovo. | |
| call_api: "web_research o fetch_url", // se REST API fallisce, cerca via web | |
| send_email: "", // nessuna alternativa sicura (azione irreversibile) | |
| web_research: "web_search o fetch_url", // fallback a ricerca semplice | |
| generate_image: "", // nessuna alternativa — tool unico | |
| create_pdf: "", // nessuna alternativa — tool unico | |
| apply_patch: "write_file", // se patch fallisce, riscrivi il file | |
| // S671: CB_ALT mancava di entries per move_file/iterate_file/list_files/create_webpage/git_sync_vfs. | |
| // Senza entry, cbSkipMsg() non suggerisce alternative → LLM rimane bloccato invece di provare fallback. | |
| move_file: "write_file", // se move fallisce: scrivi nella destinazione + cancella sorgente manualmente | |
| iterate_file: "read_file", // se iterate fallisce: leggi il file e riscrivi con write_file | |
| list_files: "git_list_files", // fallback al listing da GitHub remoto | |
| git_sync_vfs: "git_list_files", // se sync fallisce: lista file remoti con git_list_files | |
| create_webpage: "write_file", // se create_webpage fallisce: scrivi HTML con write_file | |
| get_datetime: "", // nessuna alternativa — tool unico per data/ora corrente | |
| // S677: CB_ALT esteso con 21 alternative per tool ad alta frequenza d'uso. | |
| // Senza entry: quando il CB si apre, cbSkipMsg() non suggerisce fallback → LLM bloccato. | |
| // Convenzione: "" = nessuna alternativa sicura (irreversibile o tool unico). | |
| get_weather: "web_search o fetch_url (wttr.in)", // comune: fallisce per API key o CORS | |
| get_currency: "web_search 'tasso cambio XYZ EUR'", // comune: fallisce per rate limit | |
| get_news: "web_search 'site:ansa.it OR corriere.it'", // comune: fallisce per CORS | |
| get_stock: "web_search 'quotazione [TICKER] oggi'", // comune: fallisce per API | |
| search_github: "fetch_url con URL GitHub API diretta", // fallisce per rate limit | |
| search_wikipedia: "web_search 'site:it.wikipedia.org'", // fallisce per CORS | |
| search_npm: "fetch_url 'https://registry.npmjs.org/[pkg]/latest'", // fallisce per CORS | |
| run_python: "run_code", // Python → JS/WASM equivalente | |
| execute_sql: "run_python con sqlite3 in-memory", // DB → Python sqlite3 | |
| database_query: "execute_sql con SQLite in-memory", // DB remoto → locale | |
| translate: "web_search 'traduzione [testo] in [lingua]'", // common: API key | |
| recall: "remember (verifica chiave esatta)", // memoria: chiave sbagliata | |
| read_file: "git_read_file", // VFS → fallback a GitHub remoto | |
| write_file: "apply_patch", // riscrittura → patch parziale | |
| run_tests: "run_code con test inline", // test runner → codice diretto | |
| validate_json: "run_code con JSON.parse()", // validazione → codice | |
| math_eval: "run_code con espressione JS", // eval → codice | |
| analyze_image: "read_page sull'URL dell'immagine", // vision → fetch testo | |
| git_read_file: "read_file da cache VFS locale", // remoto → locale | |
| git_list_files: "list_files da VFS locale", // remoto → locale | |
| check_package: "search_npm o web_search 'site:npmjs.com'", // check → search | |
| // S684-B: CB_ALT esteso con 46 tool ADVANCED. Senza entry cbSkipMsg() non suggerisce | |
| // alternative → LLM bloccato quando questi tool aprono il circuit breaker. | |
| // Convenzione: "" = nessuna alternativa sicura (tool unico o azione irreversibile). | |
| profile_code: "run_code con console.time()/performance.now()", | |
| screenshot_url: "read_page o fetch_url", | |
| test_url: "fetch_url", | |
| check_performance: "screenshot_url o fetch_url", | |
| search_images: "web_search 'immagini [query]'", | |
| text_to_speech: "", | |
| read_pdf: "fetch_url con URL PDF diretto", | |
| create_chart: "run_code per generare SVG/Canvas", | |
| extract_table: "run_python con pandas", | |
| regex_test: "run_code con RegExp nativa", | |
| encode_decode: "run_code con Buffer/atob/btoa", | |
| format_code: "run_code con Prettier API", | |
| get_ip_info: "web_search 'IP info [ip]'", | |
| color_palette: "run_code con algoritmo HSL", | |
| write_files: "write_file (singolo file per volta)", | |
| delete_file: "move_file verso cartella .trash o rinomina con .bak", | |
| set_vercel_env: "", | |
| github_issue: "git_push_multiple con note nel AGENT_PLAN", | |
| minify_code: "run_code con terser/minify", | |
| generate_types: "run_code con TypeScript compiler API", | |
| bundle_size: "check_package o search_npm", | |
| test_links: "fetch_url per URL specifico", | |
| find_in_vfs: "read_file + run_code per grep", | |
| convert_data: "run_code con parser JSON/CSV", | |
| generate_mock_data:"run_code con algoritmo random", | |
| diff_commits: "git_read_file su entrambi i commit", | |
| accessibility_check:"read_page + analisi manuale", | |
| store_token: "", | |
| check_deploy: "fetch_url sull'URL del deploy", | |
| get_trending: "web_search 'trending tech oggi'", | |
| browser_navigate: "read_page o fetch_url", | |
| browser_open: "browser_navigate", | |
| browser_act: "browser_navigate", | |
| browser_close: "", | |
| browser_screenshot:"screenshot_url", | |
| set_project: "write_file con config progetto", | |
| record_bug: "write_file con bug report", | |
| run_background_task:"", | |
| restore_file: "git_read_file + write_file", | |
| ocr_image: "analyze_image", | |
| edit_image: "", | |
| generate_xlsx: "run_python con openpyxl", | |
| generate_docx: "run_python con python-docx", | |
| create_zip: "run_python con zipfile", | |
| remove_background: "", | |
| validate_project: "run_tests o lint_code", | |
| generate_readme: "write_file con README manuale", | |
| }; | |
| // ─── Factory (mancante dal S488 split — ripristinata da git history S488-fix) ─ | |
| const CB_TRIP = 4; // S656: innalzato da 2→4 (meno falsi positivi su rete lenta) | |
| const CB_COOL = 2; | |
| const CB_HARD = 5; | |
| export function createCircuitBreaker() { | |
| const _cb = new Map<string, BreakerState>(); | |
| function _cbGet(name: string): BreakerState { | |
| if (!_cb.has(name)) | |
| _cb.set(name, { consecutiveFails: 0, totalFails: 0, openSince: -1, lastError: "" }); | |
| return _cb.get(name)!; | |
| } | |
| function _cbIsOpen(name: string, currentIter: number): boolean { | |
| const s = _cbGet(name); | |
| if (s.openSince < 0) return false; | |
| if (s.totalFails >= CB_HARD) return true; | |
| return currentIter - s.openSince < CB_COOL; | |
| } | |
| function _cbSkipMsg(name: string): string { | |
| const s = _cbGet(name); | |
| const alt = CB_ALT[name] ? ` — Usa invece: ${CB_ALT[name]}` : ""; | |
| const perm = s.totalFails >= CB_HARD | |
| ? " [PERMANENTE per questo task]" | |
| : ` [riprova tra ${CB_COOL} iter]`; | |
| return `❌ [CIRCUIT OPEN${perm}] "${name}" ha fallito ${s.consecutiveFails}× di fila${alt}. Ultimo errore: ${s.lastError.slice(0, 120)}`; | |
| } | |
| function _cbUpdate(name: string, result: string, currentIter: number): void { | |
| const s = _cbGet(name); | |
| if (result.startsWith("❌")) { | |
| s.consecutiveFails++; | |
| s.totalFails++; | |
| s.lastError = result.slice(0, 200); | |
| // S502-BUG4-FIX: rimosso `&& s.openSince < 0` — il cooldown deve essere resettato | |
| // anche quando una probe half-open fallisce. Senza il fix, openSince rimane al valore | |
| // originale → (currentIter - oldOpenSince) > CB_COOL immediatamente → la probe viene | |
| // ripetuta ogni iterazione invece di aspettare CB_COOL iterazioni dopo il fallimento. | |
| if (s.consecutiveFails >= CB_TRIP) | |
| s.openSince = currentIter; | |
| } else { | |
| s.consecutiveFails = 0; | |
| s.openSince = -1; | |
| } | |
| } | |
| function _cbStatus(): string { | |
| const open = [..._cb.entries()] | |
| .filter(([, s]) => s.openSince >= 0) | |
| .map(([n, s]) => `${n}(${s.totalFails}✗)`); | |
| return open.length ? ` · CB-OPEN: ${open.join(", ")}` : ""; | |
| } | |
| return { _cbIsOpen, _cbSkipMsg, _cbUpdate, _cbStatus }; | |
| } | |