Claude Code, Codex, Cursor, and OpenCode increasingly feel like interchangeable work environments. They certainly have differences, but from the perspective of a developer’s working boundary, the core capabilities are converging: let a model read and write files, execute commands, invoke tools, read error messages, then iterate based on the results. In AI Scaffolding Is Becoming a Commodity — What’s Left for Humans Is Defining the Boundaries of Judgment, we discussed how these general execution capabilities are becoming standard equipment.
When coding agent tools grow more alike, developers naturally assume that cloud platforms for running agents in production for customers should be similar too. It seems like you could just make a table comparing who has a sandbox, memory, streaming, scheduled tasks, and a tool loop, and pick a winner.
The confusion sets in when you actually prepare to ship a demo to users. When different platforms all say “memory,” one means records in a business database, another means the local state of a particular customer’s assistant, and a third records which steps of a task have been completed. When they all say “sandbox,” it might follow a background process, or it might just be a temporary working directory for a single task. Feature names look more and more alike, but when you start writing code, they are not describing the same thing at all: where data lives, who receives the next message, where a task picks up after interruption — each platform answers these questions differently.
A more detailed feature table won’t solve this. The divergence begins at the very first step of creating an agent: one platform asks you to deploy a program first, another asks you to establish who this agent is, and a third asks you to write down the steps a task will go through. Those three starting points go on to determine where state is stored, what gets recovered after an interruption, and where the application ultimately forms its dependencies.
Suppose you are building a customer support agent. A customer sends a question, the agent looks up information, runs tools, waits for human approval on sensitive operations, and continues hours later. No matter which product page you look at, you’ll find state persistence, real-time output, scheduled tasks, and sandboxes. The differences only emerge when you start implementing.
The first kind of platform asks you to deploy a Service or Worker first. How the program runs is the platform’s responsibility; who this customer is and where the task stands are tracked by your application and database.
The second kind of platform asks you to first give this customer’s agent a fixed ID. When the customer sends another message later, the platform is responsible for routing the message back to wherever that same agent is managed. Local state and connections travel with that ID.
The third kind of platform asks you to first write out the work as a sequence of steps: look up information, call a tool, wait for approval, generate a reply. The platform records which step this task has reached and resumes when conditions are met.
So the market has produced three representative approaches: run the program first, establish who the agent is first, or define how the task progresses first. Koyeb, Cloudflare, and Vercel each clearly demonstrate one of these starting points.
Different first steps also mean different ways of recovering state afterward. When the current process ends, some platforms can only restart the compute — business state has to be queried from an external database by the application. Others can recover an object’s local state based on its Agent ID. Still others recover execution progress by a task run number, and recover sandbox files by yet another ID. This is where the three paths truly diverge.
Put the customer support program on Koyeb, and what the platform guarantees first is that this program keeps running. After a code update or instance failure, the old instance may be replaced. The new instance will start, but it won’t bring back the previous process, memory, or network connections. Nor will it know how far a particular support ticket had progressed.
So the application needs to use its own ticket ID or customer ID to read business state back from an external database like Postgres, then continue processing. This is the division of labor on the Koyeb path: the platform recovers the compute, and the application recovers the specific agent’s work progress.
On Koyeb, the customer support program runs as a Service. Programs that handle web requests and Workers dedicated to background tasks are both managed by the platform — networking, health checks, instance replacement, and scaling. The Koyeb Services documentation describes this mode of operation. Koyeb doesn’t need to understand what a “ticket” or “customer assistant” is; it just needs to keep the program running.
This approach preserves the development habits of traditional SaaS. Global queries, cross-customer statistics, and data repair still revolve around the central database; specific Python dependencies or native binaries can be bundled into the container together. Containerized business code is generally reusable in other environments, though migration still requires reworking deployment configurations, networking, and supporting managed services.
Put the same customer support agent on Cloudflare, and what the platform remembers first is: this message belongs to which customer. When customer A sends another message, the system uses their Agent ID to find the previously saved local state, then delivers the new message to wherever that agent is managed. Even if the agent has gone dormant, the ID remains unchanged.
The Cloudflare Agents state documentation describes this approach. Each agent can have its own SQLite state. The program doesn’t need to stay online — when the next message arrives, the platform can still find the agent by the same ID. The underlying capability responsible for this is Cloudflare Durable Objects.
The ID solves the question of “where should this message go,” but it doesn’t automatically solve all message ordering problems. While the agent is waiting for a model API response, other events may still interleave; network retries can also deliver the same message twice. The Cloudflare Durable Objects best practices guide explains this boundary. Applications still need to filter duplicate requests, recognize ordering, and ensure that retrying the same action twice doesn’t double-charge or double-send.
Trouble appears when you need global queries. Each customer’s state lives separately inside their own object, so developers can’t join all the data with a single ordinary SQL query. To tally all tickets, reconcile accounts across the board, or perform batch modifications, you typically still need Postgres or object storage to aggregate the key information each agent produces.
Put the customer support task on Vercel, and what the platform remembers first is how far this ticket has progressed. The agent has looked up the information and is ready to issue a refund, but needs to wait for human approval. The program can pause without keeping a server idling. When the approval result comes back, the task resumes from the following step.
What tracks this progress is Vercel Workflow. The Vercel Workflow concepts documentation explains that the platform saves the inputs and outputs of completed steps. Regular function calls don’t save this progress on their own — what is truly resumable is a Workflow run.
If the agent also needs to run code or commands in isolation, you can use Vercel Sandbox. Sandbox can save filesystem snapshots, later restoring the code directory and intermediate files, but it won’t restore the prior memory, active processes, network connections, or function call stacks. What it saves is the working directory, not the still-running program.
As a result, a single customer support ticket will involve several
IDs at once: the ticket ID in the business database, the Workflow run
ID, and, when needed, the Sandbox ID. The platform won’t automatically
know they belong to the same thing. The temporary recommendation in vercel/workflow#2376
is for the application to save the mapping between business IDs and
Workflow IDs in an external database, to avoid accidentally launching
multiple tasks for the same business event.
The fact that files can be recovered doesn’t mean the application can
always find them. Vercel’s own eve project once tied sandbox names to
deployment versions, which caused new deployments to lose the correct
references to old working directories. vercel/eve#508
reflects how the application associates different components, not that
the underlying snapshots were lost. We previously discussed Vercel
AI Cloud’s product integration and eve’s
approach of defining agents as directories separately; looking at
these capabilities together, Vercel’s center of gravity remains a
user-initiated task that can suspend and resume.
The state in question is not just a piece of text in a database. Who receives the next message, which entity a scheduled task should wake up, which version the working directory should restore to, which steps of a task have been completed — these are all part of the state the product needs to keep running.
Koyeb recovers the compute service. When an instance exits, the platform starts a replacement based on the Service configuration; the specific agent’s identity, task progress, and business records are recovered by the application using its own business IDs from an external database and queue. Compute is recovered by the platform, business state by the application — this asymmetry defines the path.
Cloudflare natively understands Agent ID. This ID is used for both message routing and local SQLite state. When an object goes dormant or the current instance ends, new events can still find the corresponding state through the same ID. Applications still need to separately handle global queries, duplicate messages, and business ordering.
Vercel natively understands the various IDs within a single execution. The Workflow run ID recovers completed steps, the Sandbox or snapshot ID recovers the filesystem, and the real ticket retains its own business ID. The application needs to preserve the relationships among these IDs — the platform won’t automatically synthesize them into a single long-lived agent.
So “what the platform is oriented around” is not just a product philosophy. It directly determines which parts of state the platform natively preserves, and which IDs it uses to recover them. Dependencies also form at the same location: Koyeb’s deployment configuration and external services, Cloudflare’s object ID and local state, Vercel’s step boundaries and multiple run IDs.
Compute services, long-lived identities, and task execution are not three mutually exclusive, complete products. They describe three different kinds of responsibility: a service needs to keep running, an object needs to maintain a consistent identity, and a task needs to remember how far it has gotten. A real system may need all three at once.
Hybrid deployment is therefore a natural option. Web pages and approval workflows can use a Workflow-oriented platform; Workers that need long-lived connections and specialized runtime environments can run on a container service; the few objects that truly need fixed identity and local coordination can be managed separately. At the same time, orders, permissions, financial records, and the ultimate business records should still live in standard databases or object storage — runtime state must not substitute for the product’s authoritative ledger.
More combinations mean more operational boundaries. Teams need to maintain multiple release pipelines, permissions, billing, and monitoring. Hybrid deployment only makes sense when the development work saved by specialized capabilities outweighs these added costs. Otherwise, fewer platforms with clear data boundaries are generally easier to verify.
Platform selection can begin with three questions: Does your product need to run a long-lived program first? Is its center a large number of long-lived objects that receive messages repeatedly, each with its own local state? Or does the user initiate a task where the program needs to wait hours or days before continuing from the subsequent steps?
These three questions point respectively to compute services, long-lived identities, and task execution. A more concrete way to check: when the current process disappears, which ID do you plan to use to recover the state your work needs? If the answer is your own business ID, an Agent ID, or a Workflow run ID, the system’s primary responsibility boundary becomes clear.
The final judgment shouldn’t come only from a smoothly-running demo. Replace an instance at runtime, send duplicate events to the same object, deploy a new version while a Workflow is waiting, or interrupt a session mid-way through a Sandbox operation. Then check whether the service continues, whether messages are processed twice, whether the task resumes from the correct step, and whether business records remain intact.
The general execution capabilities of agents are indeed converging, but between demo and product there remains a layer of choice that a unified feature list cannot answer for you: what are you prepared to let the platform manage over the long term? Once you see this question clearly, Koyeb, Cloudflare, and Vercel become comparable again.