Local-first provenance for agent runs: content-addressed traces, fork, replay, and scoped resume.
Project description
opentine
Git for agent runs: record, verify, fork, replay, and diff execution history.
Portable .tine artifacts for native agents and external CLI harnesses.
A tine is the prong of a fork. opentine forks agent runs.
Every run becomes a content-addressed graph of model calls, tool calls, outputs, errors, cache provenance, and transcript state. Save it as a .tine file, verify its checksum, fork from a known-good step, replay recorded work, rerun through an explicit harness, and diff the branch that worked against the branch that failed.
What You Can Do
# Inspect a saved execution tree.
tine show result.tine
# Check that the artifact body still matches its recorded checksum.
tine verify result.tine
# Branch from a step before a bad tool call.
tine fork failed.tine --from-step 3 --save retry.tine
# Reuse recorded steps without re-spending model/tool work.
tine replay result.tine --mode cache --save replayed.tine
# Compare the graph shape of two runs.
tine diff failed.tine retry.tine
The current 0.1.x public beta validates the core surface, Ollama, Codex CLI, and Kimi Code CLI through the gates listed in Release Validation. Other providers and harnesses are compatibility targets until their own live gates pass.
Install
pip install opentine
Core runtime dependencies install normally. Provider SDKs are optional extras, and OpenAI-compatible providers share the OpenAI SDK path.
pip install "opentine[anthropic]"
pip install "opentine[openai]"
pip install "opentine[google]"
pip install "opentine[compat]"
Quickstart
Write a native opentine agent:
from opentine import Agent
from opentine.models.anthropic import Anthropic
agent = Agent(model=Anthropic("claude-sonnet-4-20250514"))
run = agent.run_sync("What is opentine?")
run.save("result.tine")
Then inspect and branch the run:
tine show result.tine
tine verify result.tine
tine fork result.tine --from-step 0 --save forked.tine
tine replay result.tine --inspect
tine diff result.tine forked.tine
Example tree shape:
# fe3a767307a4 model=claude-sonnet-4-20250514 steps=3 cost=$0.0006 completed
|-- # 9e4b8c2a19dd think "Planning the answer..."
|-- > 81bf0f67bb12 tool search(query="opentine")
`-- + 6d4a0b270a5f done "opentine records agent runs as portable artifacts..."
The Debugging Loop
Your agent fails after ten steps. Instead of rerunning everything:
tine show failed_run.tine
tine fork failed_run.tine --from-step 3 --save fixed_run.tine
tine diff failed_run.tine fixed_run.tine
That gives you the first three steps as known provenance, a new branch for the repair attempt, and a graph diff that shows what changed.
How It Works
Every .tine file stores a content-addressed DAG. Step IDs are full SHA-256 hashes over canonical immutable step payloads: parent links, kind, inputs, outputs, model/tool metadata, and errors.
Core operations are graph operations:
| Operation | Meaning |
|---|---|
| Save/load | Serialize the run graph, transcript, cache, manifest, policy metadata, and checksum. |
| Verify | Recompute the SHA-256 checksum in metadata.integrity. |
| Fork | Copy ancestors up to a chosen step and continue from there. |
| Replay | Reuse recorded steps, or rerun through an explicit native runtime or harness. |
| Diff | Compare two run graphs and show the common ancestor plus divergent steps. |
Run.steps remains a stable traversal view, but the artifact stores a graph with parent_ids, named refs, and branch metadata.
Harnesses
opentine can wrap external CLI agents and record observable events as a .tine run:
tine run --harness codex --prompt "Inspect this repo"
tine run --harness kimi-code --prompt "Summarize README.md"
tine run --harness generic --harness-command "your-agent run" --prompt "Fix tests"
graph LR
Task["user task"] --> Wrapper["opentine harness wrapper"]
Wrapper --> Codex["Codex CLI"]
Wrapper --> Kimi["Kimi Code"]
Wrapper --> Generic["custom command"]
Codex --> Artifact["portable .tine"]
Kimi --> Artifact
Generic --> Artifact
Artifact --> Fork["fork"]
Artifact --> Replay["replay"]
Artifact --> Diff["diff"]
Python wrapper API:
from opentine.harnesses import CodexCLIHarness, OpentineHarness
harness = CodexCLIHarness()
wrapped = OpentineHarness(harness, autosave_path="latest.tine", autosave_steps=5)
run = wrapped.run_sync("Refactor auth middleware")
run.save("codex_run.tine")
forked = wrapped.fork(from_step=3)
forked.save("retry_from_step_3.tine")
Harness subprocesses are isolated by default. For logged-in CLIs, pass --harness-login-env to allow only PATH, home/config directory variables, and tool-specific config directory variables. opentine does not write pasted secrets to repo files.
Support Matrix
Status values are intentionally conservative:
| Target | Status | Evidence |
|---|---|---|
Native .tine v1 load/save/fork/diff/replay |
Validated | Fast tests, golden fixture, and CLI smoke. |
| Artifact checksum verification | Validated | Run.verify_integrity(...), tine verify, and failure-path tests. |
| Secure tool defaults | Validated | Filesystem, symlink, network, shell, Python, env, redaction, and output-cap tests. |
Ollama llama3.1 and qwen3 |
Validated | Live gate passed for the current 0.1.x beta. |
| Codex CLI | Validated | Live harness gate passed for the current 0.1.x beta. |
| Kimi Code CLI | Validated | Live harness gate passed for the current 0.1.x beta. |
| Anthropic, OpenAI, Google | Scoped | Adapter contract tests; cloud live gates require user credentials. |
| Kimi API, DeepSeek, GLM, Groq, Together, Mistral, Qwen API | Scoped | OpenAI-compatible adapter shape; provider-specific live gates required. |
| LM Studio, vLLM, llama.cpp, LocalAI, Jan, Unsloth-compatible endpoints | Scoped | Endpoint-specific local live gates required. |
Native Models
The native Agent API accepts any implementation of the Model protocol. Built-in adapters include:
from opentine.models.anthropic import Anthropic
from opentine.models.google import Google
from opentine.models.ollama import Ollama
from opentine.models.openai import OpenAI
agent = Agent(model=Anthropic("claude-sonnet-4-20250514"))
agent = Agent(model=OpenAI("gpt-4o"))
agent = Agent(model=Google("gemini-2.0-flash"))
agent = Agent(model=Ollama("llama3.1"))
Not every local model can call tools. Ollama models such as gemma, codellama,
phi4, and deepseek-r1 advertise no tools capability; opentine detects this
(Ollama(...).supports_tools), runs them without tools instead of crashing, and
records a note in run.metadata["warnings"]. For tool-using agents pick a
tools-capable model like llama3.1, qwen2.5, qwen3, or mistral.
OpenAI-compatible wrappers:
from opentine.models.compat import DeepSeek, GLM, Groq, Kimi, Mistral, Qwen, Together
agent = Agent(model=Kimi("moonshot-v1-8k"))
agent = Agent(model=DeepSeek("deepseek-chat"))
agent = Agent(model=Qwen("qwen-plus"))
agent = Agent(model=GLM("glm-4-flash"))
agent = Agent(model=Groq("llama-3.1-70b-versatile"))
agent = Agent(model=Together("meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo"))
agent = Agent(model=Mistral("mistral-large-latest"))
These wrappers make integration easy, but a provider is only counted as live-validated after its project validation gate passes.
Tools And Policies
Tools are plain Python callables with type hints:
from opentine.tools import fs, python, search, shell, web
agent = Agent(
model=Ollama("llama3.1"),
tools=[web.fetch, search.search, fs.read],
)
Defaults are restrictive:
- Filesystem access is rooted, size-capped, and denies symlinks by default.
- Network fetch blocks private, link-local, loopback, reserved, and multicast hosts by default.
- Shell execution is disabled unless a
ShellPolicyenables it. - Python execution is disabled unless a
PythonPolicyenables it. - Harness subprocesses do not inherit the parent environment by default.
- Saved artifacts redact common secret-bearing keys before writing.
See SECURITY_MODEL.md for the detailed model.
.tine Format
Top-level fields:
format_version, run_id, created_at, status, graph, refs, transcript, manifest, policies, cache, metadata.
Current compatibility promise: format_version == 1 only. Run.load() rejects missing, old, or future format versions instead of guessing. Migration support is future work.
Saved artifacts include:
{
"metadata": {
"integrity": {
"algorithm": "sha256",
"digest": "..."
}
}
}
This is an integrity checksum, not tamper-proof signing. A user who can edit the file can also rewrite the digest. HMAC/signature support is future work. See TINE_FORMAT.md.
CLI Reference
tine run <script.py> Execute a Python script that produces a Run
tine run --harness codex Execute an external harness and save the graph
tine ls List recent runs
tine show <run.tine> Pretty-print the graph
tine verify <run.tine> Verify the artifact checksum
tine replay <run.tine> --inspect Print recorded events without replaying
tine replay <run.tine> --mode cache --save replayed.tine
tine fork <run.tine> --from-step 3 --save retry.tine
tine diff <run_a.tine> <run_b.tine>
tine resume <run.tine> Resume only when the manifest declares support
MCP Server
opentine.mcp_server exposes saved runs to MCP clients:
list_runs: show recent.tinefilesshow_run: render a run as LLM-readable contextfork_run: fork a run from a step indexdiff_runs: compare two runsrun://{run_id}: resource URI for a saved run
Run it with an MCP-compatible environment:
python -m opentine.mcp_server
The MCP dependency is optional; install the mcp package only for this server.
Release Validation
Opentine is a public 0.1.x open-source beta. Package metadata remains Development Status :: 4 - Beta.
Current local gates:
ruff check .
ruff format --check .
pytest tests -m "not live and not live_harness" -q
pytest tests -m "not live_harness" -q
python -m build --sdist --wheel --outdir dist
python -m twine check dist/*
python scripts/wheel_smoke.py
Current live gates:
pytest tests/test_live.py --provider ollama -q
pytest tests/test_live_harness.py -m live_harness --agent-harness codex -q
pytest tests/test_live_harness.py -m live_harness --agent-harness kimi-code -q
GitHub Actions runs the local gates on every push and pull request across
Ubuntu, macOS, and Windows for Python 3.11–3.13, and uploads the built
wheel/sdist as a downloadable opentine-dist artifact. Tagged v* releases
additionally publish SHA256SUMS and a build-provenance attestation you can
verify against the source workflow:
gh attestation verify opentine-0.1.1-py3-none-any.whl --repo 0xcircuitbreaker/opentine
Related docs:
| Document | What it covers |
|---|---|
| CHANGELOG.md | Verified user-visible changes. |
| SUPPORT.md | Supported Python versions and support levels. |
| TROUBLESHOOTING.md | Common validation, harness, and policy failures. |
Comparison
Git stores content-addressed source history. opentine stores content-addressed agent execution provenance.
LangGraph is the closest technical comparison for checkpoint replay and time travel, but it is framework/checkpointer-oriented rather than a standalone .tine artifact. LangSmith and CrewAI tracing are stronger observability/evaluation surfaces; opentine is a local provenance artifact and graph-operation layer.
The Name
A tine is a prong of a fork. The .tine extension stores serialized run graphs, and tine is the CLI command.
Contributing
git clone https://github.com/0xcircuitbreaker/opentine.git
cd opentine
uv sync --all-extras
uv run pytest tests -m "not live and not live_harness"
uv run ruff check .
uv run ruff format --check .
When Docker is available, python scripts/ci_docker.py rehearses the Ubuntu GitHub
Actions matrix locally for Python 3.11, 3.12, and 3.13. GitHub Actions remains
authoritative for macOS and Windows.
License
Apache-2.0. See LICENSE.
Built by the opentine contributors.
Project details
Release history Release notifications | RSS feed
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 opentine-0.1.1.tar.gz.
File metadata
- Download URL: opentine-0.1.1.tar.gz
- Upload date:
- Size: 334.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b57f3d2cbe34581e73877f2431303c7fbfcd0dcf13b16ba984975496644432db
|
|
| MD5 |
e99ad40dec7e588efeafc864beb8897c
|
|
| BLAKE2b-256 |
697da932b22b4c0c809b9894862f3500d2376f42ddf02433e293e9e203fdd107
|
File details
Details for the file opentine-0.1.1-py3-none-any.whl.
File metadata
- Download URL: opentine-0.1.1-py3-none-any.whl
- Upload date:
- Size: 54.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb0a1f14ffbd02489f42d5478a82dea0a11a78562362b6133361e74ea6d26656
|
|
| MD5 |
e76ab2cf9ca6133f0aee812f2d41cd1f
|
|
| BLAKE2b-256 |
205fafc9e8a83b823e839b732c5d6cad78e9c8158036640db93be63450f2da63
|