Durable checkpoint/resume runner for async state-machine loops built on loom-tailcalls.
Project description
loom-runner
Small durable checkpoint/resume runner for async state-machine loops built on
top of loom-tailcalls and flow-xray.
This is not a planner, memory system, graph DSL, hosted tracing product, or full agent SDK. It is the first slice of a Loom-based agent runtime: run a typed async transition loop, checkpoint each state transition, resume later, inspect history, and explain a run.
Runtime transitions are logged as logical steps with attempt history. A retry
does not create a new transition: for the same run_id, step_index, and
stable input hash, the runner reuses the committed outcome. Transient errors
are retryable by default; validation, business, permission, and unknown errors
fail the run unless the caller supplies a different policy.
Tool side effects are only idempotent when invoked through
RunContext.call_tool(...). Direct tool calls or external effects inside a
transition are intentionally treated as unmanaged user code in this first
runtime slice.
Long runs can use bounded reads and explicit storage policies. By default the
runner keeps every checkpoint and every inline tool payload for maximum
inspectability. For larger runs, use CheckpointPolicy(mode="interval", every=N) to retain only periodic history checkpoints while preserving the
current resumable state, and PayloadPolicy(max_inline_bytes=N) to replace
large managed tool payloads with hash/size metadata.
The import package remains loom_agent; the distribution and CLI are named
loom-runner because loom-agent is already occupied by an unrelated package
on PyPI.
Install
python3.13 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Minimal Shape
from dataclasses import dataclass
from loom_agent import AgentRunner, Complete, Continue, RunContext, SQLiteCheckpointStore
@dataclass(frozen=True)
class State:
current: int
target: int
async def step(state: State, ctx: RunContext):
if state.current >= state.target:
return Complete({"current": state.current})
return Continue(State(current=state.current + 1, target=state.target))
runner = AgentRunner(
step=step,
store=SQLiteCheckpointStore("runs.sqlite"),
encode_state=lambda state: {"current": state.current, "target": state.target},
decode_state=lambda data: State(**data),
encode_result=lambda result: result,
decode_result=lambda data: data,
)
Example
loom-runner run examples/counter_agent.py --run-id demo --db runs.sqlite --max-steps 5
loom-runner resume examples/counter_agent.py --run-id demo --db runs.sqlite --max-steps 100
loom-runner list examples/counter_agent.py --db runs.sqlite
loom-runner get examples/counter_agent.py --run-id demo --db runs.sqlite
loom-runner history examples/counter_agent.py --run-id demo --db runs.sqlite
loom-runner attempts examples/counter_agent.py --run-id demo --db runs.sqlite --limit 20
loom-runner tool-calls examples/counter_agent.py --run-id demo --db runs.sqlite --limit 20
loom-runner explain examples/counter_agent.py --run-id demo --db runs.sqlite
Add --trace trace.html to either command to emit a local flow-xray HTML
trace. The runner traces step leaves and keeps the tail-recursive driver as the
durable loop boundary.
Or directly:
python3.13 examples/counter_agent.py
Tests
python3.13 -m pytest
Runtime Benchmark
python3.13 scripts/bench_runtime.py --steps 100000
python3.13 scripts/bench_runtime.py --steps 100000 --checkpoint-every 100
The benchmark reports wall time, retained checkpoint rows, attempt rows, DB size, and peak Python memory. It is a local regression tool, not a hosted-scale performance claim.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file loom_runner-0.1.0.tar.gz.
File metadata
- Download URL: loom_runner-0.1.0.tar.gz
- Upload date:
- Size: 21.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2071db201dc0dce6cab1eb2bdb5caa735aabc085acca1e0fbbf476648f870244
|
|
| MD5 |
7019a7d8778b5f6851412d25fccdbcbe
|
|
| BLAKE2b-256 |
bf2a5d550cede5b544c6813c979d8ce0c6bc40a493add4cd83cd4a7b577d939a
|
File details
Details for the file loom_runner-0.1.0-py3-none-any.whl.
File metadata
- Download URL: loom_runner-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c4d6c459b59685e4e78bde5ee872d450965371773a105c2450c8b0fd588c5f9
|
|
| MD5 |
fd5d3b3f20cdd93b16c873420cb750c2
|
|
| BLAKE2b-256 |
4a26956463a093c3f1921fc86c22ae3bcc0ac696eeb0a6e288158ee922c4e840
|