AnyAgent

Sessions

Run one conversation across many turns, persist it, branch it, and steer it live.

A session is one conversation spanning many runs: open it with agent.session(), and each turn continues where the last left off. You never thread a session id by hand. These examples assume an agent from Run an agent.

Run turns

Works withClaude CodeCodexopencodeKilo CodePigooseClineAntigravityCursoragent.supports("resume")full matrix

session.run works like the agent’s own, returning the same awaitable, iterable handle:

const session = agent.session({ cwd: "./repo" });
await session.run("Review this repo for unhandled rejections.");
const fixes = await session.run("Fix the issues you found.");
console.log(fixes.text);

A session is a thread: the settings you open it with — model, effort, cwd, env, mcp, extraArgs — are fixed for its lifetime and ride every turn, so session.run takes only per-turn options (attachments, schema, systemPrompt, readOnly, signal). Passing a setting per-turn throws InvalidOptions; to run under different settings, open a new session, or fork this one to keep the history.

Turns queue: a run called while another is in flight starts when it settles, threaded automatically. A failed turn rejects the turns queued behind it; calling run again retries from the last good point.

session.close() ends the conversation and releases whatever it holds — in ACP mode, the connection behind it. Turns still queued reject, a closed session refuses new ones (InvalidOptions), and calling it twice is safe. session.id stays valid, so you can resume later.

Persist and continue later

session.id is a plain string, set once the first turn reveals it. Store it anywhere and continue from any process:

const session = agent.session();
await session.run("Start reviewing this repo.");
await save("review-session", session.id);

// Days later:
const resumed = agent.session({ resume: await load("review-session") });
await resumed.run("Continue with src/.");

Conversation state lives with the CLI, so nothing else needs persisting. Iterating a turn surfaces the session event early, so you can persist the id before the first turn even ends.

Branch a conversation

Works withClaude CodeopencodeKilo CodePiagent.supports("forkSession")full matrix

Where the CLI can copy-on-resume (sessionFork: claude-code, opencode, Kilo Code, Pi), fork: true branches instead of continuing — session.id becomes the new conversation’s id:

const mainline = agent.session({ resume: saved });
const experiment = agent.session({ fork: true, resume: saved });

Resuming one id into two sessions without fork is a relay, not a branch: both continue the same conversation, in whatever order their turns arrive.

Which agents run sessions

Every built-in adapter opens a session. Continuing one needs resume, which agent.supports("resume") guards; where it is false, agent.session({ resume }) throws and a session can only start fresh. Agents in ACP mode add steering, covered next; the Mode and Resume columns in the matrix mark both.

Steer an ACP-mode session

Some agents run sessions in ACP mode, holding one connection open instead of respawning per turn. That connection unlocks two things: steering the turn as it runs, and visibility into permission requests. Gate on session.supports("steer").

Steer a running turn

Works withopencodeKilo CodegooseClineGemini CLICursorsession.supports("steer")full matrix

session.steer(text) injects guidance into the turn that is currently running:

const session = agent.session();
const turn = session.run("Migrate config/ to TypeScript.");
if (session.supports("steer")) {
  session.steer("Skip the legacy directory.");
}
await turn;

Steering adds guidance to an autonomous turn; it does not add an approval gate. On stdout-mode agents, steer throws UnsupportedCapability; guard and degrade, as with any capability.

Watch permission requests

An ACP-mode agent asks permission before some tools. AnyAgent answers for you, allowing so a run never blocks, and surfaces each exchange as a permission-request event carrying the agent’s own options:

for await (const event of session.run("Refactor the auth module.")) {
  if (event.type === "permission-request") {
    console.log(`[allowed ${event.name}]`, event.options.map((o) => o.label));
  }
}

Answering requests yourself (session.respond, an onPermission handler) arrives with a later release; today session.supports("respond") is false everywhere.

Next steps

On this page