Quickstart
Build and deploy your first Tardie agent
Tardigrade is a TypeScript framework for building durable, composable agents around an immutable event log. By the end of this guide, you will know how to run a Tardigrade agent locally, inspect its trajectory, and deploy it to the cloud.
Prerequisites
- Bun 1.4 or above
- The latest Tardie CLI
- An LLM API key from a provider of your choice
For the fastest setup, create an OpenRouter API key before you begin. For deployment, install either the Wrangler CLI for Cloudflare or the Celld CLI for your own cloud.
Initialize your project
First, install the Tardie CLI.
bun add -g tardie@latest
Now, use the Tardie CLI to create a new tardie-agent directory and configure a language model provider.
tdg init tardie-agent --template quickstart
Initialization locks the selected model. Run tdg models lock to include additional configured models with complete context metadata.
After the setup, you should see the following files:
actor.tsActor and atomsservices.tsModel and library implementationsgenerated/providers.tsConfigured model implementationsserver.tsBun server entryworker.tsCloudflare and Celld Worker entrywrangler.jsoncCloudflare Worker configurationcelld.jsoncSelf-hosted Celld configurationmodels.lock.jsonResolved model metadatapackage.jsonDependencies and module metadata
Move into the new project directory:
cd tardie-agent
Define your first actor
Hello world!
For this Quickstart, we'll implement a simple agent with a tool that returns dummy weather data for a given city. In another guide, we'll explore a more advanced setup by building an agent that can execute JavaScript code, call MCP servers, and run subagents within a sandbox.
As you follow along, you will see tip boxes that you can expand to learn what happens behind the scenes.
About these tips
These notes explain what Tardigrade is doing behind the scenes. You can skip them and still complete the guide.
Within actor.ts, you will see the following code:
import { Effect, Schema } from "effect"
import { Rpc } from "effect/unstable/rpc"
import { atom, defineActor } from "tardie/core"
import { agentMethods, compact, infer, messages, tools as libraryTools } from "tardie/agent"
import { defineLibrary, MethodDescription, MethodHints } from "tardie/libraries"
const actorName = "tardie-agent"
const actorInstructions = `
Answer questions about the weather.
Use the weather tool for current conditions.
`.trim()
export const weather = defineLibrary({
name: "weather",
description: "Weather information",
toolNames: { current: "get_weather" },
methods: [Rpc.make("current", {
payload: Schema.Struct({ city: Schema.String }),
success: Schema.Struct({ city: Schema.String, temperature: Schema.Finite }),
})
.annotate(MethodDescription, "Get example weather data for a city")
.annotate(MethodHints, { readOnlyHint: true, openWorldHint: false })],
})
export default defineActor(actorName, Effect.gen(function* () {
const system = atom(actorInstructions)
const tools = yield* libraryTools([weather])
const context = yield* compact(messages)
const agent = yield* infer(get => ({
system: get(system),
tools: get(tools),
context: get(context),
}))
return { atom: agent, methods: agentMethods }
}))
messages derives message history from the event log. compact prepares model context, and infer proposes model calls. The actor composes these atoms and exposes agentMethods.
The weather library declares a typed method with Effect RPC. Its implementation belongs in services.ts:
const tools = weather.implement({
current: ({ city }) => Effect.succeed({ city, temperature: 21 }),
})
The example returns dummy weather data. Replace the handler with your weather service. toolActs([tools]) supplies the handler to the runtime; the actor sees its method schema and annotations.
Running it locally
Run the generated Bun API server directly:
bun run dev
The server runs on localhost:4242 by default. Set PORT to change it. Check curl localhost:4242/healthz from another shell to confirm it is ready. Edit server.ts to change how the actor and API are assembled.
Send your first message
Once you have the agent running, create a root thread named quickstart, then send it your first message. The creation command prints the assigned thread ID.
tdg thread create --name quickstart
tdg call message '{"text":"What is the weather in Singapore?"}' --thread quickstart
The message method records a TurnRequested event. Model and tool results update durable atoms, and a TurnSettled event completes the call with { text: string }. Each call has an invocation ID. To safely retry a call, supply the same --id and the same input.
Inspecting the actor logs
After your call, you will see .tardigrade/data/. This directory holds one SQLite database for each actor instance. An event log is an immutable, append-only ledger of events sent to the actor and events generated by the actor.
<actor-instance>.sqliteRegistry for themainactor instance<actor-instance>.sqlite.threads/<thread>.sqliteEvent log for thequickstartthread
A simplified view of the message includes the following events. The log also records the model requests and responses.
ThreadCreated{ address: { ... } }TurnRequested{ text: "What is the weather..." }ToolCalled{ callId: "...", counted: true }ToolReturned{ callId: "...", output: "...", error: null }TurnSettled{ settlement: "completed", ... }
These events persist across runs. Each thread has a separate SQLite event database in the .sqlite.threads/ directory beside its actor instance database. Set TARDIGRADE_ACTOR_DATA to change the storage directory.
You can use the following command to view the events:
tdg events <thread>
If you used the command in this guide, the first message landed in a thread called quickstart. To view its events, run:
tdg events quickstart
Send a follow up message
To send a follow up message, simply send another message to the same thread:
tdg call message '{"text":"How about San Francisco?"}' --thread quickstart
Deploy your agent
Set your provider credential as a Worker secret before deploying. For example, with OpenRouter:
bunx wrangler secret put OPENROUTER_API_KEY
The generated Worker uses an ActorDO binding and the same actor, service layer, and model lock as the Bun server. Deploy it:
bun run deploy:cloudflare
To deploy it on your own cloud, use Celld:
bun run deploy:celld
Send a message to your cloud agent
Once your deployment is ready, create a root thread on the remote host, then invoke its message method. Set ACTOR_URL to your deployment URL.
tdg thread create --name quickstart --url "$ACTOR_URL"
tdg call message '{"text":"What is the weather in Singapore?"}' --thread quickstart --url "$ACTOR_URL"
Going further
See the README for atom composition and the SDK reference for custom libraries. To migrate an existing event log at a settled boundary, use state initialisation.