Context Window
In Turn, the context window is the set of information the LLM receives when you call infer. You build it up explicitly using context.append and context.system. Every process has its own isolated context window. Child processes always start empty.
The Context Problem
Every LLM agent framework eventually hits the same wall: the model's context limit. When you hit it, you either crash with a context_length_exceeded error, or you silently truncate from the front of history and hope the model doesn't notice the discontinuity.
Worse, research (like Liu et al.'s "Lost in the Middle") shows that LLMs recall information at the beginning (primacy) and end (recency) of the context window at ~90% accuracy, but performance drops to ~50% in the middle.
Turn solves this at the language level by implementing a Tripartite Structured Context instead of a flat list of messages. It makes context a first-class, per-process resource that structurally protects the high-recall zones.
The Tripartite Architecture (P0, P1, P2)
Every process context is strictly managed across three tiers:
- P0 (System): Directives set by
context.system(). This always occupies the primacy position (rendered first). - P1 (Working): Recent items appended by
context.append(). Bounded at 100 entries. This always occupies the recency position (rendered last). - P2 (Episodic): When P1 is full, the oldest working item is demoted to P2 rather than dropped. Bounded at 200 entries. This occupies the middle zone.
When infer is called, the VM automatically flattens this into the optimal order: P0 -> P2 -> P1. This guarantees your system prompts and most recent events are always in the highest-attention zones.
context.append : Writing to the Context Window
context.append(expression);Appends a string to the agent's context window. The accumulated context is automatically injected into every subsequent infer call within this process.
context.append("You are a senior financial analyst. Be precise and cite data.");
context.append("Client portfolio: " + portfolio_summary);
context.append("User question: " + user_query);
// The LLM will receive all three in order when infer is called
let result = infer Analysis { "Provide your recommendation."; };context.system : Setting the System Prompt
context.system(expression);Sets the system-level instruction for this process. This is sent as the system role message to the LLM. Use it to establish the agent's persona and fundamental constraints.
context.system("You are a world-class security auditor. You identify vulnerabilities with precision and concision. You never speculate. If you are uncertain, you say so.");
context.append("Code to review: " + source_code);
let audit = infer AuditReport { "Identify all security issues in the provided code."; };
call("echo", audit["summary"]);NOTE
context.system overrides any previous system prompt for this process. Call it once at the start of the process before any infer calls.
Context Is Per-Process
Every spawned agent has its own isolated context window. When you spawn or spawn_link a child process, it starts with an empty context:
context.append("Parent context: I am the orchestrator.");
let child = spawn turn() {
// This process starts fresh. The parent's context is NOT inherited
context.append("Child context: I am the analyst.");
let result = infer Summary { "Summarize my role."; };
return result;
};
// The parent's infer calls see "orchestrator" context
// The child's infer calls see "analyst" context
// Fully isolated. No accidental context bleeding between agentsChaining Inference Calls
Because context.append feeds every subsequent infer call in the same process, you can build typed inference pipelines where each step observes the structured output of the previous one. The model's earlier work becomes part of its working context for the next call.
struct ResearchNotes { key_facts: Str, gaps: Str };
struct Hypothesis { claim: Str, supporting_evidence: Str };
struct Report { executive_summary: Str, recommendation: Str };
let raw_data = recall("market_data");
// Step 1: Extract structured facts from raw input
context.append("Raw market data: " + raw_data);
let notes = infer ResearchNotes { "Extract the key facts and identify any knowledge gaps."; };
// Step 2: Form a hypothesis informed by the extracted facts
context.append("Key facts found: " + notes.key_facts);
context.append("Knowledge gaps: " + notes.gaps);
let hypothesis = infer Hypothesis { "Propose a market hypothesis based on this evidence."; };
// Step 3: Write the final report informed by both prior steps
context.append("Working hypothesis: " + hypothesis.claim);
let report = infer Report { "Write the executive summary and final recommendation."; };
return report;Each step receives not just its own prompt, but the full accumulated context of everything the agent has observed and concluded. This is a typed reasoning chain. Every intermediate result has a schema-validated shape before it enters the next step, so the downstream infer call is never guessing about the structure of what it received.
Context vs. Memory
This distinction is critical:
context = the agent's working scratchpad for THIS TURN
token-bounded, per-process
what the model can see right now
written with context.append() / context.system()
memory = the agent's LONG-TERM KNOWLEDGE
unlimited, persisted across restarts
written with remember(), read with recall()Think of context as a whiteboard: you write on it, the model reads it, and you erase it at the end of the session. Memory is a notebook: permanent, exact-key access, and you look things up when you need them.
Compute Budget (Gas Meter)
The VM enforces a computational gas budget per process. This is not a count of LLM tokens. It is a counter of VM instructions executed, which bounds CPU time and prevents infinite loops and runaway agents. When the budget reaches zero, the VM halts the process cleanly.
TIP
The token budget is a VM-level computational guard. It is independent from the LLM's context window size. Both are enforced separately. The VM budget guards compute, and the LLM limit guards context.