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

# Quickstart: your agent

> Add Vendo's guarded tool pack to an AI SDK or Mastra loop you already run: vendo init, one spread, one component.

export const EXISTING_AGENTS_PROMPT = "This repo already runs its own agent loop. Keep it, and add Vendo's guarded tools, micro-apps, " + "and approvals to it. Fetch https://vendo.run/agents.md and follow it for this repo. Ask me before " + "creating any account or key. You're done when `vendo doctor --json` reports all green and my " + "chat renders Vendo tool outputs. Then ask me whether I'd like to star it via " + "https://vendo.run/star?src=docs-byo to support the project.";

export const AgentPrompt = ({prompt, lead, title = "Or install with your coding agent"}) => {
  const AGENT_LOGOS = [{
    name: "Claude Code",
    viewBox: "0 0 256 257",
    fill: "#D97757",
    path: "m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z"
  }, {
    name: "Cursor",
    viewBox: "0 0 466.73 532.09",
    fill: "currentColor",
    path: "M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z"
  }, {
    name: "GitHub Copilot",
    viewBox: "0 0 256 208",
    fill: "currentColor",
    path: "M205.3 31.4c14 14.8 20 35.2 22.5 63.6 6.6 0 12.8 1.5 17 7.2l7.8 10.6c2.2 3 3.4 6.6 3.4 10.4v28.7a12 12 0 0 1-4.8 9.5C215.9 187.2 172.3 208 128 208c-49 0-98.2-28.3-123.2-46.6a12 12 0 0 1-4.8-9.5v-28.7c0-3.8 1.2-7.4 3.4-10.5l7.8-10.5c4.2-5.7 10.4-7.2 17-7.2 2.5-28.4 8.4-48.8 22.5-63.6C77.3 3.2 112.6 0 127.6 0h.4c14.7 0 50.4 2.9 77.3 31.4ZM128 78.7c-3 0-6.5.2-10.3.6a27.1 27.1 0 0 1-6 12.1 45 45 0 0 1-32 13c-6.8 0-13.9-1.5-19.7-5.2-5.5 1.9-10.8 4.5-11.2 11-.5 12.2-.6 24.5-.6 36.8 0 6.1 0 12.3-.2 18.5 0 3.6 2.2 6.9 5.5 8.4C79.9 185.9 105 192 128 192s48-6 74.5-18.1a9.4 9.4 0 0 0 5.5-8.4c.3-18.4 0-37-.8-55.3-.4-6.6-5.7-9.1-11.2-11-5.8 3.7-13 5.1-19.7 5.1a45 45 0 0 1-32-12.9 27.1 27.1 0 0 1-6-12.1c-3.4-.4-6.9-.5-10.3-.6Zm-27 44c5.8 0 10.5 4.6 10.5 10.4v19.2a10.4 10.4 0 0 1-20.8 0V133c0-5.8 4.6-10.4 10.4-10.4Zm53.4 0c5.8 0 10.4 4.6 10.4 10.4v19.2a10.4 10.4 0 0 1-20.8 0V133c0-5.8 4.7-10.4 10.4-10.4Zm-73-94.4c-11.2 1.1-20.6 4.8-25.4 10-10.4 11.3-8.2 40.1-2.2 46.2A31.2 31.2 0 0 0 75 91.7c6.8 0 19.6-1.5 30.1-12.2 4.7-4.5 7.5-15.7 7.2-27-.3-9.1-2.9-16.7-6.7-19.9-4.2-3.6-13.6-5.2-24.2-4.3Zm69 4.3c-3.8 3.2-6.4 10.8-6.7 19.9-.3 11.3 2.5 22.5 7.2 27a41.7 41.7 0 0 0 30 12.2c8.9 0 17-2.9 21.3-7.2 6-6.1 8.2-34.9-2.2-46.3-4.8-5-14.2-8.8-25.4-9.9-10.6-1-20 .7-24.2 4.3ZM128 56c-2.6 0-5.6.2-9 .5.4 1.7.5 3.7.7 5.7 0 1.5 0 3-.2 4.5 3.2-.3 6-.3 8.5-.3 2.6 0 5.3 0 8.5.3-.2-1.6-.2-3-.2-4.5.2-2 .3-4 .7-5.7-3.4-.3-6.4-.5-9-.5Z"
  }, {
    name: "OpenAI Codex",
    viewBox: "0 0 256 260",
    fill: "currentColor",
    path: "M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z"
  }, {
    name: "Windsurf",
    viewBox: "0 0 1024 1024",
    fill: "currentColor",
    path: "M897.246 286.869H889.819C850.735 286.808 819.017 318.46 819.017 357.539V515.589C819.017 547.15 792.93 572.716 761.882 572.716C743.436 572.716 725.02 563.433 714.093 547.85L552.673 317.304C539.28 298.16 517.486 286.747 493.895 286.747C457.094 286.747 423.976 318.034 423.976 356.657V515.619C423.976 547.181 398.103 572.746 366.842 572.746C348.335 572.746 329.949 563.463 319.021 547.881L138.395 289.882C134.316 284.038 125.154 286.93 125.154 294.052V431.892C125.154 438.862 127.285 445.619 131.272 451.34L309.037 705.2C319.539 720.204 335.033 731.344 352.9 735.392C397.616 745.557 438.77 711.135 438.77 667.278V508.406C438.77 476.845 464.339 451.279 495.904 451.279H495.995C515.02 451.279 532.857 460.562 543.785 476.145L705.235 706.661C718.659 725.835 739.327 737.218 763.983 737.218C801.606 737.218 833.841 705.9 833.841 667.308V508.376C833.841 476.815 859.41 451.249 890.975 451.249H897.276C901.233 451.249 904.43 448.053 904.43 444.097V294.021C904.43 290.065 901.233 286.869 897.276 286.869H897.246Z"
  }];
  const [copied, setCopied] = useState(false);
  const copy = () => {
    const done = () => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    };
    const fallback = () => {
      const textarea = document.createElement("textarea");
      textarea.value = prompt;
      textarea.setAttribute("readonly", "");
      textarea.style.position = "fixed";
      textarea.style.opacity = "0";
      document.body.appendChild(textarea);
      textarea.select();
      try {
        if (document.execCommand("copy")) done();
      } finally {
        document.body.removeChild(textarea);
      }
    };
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(prompt).then(done, fallback);
    } else {
      fallback();
    }
  };
  return <div className="vendo-agent-prompt not-prose">
      <div className="vendo-agent-prompt-head">
        <div className="vendo-agent-prompt-text">
          <span className="vendo-agent-prompt-title">{title}</span>
          <span className="vendo-agent-prompt-lead">{lead}</span>
        </div>
        <div className="vendo-agent-prompt-logos">
          {AGENT_LOGOS.map(logo => <span key={logo.name} className="vendo-agent-prompt-logo" title={logo.name}>
              <svg viewBox={logo.viewBox} width="20" height="20" role="img" aria-label={logo.name} xmlns="http://www.w3.org/2000/svg">
                <path fill={logo.fill} d={logo.path} />
              </svg>
            </span>)}
        </div>
      </div>
      <div className="vendo-agent-prompt-body">
        <pre className="vendo-agent-prompt-pre">{prompt}</pre>
        <button type="button" className="vendo-agent-prompt-copy" onClick={copy} aria-live="polite">
          {copied ? "Copied" : "Copy prompt"}
        </button>
      </div>
    </div>;
};

