Admit the prompt first, run it later
A durable inbox between “you pressed Enter” and “the model sees it.”
- Problem
In most agents, pressing Enter saves your message and starts the model in one step. After a crash, a retry or a message sent mid-task, nobody can say what the model actually saw.
- Idea
Save the prompt to a durable inbox first. Move it into the conversation later, at a safe point between model calls.
Try it: the prompt inbox
send, steer, queue, retry, crash
Prompts land in the inbox first. At each turn boundary, the agent moves them into the conversation. A crash loses the running work, never the inbox.
sessions.prompt({ id, sessionID, prompt, delivery: "steer" | "queue" })
-> same ID again: same receipt, no duplicate
-> same ID, different prompt: fails loudly
How it works
- AdmitYour prompt becomes one row in a
session_inputtable. It's saved and replayable, but the model hasn't seen it. - PromoteAt the next safe boundary, one atomic step removes the row from the inbox and appends it to the conversation.
- RunThe model works on the conversation. If the process dies, whatever is still in the inbox is waiting for the next run.
Steer or queue
Steer (default)
Joins at the next turn boundary, while the current task keeps going.
"Also add a test."
Queue
Waits until the agent would otherwise go idle, then starts one at a time.
"After that, update the docs."
What isn't saved, on purpose
The running part, a drain, isn't a stored thing: "A Session Drain has no durable identity or transcript boundary." Interrupting stops the work but keeps the inbox, and interrupting an idle session does nothing. After a crash, recovery starts from what was saved (prompts, history, tool results), not from an invented "job" record.
Steal this
Put a durable inbox between "the user sent it" and "the model sees it." Make prompt IDs idempotent, make steer and queue explicit, and don't save the parts that are only coordination.
Use it in your code
A prompt for your coding agent
Run it from the root of your repo. It checks whether the idea fits before it changes anything.
Check whether this app needs a durable inbox between "the user sent it" and "the work starts".
The idea: save each incoming request (a prompt, a job, a message) as a durable row first, then move it into processing at a safe point. A retry with the same ID returns the same receipt instead of duplicating work. "Steer" (apply to the current task) and "queue" (run after it) are explicit modes. A crash loses only in-flight work, never accepted input.
1. Find where user input or jobs are accepted and processed, and show me whether accepting and processing happen in one step.
2. Tell me whether a crash, retry or concurrent submission could lose, duplicate or reorder input. If not, say so and stop.
3. If it could, propose the smallest inbox: a table keyed by a client-supplied ID, an idempotent insert that returns the existing receipt on retry and fails on a conflicting reuse, and one atomic step that marks a row as consumed when processing picks it up.
4. Implement it, with tests for a duplicate submission, a conflicting ID, and a crash between accepting and processing.
Keep "who is running right now" in memory unless there's a real reason to store it.Read the source
Where this comes from
OpenCode (anomalyco/opencode), the open-source coding agent (these lessons are from its upcoming v2). The files behind this lesson: