hitlwright
A typed, versioned, auditable human-in-the-loop approval contract.
When an agent or a workflow must pause for a human decision, hitlwright writes a
typed request and reads a typed decision — both following a versioned JSON
Schema (hitl-request-v1), stored as append-only JSONL so the whole approval
trail is auditable and nothing is overwritten. Zero dependencies.
from pathlib import Path
from hitlwright import write_request, read_decision, write_decision, pending_request
run = Path("runs/42")
req_id = write_request(run, step_id="publish", action="publish",
allow=["approve", "reject"], title="Approve publish")
# the run pauses; is a human still owed a decision?
if pending_request(run):
... # wait / notify
# a human (or another process) responds:
write_decision(run, req_id, response="approve", responder_id="user:ana", comment="ok")
decision = read_decision(run) # {"response": "approve", ...} or None
Or from the shell:
$ hitlwright request runs/42 --step publish --action publish
hitl_1b2c...
$ hitlwright decide runs/42 hitl_1b2c... --response approve --by user:ana
$ hitlwright decision runs/42 # the latest decision as JSON
Why it exists
Most frameworks offer a generic "interrupt" and leave the rest to you: no typed request, no versioned contract, no audit trail, and no language-neutral way for a different system to answer. hitlwright is the opposite:
| Property | hitlwright |
|---|---|
| Typed | every request/decision follows hitl-request-v1 (shipped, schema_path()) |
| Versioned | the schema id is in every record; v2 can coexist |
| Auditable | append-only JSONL — the full history, nothing overwritten |
| Two-sided & language-neutral | just two JSONL files; any producer writes requests, any responder writes decisions (Python, Node, a CLI, a web UI) |
| Resumable | read_decision / pending_request after a pause — survives a restart |
| Zero-dep | pure stdlib; the schema is validated in your tests, not at runtime |
The producer and the responder never call each other — they share two files and one schema. That is what makes it survive restarts and cross language boundaries.
API
write_request(run_dir, *, step_id, action=None, allow=None, title=None, ...) -> strread_decision(run_dir) -> dict | None— the latest valid decision (last wins)write_decision(run_dir, request_id, *, response, responder_id="cli", comment=None) -> dictpending_request(run_dir) -> dict | None— latest request with no decision yetlatest_request(run_dir) -> dict | Nonebuild_request(...)/build_decision(...)— construct & validate shape without IOschema_path() -> Path— the shippedhitl-request-v1JSON Schema
How it compares
See POSITIONING.md for the evidence-based comparison with LangGraph interrupts,
Temporal signals, Inngest waitForEvent, and Humanlayer — and the honest limits
(polling-based, no built-in notifications or auth).
Install
pip install hitlwright
MIT licensed. Part of the network-agents-setup ecosystem; extracted from its governed runner so any project can gate on typed human approval.
Metadata
Release files for hitlwright 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hitlwright-0.1.0.tar.gz | 11.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hitlwright-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.7 kB
Release files / hitlwright-0.1.0.tar.gz
| Download URL | hitlwright-0.1.0.tar.gz |
|---|---|
| Size | 11.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c15d1874128805c52c2b3e44e2626e96bf23b9953719027930d8eb22cfb6c082
|
|
BLAKE2b-256 checksum How to use checksums |
bbe081bdfffa8b8bf54f96ce9afc6023a2cd0bd57a6a76af135dfd39917513c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logRelease files / hitlwright-0.1.0-py3-none-any.whl
| Download URL | hitlwright-0.1.0-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f9ee88d4d31a85460f0a8dc0400e43875630c59f5a02e31c811a9a74fc2bc15b
|
|
BLAKE2b-256 checksum How to use checksums |
19f12252a5092c04fdb93a5f6094739511c3f6572fa5a41fd2f3a54a61372dfe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency log