For each agent run: who triggered it (user id, system trigger), when, what prompt, what tools called with what arguments, what tool results returned, what final answer the agent produced, and what permission decisions a human made. Together, these answer 'what happened' months later.
Hooks are your audit hook (literally)
The simplest implementation: a PostToolUse hook that appends a structured line per tool call, plus a UserPromptSubmit hook for the trigger and a final summary on Stop. Each line carries a session id so you can stitch them into a timeline.
Retention vs cost
Audit logs grow. Decide retention policy up front — 30/90/365 days based on regulatory or business need. Cold-storage tiers (S3 Glacier, GCS Coldline) make multi-year retention cheap; hot-storage queries should target the last N days only.
Principle: An action without an audit trail is a fingerprint-free incident. Build the trail first; add the cool features later.
import json
from collections import defaultdict
def session_timeline(audit_path: str):
runs = defaultdict(list)
for line in open(audit_path):
ev = json.loads(line)
runs[ev["run_id"]].append(ev)
for run_id, events in runs.items():
events.sort(key=lambda e: e["ts"])
print(f"\n== run {run_id} ({len(events)} events) ==")
for e in events:
print(f" {e['ts']:.3f} {e['event']}")
Implement the audit logger pattern in one agent. Run a session, then write a query that answers 'how many tool calls in the last hour, by tool name'. Verify the log answers it cheaply.
Hint
If your log is text-only, switch to JSONL — every observability question gets harder when the format is not structured.
Progress
Progress is local-only — sign in to sync across devices.