There is a quiet assumption baked into most AI products: that the human is always there. You type, it answers, you type again. Close the tab and the whole thing evaporates. That is fine for a chatbot. It falls short for work that runs for days.
Real work is a job that runs over hours and days: chase the invoice, watch the pipeline, triage the tickets, brief me in the morning. None of that fits inside a single request-and-response. So when we started building AgentNava, the first question was a simple one: how does an agent keep working when nobody is watching?
The close-the-tab problem
A typical AI app is stateless by design. Each message is a fresh call to a model, with the previous turns replayed as context. That works well for a fast exchange, and it scales because there is nothing to hold onto between requests. But it means the “agent” only exists for the few seconds it is generating a reply. There is no it the rest of the time.
For a proactive agent, that is the whole ballgame. If the work only advances while you have the tab open, what you have is a very expensive autocomplete.
An agent you have to babysit is just a tool with extra steps. The point is to hand off the job and get on with your day.
So the runtime needs two things a stateless app does not have: somewhere durable to keep the agent’s state, and a way to start running again without a person triggering it.
State that outlives the session
On our default runtime, every AgentNava conversation runs in its own Durable Object: a single-threaded, addressable piece of compute with its own strongly-consistent SQLite storage, living on Cloudflare’s edge. That is where the conversation’s history and files live: in a long-lived object that stays put between visits.
Because it is durable, a conversation can accumulate context across days: what the agent did last Monday, which deals it already followed up on, what is still waiting on you. When you reopen the workspace, you check in on work that has continued without you.
Waking without a human
Durable storage solves memory. It does not, by itself, make anything happen. For that, the platform leans on two Cloudflare primitives: Cron Triggers and Durable Object alarms.
Every agent has trigger settings: run on a schedule (“every Monday at 8am, my time”), run when a webhook arrives, or run when someone asks. From inside a conversation, an agent can also schedule its own follow-up: “check back in an hour.” Schedules and self-set follow-ups are saved as rows. A Cron Trigger checks for due rows every minute and replays the saved instruction into the agent’s conversation, so the agent picks up with its history and files in place. Interval triggers, set through the API, run on a Durable Object alarm. Cloudflare runs both, so there is no scheduler server for anyone to operate.
- Scheduled work: a morning brief, a Monday pipeline sweep, an end-of-day summary.
- Deferred work: “follow up in three days if they have not replied.”
- Reactive work: a webhook wakes the agent to handle something the moment it lands.
Critically, all of this runs whether or not you are online. The tab is just one way to watch the agent. It was never where the agent lived.
A worked example
Here is the shape of a proactive agent that keeps a sales pipeline warm, set up with our SDK. It assumes a CRM such as HubSpot is connected. The interesting part is the last call: the schedule is part of the agent’s configuration.
import { AgentNava } from '@cerebro-labs/agentnava-sdk';
const ws = AgentNava.getWorkspace(); // reads AGENTNAVA_API_KEY
const agent = await ws.agents.create({
name: 'Pipeline warmer',
instructions:
'Find deals with no activity for seven days. ' +
'Research what changed at each company. ' +
'Draft one follow-up per deal and post the drafts here. ' +
'Do not send anything.',
});
// Every Monday at 8am Pacific, with nobody at the keyboard.
await agent.setTriggers([
{ kind: 'chat' },
{
kind: 'schedule',
cron: '0 8 * * 1',
timezone: 'America/Los_Angeles',
message: 'Run the Monday pipeline sweep.',
},
]);
When Monday comes, the agent wakes on its own, does the sweep, leaves the drafts for you, and goes back to sleep. You never opened a tab. You just find the work done the next time you look.
What we traded away
None of this is free. Putting a durable, single-threaded object behind every conversation is a stronger commitment than a stateless function, and it comes with real constraints worth naming.
| Decision | What we gain | What it costs |
|---|---|---|
| Durable Object per conversation | Consistent state that outlives the tab | One conversation is one thread; heavy fan-out needs care |
| Edge-resident state | Fast, local reads and writes | Storage is SQLite per conversation; queries across conversations need another store |
| Wakeups on a once-a-minute cron | No scheduler server to operate | A wakeup can fire up to a minute late |
For the work AgentNava is built to do (a proactive agent keeping a job moving) we think this is the right trade. The agent still gets a working filesystem and a shell: each conversation’s files live in the same object’s storage, with large files in R2 object storage.
- Real work is a job that has to survive a closed tab.
- Durable Objects give each conversation consistent state that outlives the tab.
- Schedules, webhooks and self-set wakeups let an agent wake and work with no scheduler server to run.
- The result: you hand off the work and check in on progress.
Where this goes next
The runtime that keeps an agent alive is the foundation for everything above it: connections to the tools you already use, and the phase indicator that shows you, in plain language, what the agent is doing right now. We will dig into each of those in future posts.
For now, the one idea worth keeping: the agent was never in the tab. Close it whenever you like. The work keeps moving.