Sessions

Give an action a durable identity across many events, with ordered turns, an end, a clock, approvals, and held turns.

Without a session, each execution of an action stands alone: it sees one event and remembers nothing. Some work is about one subject over time instead. Watching a newly opened account for its first 30 days means reviewing every entry posted to that account, in order, knowing what was already reviewed and which findings are open, and stopping when the 30 days are over.

A session gives an action that continuity. Each session has an ID, an input fixed when it starts, state that the action carries from turn to turn, an ordered history of everything delivered to it, and a lifecycle. The same action and session ID always address the same session.

The Sessions reference lists every field and rule. This page uses one running example: an early-fraud-detector agent that watches each new account for 30 days.

The session block

An action's session block declares how its sessions are identified, when they end, and the clock they keep; its triggers declare which events start or reach a session.

triggers: [
  {
    name: "opened"
    event: "event.type == \"account.inserted\""
    session: { start: { input: { account_id: "string(event.data.account_id)" } } }
  }
  {
    name: "entry"
    event: "event.type == \"entry.inserted\""
    session: { resume: { id: "string(event.data.account_id)" } }
  }
]
session: {
  inputSchema: {
    type: "object"
    properties: { account_id: { type: "string", format: "uuid" } }
    required: ["account_id"]
  }
  id: "string(session.input.account_id)"
  ends: "session.started_at + duration('720h')"
  schedule: [{ name: "morning", cron: "0 9 * * 1-5", timezone: "America/New_York" }]
  turns: { on: ["entry", "morning"] }
}
FieldWhat it decides
inputSchemaThe shape of each session's input, fixed when it starts. Here, the account it watches.
idThe session ID, computed from the input. One account, one session.
endsWhen each session ends. Here, 720 hours after it started. See The end of a session.
scheduleThe session's own clock. See Schedules.
turnsWhen the session works. See Turns.

Each session keeps the action version it started with. Updating the definition changes the triggers, schemas, tools, end, and clock of sessions started after the update, never of those already open.

Session roles

With a session block, every trigger has a session role: it either starts a session or resumes one.

RoleWhat a matching event does
start: {input}Maps the event to the session's input, computes the ID, and finds the open session with that ID or creates it. A closed session ID never starts again.
resume: {id}Delivers the event to the open session with that ID. It never creates a session.
resume: {input}Delivers the event to every open session whose input has a field equal to a value from the event, such as every session watching one customer.

In the example, account.inserted starts the account's session, and each entry.inserted for that account resumes it. An entry that arrives before its account's session exists waits up to 24 hours for the session to start.

An event that fails to start or resume a session, such as one whose mapped input fails inputSchema, runs nothing. It is kept as an intake record: a failed execution that names the trigger and the step that failed. Fix the definition, then rerun the record to deliver the event.

Turns

A session works in turns. A turn is one pass of the action's handler over a set of the session's pending inputs; for an agent, it is the agent's whole loop up to its outcome. Turns run one at a time, and each commits its new state, its output, and the consumption of its inputs together, so every turn builds on what the turn before committed. See Session turns.

Events delivered to a session wait in it, in the order they arrived, until a turn takes them. session.turns decides when that happens:

SettingUse it to
onName the triggers and schedule entries whose inputs start a turn. Other inputs wait for the next turn. In the example, the opening itself starts no turn: the first entry, or the next morning firing, starts a turn that reviews the opening with it.
windowAfter the session has been idle, wait this long after the first input before starting a turn, so a burst of entries is one turn instead of many.
maxInputs, maxBytesBound how much one turn takes. An agent's turn takes every pending input up to 1,000 by default; a Starlark action's, one.

An agent's turn shows each input to the model whole by default. A trigger's view shows only the fields the agent needs, such as an entry's amount, direction, and time, and the agent can still read the whole event.

The lifecycle

 account.inserted
        |
        v
   +---------+   turn reports OPEN    +---------+
   |  OPEN   | <--------------------- |  turn   |
   |         | ---------------------> | running |
   +---------+   input starts a turn  +---------+
     |   ^                                 |  |
     |   |  respondToSession               |  +--> COMPLETED  (final turn, or a turn
     |   |                                 |                   that completes it)
     |   +------------ WAITING <-----------+  approval needed, or turn held
     |                                     |
     |                                     +-----> FAILED     (turn failed for good)
     |
     +--> COMPLETED, reason "ended"   (end reached, nothing pending)
     +--> CANCELLED                   (closeSession, from any open status)
Session statusMeaningFinal
OPENAccepting inputs. A turn runs when an input starts one.no
WAITINGWaiting for a person: an approval or a held turn. New inputs wait, in order.no
COMPLETEDA turn completed it, or its end passed with nothing pending.yes
FAILEDA turn failed for good: after its retries, or with an error that is not retried. Its inputs remain, unconsumed, and the reason names the execution.yes
CANCELLEDAn operator closed it.yes

