Lifecycle

Read a session's status, and know when the message you sent is answered.

A session is a conversation with an agent. The session object reports where it is in status, why its last message ended badly in error, and whether it can take more messages in conversation. Every message you send is answered in order. The message you sent is answered once the session is idle or closed.

Status

StatusMeaning
queuedA message was received and waits to start, or waits until a session ends when your account has its limit of sessions alive
preparingThe environment for the next message is being set up
runningThe agent is working on a message
idleEvery message has been answered and the session can take another
closedThe session can take no more messages. closed is permanent

A session started with interactive: false closes when its first message is answered. After that, start a new session.

Error

error is null while the session is busy and after a message was answered. When the last message failed, was stopped, or was cancelled, error.reason says why and error.detail is a sentence you can show as is. The next message clears it.

ReasonMeaning
errorThe agent ended with an error
tool_call_failedThe agent process failed or stalled before finishing
budget_hitThe session budget or a spend limit was reached
payment_requiredThe account has no credit or a billing problem
blockedThe account or user is blocked
contact_email_requiredThe account needs a contact email
lifecycle_hook_failedAn environment hook exited with a nonzero status
missing_repo_accessThe GitHub installation can't access a repository the session needs
missing_token_permissionsThe GitHub installation lacks a permission the session needs
missing_environment_variablesAn environment variable has no value
missing_github_integrationThe account has no GitHub installation
agent_unavailableThe saved agent was deleted or disabled
upstream_unavailableThe model provider was rate limited or down after retries
unsupported_configurationThe harness, model, and settings can't be combined
execution_disabledSessions from this source are disabled
timeoutThe message's work ran for the environment's compute.timeout and was stopped
interruptedThe agent was lost before answering. The message starts again on a new environment
stoppedSomeone stopped the session

Conversation

FieldMeaning
conversation.interactiveWhether the session stays open after its first message is answered. Set it when starting a session, on an agent, or when starting an agent session
conversation.warmWhether an agent is ready to answer the next message right away. When false, the next message first prepares an environment, which takes longer
conversation.promptingWhether a message you send now would be accepted. When enabled is false, blocked_reason and detail say why

warm is a hint for what to show while a session is queued or preparing, never something to branch on. prompting.detail is a sentence you can show as is. blocked_reason takes these values.

ReasonMeaning
closedThe session is closed
mention_surfaceThe session was started by a mention. Reply on the surface surface_name names (GitHub, Slack, or Linear)
non_interactiveThe session was started with interactive: false
harness_single_turnThe harness takes one message per session
budget_exhaustedThe session spent its budget

POST /v1/sessions/{id}/messages fails with 409 while prompting.enabled is false.

Wait for the answer

Poll GET /v1/sessions/{id} or watch the stream's session frames until status is idle or closed. Waiting for the session to go quiet is wrong because a busy session can be quiet while its environment is prepared.

How a message ended is in the session's log. Its message_ended record carries status (completed, failed, stopped, or cancelled), error (the same reason and detail object the session carries, null when answered), and what answering it cost. Messages answered together report the cost once, on the last of them. GET /v1/sessions/{id}/records?message_id=... returns one message's records.

The SDK handle's wait() does both. It returns the message_ended payload of the message it waits on. That is the last one sent through send(), else the session's latest message.

1
ended = handle.wait(timeout=900)
2
print(ended.status, ended.error)
3
print(handle.session.status, handle.session.conversation.prompting.enabled)

Follow the stream

1
from ellipsis.frames import StreamFrame
2
3
4
def on_frame(frame: StreamFrame):
5
if frame.type == "session":
6
print(frame.session.status, frame.session.error)
7
elif frame.type == "records":
8
for record in frame.records:
9
print(record.feed_seq, record.kind, record.message_id)
10
elif frame.type == "error":
11
print(frame.message)

Pass this callback to the stream in the Python or TypeScript SDK. The first frame is session, resent whenever the status, the error, or the cost changes. records adds saved records in feed_seq order. A session frame with status closed is the last one. The stream then closes normally. delta frames carry live partial output that isn't saved. Frames can repeat, so ignore duplicates and types you don't recognize. See Session events for every event.

On this page

Schedule a demo