12 KiB
role, summary, see-also
| role | summary | see-also | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| in-context-state-management | In-context state management using the narration protocol with text markers. This approach tracks execution state within the conversation history itself. The OpenProse VM "thinks aloud" to persist state—what you say becomes what you remember. |
|
In-Context State Management
This document describes how the OpenProse VM tracks execution state using structured narration in the conversation history. This is one of two state management approaches (the other being file-based state in filesystem.md).
Overview
In-context state uses text-prefixed markers to persist state within the conversation. The VM "thinks aloud" about execution—what you say becomes what you remember.
Key principle: Your conversation history IS the VM's working memory.
When to Use In-Context State
In-context state is appropriate for:
| Factor | In-Context | Use File-Based Instead |
|---|---|---|
| Statement count | < 30 statements | >= 30 statements |
| Parallel branches | < 5 concurrent | >= 5 concurrent |
| Imported programs | 0-2 imports | >= 3 imports |
| Nested depth | <= 2 levels | > 2 levels |
| Expected duration | < 5 minutes | >= 5 minutes |
Announce your state mode at program start:
OpenProse Program Start
State mode: in-context (program is small, fits in context)
The Narration Protocol
Use text-prefixed markers for each state change:
| Marker | Category | Usage |
|---|---|---|
| [Program] | Program | Start, end, definition collection |
| [Position] | Position | Current statement being executed |
| [Binding] | Binding | Variable assignment or update |
| [Input] | Input | Receiving inputs from caller |
| [Output] | Output | Producing outputs for caller |
| [Import] | Import | Fetching and invoking imported programs |
| [Success] | Success | Session or block completion |
| [Warning] | Error | Failures and exceptions |
| [Parallel] | Parallel | Entering, branch status, joining |
| [Loop] | Loop | Iteration, condition evaluation |
| [Pipeline] | Pipeline | Stage progress |
| [Try] | Error handling | Try/catch/finally |
| [Flow] | Flow | Condition evaluation results |
| [Frame+] | Call Stack | Push new frame (block invocation) |
| [Frame-] | Call Stack | Pop frame (block completion) |
Narration Patterns by Construct
Session Statements
[Position] Executing: session "Research the topic"
[Task tool call]
[Success] Session complete: "Research found that..."
[Binding] let research = <result>
Parallel Blocks
[Parallel] Entering parallel block (3 branches, strategy: all)
- security: pending
- perf: pending
- style: pending
[Multiple Task calls]
[Parallel] Parallel complete:
- security = "No vulnerabilities found..."
- perf = "Performance is acceptable..."
- style = "Code follows conventions..."
[Binding] security, perf, style bound
Loop Blocks
[Loop] Starting loop until **task complete** (max: 5)
[Loop] Iteration 1 of max 5
[Position] session "Work on task"
[Success] Session complete
[Loop] Evaluating: **task complete**
[Flow] Not satisfied, continuing
[Loop] Iteration 2 of max 5
[Position] session "Work on task"
[Success] Session complete
[Loop] Evaluating: **task complete**
[Flow] Satisfied!
[Loop] Loop exited: condition satisfied at iteration 2
Error Handling
[Try] Entering try block
[Position] session "Risky operation"
[Warning] Session failed: connection timeout
[Binding] err = {message: "connection timeout"}
[Try] Executing catch block
[Position] session "Handle error" with context: err
[Success] Recovery complete
[Try] Executing finally block
[Position] session "Cleanup"
[Success] Cleanup complete
Variable Bindings
[Binding] let research = "AI safety research covers..." (mutable)
[Binding] const config = {model: "opus"} (immutable)
[Binding] research = "Updated research..." (reassignment, was: "AI safety...")
Input/Output Bindings
[Input] Inputs received:
topic = "quantum computing" (from caller)
depth = "deep" (from caller)
[Output] output findings = "Research shows..." (will return to caller)
[Output] output sources = ["arxiv:2401.1234", ...] (will return to caller)
Block Invocation and Call Stack
Track block invocations with frame markers:
[Position] do process(data, 5)
[Frame+] Entering block: process (execution_id: 1, depth: 1)
Arguments: chunk=data, depth=5
[Position] session "Split into parts"
[Task tool call]
[Success] Session complete
[Binding] let parts = <result> (execution_id: 1)
[Position] do process(parts[0], 4)
[Frame+] Entering block: process (execution_id: 2, depth: 2)
Arguments: chunk=parts[0], depth=4
Parent: execution_id 1
[Position] session "Split into parts"
[Task tool call]
[Success] Session complete
[Binding] let parts = <result> (execution_id: 2) # Shadows parent's 'parts'
... (continues recursively)
[Frame-] Exiting block: process (execution_id: 2)
[Position] session "Combine results"
[Task tool call]
[Success] Session complete
[Frame-] Exiting block: process (execution_id: 1)
Key points:
- Each
[Frame+]must have a matching[Frame-] execution_iduniquely identifies each invocationdepthshows call stack depth (1 = first level)- Bindings include
(execution_id: N)to indicate scope - Nested frames show
Parent: execution_id Nfor the scope chain
Scoped Binding Narration
When inside a block invocation, always include the execution_id:
[Binding] let result = "computed value" (execution_id: 43)
For variable resolution across scopes:
[Binding] Resolving 'config': found in execution_id 41 (parent scope)
Program Imports
[Import] Importing: @alice/research
Fetching from: https://p.prose.md/@alice/research
Inputs expected: [topic, depth]
Outputs provided: [findings, sources]
Registered as: research
[Import] Invoking: research(topic: "quantum computing")
[Input] Passing inputs:
topic = "quantum computing"
[... imported program execution ...]
[Output] Received outputs:
findings = "Quantum computing uses..."
sources = ["arxiv:2401.1234"]
[Import] Import complete: research
[Binding] result = { findings: "...", sources: [...] }
Context Serialization
In-context state passes values, not references. This is the key difference from file-based and PostgreSQL state. The VM holds binding values directly in conversation history.
When passing context to sessions, format appropriately:
| Context Size | Strategy |
|---|---|
| < 2000 chars | Pass verbatim |
| 2000-8000 chars | Summarize to key points |
| > 8000 chars | Extract essentials only |
Format:
Context provided:
---
research: "Key findings about AI safety..."
analysis: "Risk assessment shows..."
---
Limitation: In-context state cannot support RLM-style "environment as variable" patterns where agents query arbitrarily large bindings. For programs with large intermediate values, use file-based or PostgreSQL state instead.
Complete Execution Trace Example
agent researcher:
model: sonnet
let research = session: researcher
prompt: "Research AI safety"
parallel:
a = session "Analyze risk A"
b = session "Analyze risk B"
loop until **analysis complete** (max: 3):
session "Synthesize"
context: { a, b, research }
Narration:
[Program] Program Start
Collecting definitions...
- Agent: researcher (model: sonnet)
[Position] Statement 1: let research = session: researcher
Spawning with prompt: "Research AI safety"
Model: sonnet
[Task tool call]
[Success] Session complete: "AI safety research covers alignment..."
[Binding] let research = <result>
[Position] Statement 2: parallel block
[Parallel] Entering parallel (2 branches, strategy: all)
[Task: "Analyze risk A"] [Task: "Analyze risk B"]
[Parallel] Parallel complete:
- a = "Risk A: potential misalignment..."
- b = "Risk B: robustness concerns..."
[Binding] a, b bound
[Position] Statement 3: loop until **analysis complete** (max: 3)
[Loop] Starting loop
[Loop] Iteration 1 of max 3
[Position] session "Synthesize" with context: {a, b, research}
[Task with serialized context]
[Success] Result: "Initial synthesis shows..."
[Loop] Evaluating: **analysis complete**
[Flow] Not satisfied (synthesis is preliminary)
[Loop] Iteration 2 of max 3
[Position] session "Synthesize" with context: {a, b, research}
[Task with serialized context]
[Success] Result: "Comprehensive analysis complete..."
[Loop] Evaluating: **analysis complete**
[Flow] Satisfied!
[Loop] Loop exited: condition satisfied at iteration 2
[Program] Program Complete
State Categories
The VM must track these state categories in narration:
| Category | What to Track | Example |
|---|---|---|
| Import Registry | Imported programs and aliases | research: @alice/research |
| Agent Registry | All agent definitions | researcher: {model: sonnet, prompt: "..."} |
| Block Registry | All block definitions (hoisted) | review: {params: [topic], body: [...]} |
| Input Bindings | Inputs received from caller | topic = "quantum computing" |
| Output Bindings | Outputs to return to caller | findings = "Research shows..." |
| Variable Bindings | Name -> value mapping (with execution_id) | result = "..." (execution_id: 3) |
| Variable Mutability | Which are let vs const vs output |
research: let, findings: output |
| Execution Position | Current statement index | Statement 3 of 7 |
| Loop State | Counter, max, condition | Iteration 2 of max 5 |
| Parallel State | Branches, results, strategy | {a: complete, b: pending} |
| Error State | Exception, retry count | Retry 2 of 3, error: "timeout" |
| Call Stack | Stack of execution frames | See below |
Call Stack State
For block invocations, track the full call stack:
[CallStack] Current stack (depth: 3):
execution_id: 5 | block: process | depth: 3 | status: executing
execution_id: 3 | block: process | depth: 2 | status: waiting
execution_id: 1 | block: process | depth: 1 | status: waiting
Each frame tracks:
execution_id: Unique ID for this invocationblock: Name of the blockdepth: Position in call stackstatus: executing, waiting, or completed
Independence from File-Based State
In-context state and file-based state (filesystem.md) are independent approaches. You choose one or the other based on program complexity.
- In-context: State lives in conversation history
- File-based: State lives in
.prose/runs/{id}/
They are not designed to be complementary—pick the appropriate mode at program start.
Summary
In-context state management:
- Uses text-prefixed markers to track state changes
- Persists state in conversation history
- Is appropriate for smaller, simpler programs
- Requires consistent narration throughout execution
- Makes state visible in the conversation itself
The narration protocol ensures that the VM can recover its execution state by reading its own prior messages. What you say becomes what you remember.