How a session executes
The session lifecycle, durable conversations, statuses, steps, and what each session records.
Every agent execution is a session. Understanding the lifecycle tells you what any status means, why a session ended, and where its money went.
Which config a session runs
A triggered session (cron, react, mention) always runs the config that declares its trigger. An on-demand session (dashboard, API, CLI) runs the config you name: a saved config id, an inline config, or a template.
Name none of those and Ellipsis resolves your team's default agent, in order:
- Repository default. If the request carries a repository (the CLI sends the repo you are standing in) and that repository has a default agent, it runs.
- Account default. Otherwise the account-wide default agent runs, if one is set.
- Bare config. With no defaults set, the session runs a plain ad-hoc config: an empty system prompt on the default model, and your prompt is the sole instruction.
Defaults are pointers to existing synced configs, set on the agents settings page, with agent config default set in the CLI, or through the API; the configs themselves stay YAML in your repository. Which rung resolved is recorded on the session, the start response carries it (resolution_source), and the CLI prints it: using config "CI failure triager" (repo default). A default that points at a config that was deleted, disabled, or never merged fails the session start with an error naming the setting; it never silently runs a different agent.
The repository the client is standing in is also cloned into the sandbox at its default branch, merged with the config's own sandbox.repositories, even when you name a config explicitly. So agent session start inside a repo always has that repo on disk. For uncommitted work, use handoff instead.
The lifecycle
A session moves through four phases:
- Checks. Before anything is provisioned, Ellipsis verifies the agent is enabled, the account can start workflows, and no spend limit is exceeded. A failed check cancels the session here; nothing executes.
- Sandbox creation. An isolated sandbox is created with the session's repositories cloned: the ones the config declares plus any the trigger brings (a PR mention adds that PR's repository).
- Execution. The agent works step by step. Every step is recorded as it happens: assistant output, tool calls, tool results, tokens, and cost. Spend updates live, and the session streams to the dashboard, CLI (
--watch), and API in real time. - Finalization. The output is stored, costs are totaled, and the sandbox is torn down. Teardown always happens, success or failure.
Durable conversations
On conversational surfaces, a trigger starts a durable conversation, not a single-shot session. Later events on the same surface are delivered into the conversation as messages, and the agent keeps everything it already said and did as context. Each agent holds its own conversation per surface: a react agent and a mention agent on the same pull request are two separate conversations.
- Slack. A thread is one conversation, in a channel or a DM: every later message in it, a reply or another mention, is delivered into it. A DM's top-level messages are a separate continuous conversation of their own, answered top-level like a person chatting. Replying in a thread under any DM message forks a new conversation scoped to that thread, so parallel workstreams run as parallel conversations in one DM.
- GitHub pull requests. A pull request is one conversation. The first event an agent reacts to on the PR starts it; every later PR event the config declares (pushes, comments, reviews) continues it. A merge or close is the conversation's final message: the agent takes one last turn, then the conversation ends for good.
- GitHub issues. The same shape:
issue_openstarts the conversation,issue_commentcontinues it, andissue_closeis its final message. - Linear issues. A Linear issue is one conversation. The first
@ellipsiscomment starts the mention conversation and later mentions continue it; an agent reacting tolinear_issue_openholds its own conversation on the issue. - Sentry alerts. An alert starts a conversation keyed to its recurring identity: the Sentry issue for issue alerts, the alert rule for metric alerts. A re-fire is delivered into the conversation ("this alert fired again") instead of starting a session with no memory. Re-fires of one Sentry issue reach agents at most once every 6 hours.
- Mentions. An
@ellipsismention on a pull request, issue, or Linear issue joins that surface's mention conversation, and repeat mentions continue it. On GitHub and Linear, a follow-up comment must mention@ellipsisagain to be delivered; on Slack, bare replies continue the thread. See Answer @ellipsis mentions.
After a conversation ends (a merged or closed PR, a closed issue), the next event on that surface starts a fresh conversation with no memory of the old one, which is preserved as history: a comment on a merged PR is answered by a new conversation. Only those react events end conversations, and only for agents whose config declares them; mention conversations never end, they go idle. Triggers without a conversational surface (cron, push, slack_channel_created) start a fresh session per fire, as do sessions triggered by config previews on an open PR.
What the agent does with each message differs by trigger. A mention agent replies to every message on the surface it was mentioned on. A react agent posts no reply per turn: it acts through the sandbox, in the commits it pushes, the PRs it opens, and the comments it chooses to make with gh or the integrations.
Every conversation, on every surface, behaves the same way:
- A message into an active conversation is delivered within about a second; the agent attends to it at its next natural pause, like typing to Claude Code while it works.
- After a turn, the conversation stays warm for 10 minutes. While warm, the sandbox keeps running and its compute bills like any other sandbox time.
- After the window, state is snapshotted. The next message resumes the conversation, working tree and memory intact, in a fresh sandbox within seconds.
- Stopping a session stops the current turn only; the next message re-engages the same conversation.
limits.runcaps each burst of activity. A resumed conversation runs on the budget remaining after prior spend; trailing spend limits apply as always.
A conversation renders as one session everywhere sessions appear. In the dashboard it shows its conversation state — Active while a turn is executing, Idle while waiting for the next message, Closed once its surface ends it — along with its turn count, and its detail page leads with a Conversation tab: each message the surface delivered, and the turn that answered it, with per-turn timing and cost. Over the API the same structure is session_state on the session object and GET /v1/sessions/{session_id}/turns.
Statuses
status is where the session is in that lifecycle:
status | Meaning |
|---|---|
scheduled | Accepted, waiting for a worker. |
creating_sandbox | Provisioning the sandbox and cloning repositories. |
running | The agent is working. |
retrying | A transient infrastructure failure interrupted the session before the agent acted; it will re-execute from scratch. |
completed | Finished its task. |
error | Ended on an unrecoverable failure. |
cancelled | Stopped during checks, before executing. |
stopped | A user stopped it mid-flight. |
Exit statuses
Terminal sessions also carry an exit_status: the reason they ended, so a budget cutoff reads differently from a crashed tool call.
exit_status | The session ended because |
|---|---|
completed | It finished normally. |
budget_hit | It reached a per-session or trailing spend cap. |
payment_required | The account has no credits or payment method. |
tool_call_failed | An API, tool, or MCP call failed unrecoverably. |
lifecycle_hook_failed | A post_start or post_clone hook exited non-zero. |
missing_repo_access | The Ellipsis GitHub App lacks access to a repository the session needs. |
missing_token_permissions | The config's sandbox.github_token.permissions requests more than the installation was granted. |
cancelled | A pre-execution check failed. |
interrupted | The worker died mid-session. |
error | Any other unrecoverable error. |
stopped | A user stopped it. |
What each session records
A session permanently stores: the config snapshot it executed (editing the file later never changes what a past session shows), the source that started it (cron, react, mention, manual, api, cli, or laptop), attribution to the person or event behind it, its full step timeline, per-model token usage, and cost broken down into token spend, sandbox CPU, sandbox memory, and Ellipsis fee.
laptop sessions are the exception to the lifecycle above: they are Claude Code sessions synced from a developer's machine, ingested rather than executed, so no sandbox, budget, or exit status applies. They move between running and completed as syncs arrive.
The snapshot is also what replay re-executes: a replayed session reproduces the session as it was, not as the config is now.
Following a session
- Dashboard: the session detail page streams steps live and keeps the full timeline after.
- CLI:
agent session get session_7Hq2mX4p --watchfollows until terminal, or stop it early withagent session stop session_7Hq2mX4p. - API:
GET /v1/sessions/{session_id}for state, the WebSocket stream for live output with resume.