Skip to main content
The inbox is how a campaign asks you for something only a person can provide, and how your reply continues the very coding session that asked. A session that needs an API key that is not in .env, access to a private bucket or a file that exists only on your laptop records a request, the campaign pauses and exits with stopped_reason: waiting_for_user, and kapso inbox reply resumes that session with your answer and its context intact. Nothing polls and nothing runs in the background. Your reply is the only trigger, and a campaign never starts new work while a request is open.

How does a session ask?

The session first proves the blocker: the session reproduces the failure with the smallest command, rules out the causes the session can fix itself, reads the repository’s own docs for how the resource is normally obtained, retries anything that could be transient, and tries routes that need no person. Only then does the session call the request_from_user tool with everything the session needs, in one call. The call is the stop signal: the session ends its turn, the working tree is committed and pushed to the experiment branch, the node is suspended, and the campaign pauses. You read the request with kapso inbox, act on it, and answer with kapso inbox reply. The reply resumes the campaign, which continues the same session through the coding agent’s own resume command with a follow-up carrying your reply and the next steps the session recorded. The session verifies for itself; if the session is still blocked, the session asks again, and the new request shows your previous reply. A suspended node is not judged, never becomes a parent, and does not count as an iteration. The judge only ever sees completed nodes.

What does a request look like?

At the pause the terminal prints every open request and how to answer:
The summary block after kapso build says WAITING ON YOU instead of COMPLETED, and the exit code stays 0. A pause is not a failure.

How do I answer?

Two commands:
Run inside a campaign directory, both commands act on that campaign. Run anywhere else, kapso inbox lists every campaign with an open request, and kapso inbox reply takes the campaign path first:
The id may be left out when one request is open. An empty note means done. The reply with line under each listed campaign follows the same rule: outside that campaign it names the campaign’s path, so the line works where it was printed. Replying records the note and resumes the campaign in the foreground, in your terminal. Add --detach to walk away: the note is checked here, the campaign resumes in a session of its own, and the command returns with the log and the kapso watch line. Both forms work from any directory, since the launch record keeps the campaign’s paths and .env. When a node asked for several things, the node waits until every one of its requests is answered, and the command says so:
The command refuses to run while a live process holds the campaign, so nothing runs twice. Two replies that start at the same moment run one resume: the second waits for the first, then has nothing to do unless the first failed. Your reply is on disk before the resume starts. If the resume then fails or is killed, the reply stays, and kapso inbox shows it instead of saying nothing is waiting:
Fix what the error named and run the line it shows. A note replaces your earlier answer until a session has been given it. Once a session has been given it, a retry resends that answer and a different note is refused.
Replies are stored in plain text in .kapso/inbox.jsonl and handed to the coding agent as text. Put a credential where the fix row says, usually the .env the run loaded, and reply with a note. A reply shaped like a token is refused unless you add --yes at the end of the line; Kapso.reply refuses it the same way unless allow_secret=True, so a script or a plugin cannot store one by accident.
A reply that says the resource is not available is an instruction: the coding agent proceeds without the resource, and no later session in the campaign asks for that key again.

What happens when the campaign resumes?

The campaign resumes from its checkpoint, and the session that asked is continued through the coding agent’s own resume (claude -p --resume for Claude Code, codex exec resume for Codex) with a follow-up that carries your replies and the next steps the session recorded. The session’s earlier context is the CLI’s own transcript, so nothing is re-explained. The coding agent verifies for itself. If the coding agent is still blocked, the coding agent asks again with what the coding agent tried, and the new request shows your previous reply next to the new request. Kapso runs no checks of its own. Nothing is resumed until you reply, and a kapso build --resume with a request still open pauses again without running anything. Paused time does not count against the campaign’s time budget.

What does the session keep across the pause?

