Durable execution and guardrails for AI agents
Run agents in production with crash recovery, enforced policies, and human approvals. Use it with any agent framework.
- llm_callanthropic/claude-opus-5succeeded
- tool_callweb_searchsucceeded
- worker restarted, 2 steps replayed
- llm_callanthropic/claude-opus-5succeeded
- tool_callsend_emailawaiting approval
Rebuno sits between your agent and the calls it makes
Your framework still runs the loop. Every model and tool call passes through Rebuno first.
read_file
report.md
File read
Result recorded
search_docs
“agent frameworks”
Not called again
Saved result returned
drop_table
users
Blocked
Denied by policy
- allow
read_file
report.md
File read
Result recorded
- replay
search_docs
“agent frameworks”
Not called again
Saved result returned
- hold
send_email
Waiting for approval
- deny
drop_table
users
Blocked
Denied by policy
- Record
- Every model and tool call is saved with its result.
- Check
- Each call is matched against your policy before it runs.
- Hold
- Runs pause for approval. Your agent doesn't need to stay running.
- Resume
- A crashed run picks up from its saved results.
Where Rebuno fits
Crash recovery, human in the loop, and restricted access.
Recovery
Coding agent
Its worker is killed halfway through a fix. The run resumes without redoing the edit or rerunning the tests.
- edit_filesrc/client/retry.tsrecorded
- shellnpm testrecorded
- out of memory, dispatched again
- edit_filesrc/client/retry.tsreplayed
- shellnpm testreplayed
- shellgit commit -m "Retry on 429"recorded
Approvals
Support agent
All refunds need human approval.
- get_ticketZD-48213allowed
- get_chargech_3RmK8xL2allowed
- issue_refund4200.00 USDawaiting approval
- approved by finance
- issue_refund4200.00 USDallowed
Restricted access
Incident agent
Can run kubectl, but its policy only allows commands that read.
- shellkubectl get pods -n prodallowed
- shellkubectl logs deploy/api --since=1hallowed
- shellkubectl rollout restart deploy/apidenied
Works with the framework you already use
Add the highlighted parts to your agent code.
agent.py
from langchain.agents import create_agentfrom langchain_openai import ChatOpenAIfrom rebuno import Agent, http_client, tool @tool("send_email", idempotency="at_most_once")async def send_email(body: str) -> dict: """Email a written brief to a colleague.""" return await mail.send("[email protected]", body) async def process(query: str) -> dict: llm = ChatOpenAI(model="gpt-5.5", http_async_client=http_client()) graph = create_agent(model=llm, tools=[send_email]) result = await graph.ainvoke({"messages": [{"role": "user", "content": query}]}) return {"answer": result["messages"][-1].content} agent = Agent("mailer")agent.run(process)agent.py
from openai import AsyncOpenAIfrom pydantic_ai import Agent as PydanticAgentfrom pydantic_ai.models.openai import OpenAIResponsesModelfrom pydantic_ai.providers.openai import OpenAIProviderfrom rebuno import Agent, http_client, tool @tool("send_email", idempotency="at_most_once")async def send_email(body: str) -> dict: """Email a written brief to a colleague.""" return await mail.send("[email protected]", body) async def process(query: str) -> dict: client = AsyncOpenAI(http_client=http_client()) model = OpenAIResponsesModel("gpt-5.5", provider=OpenAIProvider(openai_client=client)) result = await PydanticAgent(model, tools=[send_email]).run(query) return {"answer": result.output} agent = Agent("mailer")agent.run(process)agent.py
from crewai import LLM, Agent as CrewAgent, Crew, Taskfrom crewai.tools import tool as crewai_toolfrom rebuno import Agent, execution, tool @crewai_tool("send_email")@tool("send_email", idempotency="at_most_once")async def send_email(body: str) -> dict: """Email a written brief to a colleague.""" return await mail.send("[email protected]", body) async def process(query: str) -> dict: # CrewAI builds its own HTTP client, so Rebuno intercepts at a gateway. ctx = execution() llm = LLM( model="openai/gpt-5.5", base_url=GATEWAY_URL, extra_headers={ "rebuno-execution-id": ctx.id, "rebuno-dispatch-id": ctx.dispatch_id, "rebuno-dispatch-attempt": str(ctx.dispatch_attempt), "rebuno-agent-id": ctx.agent_id, "rebuno-agent-secret": SECRET, }, ) writer = CrewAgent(role="writer", goal=query, llm=llm, tools=[send_email]) task = Task(description=query, expected_output="a short brief", agent=writer) result = await Crew(agents=[writer], tasks=[task]).kickoff_async() return {"answer": str(result)} agent = Agent("mailer")agent.run(process)agent.ts
import { createOpenAI } from "@ai-sdk/openai";import { generateText, tool } from "ai";import { z } from "zod";import { Agent, defineTool, rebunoFetch } from "rebuno"; const sendEmail = defineTool({ name: "send_email", idempotency: "at_most_once", execute: async ({ body }: { body: string }) => mail.send("[email protected]", body),}); const emailTool = tool({ description: "Email a written brief to a colleague.", inputSchema: z.object({ body: z.string() }), execute: sendEmail,}); async function process(input: { query: string }) { const openai = createOpenAI({ fetch: rebunoFetch }); const { text } = await generateText({ model: openai("gpt-5.5"), prompt: input.query, tools: { send_email: emailTool }, }); return { answer: text };} const agent = new Agent("mailer");await agent.serve({ port: 5000 }, process);agent.ts
import { createOpenAI } from "@ai-sdk/openai";import { Agent as MastraAgent } from "@mastra/core/agent";import { createTool } from "@mastra/core/tools";import { z } from "zod";import { Agent, defineTool, rebunoFetch } from "rebuno"; const sendEmail = defineTool({ name: "send_email", idempotency: "at_most_once", execute: async ({ body }: { body: string }) => mail.send("[email protected]", body),}); const emailTool = createTool({ id: sendEmail.name, description: "Email a written brief to a colleague.", inputSchema: z.object({ body: z.string() }), execute: sendEmail,}); async function process(input: { query: string }) { const openai = createOpenAI({ fetch: rebunoFetch }); const assistant = new MastraAgent({ id: "mailer", name: "Mailer", instructions: "Email the brief when asked.", model: openai("gpt-5.5"), tools: { send_email: emailTool }, }); const { text } = await assistant.generate(input.query); return { answer: text };} const agent = new Agent("mailer");await agent.serve({ port: 5000 }, process);agent.ts
import { ChatOpenAI } from "@langchain/openai";import { tool } from "@langchain/core/tools";import { createAgent } from "langchain";import { z } from "zod";import { Agent, defineTool, rebunoFetch } from "rebuno"; const sendEmail = defineTool({ name: "send_email", idempotency: "at_most_once", execute: async ({ body }: { body: string }) => mail.send("[email protected]", body),}); const emailTool = tool(sendEmail, { name: sendEmail.name, description: "Email a written brief to a colleague.", schema: z.object({ body: z.string() }),}); async function process(input: { query: string }) { const model = new ChatOpenAI({ model: "gpt-5.5", configuration: { fetch: rebunoFetch } }); const graph = createAgent({ model, tools: [emailTool] }); const result = await graph.invoke({ messages: [{ role: "user", content: input.query }], }); return { answer: result.messages.at(-1)?.content };} const agent = new Agent("mailer");await agent.serve({ port: 5000 }, process);Get started
Requirements: Go 1.26+, and Python 3.11+ or Node 22+
01
Start the dev kernel
Install the CLI and clone the repository for its examples.
$ go install github.com/rebuno/rebuno/cmd/rebuno@latest$ git clone https://github.com/rebuno/rebuno && cd rebuno$ rebuno dev --config examples/rebuno.dev.yaml02
Start the example agent
Install the SDK and start an agent that waits for runs.
Python
$ pip install rebuno$ python examples/python/hello.pyTypeScript
$ npm install rebuno$ npx tsx examples/typescript/hello.ts03
Start a run
Create a run, then watch its tool calls get checked and recorded.
$ rebuno exec create hello '{"query": "hello world"}'$ rebuno exec watch <id>