Event-sourced agent engine — CLI and Python bindings for auditable AI workflows
Project description
zymi-core
dbt for AI workflows — declarative agents, deterministic replay, human-in-the-loop, all from YAML.
Pronounced zoomi — like dog zoomies.
Why zymi-core?
Most agent frameworks are imperative Python: write a script that makes LLM calls, persist some messages, hope you logged enough to debug a bad run later.
zymi-core inverts that:
- Declarative, like dbt. Agents, pipelines, tools, connectors, approvals — all YAML. The engine validates and runs them as a DAG.
- Event-sourced. Every state change is an immutable, hash-chained event. Runs are replayable, resumable, and auditable without extra logging.
- Boundary-safe. Agents emit intentions (run shell, write file, call HTTP) that pass through policy + contracts + optional human approval before execution. The risky thing doesn't happen until someone says yes.
Bring a useful agent online in minutes without writing code. A year later, still answer exactly what this agent did on any past run.
📚 AI-assistant friendly out of the box. Every zymi init scaffold drops an AGENTS.md into the user's project — vocabulary, file map, task→file routing. Claude Code / Cursor / Aider read it automatically; the YAML they help you write gets noticeably more correct.
Run a Telegram agent in two minutes
This is the canonical demo — a real chat bot, wired declaratively.
pip install zymi-core
mkdir telegram-agent && cd telegram-agent
zymi init --example telegram
# 1. Create a bot via @BotFather in Telegram; copy the token.
# 2. Fill .env:
cp .env.example .env # edit TELEGRAM_BOT_TOKEN + OPENAI_API_KEY
# 3. Open project.yml, replace "your_username_here" with your actual
# Telegram username (no @). Keeps strangers out of the bot.
set -a; source .env; set +a
zymi serve chat
Message the bot. It replies in seconds. Every inbound message, LLM call, approval decision, and outbound reply is in .zymi/events.db; watch live with zymi observe.
The whole wiring — Telegram I/O, two-step DAG (assistant drafts, reviewer polishes), declarative + Python tools, approval channel — lives in YAML. The scaffold also drops AGENTS.md so an AI coding assistant can extend the project safely. Concrete demo of:
http_pollconnector — long-polls Telegram'sgetUpdates, no HTTPS / ngrok neededhttp_postoutput — sends eachResponseReadyback to the user- Telegram approval channel — DMs admins with ✅ / ❌ buttons when the agent calls
broadcast(requires_approval: true) - Python
@toolauto-discovery — droptools/get_weather.py(sync) ortools/translate.py(async) and the agent picks them up
Ask the bot to "announce that we're closing at 5pm" — the agent calls broadcast, you get a DM with approve/deny buttons, nothing goes out until you click. End-to-end audit trail in zymi events.
Full setup in docs/getting-started.md. Connector deep-dive in docs/connectors.md. Approvals in docs/approvals.md.
What's in the box
Pipelines — DAGs, agent steps, deterministic tool steps
A pipeline is a list of steps with depends_on: edges. Independent steps run in parallel. Each step is either an agent step (LLM ReAct loop) or a deterministic tool step (ADR-0024) — direct dispatch with templated args, no LLM hop, but the same event envelope.
Mix them freely:
steps:
- id: fetch # deterministic — no LLM
tool: http_get
args: { url: "https://api.example.com/${inputs.id}" }
- id: classify # LLM
agent: classifier
task: "${steps.fetch.output}"
depends_on: [fetch]
Conditional branches (ADR-0028) — a step can gate on an upstream output. Skipped branches cascade to descendants and emit StepSkipped events, so routing decisions land in the trace, not in the LLM's head:
- id: router
agent: concierge
task: "Pick: ${inputs.q}" # calls route('short' | 'rag')
- id: rag_lookup
tool: pinecone_query
args: { query: "${inputs.q}" }
depends_on: [router]
when: "${steps.router.output} == 'rag'"
Schema, examples, gotchas → docs/pipelines.md.
Tools — four kinds, one catalogue
All four kinds emit identical ToolCallRequested / ToolCallCompleted events; the agent doesn't know which catalogue a tool came from.
- Declarative HTTP / shell in
tools/<name>.yml— no code. - Python
@toolintools/<name>.py— sync or async, signature → JSON Schema, auto-discovered. - MCP servers — one
mcp_servers:entry gives N tools, namespacedmcp__<server>__<tool>(ADR-0023). - Builtins —
read_file,write_file,write_memory,execute_shell_command,spawn_sub_agent.
# tools/get_weather.py — auto-discovered at runtime startup.
from zymi import tool
@tool
def get_weather(city: str) -> str:
"""Return the current weather for a city."""
return f"sunny in {city}"
Schema and the four kinds in detail → docs/tools.md.
Connectors and outputs
Inbound: http_inbound (webhook), http_poll (long-poll), cron, file_read, stdin.
Outbound: http_post, file_append, stdout.
All declarative, all emit events. Filter recipes (docs/connectors.md):
# GitHub — only react to PR opens
filter:
"$.action": { equals: "opened" }
"$.pull_request.draft": { equals: false }
429 + Retry-After handled automatically. Cursors persist across restarts. Multi-process zymi serve against shared Postgres sees one cursor table, no double-fire.
Approvals — event-sourced, restart-safe
Tools with requires_approval: true publish ApprovalRequested on the bus; an approval channel routes a human decision back. Three channels in the box: terminal, http, telegram (ADR-0022).
Resolution order: pipeline override → project default → fail-closed. A zymi serve crash mid-approval is repaired on next start: in-flight requests are redelivered to live channels; expired ones are sealed with ApprovalDenied{reason: restart_timeout}.
Full schemas + telegram setup → docs/approvals.md.
Replay, resume, observe
zymi runs # all pipeline runs
zymi events --stream pipeline-chat-abc # every event in one run
zymi verify --stream pipeline-chat-abc # hash-chain integrity check
zymi observe # 3-panel TUI: runs / DAG / events live
# Fork-resume from a chosen step. Upstream steps are frozen; the fork
# step + DAG-descendants re-run against current configs on disk.
zymi resume pipeline-chat-abc --from-step polish
zymi resume pipeline-chat-abc --from-step polish --dry-run
Useful when you're iterating on a prompt: don't re-burn the expensive early steps every time you tweak the later ones. → docs/events-and-replay.md.
Store backends
SQLite (default, zero-config) for single-process / dev. Postgres for multi-process zymi serve against shared state — one store: postgres://… line in project.yml (ADR-0012). Same hash-chain semantics either way. → docs/store-backends.md.
Context window management
The agent's working context is reconstructed from the event log each iteration, not accumulated in a buffer. Older tool observations are masked in-place (~2× cost reduction, no extra LLM calls). When the budget still gets tight, hybrid compaction summarises the oldest masked batch with one fast LLM call. Tunable in runtime.context: — see docs/context.md for recommended chat / coding / evals profiles (ADR-0016).
JSON Schemas for configs
IDE autocomplete and LLM-assisted YAML come free:
zymi schema project # draft-07 JSON Schema for project.yml
zymi schema --all
Python embedding
The same pip install zymi-core exposes a Python API: Runtime, Event, EventBus, EventStore, Subscription, ToolRegistry, plus the @tool decorator.
from zymi import Runtime
rt = Runtime.for_project(".", approval="terminal")
result = rt.run_pipeline("chat", {"message": "hello"})
print(result.success, result.final_output)
rt.bus() and rt.store() share Arc-handles with the runtime — Python subscribers see exactly what the handler publishes.
Cross-process pattern (Django view / Celery task drives zymi serve over the shared store):
import uuid
from zymi import Event, EventBus, EventStore
store = EventStore(".zymi/events.db")
bus = EventBus(store)
corr = str(uuid.uuid4())
sub = bus.subscribe_correlation(corr)
ev = Event(
stream_id=f"web-{corr}",
kind={"type": "PipelineRequested",
"data": {"pipeline": "research", "inputs": {"topic": "rust event sourcing"}}},
source="django",
)
ev.with_correlation(corr)
bus.publish(ev)
result = sub.recv(timeout_secs=300)
Full surface → docs/python-api.md.
CLI cheatsheet
zymi init [--example telegram] # scaffold a project
zymi run <pipeline> -i key=value … # one-shot run
zymi serve <pipeline> # long-running: react to PipelineRequested
zymi runs # list pipeline runs
zymi events [--stream ID] [--kind TAG] # query event log
zymi verify [--stream ID] # hash-chain integrity check
zymi observe [--run ID] # interactive TUI
zymi resume <run-id> --from-step <id> # fork-resume
zymi mcp probe <name> -- <cmd> [args …] # smoke an MCP server
zymi schema {project|agent|pipeline|tool|--all}
Full reference → docs/cli.md.
Documentation
- Getting started — install → init → first run
- Project YAML —
project.ymlschema - Agents · Pipelines · Tools
- Connectors · Approvals · Store backends
- Events and replay · CLI reference · Python API
- llms.txt — flat index of this documentation tree for LLM scrapers and RAG tools
AGENTS.md— agent-onboarding doc generated byzymi initinto each new project (vocabulary, file map, task→file routing)adr/— architectural decision records, one short markdown file per decision
Contributing & License
zymi-core is built in Rust and shipped via PyPI. Bug reports, examples, PRs welcome — see CONTRIBUTING.md for the dev loop, test matrix, ADR workflow, and how to build from source.
MIT — see LICENSE.
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 Distributions
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 zymi_core-0.6.8.tar.gz.
File metadata
- Download URL: zymi_core-0.6.8.tar.gz
- Upload date:
- Size: 939.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
255c5a88f1e5dbf8f8365b33d06bfa3c2746349f6034a23f25ab5a970515fc97
|
|
| MD5 |
88aa4c4b7a196f8778ce359d95cad439
|
|
| BLAKE2b-256 |
9dd06499ffb5caad7c170f5d816a01c036ad22c08e0b904fe029a22298631c09
|
Provenance
The following attestation bundles were made for zymi_core-0.6.8.tar.gz:
Publisher:
release.yml on metravod/zymi-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zymi_core-0.6.8.tar.gz -
Subject digest:
255c5a88f1e5dbf8f8365b33d06bfa3c2746349f6034a23f25ab5a970515fc97 - Sigstore transparency entry: 1539453057
- Sigstore integration time:
-
Permalink:
metravod/zymi-core@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Branch / Tag:
refs/tags/v0.6.8 - Owner: https://github.com/metravod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zymi_core-0.6.8-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: zymi_core-0.6.8-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 6.1 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5416022e7a65ae15585c4820c4207f7af62ec23d57f865a6d7ca09ab98086d9e
|
|
| MD5 |
7b2dfcd03205e34505e35c026436cedc
|
|
| BLAKE2b-256 |
25bd0e978d03553907352ddae33935aae632f49c248fbd9c463d1bb55ec79dcf
|
Provenance
The following attestation bundles were made for zymi_core-0.6.8-cp39-abi3-win_amd64.whl:
Publisher:
release.yml on metravod/zymi-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zymi_core-0.6.8-cp39-abi3-win_amd64.whl -
Subject digest:
5416022e7a65ae15585c4820c4207f7af62ec23d57f865a6d7ca09ab98086d9e - Sigstore transparency entry: 1539453414
- Sigstore integration time:
-
Permalink:
metravod/zymi-core@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Branch / Tag:
refs/tags/v0.6.8 - Owner: https://github.com/metravod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zymi_core-0.6.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: zymi_core-0.6.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 5.9 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98f4e9cdad91eb783a13ada35f9973aa2e366e32f41fc36d15160b9f31e56e48
|
|
| MD5 |
d39dbf3cd095e456ac3a1909d7a64978
|
|
| BLAKE2b-256 |
f977f01d2e120a0a63a9f56e4186b1f1bafedb60c33f328c3706a43f73ccce09
|
Provenance
The following attestation bundles were made for zymi_core-0.6.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on metravod/zymi-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zymi_core-0.6.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
98f4e9cdad91eb783a13ada35f9973aa2e366e32f41fc36d15160b9f31e56e48 - Sigstore transparency entry: 1539453242
- Sigstore integration time:
-
Permalink:
metravod/zymi-core@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Branch / Tag:
refs/tags/v0.6.8 - Owner: https://github.com/metravod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zymi_core-0.6.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: zymi_core-0.6.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 5.5 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c217ed82d288cb3be5b0177de64b2cee82bc9b30ddf1e60ab07d1d6c1c961c1a
|
|
| MD5 |
923e39576dc9820e70adbd7eddc09cf1
|
|
| BLAKE2b-256 |
1fc00aac0998ad2887fe8c95fdeaed5230fa21d3dbb9f5a5c49d54684382386e
|
Provenance
The following attestation bundles were made for zymi_core-0.6.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on metravod/zymi-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zymi_core-0.6.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
c217ed82d288cb3be5b0177de64b2cee82bc9b30ddf1e60ab07d1d6c1c961c1a - Sigstore transparency entry: 1539453124
- Sigstore integration time:
-
Permalink:
metravod/zymi-core@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Branch / Tag:
refs/tags/v0.6.8 - Owner: https://github.com/metravod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zymi_core-0.6.8-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: zymi_core-0.6.8-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 5.3 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec48aadf2eeacf181217aee7c009badfb1ff28df778ca1713c0ce0a0e3b32c67
|
|
| MD5 |
6e91d943dcaee653033cc66b4ab27e25
|
|
| BLAKE2b-256 |
e8146a1f4bb6bc34f38eebf4fcdc8938a4cbbea3bc88ea7895b29483b1dcbbe0
|
Provenance
The following attestation bundles were made for zymi_core-0.6.8-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on metravod/zymi-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zymi_core-0.6.8-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
ec48aadf2eeacf181217aee7c009badfb1ff28df778ca1713c0ce0a0e3b32c67 - Sigstore transparency entry: 1539453489
- Sigstore integration time:
-
Permalink:
metravod/zymi-core@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Branch / Tag:
refs/tags/v0.6.8 - Owner: https://github.com/metravod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zymi_core-0.6.8-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: zymi_core-0.6.8-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 5.7 MB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6efc66ecf897dbbc2fb3ac0aa894356f29de3eab05e6b3af5c1b0e6e1cb03fc9
|
|
| MD5 |
577c48b56fdaaeb17289b393fde321b3
|
|
| BLAKE2b-256 |
85e04ea96ad2715ec34c01b38d756a89aad453f64b16862001705353dd7086a9
|
Provenance
The following attestation bundles were made for zymi_core-0.6.8-cp39-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on metravod/zymi-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zymi_core-0.6.8-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
6efc66ecf897dbbc2fb3ac0aa894356f29de3eab05e6b3af5c1b0e6e1cb03fc9 - Sigstore transparency entry: 1539453341
- Sigstore integration time:
-
Permalink:
metravod/zymi-core@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Branch / Tag:
refs/tags/v0.6.8 - Owner: https://github.com/metravod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@49b7473c96801609c0f30d4a52fcf3d59dede014 -
Trigger Event:
push
-
Statement type: