A Long-Running Agent with Sessions
Build an agent that watches each new account for fifteen minutes: start a session, resume it with entries, approve a tool call, answer a held turn, close a session, and see one end.
This tutorial builds an agent with a session: one durable conversation per account that lives across many events. Opening an account starts a session for it, every entry posted to the account resumes that session, and fifteen minutes after the account opened the session takes a final turn and ends.
On the way you list sessions and their turns, read what each turn received and did, approve a tool call the agent paused before, answer a turn the agent declined to continue, and close a session by hand.
You need a running local environment. Open GraphiQL in the local console and run each step as its own request.
The agent uses the fixture model, which answers from a script instead of calling a model, so every run behaves the same and costs nothing. The steps take about 20 minutes, most of it waiting for the session to end. If you have run this tutorial before, choose fresh UUIDs, action names, account codes, and a fresh tran code.
1. Create the tool
The agent will have one tool, notify_ops, which sends a note about an account to an operations team. This version only returns what it was given, so you can see each call's input and output.
mutation CreateNotifyOps {
actions {
createAction(
id: "8156f490-be78-47a7-830a-328960b15ead"
input: {
name: "sessions-notify-ops"
description: "Send a note about an account to the operations team."
kind: "tool"
config: {
runtime: "starlark"
src: """
def run(event):
return {"status": "sent", "account_id": event["data"]["account_id"], "note": event["data"]["note"]}
"""
}
tool: {
name: "notify_ops"
description: "Send a note about an account to the operations team."
inputSchema: {
type: "object"
properties: { account_id: { type: "string" }, note: { type: "string" } }
required: ["account_id", "note"]
additionalProperties: false
}
}
}
) {
actionId
status
pending
}
}
}
The response reports status: UNPUBLISHED and pending: ACTIVE. The agent can reference the tool before it publishes.
2. Create the agent
mutation CreateAccountWatch {
actions {
createAction(
id: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0"
input: {
name: "sessions-account-watch"
description: "Watch each new account for its first fifteen minutes."
kind: "agent"
agent: {
prompt: "Watch this new account. Review each turn's inputs and tell operations about anything unusual."
}
config: {
runtime: "strands"
harness: { builtin_tools: [] }
model: {
provider: "fixture"
script: [
{
when: "purpose == 'turn' && session.final"
responses: [[{ text: "Final review: nothing further to report." }]]
}
{
when: "purpose == 'turn' && session.step == 0"
responses: [
[
{
toolUse: {
name: "notify_ops"
input: { note: "First deposit received. Please confirm identity checks." }
}
}
]
]
}
{
when: "purpose == 'turn' && session.step == 2"
responses: [
{
refusal: {
category: "review"
explanation: "A person should look at this deposit first."
}
}
]
}
{
when: "purpose == 'turn'"
responses: [[{ text: "Reviewed the new inputs. Nothing unusual." }]]
}
{
when: "purpose == 'outcome' && session.final"
responses: [
{
outcome: {
reply: "Watch period over."
session: {
status: "COMPLETED"
reason: "Watch period ended with no findings."
}
}
}
]
}
{
when: "purpose == 'outcome'"
responses: [{ outcome: { reply: "Still watching.", session: { status: "OPEN" } } }]
}
]
}
}
tools: [
{ name: "sessions-notify-ops", input: { account_id: "session.id" }, approval: REQUIRED }
]
triggers: [
{
name: "opened"
event: "event.type == 'account.inserted' && event.data.metadata.?watch.orValue(false) == true"
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" } }
required: ["account_id"]
additionalProperties: false
}
id: "session.input.account_id"
ends: "session.started_at + duration('15m')"
schedule: [{ name: "tick", every: "1m" }]
turns: { on: ["entry"] }
}
}
) {
actionId
status
pending
}
}
}
How the definition fits together:
| Part | What it does |
|---|---|
opened trigger | A start trigger. An account inserted with metadata.watch set to true starts a session, and its input maps the event to the session's input, {account_id}. Accounts without the flag start nothing. |
entry trigger | A resume trigger. Each entry is delivered to the open session whose ID is the entry's account_id. An entry on an account with no session is not delivered anywhere. |
session.id | Each session's ID is its account ID, so the same account always addresses the same session. |
session.ends | Each session ends 15 minutes after it started. Before then the agent can only keep it open; at the end it gets a final turn that must complete it. |
session.schedule | The session's own clock: tick fires every minute after the session starts, as an input to that session alone. |
turns.on | Only entry inputs start a turn. The account event and tick firings wait and ride along with the next turn. |
tools | The agent may call notify_ops. input fixes account_id to the session's ID, so the model supplies only note, and approval: REQUIRED makes every call wait for a person. |
model.script | The fixture's answers, chosen by when. session.step counts the session's earlier turns, so the first turn (step == 0) calls notify_ops and the third (step == 2) declines to continue. Other turns reply and keep the session OPEN; the final turn completes it. |
harness | builtin_tools: [] turns off the agent tools, which this agent does not need. |
Poll until the agent is live:
query ReadAccountWatch {
actions {
action(id: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0") {
status
pending
definition {
session {
id
ends
schedule {
name
every
}
turns {
on
}
}
}
}
}
}
{
"status": "ACTIVE",
"pending": null,
"definition": {
"session": {
"id": "session.input.account_id",
"ends": "session.started_at + duration('15m')",
"schedule": [
{
"name": "tick",
"every": "1m0s"
}
],
"turns": {
"on": ["entry"]
}
}
}
}
Wait for status: ACTIVE with pending: null before opening accounts. Actions respond to new events only.
3. Set up deposits
To post entries you need a settlement account to move funds from and a tran code. The settlement account has no watch flag, so it starts no session.
mutation SetUpDeposits {
settlement: createAccount(
input: {
accountId: "c74264c6-7659-4b40-b0e0-1559b0d34aa0"
code: "SESSIONS.SETTLEMENT"
name: "Sessions tutorial settlement"
normalBalanceType: DEBIT
}
) {
accountId
}
deposit: createTranCode(
input: {
tranCodeId: "89c65b8c-659d-41ea-b9b0-70323bd411fd"
code: "SESSIONS_DEPOSIT"
description: "Move funds from settlement into a customer account."
params: [{ name: "account", type: UUID }, { name: "amount", type: DECIMAL }]
transaction: { description: "'Deposit'" }
entries: [
{
accountId: "uuid('c74264c6-7659-4b40-b0e0-1559b0d34aa0')"
units: "params.amount"
currency: "'USD'"
entryType: "'DEPOSIT_DR'"
direction: "DEBIT"
layer: "SETTLED"
}
{
accountId: "params.account"
units: "params.amount"
currency: "'USD'"
entryType: "'DEPOSIT_CR'"
direction: "CREDIT"
layer: "SETTLED"
}
]
}
) {
tranCodeId
}
}
4. Open two watched accounts
Open Alice's and Bob's accounts with metadata: { watch: true }. Each insert matches the opened trigger and starts a session.
mutation OpenWatchedAccounts {
alice: createAccount(
input: {
accountId: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b"
code: "SESSIONS.ALICE"
name: "Alice"
normalBalanceType: CREDIT
metadata: { watch: true }
}
) {
accountId
}
bob: createAccount(
input: {
accountId: "5a1933de-63f9-44ff-83aa-24987e2d9c39"
code: "SESSIONS.BOB"
name: "Bob"
normalBalanceType: CREDIT
metadata: { watch: true }
}
) {
accountId
}
}
List the agent's sessions:
query ListSessions {
actions {
action(id: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0") {
sessions(first: 10) {
nodes {
sessionId
status
input
startedAt
endsAt
turns(first: 1) {
nodes {
number
}
}
}
}
}
}
}
{
"nodes": [
{
"sessionId": "82a7bfe9-d43e-4db9-8c91-efdf40ec822b",
"status": "OPEN",
"input": {
"account_id": "82a7bfe9-d43e-4db9-8c91-efdf40ec822b"
},
"startedAt": "2026-10-11T18:03:35.390689Z",
"endsAt": "2026-10-11T18:18:35.390689Z",
"turns": {
"nodes": []
}
},
{
"sessionId": "5a1933de-63f9-44ff-83aa-24987e2d9c39",
"status": "OPEN",
"input": {
"account_id": "5a1933de-63f9-44ff-83aa-24987e2d9c39"
},
"startedAt": "2026-10-11T18:03:35.390689Z",
"endsAt": "2026-10-11T18:18:35.390689Z",
"turns": {
"nodes": []
}
}
]
}
There is one session per account, each OPEN, with an endsAt fifteen minutes after startedAt. Neither has a turn yet: the account event is not one of the inputs turns.on names, so it waits in the session for the first entry.
5. Post a deposit and read the turn
Deposit into Alice's account. The deposit's entry on her account resumes her session and starts turn 1.
mutation FirstDeposit {
postTransaction(
input: {
transactionId: "fb49b488-1d87-49a4-95f0-cf1cbcdc5b98"
tranCode: "SESSIONS_DEPOSIT"
params: { account: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b", amount: "250.00" }
}
) {
transactionId
}
}
Read the session's latest turn. You will run this query again in later steps.
query LatestTurn {
actions {
session(
actionId: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0"
id: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b"
) {
status
reason
turns(first: 1) {
nodes {
number
status
final
inputs {
kind
triggers
event {
eventType
data
}
}
execution {
executionId
status
invocation {
output
approvals {
interruptId
tool
input
}
hold {
interruptId
cause
category
explanation
}
}
}
}
}
}
}
}
{
"status": "WAITING",
"reason": "Waiting for approval to call notify_ops.",
"turns": {
"nodes": [
{
"number": 1,
"status": "COMMITTED",
"final": false,
"inputs": [
{
"kind": "EVENT",
"triggers": [0],
"event": {
"eventType": "account.inserted",
"data": {
"account_id": "82a7bfe9-d43e-4db9-8c91-efdf40ec822b",
"code": "SESSIONS.ALICE",
"metadata": {
"watch": true
},
"…": "…"
}
}
},
{
"kind": "EVENT",
"triggers": [1],
"event": {
"eventType": "entry.inserted",
"data": {
"account_id": "82a7bfe9-d43e-4db9-8c91-efdf40ec822b",
"amount": "250.00 USD",
"entry_type": "DEPOSIT_CR",
"…": "…"
}
}
}
],
"execution": {
"executionId": "33a4520c-0017-537b-88c8-2209b3104bad",
"status": "SUCCEEDED",
"invocation": {
"output": null,
"approvals": [
{
"interruptId": "v1:before_tool_call:tooluse_33a4520c_1_1:128bdd77-c3a5-5902-9bbc-0ab0209465e9",
"tool": "notify_ops",
"input": {
"account_id": "82a7bfe9-d43e-4db9-8c91-efdf40ec822b",
"note": "First deposit received. Please confirm identity checks."
}
}
],
"hold": null
}
}
}
]
}
}
What to look for:
- The turn took both inputs that were waiting: the
account.insertedevent that started the session and theentry.insertedevent that started the turn, in the order they arrived.triggerslists the positions of the triggers each one matched:0isopenedand1isentry. - The turn committed with the session
WAITING. The script made the agent callnotify_ops, which requires approval, so the turn ended before the call and committed everything up to it. It has nooutput. approvalslists the paused call: the tool, the input it will run with (account_idis the fixed input,notecame from the model), and theinterruptIdyou answer it with.
Copy the interruptId into the Variables panel:
{ "interruptId": "REPLACE_WITH_INTERRUPT_ID" }
6. Approve the call
mutation ApproveNotify($interruptId: String!) {
actions {
respondToSession(
actionId: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0"
id: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b"
interruptId: $interruptId
response: { approval: { decision: APPROVE, reason: "Identity checks confirmed." } }
) {
identity
kind
status
}
}
}
{
"identity": "2dac34bd-7b48-5074-8263-08562ad60728",
"kind": "COMMAND",
"status": "PENDING"
}
The answer is admitted to the session as a COMMAND input and runs in a turn of its own, where the agent continues from where it paused: the approved call runs, and the agent replies. Read turn 2 and the call it made:
query ApprovedCall {
actions {
session(
actionId: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0"
id: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b"
) {
status
turn(number: 2) {
status
inputs {
kind
event {
eventType
data
}
}
execution {
invocation {
output
}
children(first: 10) {
nodes {
action {
definition {
name
}
}
status
invocation {
output
}
}
}
}
}
}
}
}
{
"status": "OPEN",
"turn": {
"status": "COMMITTED",
"inputs": [
{
"kind": "COMMAND",
"event": {
"eventType": "action_session.responded",
"data": {
"decision": "APPROVE",
"interrupt_id": "v1:before_tool_call:…",
"reason": "Identity checks confirmed.",
"responder": "arn:aws:iam::000000000000:user/twisp",
"tool": "notify_ops"
}
}
}
],
"execution": {
"invocation": {
"output": "Still watching."
},
"children": {
"nodes": [
{
"action": {
"definition": {
"name": "sessions-notify-ops"
}
},
"status": "SUCCEEDED",
"invocation": {
"output": {
"account_id": "82a7bfe9-d43e-4db9-8c91-efdf40ec822b",
"note": "First deposit received. Please confirm identity checks.",
"status": "sent"
}
}
}
]
}
}
}
}
The session is OPEN again. Turn 2's only input is your answer, recorded as an action_session.responded event with your decision, reason, and identity. children holds the notify_ops call as its own execution, with its output, and the turn's output is the agent's reply.
7. Post a deposit the agent declines to review
Wait until at least a minute has passed since you opened the accounts, so that the session's clock has fired, then post a larger deposit:
mutation SecondDeposit {
postTransaction(
input: {
transactionId: "fbad78e1-2ec1-4973-a7d0-6c2e44b41b22"
tranCode: "SESSIONS_DEPOSIT"
params: { account: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b", amount: "9000.00" }
}
) {
transactionId
}
}
Run LatestTurn from step 5 again:
{
"status": "WAITING",
"reason": "Turn 3 is held: the model declined to continue it. Answer it with RETRY or SKIP.",
"turns": {
"nodes": [
{
"number": 3,
"status": "HELD",
"final": false,
"inputs": [
{
"kind": "EVENT",
"triggers": [1],
"event": {
"eventType": "entry.inserted",
"data": {
"account_id": "82a7bfe9-d43e-4db9-8c91-efdf40ec822b",
"amount": "9000.00 USD",
"entry_type": "DEPOSIT_CR",
"…": "…"
}
}
},
{
"kind": "SCHEDULE",
"triggers": [],
"event": {
"eventType": "schedule.fired",
"data": {
"missed": 0,
"schedule": "tick",
"scheduled_at": "2026-10-11T18:04:35.390689Z"
}
}
}
],
"execution": {
"executionId": "34a580e0-d174-55cb-b8d0-ca2caae55da4",
"status": "FAILED",
"invocation": {
"output": null,
"approvals": [],
"hold": {
"interruptId": "34a580e0-d174-55cb-b8d0-ca2caae55da4",
"cause": "REFUSED",
"category": "review",
"explanation": "A person should look at this deposit first."
}
}
}
}
]
}
}
- Turn 3 took a firing of the session's clock.
tickfired while the session waited, and becauseturns.onnames onlyentry, the firing waited for this turn. It is an input of kindSCHEDULEwhoseschedule.firedevent names the entry and its time.missedcounts earlier firings oftickthat the turn passed over. - The turn is
HELD. The script made the model decline to continue this turn. Instead of failing the session, Twisp discarded everything the turn did and held it: the session isWAITING, with a reason naming the turn, andholdsays why. A turn that never reports an outcome after its retries is held the same way, with causeUNFINISHED.
Copy the hold's interruptId into the Variables panel.
8. Retry the held turn
Answer the hold with RETRY and a note for the agent. SKIP would instead go on without the held turn's inputs.
mutation RetryHeldTurn($interruptId: String!) {
actions {
respondToSession(
actionId: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0"
id: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b"
interruptId: $interruptId
response: {
hold: {
decision: RETRY
note: "The deposit came from the customer's own bank. Keep watching."
}
}
) {
identity
kind
status
}
}
}
Run LatestTurn again:
{
"status": "OPEN",
"reason": null,
"turns": {
"nodes": [
{
"number": 4,
"status": "COMMITTED",
"final": false,
"inputs": [
{
"kind": "COMMAND",
"triggers": [],
"event": {
"eventType": "action_session.responded",
"data": {
"decision": "RETRY",
"interrupt_id": "34a580e0-d174-55cb-b8d0-ca2caae55da4",
"note": "The deposit came from the customer's own bank. Keep watching.",
"responder": "arn:aws:iam::000000000000:user/twisp"
}
}
}
],
"execution": {
"executionId": "53214c2d-c4e5-5190-8c90-67ba74fbc533",
"status": "SUCCEEDED",
"invocation": {
"output": "Still watching.",
"approvals": [],
"hold": null
}
}
}
]
}
}
Turn 4 committed and the session is OPEN. The turn's record lists your answer as its input, and the turn ran the held inputs again with your note. Its execution's transcript shows exactly what the model was given. Read the first event of the turn's transcript:
query RetriedTurnMessage {
actions {
session(
actionId: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0"
id: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b"
) {
turn(number: 4) {
execution {
invocation {
transcript(first: 3) {
nodes {
sequence
kind
data
}
}
}
}
}
}
}
}
The first event, model.input, holds the turn's message. Trimmed to the inputs, it reads:
<turn session="82a7bfe9-d43e-4db9-8c91-efdf40ec822b" step="3" now="2026-10-11T18:05:03.115332Z" started_at="2026-10-11T18:03:35.390689Z" ends_at="2026-10-11T18:18:35.390689Z">
<session_input>{"account_id":"82a7bfe9-d43e-4db9-8c91-efdf40ec822b"}</session_input>
<inputs count="3" inline="3">
<input n="1" identity="8fc3b2a3-…" type="entry.inserted" committed_at="2026-10-11T18:04:46.209108Z" trigger="entry">{"data":{"account_id":"82a7bfe9-d43e-4db9-8c91-efdf40ec822b","amount":"9000.00 USD",…}}</input>
<input n="2" identity="schedule/tick/2026-10-11T18:04:35.390689Z" kind="schedule" source="tick" scheduled_at="2026-10-11T18:04:35.390689Z"/>
<input n="3" identity="41dacf88-…" kind="command" type="action_session.responded" committed_at="2026-10-11T18:05:02.83208Z">{"data":{"decision":"RETRY","note":"The deposit came from the customer's own bank. Keep watching.",…}}</input>
</inputs>
</turn>
The agent saw the held deposit, the clock's firing, and your note. step is 3 because the held turn counts as one of the session's turns.
9. Close a session
Bob's account needs no watching after all. Close his session with a reason:
mutation CloseBobSession {
actions {
closeSession(
actionId: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0"
id: "5a1933de-63f9-44ff-83aa-24987e2d9c39"
reason: "Onboarding team reviewed this account."
) {
sessionId
status
reason
}
}
}
{
"sessionId": "5a1933de-63f9-44ff-83aa-24987e2d9c39",
"status": "CANCELLED",
"reason": "Onboarding team reviewed this account."
}
A closed session accepts no further input: later entries on Bob's account are not delivered to it. Its inputs and turns stay readable, and closing it again returns it unchanged. List both sessions:
query AllWatchSessions {
actions {
sessions(where: { actionId: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0" }, first: 10) {
nodes {
sessionId
status
reason
}
}
}
}
{
"nodes": [
{
"sessionId": "82a7bfe9-d43e-4db9-8c91-efdf40ec822b",
"status": "OPEN",
"reason": null
},
{
"sessionId": "5a1933de-63f9-44ff-83aa-24987e2d9c39",
"status": "CANCELLED",
"reason": "Onboarding team reviewed this account."
}
]
}
10. Watch the session end
Fifteen minutes after Alice's account opened, her session reaches its endsAt. Firings of tick that arrived since turn 4 are pending, so the session gets a final turn at once, which takes everything pending. The script's final-turn entries reply and complete the session. If nothing were pending, the session would complete with no turn, with reason ended.
After endsAt has passed, read the session and all its turns:
query WatchEnded {
actions {
session(
actionId: "2c41d6a5-4ac8-4701-882d-bb5b6fd039d0"
id: "82a7bfe9-d43e-4db9-8c91-efdf40ec822b"
) {
status
reason
endsAt
turns(first: 10) {
nodes {
number
status
final
created
inputs {
kind
event {
eventType
data
}
}
execution {
invocation {
output
}
}
}
}
}
}
}
{
"status": "COMPLETED",
"reason": "Watch period ended with no findings.",
"endsAt": "2026-10-11T18:18:35.390689Z",
"turns": {
"nodes": [
{
"number": 5,
"status": "COMMITTED",
"final": true,
"created": "2026-10-11T18:18:36.182351Z",
"inputs": [
{
"kind": "SCHEDULE",
"event": {
"eventType": "schedule.fired",
"data": {
"missed": 12,
"schedule": "tick",
"scheduled_at": "2026-10-11T18:17:35.390689Z"
}
}
}
],
"execution": {
"invocation": {
"output": "Watch period over."
}
}
},
{
"number": 4,
"status": "COMMITTED",
"final": false,
"created": "2026-10-11T18:05:02.966275Z",
"inputs": [
{
"kind": "COMMAND",
"event": {
"eventType": "action_session.responded",
"data": {
"…": "…"
}
}
}
],
"execution": {
"invocation": {
"output": "Still watching."
}
}
},
{
"number": 3,
"status": "HELD",
"final": false,
"created": "2026-10-11T18:04:46.665396Z",
"inputs": [
{
"kind": "EVENT",
"event": {
"eventType": "entry.inserted",
"data": {
"…": "…"
}
}
},
{
"kind": "SCHEDULE",
"event": {
"eventType": "schedule.fired",
"data": {
"missed": 0,
"schedule": "tick",
"scheduled_at": "2026-10-11T18:04:35.390689Z"
}
}
}
],
"execution": {
"invocation": {
"output": null
}
}
},
{
"number": 2,
"status": "COMMITTED",
"final": false,
"created": "2026-10-11T18:04:02.866639Z",
"inputs": [
{
"kind": "COMMAND",
"event": {
"eventType": "action_session.responded",
"data": {
"…": "…"
}
}
}
],
"execution": {
"invocation": {
"output": "Still watching."
}
}
},
{
"number": 1,
"status": "COMMITTED",
"final": false,
"created": "2026-10-11T18:03:46.772632Z",
"inputs": [
{
"kind": "EVENT",
"event": {
"eventType": "account.inserted",
"data": {
"…": "…"
}
}
},
{
"kind": "EVENT",
"event": {
"eventType": "entry.inserted",
"data": {
"…": "…"
}
}
}
],
"execution": {
"invocation": {
"output": null
}
}
}
]
}
}
Turn 5 is final. It took only the latest tick firing, and missed: 12 counts the earlier firings since turn 4 that it passed over. The session is COMPLETED with the reason the final turn reported. Firings stop at the end, and later entries on Alice's account are not delivered.
Troubleshooting
| Symptom | Check |
|---|---|
| No session appears after opening an account | The agent must be status: ACTIVE before the account is inserted, and the account needs metadata: { watch: true }. Open a new account with a new ID and code. |
| More sessions than you opened | Every active action with a matching start trigger starts its own session. Disable agents left from earlier runs of this tutorial. |
| The deposit starts no turn | The entry must be on a watched account whose session is open. An entry that arrives while the session is WAITING is delivered once it is OPEN again. |
respondToSession fails | The session must be WAITING on that interruptId, and each request is answered once. Read approvals or hold from the latest turn again. |
Turn 3 has no SCHEDULE input | Less than a minute had passed since the session started. The firing waits for the next turn instead. |
The approval turn or turn 3 is final | The session's end passed before that step. The final turn completes the session, so later steps have nothing to resume. Start again with a new agent and accounts, or lengthen session.ends. |
| A turn does not do what the step describes | The script keys on session.step, so the order of turns matters: post the deposits and answer the requests in the order shown, each after the turn before it has committed. |
Read Sessions for when to give an agent a session, and the Sessions reference for session roles, turns, the end and clock, approvals, and held turns. The fixture model and the turn outcome are in the Agents reference.