Replay & Resume
opentine runs are fully serializable. You can pause a run mid-flight, save it to a .tine file, transfer it to another machine, and resume exactly where you left off. Combined with forking, this gives you complete control over long-running agent workflows.
Pausing a Run
Call run.pause(path) to save the run to disk and mark its status as paused. This is a single atomic operation — the status change and file write happen together.
1from opentine import Run
2
3# Create and execute a run
4run = Run(id="long-task", model_info="claude-sonnet-4-20250514",
5 system_prompt="Research assistant", user_prompt="Analyze 100 papers")
6
7step_1 = run.add_step(kind="think", inputs={"thought": "Start with batch 1"})
8step_2 = run.add_step(kind="tool",
9 inputs={"tool": "search", "query": "batch 1"},
10 outputs={"results": ["paper_1", "paper_2"]},
11 parent_id=step_1.id, duration=2.0)
12
13# Pause the run — saves to disk and sets status to "paused"
14path = run.pause("research_checkpoint.tine")
15# => Path("research_checkpoint.tine")
16print(run.status) # => RunStatus.paused
Resuming a Run
Run.resume(path) is a classmethod that loads a paused run from disk and sets its status to running. You get back a fully hydrated Run object with all its steps intact, ready to continue.
1# Later (or on a different machine) — resume from where we left off
2from opentine import Run
3
4resumed = Run.resume("research_checkpoint.tine")
5print(resumed.status) # => RunStatus.running
6print(len(resumed.steps)) # => 2 (the steps we had before pausing)
7
8# Continue adding steps
9step_3 = resumed.add_step(
10 kind="model",
11 inputs={"prompt": "Summarize batch 1 results..."},
12 outputs={"text": "Papers 1 and 2 cover..."},
13 parent_id=resumed.steps[-1].id,
14 duration=3.1,
15 cost=0.003,
16)
Save and Load
Under the hood, pause() and resume() are built on the lower-level save() and load() methods. The difference is that save() and load()preserve the run's status as-is, while pause() and resume() explicitly set it to paused and running respectively.
1# save() and load() are the low-level primitives
2# They serialize/deserialize without changing status
3
4# Save the run as-is
5path = run.save("my_run.tine")
6
7# Load it back — status is preserved exactly
8loaded = Run.load("my_run.tine")
9print(loaded.status) # => whatever it was when saved
10
11# pause() and resume() are convenience wrappers:
12# pause(path) = set status to "paused", then save(path)
13# resume(path) = load(path), then set status to "running"
Cross-Machine Resume
Because .tine files are plain JSON (serialized via msgspec), they can be transferred between machines, stored in version control, or uploaded to cloud storage. This enables workflows like starting a run on a laptop and finishing it on a GPU server.
1# Machine A: developer laptop
2run = agent.run_sync("Expensive analysis task")
3# Agent processes 50 steps, then we want to move it
4
5run.pause("analysis.tine")
6# Upload analysis.tine to shared storage (S3, GCS, etc.)
7
8# --- transfer the .tine file ---
9
10# Machine B: GPU server
11from opentine import Run
12
13run = Run.resume("analysis.tine")
14# All 50 steps are restored — continue from where we left off
15result = agent.continue_run(run)
Replay: Re-Executing from a Step
Replay is the concept of re-executing a run (or part of a run) from a specific step. This isn't a separate API — it's a combination of fork() and continue_run(). Fork from the step you want to replay from, and the agent picks up from that point.
You can also use a loaded run purely for inspection — walk the tree, examine inputs and outputs, compute costs — without re-executing anything.
1from opentine import Run
2
3# Load a completed run
4run = Run.load("finished_research.tine")
5
6# Walk the tree to replay / inspect what happened
7for step in run.root_steps():
8 print(f"Root: {step.kind} -> {step.inputs}")
9 for child in run.children(step.id):
10 print(f" Child: {child.kind} -> {child.inputs}")
11 for grandchild in run.children(child.id):
12 print(f" Grandchild: {grandchild.kind}")
13
14# Replay from a specific step: fork and re-execute
15fork_point = run.steps[10] # step 10
16replayed = run.fork(from_step_id=fork_point.id)
17
18# Now re-run from step 10 with the same or different agent
19result = agent.continue_run(replayed)
Putting It All Together
The full power of opentine comes from combining these primitives: run an agent, fork from a failure point, pause the fork, transfer it, resume on a new machine, and finish the job. Each step in this workflow is a single method call.
1# Full workflow: run -> fail -> fork -> pause -> transfer -> resume
2from opentine import Run
3
4# 1. Initial run fails at step 15
5run = agent.run_sync("Complex multi-step task")
6# run.status => RunStatus.failed
7
8# 2. Fork from the last good step
9last_good = [s for s in run.steps if s.kind != "error"][-1]
10forked = run.fork(from_step_id=last_good.id)
11
12# 3. Pause and transfer to a different environment
13forked.pause("retry.tine")
14# scp retry.tine gpu-server:/workspace/
15
16# 4. Resume on the target machine and finish
17# (on gpu-server)
18run = Run.resume("/workspace/retry.tine")
19result = agent.continue_run(run)
20run.save("/workspace/completed.tine")