Quickstart¶
Install¶
$ uv add hansard
Zero runtime dependencies — Hansard adds nothing to your dependency tree. Requires Python 3.13+.
Integrate in about fifteen minutes¶
The smallest real integration: open a session, record a message, wrap the agent's work in a turn, record what it did.
import hansard
with hansard.session(path="./sessions", agent="support-bot") as sess:
priya = sess.message(user_id="priya", text="restart the payments worker")
with sess.turn(context=[priya]) as turn:
turn.action(
kind="tool",
name="restart_worker",
args={"service": "payments"},
caused_by=[priya],
)
turn.output(
text="Restarted the payments worker.",
caused_by=[priya],
)
A session is a directory, not a file: hansard.session() opens one
append-only .jsonl segment per writer, so concurrent callers (one per
person, one per agent process) never contend on the same file. context=
and caused_by= are optional — omit them and Hansard falls back to
inference — but passing them is what turns a guess into a recorded fact; see
Attribution for the measured difference.
Replay it¶
$ uv run hansard replay ./sessions/s_01KZVVGRNR3D5V708Z4NPGG11H
Real, unedited output from that exact command against the session the snippet above produced:
session s_01KZVVGRNR3D5V708Z4NPGG11H | corrected view | 1 person | 1 writer | less than a second
20:44:44.090 priya restart the payments worker
20:44:44.090 - turn begins
agent saw: priya's message
20:44:44.090 * restart_worker(service="payments")
`- caused by priya | recorded by the agent | 1.0
20:44:44.090 * output: Restarted the payments worker.
`- caused by priya | recorded by the agent | 1.0
20:44:44.090 - turn ends (ok)
Every attribution here reads 1.0 because caused_by was passed — a fact
the runtime recorded, not a guess. See The log format for
what a session directory actually contains, and CLI
reference for verify, inspect, and prune.
Try the multiplayer demo¶
The repository ships a demo agent that proves this against a real,
concurrent, three-terminal session instead of a single-writer toy — see
demo/README.md
in the repository for the full walkthrough, including the messy
overlapping-instructions scenario the Attribution page
quotes from.