Every generation of LLM tooling I’ve built on has made the same promise: you write against our nice interface, and we’ll handle the providers underneath. I believed it every time, and every time it was true, right up until the models changed what they were.
I’ve climbed the whole ladder. In the beginning it was raw OpenAI and Anthropic calls wrapped in a Next.js app, string-concatenating prompts like everyone else. Then I got tired of rewriting call sites every time a better model shipped, so I built my own provider-agnostic layer: a small universal interface for text generation, system-prompt templating, placeholders into arguments. Nothing clever, but it meant switching models was one line. Then the Vercel AI SDK arrived, and I adopted it early and genuinely loved it. It solved the exact problems I’d been solving by hand, with one interface for every provider, structured outputs, and streaming, and for a couple of years it was the foundation under everything I shipped.
So this isn’t a takedown of any SDK. Each rung was the right tool for its moment. The point is what all the rungs had in common.
The coat
Underneath every unified interface, mine included, was the same thing: the raw completion APIs I’d been calling in year one, wearing a fancier coat. The abstraction improved ergonomics, but it didn’t change what the model was to your application: a stateless function you POST text at. Your app held all the state, owned all the execution, and rented intelligence by the token.
That was fine when models were text-generation machines. It stopped being fine when they became agentic, when the valuable thing was no longer the paragraph the model returned but the work it could do, given tools and an environment. An agent isn’t a function you call. It’s a process that lives somewhere: it holds a session open, runs commands, reads files, gets interrupted, resumes, talks to other agents. The completion-API worldview has no place to put any of that, no matter how nice the wrapper is.
I felt this collision personally before I could articulate it. I was building goldengoose, a desktop workspace for teams of coding agents, solving durability and process execution and agent-to-agent messaging at the systems level. At the same time I was shipping AI web products on the AI SDK, coaxing agentic behavior such as orchestration, tool calls, and structured outputs out of serverless functions. The day one of those products needed a real sandbox, somewhere to actually run code, a switch flipped in my head. A serverless function is a place to call a model. It is not a place where an agent can live.
The harness
While I was digesting that, the ground shifted in a way I think is still underappreciated: the model providers started shipping their harnesses instead of just their APIs.
The Claude Agent SDK is the same harness Claude Code runs in, the environment Claude models are actually trained against, subscription auth included. OpenAI open-sourced the Codex app server, in Rust: the very surface Codex models see during reinforcement learning. This is a categorically different foundation from a completion endpoint. A model inside its own harness already knows how to run commands, edit files, manage a turn, and ask for approval because it was trained doing exactly that. Every wrapper that pulls the model out of its harness and re-serves it through a generic API is throwing that training away and rebuilding a worse version by hand.
And with ACP as the open protocol that agents like OpenCode, Gemini, and Grok speak, you can have both properties at once for the first time: provider-agnostic, and built on the surfaces the models were trained on. The old trade was flexibility or fidelity: pick one. That trade has expired.
The missing layer
Harnesses give you a well-trained agent on a machine. They don’t give you what production needs around it: durable sessions that survive a crash, event streams a client can replay after a disconnect, background processes with real lifecycles, git worktrees with ownership semantics, delivery guarantees for agents messaging each other, recovery that tells the truth at boot. That’s not an SDK’s job. That’s a runtime’s job, the same category of software as a database or a message broker, and it deserves to be built with the same seriousness.
That’s what Gooselake is: a headless Rust runtime that sits on the provider harnesses and owns the operational layer, including sessions, turns, events, processes, worktrees, and team messaging, behind one HTTP+SSE contract. The frontend goes back to being a frontend. The best model changing next month becomes a config edit. And because what the runtime really provides is agents living on a real machine, with files, a terminal, processes, and colleagues, the products you can put on top aren’t limited to coding tools. A legal workbench, an analytics app, a cloud version of goldengoose: same runtime, different pane of glass.
It seems obvious in retrospect, which I’ve come to think is the mark of infrastructure that needed to exist. The completion API era gave us wrappers. The harness era needs runtimes. I’d rather build the runtime once, properly, than keep discovering it by accident inside every product I ship.