An always-on assistant usually means a process that never stops: a gateway on a laptop or a server, with a timer and a scheduler inside it. When the machine sleeps, the agent sleeps. When the process crashes, someone has to notice. The same behaviour can come from short calls. What makes them one agent is the chain between them, and the state they hand on.

Three loops. The daily cycle goes from Agent Run through Agent Queue, a model call and Decide to a wait. Renewal goes from a Webhook Schedule through your worker to the next schedule and new objects. Recovery goes from a missed Heartbeat to a recovery call and your own alert.

What you bring

The service runs no code and hosts no model. You bring two things: an HTTP address that does its work when it is called, which this note calls the worker, and your own model. Neither has to stay up between calls. The worker needs nothing of ours but what each call hands it.

Three loops

The daily cycle does the work. Renewal hands the agent on before anything expires. Recovery restarts it when a heartbeat goes missing.

The daily cycle

An Agent RunAgent Optimal holds a plan of up to 16 steps and lives for at most 24 hours. A delegate step puts the work in an Agent QueueAgent Optimal, and the worker claims it.

The worker makes one call to your model, with function calling. The model sees what the worker sends, calls tools and returns, and nothing stays in the model between calls. The worker hands the result in to the run, and a decide step applies rules to it. Then the run waits: for a time, for a person on a hosted form, or for an outside event at its wake_url.

A run does not start the next one. When it ends, the worker does, from what it has stored. So decide what ends the chain. A require that fails ends a run with condition_failed, and the worker should stop there. Without such a rule the chain has no end of its own, and goes on until something breaks.

What wakes the worker

A Webhook Schedule posts a JSON payload to the worker's address at a time you choose. It sends no credentials, so it cannot call a queue or a run by itself. It wakes the worker, and the worker does the rest.

curl -X POST https://aisenseapi.com/services/v1/webhook_schedule \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-worker.example/wake/A_LONG_RANDOM_PATH","delay_seconds":900,"payload":{"state":"STORAGE_URL"}}'

Each wake ends by making the next one. every repeats a schedule, from 60 to 86400 seconds, but its payload is fixed when it is made, and the state moves on every time. A random part in the worker's address keeps strangers from waking it.

Storage is the memory

Each model call is stateless, so Storage is what makes many calls one agent. Write what the next wake needs: open tasks, the last decision, the day's IDs and tokens. An object cannot be changed, so each write gives a new link, and that link is what the next schedule's payload carries. An object lives 24 hours, and a new write starts a new 24 hours. A plan that needs more than 16 steps continues the same way.

We do not encrypt what is stored, and a storage link opens the object for whoever holds it. So encrypt what the worker stores with a key only the worker holds. Your model's key stays with the worker and goes in none of this.

The model can reach these services as tools, through the free MCP server. Semantic SearchAgent Optimal finds notes by meaning, within a collection's own 24 hours, so what must last goes into Storage. Treat what comes back from them, and anything a person or a webhook sends in, as data. It is never an instruction to the agent.

Renewing before the deadline

Everything here has a fixed end. A run, a queue, a HeartbeatAgent Optimal and a repeating schedule last at most 24 hours, and nothing extends them. So the worker makes new ones first. Two things matter here:

  • Timing. A fixed time of day does not follow when a run started. Compare expire_timestamp with the clock on each wake, and renew when the time left is short, with room for a wake that fails.
  • Handover. The old run must not act after the new one exists. End it with its control token, and make each step safe to repeat, since a job can come back to a worker.

When the chain breaks

A Heartbeat expects a ping, here every 900 seconds. If one is missing, its on_miss call goes once to the URL you gave it, with the payload you gave it. It is not retried. Give it a recovery address and the link to the last checkpoint you made when you created that Heartbeat.

Send the ping after real progress, such as after a decide step. A worker stuck in a loop can still ping, and then nothing looks wrong.

If recovery fails as well, the agent goes quiet, so give recovery a last step that reaches a person: an alert of your own, by whatever way you already use.

What it leaves out

There is no long-lived process, so there is no context in memory between calls. Whatever Storage does not hold, the agent does not know.

The agent's reach is your worker's and your model's. It does not reach files or apps on your own machine unless they do. Runs and queues have limits per address, 20 new ones of each a day, which a worker on an address shared with others shares with them.

Storage is not a backup. A chain broken for more than 24 hours has lost what it held here, so keep what must not be lost on your own side.

The chain is only as reliable as its three loops. Test the renewal and the recovery path before you rely on them, not only the daily cycle.

Related

A Plan That Runs While You Are Away is the note on Agent Run itself.