Your loop, your model, your chat UI. Vendo adds [a pack of guarded
tools](/existing-agents/overview) to them.

<AgentPrompt lead={<>Paste this and your coding agent wires the pack against the <a href="/agents">agent playbook</a>.</>} prompt={EXISTING_AGENTS_PROMPT} />

## 1. Init

```bash theme={null}
npm install @vendoai/vendo ai @ai-sdk/react
npx vendo init
```

Answer the questions. On the first one, pick **Through my own agent loop (AI
SDK / Mastra)**. On the model one take **Vendo Cloud**: a free key lands in
`.env.local` as `VENDO_API_KEY`, and Vendo's own screen turns run on it with
nothing else to set. Your own provider key works too. Init writes the
[`models:` line](/deploy/model-credentials) that selects it, because a key
sitting loose in the environment selects nothing.

## 2. Share one instance

Init writes `createVendo` inside the wire route. Move it to `lib/vendo.ts` so
your chat route uses the same instance, and export the identity init already
wrote — step 3 needs it to resolve the caller. Keep whichever line init wrote.
`auth` and `principal` fill the same seam, so `createVendo` refuses a config
that sets both.

<CodeGroup>
  ```ts init wired an auth preset theme={null}
  // lib/vendo.ts
  import { clerk } from "@vendoai/vendo/auth/clerk";
  import { createVendo } from "@vendoai/vendo/server";

  const auth = clerk(); // whichever preset init detected

  export const resolvePrincipal = auth.principal;

  export const vendo = createVendo({
    // …the options init wrote
    auth,
  });
  ```

  ```ts init wired no auth preset theme={null}
  // lib/vendo.ts
  import { createVendo } from "@vendoai/vendo/server";

  // the function init wrote — swap in your real session lookup
  export const resolvePrincipal = async (req: Request) =>
    ({ kind: "user" as const, subject: "demo-user" });

  export const vendo = createVendo({
    // …the options init wrote
    principal: resolvePrincipal,
  });
  ```
</CodeGroup>

