AI Architecture
How AI is wired into Studio — the prompt-to-diff-to-apply loop, models, and approval gates.
Studio's AI is not a chat window glued to a code editor. It's a structured loop: every turn produces a strict shape of patches, each patch is classified, and each is staged or applied based on the active approval mode. This page explains the moving pieces.
The model
Studio calls Claude (Anthropic) directly via @anthropic-ai/sdk. The default model in the editor is Sonnet 4.6. Workspaces can pick Haiku 4.5 for cheap iterative work or Opus 4.7 for the rare deep reasoning task. The default lives in workspace settings; per-project overrides live in project settings.
Studio does not run a router or a model-of-models setup. One call, one model, one response.
The loop
Every chat turn follows the same five steps. Hold this sequence in your head and the rest of the system clicks into place.
Prompt construction
The server route at
app/api/studio/route.tsbuilds the request from a fixed system prompt (around 340 lines of Liquid conventions, tool definitions, and Insites context) plus the live conversation history, the user's new message, and project state — current instance, connection status, selected element, list of dirty files. A repo snapshot is fetched lazily so the chat shows "Reading the repo…" while Studio inlines just enough source for the model to work.Streamed response
The model emits a stream of events. Studio defines two tools —
write_file(full-file create/replace) andedit_file(targeted find-and-replace) — and the response is a mix of plain text and tool calls. Server-Sent Events flow back to the browser as{ type: "status" | "text" | "patch" | "error" | "stop" }. Both tool inputs are validated against Zod schemas as they arrive; malformed args become atool_errorevent and the patch is skipped, but the stream continues.Patch classification
Each patch arriving in the browser is classified before it can do anything.
write_fileis additive if the path doesn't exist yet (it's a creation), non-additive if it overwrites a file.edit_fileis additive if the replacement is at least as long as the search string. Edits whose search string appears more than once in the file are rejected outright — Studio refuses to guess which occurrence you meant. A duplication scan attaches a warning if the patch repeats content that already exists nearby.Stage or apply
Approval mode decides what happens next. In Confirm mode (the default) every patch lands in a Pending Patches card and waits for your Accept or Reject. In Auto mode, additive patches apply immediately and the rest stage. In Dangerous mode, every patch applies on arrival. When a patch applies, the in-browser file contents update — the original is preserved separately so reject is non-destructive and you can always see the diff.
Deploy
Accepted patches modify in-browser files. They reach your project only when you hit Deploy, which commits the changed files to your connected GitHub repo, then triggers a CloudShell deploy to the target instance. Both legs stream their progress back via SSE and surface in the deploy panel.
Why diffs, not direct writes
The AI never writes to your filesystem in one motion. Each tool call produces a discrete patch — a before / after pair plus a kind tag — and the diff is computed in the browser using a Hunt–McIlroy LCS pass. That gives you four properties for free:
- Reviewability. Every change is line-level inspectable before it lands.
- Reversibility. Reject is a no-op on the file. Even Accept doesn't touch your repo until Deploy.
- Composition. Multiple turns can edit the same file; each layer composes on the simulated content from the previous turn so the AI sees a coherent state.
- Isolation. A bad patch doesn't poison adjacent ones — the stream skips and keeps going.
Streaming, not blocking
The whole pipeline is asynchronous. The chat stays responsive while the model is still thinking. You can accept or reject already-arrived patches mid-turn, abort the request entirely (the request carries an AbortController), and start typing the next message before the previous turn has finished.
What can go wrong
Three failure modes show up in the UI:
- Tool input invalid. A schema error becomes a
tool_errorevent. The patch is skipped. The text response continues. - Network or model error. The chat surfaces an error message with suggestions and offers a retry.
- Budget exhausted. The pre-call gate at
lib/usage.tsreturnsHTTP 402before any tokens are spent. The chat shows "raise your cap" and a link to settings.
Where this lives in code
- Server route, system prompt, streaming pipeline:
app/api/studio/route.ts - Patch classification and approval logic:
lib/approvalMode.ts - Diff computation:
lib/diff.ts - Pending Patches UI:
components/PendingPatchesCard.tsx - Editor and turn state:
app/studio/StudioApp.tsx