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:

syntax
let next = StructName { field: new_value, ..current };
state_value.tn
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:

syntax
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.

payment_flow.tn
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:

memory.tn
// 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

FeatureBest ForLifetimeRetrieval
Struct values (..spread)Structured agent state, OODA loop dataCurrent process/checkpointField access
suspend checkpointPause across external eventsAcross VM restartsAutomatic resume
remember/recallFacts and values across cyclesPermanent (persists across restarts)Exact key lookup
context.appendWorking input for the current infer callCurrent turnAutomatic (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.


Next Steps