> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rebuno.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Mastra

> Durable execution, policy, and approvals for Mastra agents

A Mastra agent runs on Rebuno unchanged apart from two seams. The model's
provider takes `rebunoFetch`, so every model call is recorded as an `llm_call`
step, and tools run through `defineTool`, so every tool call is recorded as a
`tool_call` step. On a re-dispatch the agent runs from the top and recorded
steps replay instead of calling the model or the tool again.

## Install

```bash theme={"theme":{"light":"min-light","dark":"material-theme-ocean"}}
npm install rebuno @mastra/core @ai-sdk/openai zod
```

## Model calls

Mastra takes AI SDK providers, which accept a custom `fetch`. Pass
`rebunoFetch`:

```ts theme={"theme":{"light":"min-light","dark":"material-theme-ocean"}}
import { createOpenAI } from "@ai-sdk/openai";
import { rebunoFetch } from "rebuno";

const openai = createOpenAI({ fetch: rebunoFetch });
```

See [LLM calls](/sdk/typescript/llm-calls).

## Tools

Declare the Rebuno side with `defineTool`, and pass it as the `execute` of a
Mastra tool:

```ts theme={"theme":{"light":"min-light","dark":"material-theme-ocean"}}
import { createTool } from "@mastra/core/tools";
import { defineTool } from "rebuno";
import { z } from "zod";

const sendEmail = createTool({
  id: "send_email",
  description: "Email the support summary.",
  inputSchema: z.object({ body: z.string() }),
  execute: defineTool({
    name: "send_email",
    idempotency: "at_most_once",
    execute: async ({ body }: { body: string }) => mail.send("ops@acme.com", body),
  }),
});
```

Mark anything with a side effect, such as sending an email or creating a
ticket, `at_most_once`. See [idempotency](/sdk/typescript/tools#idempotency).

## The agent

Mastra's `Agent` and Rebuno's `Agent` share a name, so import one under an
alias:

```ts theme={"theme":{"light":"min-light","dark":"material-theme-ocean"}}
import { Agent as MastraAgent } from "@mastra/core/agent";
import { Agent } from "rebuno";

async function process(input: { query: string }) {
  const openai = createOpenAI({ fetch: rebunoFetch });
  const assistant = new MastraAgent({
    id: "support",
    name: "Support",
    instructions: "Investigate customer issues and email support summaries.",
    model: openai("gpt-5.5"),
    tools: { send_email: sendEmail },
  });
  const { text } = await assistant.generate(input.query, { maxSteps: 12 });
  return { answer: text };
}

const agent = new Agent("support");
await agent.serve({ port: 5000 }, process);
```

## What Rebuno adds

* **Policy.** Every model and tool call is checked against [policy](/policy)
  before it runs. A denied tool returns the rule's reason to the model as the
  tool result, so the agent can take a different path.
* **Approvals.** A tool that requires approval parks the execution. Once it's
  approved, the agent is dispatched again, the earlier steps replay, and the
  approved call runs.
* **Recovery.** If the worker dies partway through, the next dispatch replays
  every completed step and continues from the first one that didn't finish.
  Model calls that already ran are not paid for twice.

## Full example

[`examples/frameworks/typescript/mastra_agent.ts`](https://github.com/rebuno/rebuno/blob/main/examples/frameworks/typescript/mastra_agent.ts)
is a support agent that investigates a customer issue, creates a ticket, and
emails a summary after approval.
