Your first agent
Getting Started gets you from install to a served, pausable agent. This tutorial has you write your own agent and walks it through the full durability loop: run it, trace its journal, pause it for a human, replay it with zero LLM calls, and turn the recording into a CI test. Plan for about fifteen minutes.
You need the chidori binary and a connected model —
Getting Started covers both (chidori model-login
or a provider key). Work in the project chidori init scaffolded there, or
any fresh directory.
No API key? Set
CHIDORI_TEST_LLM_RESPONSE="(test reply)"and every prompt call returns that static string instead of calling a provider — so the model won't actually exercise the tool, but the durability mechanics this tutorial is about (journaling, pause, replay, verify) behave identically.
1. Write the agent
An agent is one ordinary TypeScript file. Save this as research.ts:
import { chidori, run, defineTool } from "chidori:agent";
const NOTES = [
"2026-05-04 standup: replay divergence bug traced to unseeded RNG in retry helper.",
"2026-05-11 standup: prompt cache hit rate at 87% after context() refactor.",
"2026-05-18 standup: actors supervision tree shipped; joins fold logs correctly.",
];
// A tool is just a function with a documented signature; closures over
// NOTES work, and every invocation is journaled.
const searchNotes = defineTool({
name: "search_notes",
description: "Keyword search over the team's standup notes.",
parameters: {
type: "object",
properties: { query: { type: "string", description: "Search keyword" } },
required: ["query"],
},
run: async ({ query }: { query: string }) =>
NOTES.filter((n) => n.toLowerCase().includes(query.toLowerCase())),
});
run(async (input: { question: string }) => {
await chidori.log("Researching", { question: input.question });
// One call runs the whole provider tool-use loop, up to maxTurns.
const answer = await chidori.prompt(
`Answer from the standup notes, citing dates: ${input.question}`,
{ tools: [searchNotes], maxTurns: 4, type: "final" },
);
// Pause for a human. `details` carries the artifact under review, so the
// approval is never blind.
const ship = await chidori.input("Publish this answer?", {
type: "approval",
choices: ["yes", "no"],
default: "no",
details: answer,
});
return { answer, published: ship.toLowerCase() === "yes" };
});Three things to notice before running it:
- Every side effect goes through a host call.
chidori.log,chidori.prompt,chidori.input— each is recorded in the run's journal. Theifs,awaits, and string munging between them are plain TypeScript. - The tool needs no registration — a tool is just a function, and you
pass the
defineToolhandle straight intoprompt()(Core Concepts covers the tool model). - Type the input with an object type, not an
interface— interfaces fail the handler's JSON constraint with a confusing error (Host API).
2. Run it
chidori run research.ts --input question="what happened with the prompt cache?"The model runs the tool loop — calling search_notes, reading the results,
composing an answer — and then the run stops and asks you:
Publish this answer? [yes/no]The answer text prints above the prompt — that's details. Type yes. The
run completes and prints its JSON output, something like:
{
"answer": "…the 2026-05-11 note reports an 87% prompt cache hit rate…",
"published": true
}
chidori runasks for approval before powerful effects — this agent's tool is pure in-VM compute, so the only pause you see is your owninput()gate; postures and--trustedare in the CLI reference.
3. Read the record
Every run journals to .chidori/runs/<run_id>/ next to the agent file. Look
at what was recorded:
RUN_ID=$(ls -t .chidori/runs | head -1)
chidori trace "$RUN_ID"The trace is the run's complete story: the log call, each model turn and
search_notes invocation inside the prompt loop, your input() answer, and
the token counts and cost of every prompt. This journal (the run's call
log) — not a framework abstraction — is what makes everything in the next
two steps possible.
4. Replay it for $0
The concept is replay; the CLI command that performs it is
chidori resume (one command covers both replaying a finished run and
resuming a paused one — a completed journal simply has nothing left to
continue):
chidori resume research.ts "$RUN_ID"The agent code re-executes from the top — but every host call returns its recorded result from the journal instead of touching the world. No model is called, no tokens are billed, nobody is asked to approve anything, and the output is byte-identical to step 2. This is the same mechanism that powers crash recovery: a run that dies halfway replays to the frontier of its journal and continues live from there (how replay works).
Three variants worth knowing now:
chidori resume research.ts <run_id> --allow-source-change— replay against edited agent code, divergence-checked. Fix a bug three runs deep without re-paying the first three runs.chidori dev research.ts— the edit-iterate loop as a command: it watches the file and re-runs on every save, replaying recorded calls from the journal so edits cost zero tokens.- A paused server-mode session resumes the same way, minutes or days later, in a fresh process (Signals).
5. Turn the recording into a test
Don't commit the raw run directory — it contains a multi-MB snapshot blob.
Export just the four small artifacts verify reads, and commit those:
chidori export "$RUN_ID" --fixture tests/fixtures
git add tests/fixturesThen, in CI:
chidori verify research.ts "$RUN_ID" --runs-dir tests/fixturesverify replays the run with no providers, no tools, and the untrusted
policy profile and asserts it completes with byte-identical output. Exit
code 0 means the agent's behavior hasn't drifted from the recording — a
full integration test of your agent, prompts, tool loop, approval gate and
all, that costs $0 and runs in milliseconds. Full contract:
Replay as test; see
Value Checkpoints for bounding replay cost as
runs grow.
Where next
- Core Concepts — the full host-function surface and the mental model behind it.
- Common Patterns — approval gates, fan-out, multiplayer review, scheduled agents: which primitive fits which job.
- Host API Reference — every
chidori.*method, option by option. chidori serve research.ts --port 8080turns this same file into an HTTP session API whereinput()pauses become resumable sessions — Running Modes.
Getting Started
Install the binary, chat with the bundled docs in 30 seconds, run and inspect your own durable agent, and exercise the pause/resume session loop.
Core Concepts
Agents, host functions, and the shared vocabulary: the chidori.* surface, the journal, safepoints, tools, streaming, and prompt caching.