Everything git can hold, plus the campaign directory and the transcript.
  • Code and every file in the session’s working tree. When the session ends after the call, the session close commits the run directory and then everything else on the node’s branch, pushes the branch to the campaign repository, and only then deletes the session folder. The resumed session is a fresh checkout of that branch.
  • Datasets. kapso_datasets/ is tracked on the branch, since data files are committed at setup. A file you drop into the campaign’s kapso_datasets/ after launch, because a request asked for the file, is copied into the resumed session and becomes tracked when that session closes.
  • Evaluation files, run outputs, changes.log and the session’s plan file. On the branch.
  • The idea, the requests and the session’s own next steps. In the checkpoint node and in .kapso/inbox.jsonl in the campaign directory.
  • The conversation. The coding agent’s transcript on disk, under ~/.claude/projects or ~/.codex/sessions, keyed by the session id stored in the checkpoint node. Claude Code keeps transcripts for 30 days.
Not kept: files git ignores inside the session folder (*.log other than changes.log, __pycache__), anything written outside the session folder such as /tmp, exported environment variables, and processes the session started. The resumed session is a fresh process that loads the campaign’s .env at start.

Where does the pause show up?

A campaign started from Python with an iteration_evaluator callback cannot be resumed from the command line, because the callback lives in your script. kapso inbox reply records the note and tells you to call build() again with resume=True.

When does the coding agent ask, and when not?

The implementation prompt sets the bar, and the tried row of every request shows the bar was met. A session asks only for something a person must do: installing a package, downloading public data or waiting out a rate limit is the session’s own job. A request is load-bearing or the request is not made: a missing key for optional logging is dropped and mentioned in the report. Before asking, the session reproduces the failure with the smallest command, rules out every cause the session can fix itself, reads how the resource is normally obtained in the repository, retries when the failure could be transient, and tries routes that need no person. A location the repository’s own README or config names for a credential is such a route, not a search. The session asks for everything the session needs in one call and does nothing after the call. The session never stubs or mocks the resource, never searches the machine for credentials, and never prints a secret’s value. An idea that plans around a missing resource with an “honest zero” or a placeholder is not the goal: the session asks instead.

How do I turn the inbox on or off?

The block is a top-level key of the config. To turn the inbox off, put inbox: {enabled: false} in your override file; it is merged over the packaged block, so the other two keys keep their values. The benchmark override files ship with the inbox off. A campaign whose node_expansion_value is above 1 runs with the inbox off as well: a campaign with several implementation lanes cannot pause on one of them yet. With the inbox off, sessions do not get the tool, the prompt is unchanged, no campaign pauses, and no launch record or registry line is written. The inbox block is part of the checkpoint’s configuration fingerprint, so changing the block on an existing campaign blocks --resume for that campaign.

What can go wrong?

  • The resume cannot start. A moved config or evaluation directory, a missing coding agent CLI or a changed configuration raises from kapso inbox reply. The reply is kept and the node stays suspended. Fix the cause and retry with kapso inbox reply.
  • The transcript is gone. Claude Code keeps transcripts for 30 days. If the transcript has been deleted, the reply fails with the coding agent’s own error, the node stays suspended and nothing else runs. The code is still on the branch, but the context is lost: start a new campaign.
  • Kapso is killed during the continuation. The checkpoint still marks the node suspended. kapso inbox reply or kapso build --resume continues the same session. Work the continuation had not pushed before the kill is lost; the coding agent redoes the work.
  • You supplied a wrong value. The coding agent verifies, asks again, and the new request quotes your previous reply. Reply again after fixing the value.
  • The session asked for two things. Answer each request; the node resumes after the last one, and every reply before that prints which requests are still open.

Resuming runs

The checkpoint a paused campaign resumes from

CLI reference

Every form of kapso inbox

MCP gates

The gate that carries the request_from_user tool

Configuration

The inbox block and its defaults
Related pages: Resuming runs · CLI reference · Configuration · MCP gates · Execution flow Kapso is an open-source framework by Leeroo that builds software toward measurable goals through experiment campaigns. Source code: github.com/Leeroo-AI/kapso · Install: pip install leeroo-kapso · Every page as plain text: llms.txt.