All model parameters can be configured in model_config.dart and all harness parameters can be configured in harness_config.dart.
All agent code lives under packages/frontend/lib/features/agent/.
| Folder | Purpose |
|---|---|
config/ | Static constants (HarnessConfig, ModelConfig) — run-loop limits, timeouts, model name, thinking budget. |
data/ | Repository interfaces (AgentChatRepository) and their implementations (GenkitAgentChatRepository), plus the conversation model abstraction. |
logic/coordination/ | Coordinators — stateful classes that serialize, deduplicate, and orchestrate async workspace operations. See below. |
logic/policy/ | Policies — mostly stateless rules and evaluation logic (validation prompts, path sandboxing, runtime observation evaluation). |
logic/run/ | Run lifecycle — the turn loop (AgentRunController), session state (AgentSession), system prompt, and bootstrap context builder. |
models/ | Immutable value types, enums, and extensions shared across layers (AgentAutomationResult, AgentToolEvent, AgentValidationResult, etc.). |
tools/ | Tool definitions: the AgentToolRegistry that builds Genkit tools, tool I/O schemas, and individual tool implementations grouped into search/ and workspace/. |
view_models/ | UI-facing view models (AgentChatViewModel). |
views/ | Flutter widgets for the agent chat panel, message bubbles, tool call rows, etc. |
The coordination/ folder contains one central coordinator and three sub-coordinators:
AgentHarnessCoordinator ← central: orchestrates validation gate sequence ├── AgentPubGetCoordinator ← sub: serializes/deduplicates `flutter pub get` ├── AgentDiagnosticsCoordinator← sub: bridges mutation generations with LSP diagnostics └── AgentPreviewCoordinator ← sub: idempotent preview start/reload/rebuild
The sub-coordinators don't know about each other. Only the AgentHarnessCoordinator knows all three and runs them in the correct order (dependency → analyzer → preview → runtime).
| Term | Meaning |
|---|---|
| Run | One user message → agent processes it (potentially multiple model turns) → result. Managed by AgentRunController. |
| Model Turn | One call to generateTurn() producing new messages. A run can have many turns (tool calls, repairs). |
| Validation Gate | A checkpoint before the agent's final response is accepted. Gates run in order: dependency resolution, analyzer, preview load, runtime observation, preview start. |
| Repair Attempt | When a validation gate fails, the harness injects a repair prompt and gives the model another chance. Bounded by maxRepairAttempts. |
| Mutation Generation | A monotonically increasing counter that tracks workspace changes (pubspec edits, Dart file writes). Used by coordinators to deduplicate and sequence work. |
| Coordinator | A stateful class that serializes async operations, deduplicates in-flight work by generation, and tracks pending/completed state. |
| Policy | A (mostly) stateless class that encapsulates decision rules — e.g. whether a diagnostic is blocking, how to format a repair prompt, whether a file path is allowed. |
The harness drives the model through a bounded validation loop. It provides tools for the model, environment observations and enforces validation. The model is free to generate tool calls and edits for up to maxModelTurnsPerRun turns. A turn is one call to generateTurn(...) that produces new messages. One turn can have tool calls, tool responses, and a final plain-text answer. When the model produces a final answer, the harness checks if code was modified. If the code was modified, the harness pipes messages from the analyzer to the model so it can validate the code. If problems are reported from the analyzer, the harness feeds a focused repair prompt to the model. To not get stuck in a loop the model is only allowed to make maxRepairAttempts repair attempts before the harness gives up and fails the run. Context grows incrementally — after each completed tool roundtrip and validation result, messages are appended directly to the session's model context. There is no separate batch-commit step. Changes to the pubspec.yaml are treated as a special case and trigger an immediate async pub get call while the agent can modify other files in the meantime. The harness waits for the pub get to complete before it sends the analyzer messages to the model for validation. After a successful validation the harness enters the next phase where it loads the code changes into the preview and observes for runtime errors.
After the analyzer passes, the harness loads pending code changes into the running preview and actively monitors for runtime errors. This happens inside validateCandidateFinal() as part of the same validation pipeline — analyzer errors, compile errors, and runtime errors all count against the same `maxRepairAttempts budget.
Every preview operation (start, rebuild, hot reload) follows the same three-phase sequence inside AgentPreviewAutomation._runPreviewOperation()
Serialization gate — waitUntilAvailable() polls isBusy at 100ms intervals for up to 10 seconds. This prevents concurrent preview operations from racing (e.g. a rebuild still running when the next validation triggers a hot reload). If the timeout expires, the operation fails with a “preview is currently busy” error.
Await the operation — await operation(preview) runs the actual start/rebuild/hot reload. This has no timeout; the harness waits as long as the compiler needs. Compile errors surface as exceptions and are caught and reported as blocking preview errors.
Runtime observation window — After the operation completes successfully, AgentPreviewRuntimeEvaluator.waitForNewBlockingObservation() checks for runtime errors. It first checks immediately (the error may already be present), then waits previewObservationWindow (currently 1s) and checks again. This gives the Flutter framework time to build and lay out the widget tree, which is when runtime errors like RenderFlex overflowed occur.