# Active Graph: an event log that forks a run without paying for the prefix again

> yoheinakajima/activegraph is a Python runtime where agents write to an append-only event log rather than calling each other, so a run can be branched at any event and diffed structurally, with the shared prefix served from cache instead of re-executing.

**yoheinakajima/activegraph** — Event-sourced graph runtime for durable and stateful agents

- Repository: https://github.com/yoheinakajima/activegraph
- Website: https://activegraph.ai
- Stars: 702 · Forks: 65
- Language: Python
- License: Apache-2.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/yoheinakajima-activegraph

## Fork-and-diff replays the prefix from cache instead of re-running it

The differentiating claim is structural rather than ergonomic. Any run can be branched at any event into an independent fork, that fork can be configured differently, and the result can be structurally diffed against the parent. The cost argument is the part that separates this from a replay button: cache replay means the shared prefix does not execute again, so branching produces no new LLM calls for work the fork did not change. Everything before the branch point stays in the log, and everything after it is a second history rather than an overwrite. The repository ships examples for exactly this shape, with `examples/resume_and_fork.py` and `examples/operate_a_run.py` alongside `examples/quickstart_session.txt`, a recorded session rather than a live one. The project's own wording is blunt about the comparison: most agent frameworks cannot do this.

## run_quantum exists so a yielded drain cannot report a false idle

Cooperative bounded drains are the mechanism underneath that replay story. Instead of one unbounded `run_until_idle()`, a single-writer host can call `Runtime.run_quantum(max_queue_events=25, max_seconds=0.25)` and get control back on a budget, which is what keeps reads responsive while a large piece of derived work drains. The guarantee attached to it is specific and testable: a yielded quantum never writes a false `runtime.idle`, and a sequence of repeated quanta is byte-identical to a single `run_until_idle()` under the same deterministic inputs. That contract is cited in the code as CONTRACT v1.10 #3, which means it lives in a document rather than in a comment. The byte-identity claim is what makes fork-and-diff trustworthy: if incremental execution could drift from bulk execution, a structural diff would be reporting differences that came from the scheduler rather than from the fork.

## The root carries a contract, its review findings, and a docstring gap list

Process artefacts are unusually visible here. `CONTRACT.md` states the guarantees, `CONTRACT-INDEX.md` indexes them, and `CONTRACT-review-findings.md` records what reviewing them turned up. Alongside those sit `docstring_gaps.toml`, three design notes in `compaction-design.md`, `promote-design.md`, and `trial-isolation-design.md`, and four planning files in `ROADMAP.md`, `TRIAGE.md`, `FUTURE_IDEAS.md`, and `HANDOFF.md`. Two naming details are worth noting for a project at version 1.12.0: the plan file is still called `v1.1-plan.md`, and the packaging metadata carries a `Development Status :: 3 - Alpha` classifier. A repository that keeps its own promise catalogue and its own docstring audit is doing more discipline work than most libraries of its size, and the alpha label is the honest counterweight to that.

## Every error message ends with a More: link

Diagnostics are treated as a feature rather than a log format. Every error message the runtime emits ends with a `More:` link to a dedicated page that explains when the error fires, why it fires, and how to fix it, and the pages are collected in a catalog at `docs.activegraph.ai/reference/errors`. That is a different failure mode from the usual one. A traceback tells you what broke after the fact; a catalog entry tells you what the author expected to break and what the intended remedy was. The limitation is that the catalog is a web property rather than something that ships in the package, so the reference material for debugging lives at the same address as the tutorials and the API reference. The docs site also exposes `llms.txt` and `llms-full.txt` as machine-readable entry points, and the project describes itself as spread across sites, papers, and sibling repositories rather than contained in this one.

## EventSink keeps adapter I/O off the runtime hot path

Exporting live events is where a runtime usually grows a second weak point, and this one isolates it deliberately. `EventSink` streams accepted live events through a bounded per-sink worker, so the I/O an adapter does never sits on the runtime's own hot path. `JSONLEventSink` is the first-party adapter. The design makes failure accounting part of the interface rather than a log line to grep for: drops, queue depth, and failures are all explicit in status and metrics, and a normal replay never redelivers history, so a consumer cannot be made to process the same past twice by reconnecting. The bound is the point. An unbounded exporter on the write path turns every slow disk into a backpressure problem for the agent itself, and the metrics are what let an operator notice that before the queue fills.

## Eight install lines, and one extra that installs nothing

The install block is the whole dependency story in one screen:

```bash
pip install activegraph                    # core runtime + SQLite store + Diligence pack
pip install "activegraph[llm]"             # Anthropic + OpenAI providers
pip install "activegraph[anthropic]"       # Anthropic provider only
pip install "activegraph[openai]"          # OpenAI provider only (+ tiktoken)
pip install "activegraph[postgres]"        # Postgres-backed event store
pip install "activegraph[prometheus]"      # Prometheus metrics
pip install "activegraph[opentelemetry]"   # OpenTelemetry metrics
pip install "activegraph[all]"             # everything
```

The base line carries the core runtime, the SQLite store, and the Diligence pack, and the declared hard dependencies are only `click>=8,<9` for the CLI and `pydantic>=2` for the pack format, on Python 3.11 or newer. The `[sqlite]` extra exists but resolves to an empty list, because SQLite is stdlib and the store arrived back in v0.5. The `[llm]` extra deliberately ships both provider SDKs so one line lights up both, while `[anthropic]` and `[openai]` install one at a time for deployments watching cost. `tiktoken` sits under `[openai]` to keep token counting accurate, and the fallback when it is absent is described in the metadata as documented but loud.

## The bundled pack runs on recorded fixtures, so the first run needs no key

The entry command is short:

```bash
pip install activegraph
activegraph quickstart
```

What that exercises is the Diligence pack, which bundles 8 object types, 7 behaviors, 3 tools, and recorded fixtures. It needs no API key and no configuration, and the output is described as byte-deterministic, which means two runs on the same fixtures produce identical traces and the quickstart doubles as a determinism check. The interactive form, `activegraph quickstart --interactive`, scaffolds a behavior, runs it against those same fixtures, and finishes on the fork-and-diff workflow rather than on a summary, so the differentiated capability is the last thing you see. The examples directory carries the live counterparts: `diligence_real_run.py` and `diligence_with_tools.py` sit next to `quickstart.py` and `llm_claim_extraction.py`. A pack is the unit of distribution, bundling object types, behaviors, tools, prompts, and policies for one domain, and the wider library of roughly 20 packs, demo bundles, a demo server, and a React Inspector UI lives in a separate repository.

## Twelve primitives, each defined by a link out to the docs site

The concept surface is enumerated in the README as twelve primitives, given in the order a reader meets them while reading a trace, and every one of them is defined by a link to a concept page on the documentation site rather than in the repository. The first three carry the load of the whole model: the graph of objects and typed relations, the events that form the append-only history, and the behaviors that fire in response to events and produce more events. Behaviors can be functions, classes, LLM-backed, or attached directly to typed edges, which the project calls the relation-behavior primitive. Subscriptions are a triple of event type, predicate, and a Cypher subset for graph-shape patterns, so a behavior can react to shape and not only to a record. A graph is a projection of the log rather than a separate store, which is why every mutation is an event.

## Conclusion

Active Graph earns attention for one property rather than for breadth: the trace is the state, and every run is a log you can cut. That is the right shape for agents that need an audit trail, for experiments that need a fork-and-diff, and for anyone who has been burned by a retry that silently re-billed a model. It is not the right shape if you want a quick single-shot tool, because the cost is ceremony: Python 3.11, pydantic for pack schemas, contract files, and a project still classified as alpha. Before committing, read CONTRACT.md to see which guarantees are actually written down, run `activegraph quickstart` against the bundled fixtures so you can tell whether byte-determinism matters for your workload, and check that the error catalog on the docs site covers the failures you expect rather than the ones the project found first.

## FAQ

### What is Active Graph?

An event-sourced reactive graph runtime for long-running, auditable agentic systems, published as a Python package. Objects and typed relations form a graph, every mutation is an append-only event, and behaviors react to that shared graph instead of calling each other.

### What does a graph mean in an AI agent runtime like Active Graph?

Here the graph is the world the runtime reasons about: objects joined by typed relations, with behaviors attached either to the objects or to the edges themselves. Subscriptions match on event type, a predicate, and a Cypher subset for graph-shape patterns, and the graph is a projection of the event log rather than a separate store.

### Does the Active Graph quickstart need an API key?

No. The bundled Diligence pack runs against recorded fixtures with no API key and no configuration, and the output is described as byte-deterministic. The interactive form ends on the fork-and-diff workflow.

### What does Active Graph require to install and run?

Python 3.11 or newer, with two hard dependencies: `click>=8,<9` for the CLI and `pydantic>=2` for the pack format. The base install also gives you the SQLite store and the Diligence pack. Providers, Postgres, and metrics are opt-in extras.

## Sources

- [License: Apache-2.0](https://github.com/yoheinakajima/activegraph/blob/main/LICENSE)
- [Project website](https://activegraph.ai)
- [README](https://github.com/yoheinakajima/activegraph/blob/main/README.md)
- [Releases](https://github.com/yoheinakajima/activegraph/releases)
- [yoheinakajima/activegraph on GitHub](https://github.com/yoheinakajima/activegraph)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/yoheinakajima-activegraph
