Quickstart
Define an agent, run it, read the result. 10 minutes.
This tutorial runs a tiny agent end-to-end against OpenRouter. By the end you’ll have a working agent that fetches a question, replies with valid JSON, and reports its tokens + cost.
Prerequisites
-
Bun runtime
drover runs on Bun 1.3+. No Node-only deps assumed.
-
OpenRouter API key
Default model aliases route through OpenRouter. Set it once:
bash export OPENROUTER_API_KEY=sk-or-v1-...See
@drover/modelfor using a different provider.
Install
bun add @drover/core @drover/facade @drover/sandboxDefine + run an agent
import { Type } from "@sinclair/typebox";
import { defineAgent } from "@drover/core";
import { runAgent } from "@drover/facade";
const echo = defineAgent({
id: "echo",
systemPrompt:
"Reply with a JSON object matching the outputSchema. No prose.",
inputSchema: Type.Object({ word: Type.String() }),
outputSchema: Type.Object({
upper: Type.String(),
length: Type.Integer(),
}),
model: "cheap", // alias for gemini-3.1-flash-lite-preview
tools: [],
quota: { maxTurns: 2 },
});
const handle = runAgent(echo, { word: "drover" });
for await (const e of handle.events) {
if (e.kind === "assistant_text") console.log("[text]", e.text);
}
const result = await handle.result;
console.log("status:", result.status);
console.log("output:", result.output);
console.log("tokens:", result.usage.inputTokens + "/" + result.usage.outputTokens);Run it:
bun quickstart.tsExpected output:
[text] {"upper":"DROVER","length":6}
status: success
output: { upper: "DROVER", length: 6 }
tokens: 179/24
What happened
-
Input validation
drover decoded
{ word: "drover" }against yourinputSchemabefore calling the model. Validation failure short-circuits with anInputValidationError— the model never sees malformed input. -
Model dispatch
"cheap"resolved through the alias map togoogle/gemini-3.1-flash-lite-previewon OpenRouter. The harness wrapped pi-agent-core’srunAgentLoopand streamed events as they fired. -
Output validation
The model’s final text was decoded against
outputSchema. If it had failed, drover would have fed back a corrective message and retried up tooutputRetriestimes (default 2). -
Terminal status
result.statusis one ofsuccess,quota,cancelled,paused, orerror.result.errorpopulates on the error path — the Promise never rejects.