State & Persistence
Turn uses immutable values and file-backed checkpoints to preserve suspended execution without application-level save/load callbacks.
Immutable State Values
Turn bindings and structured values are immutable. State evolution creates a new value, and the struct spread operator (..base) makes this concise:
let next = StructName { field: new_value, ..current };struct AgentState {
cycle: Num,
last_action: Str,
confidence_floor: Num
};
let state = AgentState { cycle: 1, last_action: "init", confidence_floor: 0.85 };
// Create a new state value while preserving untouched fields
let next_state = AgentState {
cycle: state.cycle + 1,
last_action: "analysis_complete",
..state
};
call("echo", "Cycle: " + next_state.cycle);At effect boundaries, the runner checkpoints the continuation and pending request. If execution restarts before the pending effect completes, Turn replays that request and resumes with its result.
Orthogonal Persistence (suspend)
For workflows that require waiting for external events, human-in-the-loop approvals, or long pauses between cycles, use suspend to checkpoint the entire VM execution state:
suspend;When the VM hits suspend, it halts execution cleanly and writes its serializable state to .turn_store/. Running the same agent ID loads that continuation and proceeds after the suspension point.
struct PaymentConfirmation { transaction_id: Str, amount: Num, status: Str };
call("echo", "Waiting for payment confirmation...");
// Checkpoint: VM state is persisted to disk here.
// External system can wake this process when payment arrives.
suspend;
// Execution resumes here after the VM is restarted externally
let payment = recall("pending_payment");
call("echo", "Payment received, continuing execution.");
let receipt = infer PaymentConfirmation {
"Generate a receipt for this payment: " + payment;
};NOTE
suspend is a synchronization primitive for the external world. It does not return a value. Use remember/recall to pass data across suspension boundaries.
Working Memory (remember / recall)
For state that must survive across suspend boundaries and process restarts, use the built-in key-value memory store:
// Write to the process's durable key-value store
remember("last_summary", "Q4 results: revenue up 18%, churn up 3%");
remember("cycle_count", 7);
// Read it back later, even after a restart or suspend
let summary = recall("last_summary");
let count = recall("cycle_count");
call("echo", "Cycle " + count + ": " + summary);remember writes to the process's isolated key-value store and recall retrieves by exact key. It returns null if the key does not exist. Memory is included in suspension/effect checkpoints.
The .turn_store Directory
The Turn VM writes all durable state to a .turn_store/ directory in the working directory. This includes:
- Process memory (from
remember) - Suspension checkpoints (from
suspend) - Pending effect requests
- Struct definitions loaded at runtime
You do not need to configure or manage this. It is created automatically on first run.
TIP
Add .turn_store/ to .gitignore. Checkpoints may contain prompts, model input, tool arguments, mailbox messages, and application state; do not commit them.
State vs. Context vs. Memory
| Feature | Best For | Lifetime | Retrieval |
|---|---|---|---|
Struct values (..spread) | Structured agent state, OODA loop data | Current process/checkpoint | Field access |
suspend checkpoint | Pause across external events | Across VM restarts | Automatic resume |
remember/recall | Facts and values across cycles | Permanent (persists across restarts) | Exact key lookup |
context.append | Working input for the current infer call | Current turn | Automatic (injected into LLM) |
WARNING
Effect replay is at-least-once. A crash after an external system accepts an operation but before Turn persists later progress can execute it again. Use idempotency keys for non-idempotent tools.