No input is delivered to a session in a final status. Each turn has a status of its own: RUNNING, COMMITTED, FAILED, HELD, or CANCELLED.

The end of a session

session.ends makes "30 days" a rule the platform enforces rather than an instruction to the model. It is evaluated when the session starts, and the result is the session's endsAt. See Session end.

  • Before the end, no turn may complete the session. An agent is offered only OPEN.
  • At the end, a session with pending inputs gets a final turn at once. It takes every pending input, whatever turns.on names, and must complete the session with a reason.
  • With nothing pending, the session completes with the reason ended, and no turn runs. A quiet account costs nothing.
  • Nothing goes unreviewed. A turn is final only when it takes every pending input; if maxInputs leaves some, the next turn continues.

Without ends, a session completes only when a turn reports COMPLETED or an operator closes it.

Schedules

session.schedule gives each session its own clock: up to 8 named entries, each a cron expression in a timezone or an every period from the session's start. In the example, the morning entry gives each account's session a weekday review at 9:00 in New York, even on a day with no entries. See Session clock.

A firing is an input to that session alone, of kind SCHEDULE. It starts a turn at once when turns.on names its entry or is omitted, and never waits for a window. A turn takes at most one firing of each entry, the latest due, and missed counts the firings it passed over, for example while the session was waiting for an approval. No entry fires at or after the session's end.

Use a schedule trigger, not a session schedule, for work that is about no particular subject, such as a nightly report.

Approvals

Some tool calls should not happen without a person. Give a tool reference approval: REQUIRED, and before each call to it the agent's turn commits with the session WAITING. Everything the turn did before the call commits with it. See Approvals.

tools: [
  { name: "get_account_activity", input: { account_id: "string(session.input.account_id)" } }
  { name: "notify_human_ops", approval: REQUIRED }
]

The turn's execution lists the paused call in invocation.approvals, with the tool, the version it would run, and its input. Answer with respondToSession and response.approval: APPROVE, optionally with replacement input, or DENY, each with an optional reason. The answer is delivered as a COMMAND input in a turn of its own, and the agent continues from where it paused: an approved call runs, and a denied call is reported to the agent with the reason. Answering requires the RESPOND policy action on the agent's action record, and the answer is recorded with the responder.

Only an agent with a session can require approval. A gated tool cannot be called from programmatic tool calling. In preview mode, a gated call runs at once as if approved, and is marked grantedInPreview so the record shows no person approved it.

Held turns

A turn that the model declines to continue, or that never reports a valid outcome, does not fail the session. It is held: nothing it did commits, its transcript is kept, and the session waits with the state the turn before left. See Held turns.

The turn's execution has invocation.hold, with the cause, REFUSED or UNFINISHED, and, for a refusal, what the provider reported. Read its transcript, then answer with respondToSession and response.hold:

  • RETRY runs the held turn's inputs again, with your optional note shown to the agent.
  • SKIP goes on without them, with no model call. A turn that resumed from approval responses cannot be skipped, since the responses cannot be given again: retry it, or close the session.

A retried turn runs at the session's pinned version, so fixing the agent's definition helps sessions started after the fix, not the held one.

Inspecting and closing a session

A session reaches everything that happened in it: its state, its turns with each turn's inputs, checkpoint, and execution, its inputs in delivery order, and its history of versions. Read one with session(actionId, id), or list them with sessions(where: ...). See Inspecting and closing sessions.

Closing a session

closeSession(actionId, id, reason) cancels one session. It accepts no further input, a running turn cannot commit, and its history stays readable. Effects already performed are not undone. Close a session to stop one subject's work without touching the others; disable the action to stop new sessions.

Tool sessions

A tool can have a session too. A tool session is call history only: each call names its session with a start mapping over tool.input, and each committed call adds one CALL input to it, with its input and output. It has no state, turns, end, or schedule, and calls never wait for one another. Use it when a tool should see its earlier calls for the same subject, such as a notification tool that must not notify twice about one finding.

tool: {
  name: "notify_human_ops"
  description: "Notify human operations about a finding."
  inputSchema: {
    type: "object"
    properties: { account_id: { type: "string" }, note: { type: "string" } }
    required: ["account_id"]
  }
  session: { start: { input: { account_id: "tool.input.account_id" } } }
}

An MCP server records every request in a session of its own, grouped by user, host, or your run ID; see MCP sessions.

Next

Memory

Keep facts beyond a session, and let a session search its own earlier turns.

Sessions tutorial

Build the 30-day account watch, step by step.

Sessions reference

Every field, rule, and operation.

Previous
Agents