Agents that pauseand resumeanywhere.
A run pauses for a human, an approval, or a tool in your user's browser, persists its full state to your database, and resumes in any process, even after a restart. Every step recorded as evidence.
import asyncio
import os
from dendrux import Agent, tool
from dendrux.llm.anthropic import AnthropicProvider
@tool()
async def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
async def main():
async with Agent(
provider=AnthropicProvider(
model="claude-sonnet-4-6",
api_key=os.environ["ANTHROPIC_API_KEY"],
),
prompt="You are a calculator.",
tools=[add],
) as agent:
result = await agent.run("What is 15 + 27?")
print(result.answer)
asyncio.run(main())Every call, every pause, every resume. Recorded.
Click a node to inspect. The timeline is the dashboard. No JSON walls, no decoration.
Six commitments the runtime makes.
Every feature in dendrux exists to serve one of these. No framework magic, no hidden loops.
Pause for the real world
Stop for an approval, a human answer, or a tool that runs in the user's browser, spreadsheet, or desktop. The run resumes from any process, by ID.
Persist everything
Every run's full state lives in your database, not memory. A restart loses nothing: resume by ID. Reconcile stale runs with sweep(), restart terminal ones with retry(), both developer-invoked.
Explain everything
Every LLM call, tool execution, pause, and lifecycle event is persisted as evidence. Fail-closed recorder + best-effort notifier.
Govern behavior
Tool deny, HITL approval, advisory budgets, PII redaction, secret detection. Four layers of runtime governance.
Control execution
Tool constraints, timeouts, parallel/sequential policy, delegation depth guards. No runaways.
Coordinate agents
Parent-child delegation with automatic linking via contextvars. Depth guards and lifecycle coupling.
Agents cross the server/client boundary.
When an agent calls a client-side tool, the run enters WAITING_CLIENT_TOOL. State is persisted. The client SDK executes on the user's device. When results return, the run resumes from exactly where it left off, with the same context and reasoning.
PENDING → RUNNING → WAITING_CLIENT_TOOL → RUNNING → SUCCESS
Production essentials, opt-in.
Four layers, all kwargs.
Tool deny. Human approval. Advisory budgets. Content guardrails with PII redaction and secret detection. Stack them or don't.
Token-by-token, clean cancel.
Stream text, tool calls, and lifecycle events. Break early and the run cancels cleanly. Works with every provider.
print(chunk, end="")
Agents as tools.
Parent-child run linking via contextvars. Zero developer code. Dashboard shows the full tree.
SQLite to Postgres.
Zero config for dev. One env var for prod. Alembic migrations included.
Tool source, not rival.
MCP servers become dendrux tools automatically. Schema translated, rate-limited, traced.
Thinking, on one switch.
Extended thinking for Anthropic and OpenAI behind one uniform set of controls. Off by default, so nothing changes until you ask for it. The reasoning tokens — and the summary trace, where the vendor returns it — land on every run result, stream, and dashboard.
Swap one import. Everything else stays.
Three first-class provider classes. Any OpenAI-compatible server works through OpenAIProvider with a custom base_url.
Start with ten lines.
Scale without rewrites.
Dendrux is in active development at v0.2.0a1. Core API stabilizing. Apache 2.0. Bring your own infra.