Pitot
Guides / 04 — Build: agent metrics

Guide 04 / Build — tooling

One metrics feed for every agent

Your team runs Claude, Cursor, Kimi, and half the supported host list — and nobody can say what the agents actually did this week. This build is a passive Consumer, about fifteen lines of ordinary Python, that turns projected events from every host into one metrics and audit feed. It observes everything and can affect nothing.

01What you get

One newline-delimited feed of agent activity — host, action id, action kind — across every wired agent, with the command content projected out before it reaches your process. You can count actions per host, attribute usage, append an audit trail, or forward to your telemetry stack. Because it is a Consumer, it needs no request kind, no deadline, and no coordination with any other tool: consumers always compose.

02See it on the bench

Bench demo / metrics consumerSimulated · illustrative

Every host emits the same envelope, so one Consumer covers all nine — that is the point.

03The fragment: one file, yours

Register the Consumer as your tenant fragment. It composes with anything already in .pitot/conf.d/ — any number of tenants can observe action.requested events:

# .pitot/conf.d/action-audit.yaml
consumers:
  - id: action-audit
    command: ["python3", "./examples/action-audit.py"]
    events: ["action.requested"]
    projection:
      content: omit

The projection line is the privacy decision: omit drops command content entirely, sha256 keeps a correlatable digest, full passes it through. Projection happens before bytes enter the Consumer pipe — not inside your application.

04Your code: ordinary Python, no SDK

Pitot writes newline-delimited JSON to your program's standard input. Each event carries its host, action, and observation quality:

{"pitot_version":"1","type":"action.requested","host":{"name":"claude"},"action":{"id":"act_7f2","kind":"shell"},"content":{"mode":"omit"},"observation":{"source":"host_hook","fidelity":"direct"}}

The reader is the README's no-SDK Consumer with a counter attached — this is the whole program:

# examples/action-audit.py — your code, sketched
import json, sys
from collections import Counter

actions = Counter()

for line in sys.stdin:
    event = json.loads(line)
    host = event["host"]["name"]
    kind = event["action"]["kind"]
    actions[(host, kind)] += 1
    print(json.dumps({
        "host": host,
        "kind": kind,
        "action_id": event["action"]["id"],
        "total": actions[(host, kind)],
    }), file=sys.stderr)

Swap the print for a StatsD emit, an OpenTelemetry span, a SQLite insert, or a daily-digest file — from here it is your tooling, in your language.

05Why it cannot break anything

  • A Consumer cannot reply. It has no path to the response channel, so a bug in your metrics code cannot allow or deny a waiting agent action.
  • Consumer failure is contained. If your process crashes, the host action proceeds; you lose a data point, not an afternoon.
  • Privacy is structural. With content: omit, the sensitive bytes were removed before your process existed in the data path.
Start here

pitot init --template blank-consumer scaffolds a runnable Consumer project in python, typescript, go, or rust — then wire it to a real agent with pitot dev --host kimi -- kimi -p "Run: echo hello" and watch the feed.