"""smolbuilder — a Lovable/Replit-style web-app builder on a tiny local model. Where `Router` (engine/router.py) answers one coding *task* per call with a fresh workspace, `WebBuilder` is a **stateful session**: you describe a web app, the agent builds a self-contained `index.html`, and then you keep talking to it ("make it dark mode", "add a reset button") and it edits the *same* workspace. First build uses the router's escalation idea — start small, and if the tiny model can't produce a usable app, retry on the next-bigger model — but once a tier succeeds we **lock onto that agent and its workspace** so every later turn is a cheap incremental edit rather than a from-scratch rebuild. The build is verified by rendering: did the agent leave a non-trivial HTML entrypoint behind? Static apps have no `run_python` signal, so "it produced an app you can preview" is the success criterion the UI also relies on. """ from __future__ import annotations from collections.abc import AsyncIterator from dataclasses import dataclass, field from .agent import SmallCodeAgent, Step from .config import Preset, Tier, load_preset from .live_run import LiveFrame from .preview import find_entry, inline_app, preview_iframe from .router import classify_tier from .sandbox import Workspace from .tools import build_web_registry from .trace_collector import TraceEvent from .ui_trace import merge_step_metadata from . import browsercheck BUILD_SYSTEM_PROMPT = """You are smolbuilder, a web app builder running on a small local model. You build small, self-contained web apps that run directly in a browser — like a tiny Lovable or Replit. Your workspace tools: - write_file(path, content): create or overwrite a file. - read_file(path): read a file back. - list_files(): see what already exists. - check_app(): run the current app in a headless browser — load index.html, execute its JavaScript, click every button — and report any errors. Hard rules: 1. The app's entrypoint is ALWAYS a single file named index.html, and it must start with and include
and . 2. Put the CSS in a