The life of a session
Runs, the environment between them, pause and resume, teardown, and why Blaise owns the clock.
A conversation with an agent is rarely continuous. Someone asks for a change, the agent works, and the next message comes an hour later, or the next morning. This page explains what Blaise and the runner do in between, and why.
A session is a series of runs
A run is one process of the runner working on one session, from the moment it is accepted to the moment Blaise stops it. A session has one or more runs, one at a time:
| Run | When | What it does |
|---|---|---|
start | The first message of the session | Creates the environment, starts the agent, answers the message. |
resume | A message arrives while the session is paused | Re-attaches to the environment, loads the conversation, answers. |
teardown | The session ends, or expires, while it is paused | Destroys the environment. There is no agent. |
The environment is everything the plugs created for the session: a checkout, a container, a sandbox, a database branch. It outlives the runs. That is what makes the second message cheap: the dependencies are installed and the files are where the agent left them.
The states of an environment
Blaise owns the state of every environment. The runner only reports what happened.
The dashboard shows the state in the session view. See the reference for what each state means.
Why Blaise owns the clock
Whether a session is idle, how long it stays paused and when it times out are settings of the agent
in Blaise. The runner has no timers, and blaise.yml has no timing keys.
Blaise already receives every message, so it can tell that nothing has happened for a while. When it
decides to end a run, it sends the run a stop directive with a reason and a flag, mayResume.
The run pauses its plugs and exits. This has three advantages:
- One source of truth. The runner, the dashboard and the monitors cannot disagree about whether a session is idle.
- A dead runner cannot hold a session open. If a runner stops responding, Blaise times its run out as it always did, and the next message resumes the session on another run.
- Policy changes apply at once. Change the idle policy of an agent and the next idle session follows it, without restarting any runner.
Whether an idle session pauses or ends is Blaise's decision alone, made from the agent's policy and from whether the run's plugs can resume. The runner follows the directive. If Blaise asks to pause but a plug cannot resume, the runner logs a warning naming the plug, and a later resume may fail. Blaise makes the decision again when the pause is reported, and overrules a run that reports otherwise.
A session is never idle during a turn, however quiet the agent is: the clock stops from the moment a prompt is sent to the agent until its response comes back.
Pausing, resuming and the conversation
When a session goes idle and the agent's policy is to pause, the run pauses its plugs (in reverse order, outermost last) and exits. The environment stays. Nothing runs and nothing is billed by the runner, though what the plugs left behind may be: a stopped container, a worktree on disk.
The agent process does not survive the pause. The conversation does, because the runner records the
ACP session the agent created. On resume, it asks the agent to load that session (session/load)
and the agent replays its history. The runner shows the replay to the plugs, so redaction still
applies, but does not record it a second time in the transcript.
An agent that cannot load sessions starts a new conversation in the old environment: the files are there, the context is not. A notice in the transcript says so.
When the session is over
A session ends when it is closed or completed, times out, fails, or has been paused for longer than
the agent allows. Then the environment is torn down: each plug that has an onTeardown handler
destroys what it created.
- If the run was still alive, it tears down in its own process, right after its pause.
- If the session ended while paused, Blaise starts a
teardownrun, which is accepted like any other.
When someone ends a running session from the dashboard, the session shows as complete as soon as the run has received the stop, and the run then tears down. If no runner is connected to receive the stop, the request fails with an error and changes nothing, so it can be repeated. Follow-up messages from the web to a session that has ended are rejected.
The result is reported either way. A teardown that fails is retried with a growing delay, as a
teardown run. After a few attempts the environment is orphaned: Blaise stops trying and shows
it in the session view with the last error, because what the plugs created may still be running.
Someone fixes the cause and retries from the dashboard.
When the runner receives SIGINT or SIGTERM, it stops the conversation, closes its directive
subscription and terminates the agent process. The run ends as cancelled and tears down its plugs.
An unanswered permission request does not delay shutdown.
Transcript-report failures fail the run. If a lifecycle report loses its response, the runner exits without sending a contradictory report; Blaise ignores duplicate reports from finished executions.
What goes wrong, and what happens
| What happens | What Blaise does |
|---|---|
A resume run fails before the agent starts | Tears the broken environment down and starts a fresh start run, so the waiting message is still answered. A transcript notice says the environment was recreated. |
| A run fails otherwise | The session fails. The run still tears down what it can. |
| A teardown fails | Retries it with backoff as a teardown run, then orphans the environment. |
| The runner process dies | The run times out as before, and Blaise starts another run in the same environment (it gives up after three failures in a row). |
| The runner cannot start the process for a run it accepted | It tells Blaise at once. Blaise fails the run and recovers the session without waiting for the connection timeout. |
| A run cannot reach Blaise for about 30 minutes | The run stops with "Lost contact with Blaise" (90 heartbeats in a row failed) and Blaise recovers the session as for a dead runner. |
| The agent is disabled or archived while a run is connected | The run can still report its pause and teardown, so its environment is not stranded. No new runs start for it, except teardown runs. |
| A message arrives for an environment that is orphaned | The message is not accepted: the web shows an error and Linear gets a reply saying an admin must retry the teardown. |
| A message arrives while the environment is being torn down | The message waits. A fresh run starts when the teardown is done. |
| Two things happen at once | Blaise serializes the transitions of a session: it has at most one run at a time. |
Which runner resumes
A resume or teardown run is offered only to the runner that ran the session before, because that
is where its environment is. If that runner was revoked, any runner that matches the agent's
requirements takes the run and starts a fresh environment; if it is only offline, the run waits for
it. Instances that share one runner token count as one runner, so any of them can take the run.
Keeping a paused environment reachable from every instance of a runner is up to the plugs: a Docker daemon the instances share, worktrees on a shared file system, or a cloud sandbox that can be reached by id. A plug that derives its resources from the session id (a container named after the session) finds them on any instance that can reach them, which is one more reason to prefer stateless plugs. A runner with a single instance needs nothing special. See How sessions find a runner.
Changing blaise.yml while a session is paused
The file can change between a pause and the next message.
- Stateless plugs work out what they need again on every run, so a change simply applies.
- Stateful plugs find their persisted state by
id. Reordering plugs, or adding and removing stateless ones, changes nothing. A new stateful plug finds no state and creates what it needs. A stateful plug removed from the file leaves a record that no plug can tear down, which the dashboard lists as one that nothing owns.
What is not there
Some providers can keep running processes across a pause. The agent process itself could then continue without reloading its session. That "hot resume" is not part of the runner yet: a resume always starts the agent again and loads its session.