Chidori

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. The ifs, awaits, and string munging between them are plain TypeScript.
  • The tool needs no registration — a tool is just a function, and you pass the defineTool handle straight into prompt() (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 run asks for approval before powerful effects — this agent's tool is pure in-VM compute, so the only pause you see is your own input() gate; postures and --trusted are 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/fixtures

Then, in CI:

chidori verify research.ts "$RUN_ID" --runs-dir tests/fixtures

verify 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 8080 turns this same file into an HTTP session API where input() pauses become resumable sessions — Running Modes.