Pausing and Resuming Agents Through API Calls
Introduction: Adding Lifecycle Control to Agent APIs
In the previous lesson, you implemented database persistence and progress callbacks that save the agent's state after each step. This allows clients to monitor progress in real time, but they still have no control over the agent once it starts running. Now it's time to complete Factor 6 — Launch / Pause / Resume with simple APIs. You already have the /agent/launch endpoint from lesson one and the durable persistence layer from lesson two; in this lesson, you add the /agent/pause and /agent/resume endpoints that give users the power to stop expensive computations temporarily or correct mistakes without losing progress.
These endpoints are standard Express route handlers registered with app.post(...). Input validation uses the requireStringField(...) helper already present in the codebase; all state reads and writes go through the StateStore, and long-running execution is managed with void runAgentInBackground(...), which starts the agent asynchronously without blocking the HTTP response. You will implement a cooperative pause mechanism that lets agents stop gracefully between steps.
How the Pause Mechanism Works
Unlike forcefully terminating a running process, a cooperative pause lets the agent finish its current step before stopping. The flow works like this:
- A client calls
/agent/pause, which usesstore.update(...)to flip the persisted status to"paused". - After each step, the progress callback running inside
runAgentInBackgroundcallsstore.get(stateId)to read the latest persisted state. - If the persisted status is
"paused", the callback writesupdatedState.status = "paused"onto the in-memory state object and saves it, then returns. - Because the agent loop's continuation condition reads the
statusfrom that same in-memory state object, it exits on the next check.
The agent never stops mid-step — it always completes the current operation and only then discovers the pause signal. This keeps the state consistent and all fields properly synchronized, which is exactly what Factor 6 calls for: the agent can checkpoint, wait, and resume reliably across time.
Modifying the Progress Callback to Detect Pause Requests
In the previous lesson, the progress callback simply saved each updated state to the database. To support pause detection, it needs one extra check. The callback is defined inline — inside runAgentInBackground — and is async, making it ready for any asynchronous work that may be needed later:
After each step, the callback reads the record from the store with store.get(stateId). This is a fast, synchronous lookup (backed by SQLite) that returns the most recently persisted version of the state. If that record's status is "paused", the callback knows that a /agent/pause request arrived while the step was running. It writes "paused" back onto updatedState — the live object the agent loop is reading — saves it, and returns early. The agent loop sees the updated status on its next iteration and exits. In the normal case where no pause was requested, the callback calls store.save(updatedState) as before.
