Quickstart
Build and deploy your first Tardie agent
Tardigrade is a TypeScript framework for building durable, modular 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
- Tardie CLI 0.18.0 or above
- 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 my-agent directory and configure a language model provider.
tdg init my-agent --template quickstart
After the setup, you should see the following files:
actor.tsActor, model, and componentsworker.tsWorker runtime entrywrangler.jsoncCloudflare Worker configurationcelld.jsoncSelf-hosted Celld configurationmodels.lock.jsonResolved deployment model scopepackage.jsonDependencies and module metadata
Move into the new project directory:
cd my-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, actor, agentMessageMethod, infer, nativeOutput, system, tool } from "tardie"
const actorName = "my-agent"
const actorInstructions = `
Answer questions about the weather.
Use the weather tool for current conditions.
`.trim()
const weather = tool({
spec: {
name: "get_weather",
description: "Get the current weather for a city",
inputSchema: { type: "object" }
},
run: () => Effect.succeed({ temperature: 21 })
})
export default actor({
name: actorName,
methods: { message: agentMessageMethod },
components: [infer([
system(actorInstructions),
weather,
nativeOutput
])]
})
About actor components
A Tardigrade component has the following interface:
interface ComponentDefinition<State, View, Requirements = never> {
readonly name: string
readonly initial: () => State
readonly step: (state: State, event: Event) => State
readonly output: (state: State) => {
readonly view: View
readonly transitions: ReadonlyArray<Transition<never, Requirements>>
}
readonly cancelState?: (
state: State,
cancellation: InvocationCancellation
) => ReadonlyArray<Transition<never, Requirements>>
readonly keys?: KeyFragment
}
initial creates private component state. step folds each event into that state. output derives the component's view and enabled transitions. When components are composed, Tardigrade combines their views and reconciles their transitions. The Requirements type records the Effect services needed to execute those transitions.
The weather component contributes get_weather to the agent's view and keeps its specification beside its Effect handler:
const weather = tool({
spec: {
name: "get_weather",
description: "Get the current weather for a city",
inputSchema: { type: "object" }
},
run: () => Effect.succeed({ temperature: 21 })
})
When the model selects get_weather, Tardigrade records ToolCalled. The inference component finds the handler in the current view, runs its Effect, and records ToolReturned. The next model request includes the result.
Running it locally
We'll test the agent by running it locally. Use the following command to bundle and serve actor.ts.
tdg dev
The server runs on localhost:4242 when the port is available and prints the selected address. It also opens a local explorer for the agent's log.
Send your first message
Once you have the agent running, you can send your first message.
tdg call message '{"text":"What is the weather in Singapore?"}' --thread quickstart
About actor methods
The actor code defines the following method:
export default actor({
name: actorName,
methods: { message: agentMessageMethod },
components: [infer([
system(actorInstructions),
weather,
nativeOutput
])]
})
An actor method defines a typed, durable call boundary. Its input and output schemas validate the public API, its event function records the call, and its projection derives the current call state from events. A method can also define how cancellation is recorded.
const AgentMessageInput = Schema.Struct({
text: Schema.String,
input: Schema.optionalKey(Schema.Unknown),
model: Schema.optionalKey(ModelRef)
})
const agentMessageMethod = actorMethod({
input: AgentMessageInput,
output: Schema.String,
event,
projection,
cancellation: { event: cancellationEvent }
})
Each message call remains pending until the method projection observes a completed, failed, or cancelled turn. The Quickstart command appends MessageReceived, and a later TurnCompleted resolves the call with its string output.
Inspecting the actor logs
After your call, you will see .tardigrade/actor.sqlite.actors/. 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.
bWFpbg.sqliteEvent log for themainactor instance
A simplified view of the message includes the following events. The log also records the model requests and responses.
ThreadCreated{ address: { ... } }MessageReceived{ text: "What is the weather..." }ToolCalled{ name: "get_weather", arguments: { ... } }ToolReturned{ result: { ... } }TurnCompleted{ output: "..." }
These events are stored locally and persist across runs. Each thread has its own event log within its actor instance's SQLite database.
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
Now that we've tested our agent, let's deploy it. To deploy the agent on Cloudflare, run:
wrangler deploy
To deploy it on your own cloud, use Celld:
celld deploy
Send a message to your cloud agent
Once your deployment has gone through, your agent could be accessed through an endpoint on the internet. Try making the following request to see it respond:
curl -X PUT "$ACTOR_URL/v1/threads/quickstart/methods/message/calls/first" \
-H 'content-type: application/json' \
-d '{"text":"What is the weather in Singapore?"}'
curl "$ACTOR_URL/v1/threads/quickstart/methods/message/calls/first"
Going further
Congrats! You've got your first durable agent running. In an upcoming guide, you will learn how to build your own Slack or Telegram bot, connect it to external services, and use its logs as a source of durable memory.