Skip to main content

chitra

License: MIT Python 3.12+ PyPI

chitra persistently supervises tmux-hosted AI-agent sessions against exact frozen goals. It records transcript evidence, detects drift and stalls, publishes crash-safe corrective orders, answers bounded goal questions, and independently verifies completion. dispatchd remains the sole terminal writer.

It was built to manage large parallel sessions with LLM coding agents, allowing the user to do more while chitra handles delivery and state tracking, applies optional LLM-backed judgment gates, and gates external harnesses such as Claude and Codex against clearly defined goals.

Scope

chitra delivers messages to LLM-driven sessions in tmux and observes their state. Delivery, queueing, routing, rate limiting, ledger writes, and state tracking are deterministic; routing is config lookup, not content judgment.

LLM judgment is layered on deliberately for specific gates: goal nudging, completion-claim review, and interview functionality. Chitra also dispatches to and gates external coding-agent harnesses such as Claude via claude-code and Codex; those agents do the content work, while chitra records, routes, reviews, and releases according to its own ledgers and policies.

Quickstart

pip install chitra-monitor  # or: pip install git+https://github.com/ReticleWorks/chitra.git@<tag>

Replace <tag> with a released version from the tags page, or drop @<tag> to install from the default branch.

Requires Python 3.12+ and tmux on the host. See Install for local development setup, Configuration for environment variables, and Delivering into a tmux pane for what chitra actually does to a pane.

Why "chitra"

The name is a short form of Chitragupta, a figure from Hindu tradition described as the divine registrar and keeper of a complete, accurate ledger of deeds. That remains this package's contract: it observes, verifies against frozen goals and cited artifacts, and takes bounded supervisory action without doing an agent session's work or making protected decisions for the operator. The name is used respectfully as a functional reference, not as religious imagery.

BrowserStack's chitragupta-node and chitragupta-rails are open-source SDKs that use the same name for structured JSON (JavaScript Object Notation) logging — attaching metadata to log lines rather than relaying or signing them. Different tool, same naming logic: the name attaches to something that records and structures what happened, not something that decides what should happen.

What's in this repo

chitra installs command-line entrypoints backed by small, single-purpose modules. monitord and dispatchd are the current always-on daemons. watchd, triaged, and sweepd remain available for older deployments but are deprecated.

Delivery

  • chitra.dispatch / chitra.dispatchd — drain a JSON order queue and deliver each message into a tmux session via bracketed paste, confirming delivery by grepping the session's own transcript. One writer per session (LaneLock); idempotent and crash-safe (see Delivery guarantees).
  • chitra.ledger — an append-only, HMAC-signed log of every delivered message.

Monitoring

  • chitra.monitord / chitra.supervisor / chitra.supervision — bind transcripts to exact goals, persist corrective actions across restart, prove delivery consumption, answer bounded questions, and continue until enrolled completion evidence verifies.
  • chitra.watchd — derives semantic pane status from authoritative lifecycle reports or declarative screen manifests, serves the local coordination socket, and runs a completion audit on each finished turn.
  • chitra.triaged / chitra.sweepd — deduplicated state-change events and a compact fleet-state feed for downstream monitors.
  • chitra.draft_scanner — flags unsubmitted drafts left sitting in a tmux input box.
  • chitra.agent_cli (chitra-agent) — reports lifecycle state, explains status evidence, waits for semantic state, and prints the socket API schema.

PR review

  • chitra.pr_review / chitra.pr_reviewd (chitra-pr-review) — deterministic blast-radius/diff-size pre-checks plus an isolated multi-reviewer security pass over one pull request's diff, logged to a signed ledger and reported as a plain PR comment. Never merges, approves, requests changes, or fails a required check by default; see the pr_reviewd module docstring and PRReviewPolicy.block_on_findings. Stock trigger: .github/workflows/pr-security-review.yml.

Goals and completion

  • chitra.goals — a per-lane goal store with atomic interview enrollment and frozen structured done items, guarded by flock.
  • chitra.goal_enforcement / chitra.completion_gate / chitra.close_gate — require exact named, validator-bearing receipts for every frozen done item; spend, credentials, and irreversible actions stay operator-gated.

Rate limiting

  • chitra.usage / chitra.rate_limit_guard / chitra.account_registry — read account usage and pause/resume lanes on provider limits or host load pressure, over a durable, crash-safe transaction. See docs/pause-recovery.md.

Rendering

  • chitra.board / chitra.convlog — a terminal roster of goals and open asks, and an append-only operator-brief conversation log.

Other shipped modules

  • chitra.artifacts, chitra.capabilities, chitra.goals_cli, and chitra.replay_eval — operator tools for artifact state, capability control, goals, and replay checks.
  • chitra.ownership, chitra.ownership_provider, and chitra.petra — read-only ownership lookup, a fail-closed provider, and an observe-only pressure authority.
  • chitra._fsio, chitra.board_updater, chitra.lane_activity, chitra.lane_read, chitra.lexicon, chitra.load_shed, chitra.orders, chitra.outcomes, and chitra.rate_limit_state — shared queue, state, lane, and load-shed primitives.
  • chitra.policy_config, chitra.reasoned_dispatch, chitra.reasoning, chitra.recovery, chitra.routing_config, chitra.state_paths, and chitra.taxonomy — policy, reasoning, recovery, routing, state-path, and taxonomy primitives.

Delivering into a tmux pane

Delivery into a live tmux session follows one path:

  1. tmux display-message -p -t <target> '#{pane_in_mode}' — if 1, the pane is in copy-mode (which silently swallows input); run tmux send-keys -X cancel and wait briefly.
  2. printf '%s' "$text" | tmux load-buffer -b <name> -
  3. tmux paste-buffer -p -b <name> -t <target> — the -p flag is mandatory; without it, newlines act as Enter keypresses and the message can self-submit early.
  4. tmux send-keys -t <target> Enter
  5. Confirm delivery by grepping the target session's transcript for the delivered text. "Looks sent" is not evidence.

For a remote target, each command is the same, ssh-wrapped to run on the actual target host. Checking the local tmux server's state, or grepping local transcripts, when the target is remote reports on the wrong host.

Delivery guarantees

  • Single writer. dispatchd holds a LaneLock per session id across each delivery, so two writers can't race to paste into the same session and corrupt its next turn.
  • Idempotent. Once a ledger-proven result file exists for an order, it is never redispatched, even across a restart. A crash between paste and result is reconciled with a send-nonce marker plus the exact bound-transcript check, not a blind second paste. A pre-existing SENT result cannot create its own proof.
  • Authenticated. Every successful delivery appends an HMAC-SHA256-signed record to an append-only JSONL ledger; a reader with the signing key can prove a given message was delivered. Append-only behavior is a convention backed by file permissions, not a hash chain or counter, so a reader cannot detect ledger truncation or edits. Under the trusted-host assumption, "not in the ledger" suggests no chitra delivery; it is not proof of non-delivery. See chitra.ledger.verify_delivery.

Running the daemons

Trailhead installs one chitra Debian package built with fpm. The package contains one released application virtual environment at /opt/chitra/venv, the service account, and five core systemd unit files: four shared daemons and the chitra@.service session-anchor template. Build it with CHITRA_VENV_SOURCE=/path/to/released-venv packaging/build-deb.sh 0.9.4 /path/to/chitra.deb.

The package daemons read /etc/chitra/lanes.yaml. Each declaration supplies the lane identity, account, roots, tmux socket and credential bindings. The declaration has no model field. chitra-lane-anchor selects the backend and model at launch. It supports Claude, Codex, and OpenCode models such as opencode/x-preview-f-free. It refuses unless the Tophand lane has a passing goal-ingestion record and no active usage pause. See docs/governed-tmux-lanes.md.

Provider routing remains upstream of Chitra. oss-step task metadata selects OpenRouter through Crush, the anonymous OpenCode free route, or authenticated OpenCode Zen. Chitra receives only the allowlisted executable backend and passes one provider/model value as one argument. It never accepts a free-form shell command or stores a provider secret in the lane manifest.

Watchd also owns a mode-0600, newline-delimited JSON socket at /run/chitra/chitra.sock. Supervised agents receive their lane, session, tmux-pane, target, and socket identity as CHITRA_* variables. A replacement Watchd process can use --handoff-from /run/chitra/chitra.sock to transfer verified semantic state and socket ownership without restarting the tmux pane processes. See the agent-status design and the status migration guide.

The host role enables the shared units once. Adding a lane uses one command:

sudo systemctl enable --now chitra@<lane>.service

chitra-rate-limit-guard remains a one-shot CLI meant to run on a timer. Example timer units live under packaging/systemd/:

sudo cp packaging/systemd/chitra-rate-limit-guard.service.example /etc/systemd/system/chitra-rate-limit-guard.service
sudo cp packaging/systemd/chitra-rate-limit-guard.timer.example /etc/systemd/system/chitra-rate-limit-guard.timer
sudoedit /etc/systemd/system/chitra-rate-limit-guard.service   # fill in placeholders
sudo systemctl daemon-reload
sudo systemctl enable --now chitra-rate-limit-guard.timer

Configuration

Each entrypoint is configured with CLI flags (--help on any command lists them) and a small set of environment variables. The most common:

Env var Default Notes
CHITRA_LANES_FILE /etc/chitra/lanes.yaml One rendered lane declaration read by the shared daemons.
CHITRA_STATE_DIR /var/lib/chitra Base directory for the queue, ledger, and ledger key
CHITRA_SOCKET_PATH /run/chitra/chitra.sock Local semantic-status, subscription, wait, and handoff socket.
CHITRA_AGENT_MANIFEST_DIR ${XDG_CONFIG_HOME:-~/.config}/chitra/agent-detection Local agent-detection manifests; a local file replaces the bundled file for that agent.
REMOTE_DISPATCH_HOSTS (empty) Comma-separated allowlist of hosts dispatch may target over ssh
CHITRA_CLAUDE_PROJECTS ~/.claude/projects Root, or os.pathsep-separated list of roots, searched locally for transcript-grep delivery verification. List more than one root when a local session runs under a non-default CLAUDE_CONFIG_DIR (e.g. a dedicated persona/harness identity) — its transcripts live under that root's projects/, not the default
CHITRA_ROUTING_CONFIG (unset) Optional task_type → routing-hint config; see docs/routing.yaml.example
CHITRA_POLICY_CONFIG (unset) Optional completion-gate and dispatch policy; see docs/policy.yaml.example
CHITRA_SHARED_DIR /var/lib/polyphony-chitra-coordination Shared advisory presence and peer inbox root.

The full set — ssh options, triage log paths, transcript globs — is documented per-command via --help.

chitra-presence using <instance> <resource> appends to that instance's own presence file and prints peers already using the same resource. It never waits, claims, expires, steals, or grants authority. Use released to append an explicit release and list to merge all instance files. chitra-peer say <instance> <text> enqueues a real dispatch order for the named peer's session so dispatchd delivers it with its own verification receipts; chitra-peer inbox reads a non-authoritative mirror of what was asked, never proof that it arrived.

Routing. A caller can tag a DispatchOrder with an opaque task_type. If a routing config is set, dispatchd maps that to a routing_hint (a model/harness preference the caller's system uses); an explicit routing_hint always wins. chitra carries both fields through DispatchResult and, for a successful delivery, the signed ledger; it does not make routing decisions beyond the configured lookup.

Install

Requires Python 3.12+ and tmux (chitra shells out to the tmux binary; there is no Python tmux dependency).

pip install chitra-monitor  # or: pip install git+https://github.com/ReticleWorks/chitra.git@<tag>

chitra-monitor is published on PyPI and the recommended installation method.

For local development:

git clone https://github.com/ReticleWorks/chitra.git
cd chitra
pip install -e '.[test]'
pytest

Documentation

For comprehensive guides, API reference, and daemon documentation, see the docs/. Start with Getting Started for install and first dispatch, Concepts for the architecture, or Semantic agent status for lifecycle reports, manifests, waits, and live handoff.

Getting help

Questions and bug reports: open an issue. See CONTRIBUTING.md before opening a nontrivial PR; security reports go through SECURITY.md.

Authors

Trey (Reticle Works) with Claude and Codex.

License

MIT © 2026 Reticle Works. See LICENSE for the full text.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chitra_monitor-0.17.0.tar.gz (765.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chitra_monitor-0.17.0-py3-none-any.whl (495.9 kB view details)

Uploaded Python 3

File details

Details for the file chitra_monitor-0.17.0.tar.gz.

File metadata

  • Download URL: chitra_monitor-0.17.0.tar.gz
  • Upload date:
  • Size: 765.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for chitra_monitor-0.17.0.tar.gz
Algorithm Hash digest
SHA256 bbc12c14876f5fe90b87b156e619d41b198bbaf5560627ff0753dc5e0da2ee82
MD5 6c1a7aa22c1b185a765632efdc0d75e6
BLAKE2b-256 b78256d3405e1ba2adbf1f00174d03bfc948ab7515369d3c03a319c17bbf4feb

See more details on using hashes here.

File details

Details for the file chitra_monitor-0.17.0-py3-none-any.whl.

File metadata

  • Download URL: chitra_monitor-0.17.0-py3-none-any.whl
  • Upload date:
  • Size: 495.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for chitra_monitor-0.17.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5c829eb5b0a47b3ead35d3fc41ef342348b2036af7041e4670c770c2a77208c4
MD5 ada7c1e24dc2742ddc882c607ab26753
BLAKE2b-256 6594214145a822310defcd4143bb9144fd41f0d66c48342bcc512104f6c7213b

See more details on using hashes here.

Release history Release notifications | RSS feed

0.19.2

2 files

0.19.1

2 files

0.19.0

2 files

This release

0.17.0 This release

2 files

0.15.0

2 files

0.14.8

2 files

0.12.2

2 files

0.12.1

2 files

0.11.0

2 files

0.10.2

2 files

0.10.1

2 files

0.10.0

2 files

0.9.11

2 files

0.9.10

2 files

0.9.9

2 files

0.9.8

2 files

0.9.7

2 files

0.9.6

2 files

0.9.5

2 files

0.9.4

2 files

0.9.3

2 files

0.9.2

2 files

0.9.1

2 files

0.9.0

2 files

0.8.2.8

2 files

0.8.2.7

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page