AI Coding Platform
An AI-powered coding platform in the shape of Lovable: isolated Kubernetes workspaces, an agent loop driven by LLM tool calling, real-time streaming, live previews, and sub-agent orchestration for focused work.
Technical deep dive6 sections
Architecture
A prompt enters through the session API, which owns the conversation and nothing else. The work is queued in Redis rather than handled inline, because a coding turn takes minutes, not milliseconds.
A worker leases the job and runs the agent loop: send the conversation and the tool schema to the model, receive a tool call, execute it against the session’s workspace, append the result, repeat until the model stops asking. Token and tool events stream back to the browser as they happen.
The workspace is a pod in Kubernetes holding the project files, a package manager and a dev server. Its preview is exposed on a per-session URL, so what the user sees is the code actually running, not a re-render of it.
Key engineering decisions
The model never touches the filesystem directly. Every effect goes through a named tool with a typed schema — read, write, list, run — so the blast radius of a bad generation is exactly what those tools allow.
One workspace per session, isolated at the pod boundary. Generated code is untrusted code, and the isolation boundary is the one the platform already has.
Long work is queued, not awaited. The request that starts a turn returns immediately; everything after that arrives over the stream, so a browser tab is never the thing holding the work open.
Sub-agents get their own context. A focused task — find where this is configured, summarise this directory — runs as a fresh conversation and returns only its answer, so the parent’s context stays about the parent’s problem.
Failure and recovery
Jobs are leased, not consumed. A worker that dies mid-turn loses its lease and the job returns to the queue rather than disappearing with the process.
The conversation is the state, so a retry resumes from the last completed tool call instead of starting the turn again.
A model call that fails, times out, or returns arguments that do not fit the schema is handed back to the model as a tool error. Recovering from its own mistake is something the model is good at; throwing at the boundary is not.
Workspaces carry TTLs and resource limits and hold no outbound credentials, so an abandoned session expires on its own and a runaway process is bounded by the pod rather than by the cluster.
A disconnected browser does not cancel a turn. Reconnecting replays the events it missed.
Performance
Responses stream token by token, so the first useful output appears in about the time the model takes to start talking rather than the time it takes to finish.
Context is managed rather than accumulated: superseded tool output is compacted out of the conversation, which keeps both latency and cost roughly flat as a session gets long.
Workspace pods are pre-warmed, because cold-starting a container and installing dependencies is the slowest thing in a first turn.
File reads are ranged and directory listings are depth-limited, so a large repository does not turn into a large prompt.
Trade-offs
A pod per session buys strong isolation and pays for idle capacity. Pre-warming makes it faster and more expensive still.
Tool calling is slower than letting the model write a script and running it, and worth it: a typed tool surface is something you can reason about, and a shell is not.
Compaction trades fidelity for room. Anything summarised away is gone, so what gets compacted is a product decision, not a technical one.
Streaming makes the interface feel immediate and makes every failure partial. The client has to be able to render a turn that stopped halfway.
Implementation notes
React and TypeScript on the front end; the session API runs on Bun with Express-compatible routing; Redis carries both the queue and the event streams.
Workspace lifecycle is driven through the Kubernetes API — create, expose, expire — rather than through a bespoke scheduler.
The agent loop is a plain state machine over the message list, which keeps it testable without a model in the loop.