```ts theme={null}
// app/api/vendo/[...vendo]/route.ts
import { nextVendoHandler } from "@vendoai/vendo/server";
import { vendo } from "@/lib/vendo";

export const { GET, POST, PUT, PATCH, DELETE } = nextVendoHandler(vendo);
```

## 3. Spread the pack

The pack takes the *resolved* caller — a `{ kind, subject }` object — so build
it inside the handler, once you know who is asking. Vendo mints no principals,
so a visitor your resolver can't place gets a 401.

<CodeGroup>
  ```ts AI SDK theme={null}
  // app/api/chat/route.ts
  import { convertToModelMessages, streamText } from "ai";
  import { vendoTools } from "@vendoai/vendo/ai-sdk";
  import { vendoModel } from "@vendoai/vendo/server";
  import { resolvePrincipal, vendo } from "@/lib/vendo";

  export async function POST(req: Request) {
    const { messages } = await req.json();
    const caller = await resolvePrincipal(req);
    if (!caller) return new Response("Unauthorized", { status: 401 });
    return streamText({
      model: vendoModel(),
      messages: await convertToModelMessages(messages),
      // your own tools spread in here too
      tools: { ...(await vendoTools(vendo, { principal: caller })) },
    }).toUIMessageStreamResponse();
  }
  ```

  ```ts Mastra · agent theme={null}
  // src/mastra/agents/your-agent.ts
  import { Agent } from "@mastra/core/agent";
  import { vendoMastraTools } from "@vendoai/vendo/mastra";
  import { vendo } from "@/lib/vendo";

  export const yourAgent = new Agent({
    id: "your-agent",
    name: "your-agent",
    instructions: "…your system prompt as it is",
    model: "openai/gpt-4.1-mini", // your own model and your own key
    tools: async () => ({ ...(await vendoMastraTools(vendo)) }), // yours spread in too
  });
  ```

  ```ts Mastra · register theme={null}
  // src/mastra/index.ts — agentId below resolves against this instance
  import { Mastra } from "@mastra/core/mastra";
  import { yourAgent } from "./agents/your-agent";

  export const mastra = new Mastra({ agents: { yourAgent } });
  ```

  ```ts Mastra · route theme={null}
  // app/api/chat/route.ts — the pack reads its caller from here
  import { handleChatStream } from "@mastra/ai-sdk";
  import { RequestContext } from "@mastra/core/request-context";
  import { VENDO_PRINCIPAL_KEY } from "@vendoai/vendo/mastra";
  import { createUIMessageStreamResponse } from "ai";
  import { resolvePrincipal } from "@/lib/vendo";
  import { mastra } from "@/mastra";

  export async function POST(req: Request) {
    const params = await req.json();
    const caller = await resolvePrincipal(req);
    if (!caller) return new Response("Unauthorized", { status: 401 });
    const requestContext = new RequestContext();
    requestContext.set(VENDO_PRINCIPAL_KEY, caller);
    params.requestContext = requestContext; // omit this and every vendo_* call fails
    const stream = await handleChatStream({ version: "v6", mastra, agentId: "your-agent", params });
    return createUIMessageStreamResponse({ stream });
  }
  ```
</CodeGroup>

Your loop keeps its own model credential. `vendoModel()` rides the same ladder
Vendo's internal turns do, so the Cloud key from step 1 pays for both seats;
Mastra's `openai/…` pin needs an `OPENAI_API_KEY` of its own. Either way:
[Two models, two credentials](/deploy/model-credentials#two-models-two-credentials).

Framework notes: [AI SDK](/existing-agents/ai-sdk) · [Mastra](/existing-agents/mastra).

## 4. Render the results

`vendo_*` calls arrive in your chat as `dynamic-tool` parts. Hand the finished
ones to `<VendoToolResult>`, anywhere under one `<VendoProvider>`.

```tsx theme={null}
// app/page.tsx
"use client";
import { useChat } from "@ai-sdk/react";
import { VendoProvider, VendoToolResult } from "@vendoai/vendo/react";

export default function Chat() {
  const { messages } = useChat();
  return (
    <VendoProvider baseUrl="/api/vendo">
      {messages.map((message) => message.parts.map((part, i) => {
        const key = `${message.id}-${i}`;
        if (part.type === "text") return <p key={key}>{part.text}</p>;
        if (part.type === "dynamic-tool" && part.state === "output-available") {
          return <VendoToolResult key={key} output={part.output} />;
        }
        return null;
      }))}
    </VendoProvider>
  );
}
```

## 5. Ask for a screen

Say **"Make me a dashboard of this month's spending by category"** in your own
chat. A live view builds inline. Then check the wiring:

```bash theme={null}
npx vendo doctor
```

Next: [embeds and envelopes](/existing-agents/embeds).
