Skip to content

Sessions

An Agent is immutable reusable behavior. An Engine owns session identity, history, and runtime state. A Session is an engine-bound live conversation.

use everruns::{Agent, Engine, Model};
# #[tokio::main]
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
let agent = Agent::builder()
.instructions("Remember the conversation.")
.model(Model::simulated("Acknowledged."))
.build()?;
let engine = Engine::new();
let session = engine.create(agent);
let first = session.send_and_wait("My project is Atlas.").await?;
let second = session.send_and_wait("Continue with that project.").await?;
assert!(first.success);
assert!(second.success);
# Ok(())
# }

Session is always a live conversation. send accepts a message without waiting for a response. If a turn is active, the message steers that turn; if the previous turn has already finished, it starts a follow-up turn. The receipt reports which case occurred, so applications do not need to race on session state themselves:

# use everruns::{Agent, Engine, Model, SendDisposition};
# #[tokio::main]
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
# let agent = Agent::builder().instructions("Remember input.").model(Model::simulated("Done.")).build()?;
# let engine = Engine::new();
let session = engine.create(agent);
let initial = session.send("Plan my trip.").await?;
let latest = session.send("Prefer trains.").await?;
match latest.disposition {
SendDisposition::Steered => {
assert_eq!(latest.turn_id, initial.turn_id);
}
SendDisposition::Started => {
// The first turn completed before the second message was accepted.
}
_ => {}
}
let result = latest.wait().await?;
# let _ = result;
# Ok(())
# }

Waiting on the latest receipt works in both cases. send_and_wait (also available as the shorter run alias) is request/response convenience over the same live session, not a separate mode.

The first asynchronous operation materializes the in-process host. Later turns reuse it and send accumulated history through the same context-assembly path. Two sessions opened on one engine have different opaque IDs and isolated histories. The engine retains each immutable Agent snapshot, so a session keeps working after the original Agent handle is dropped. Volatile resume is engine-scoped: another Engine rejects the id rather than guessing its configuration. A local profile can be attached to another Engine with the trusted Agent snapshot; live Engines for the same profile share its backend bundle.

Conversation isolation does not imply filesystem isolation. The concise engine.create(agent) path permanently selects the Agent’s default head before its first inspection or turn; call session.start().await to make that selection observable earlier. To fix a session to an isolated project view, bind an Environment before execution. A session can never switch heads after it starts.

Session::inspect returns the context assembled for the next model call. Use it for application assertions and debugging rather than reaching into runtime records or backend stores.

Keep Session::session_id() when the application may need to reopen a conversation. Session History and Resume covers typed resume, bounded transcript pages, and cursor snapshots. See Workspaces and Environments for exact-head resume, isolation, sharing, and lifecycle. See Persistence to choose engine-lifetime memory or a crash-durable local profile, and Events and cancellation to observe a turn in flight.