Skip to main content

agentlens

Tamper-evident audit logging for Claude agents — Claude Code hooks and Anthropic SDK. Local-first, append-only, OSS.

Why

Anthropic logs API calls for their own safety monitoring — but that log is not yours. When your Claude-powered agent takes an action, you need your own tamper-evident record: for compliance (EU AI Act Art. 12, ISO/IEC 42001 A.6.2.8), incident response, and accountability.

agentlens captures every tool_use / tool_result event into a SHA-256 hash-chained JSONL file on your own machine — via Claude Code hooks (recommended) or as a drop-in Anthropic SDK wrapper. It can also block dangerous tool calls before they execute (deterministic rules, no LLM in the loop).

Quickstart: Claude Code / Claude Agent SDK (v0.6.0+)

pip install agentlens-io
agentlens hook install   # prints the settings.json snippet

.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {"matcher": "*", "hooks": [
        {"type": "command", "command": "agentlens hook pre --log ~/.agentlens/audit.jsonl --block critical"}
      ]}
    ],
    "PostToolUse": [
      {"matcher": "*", "hooks": [
        {"type": "command", "command": "agentlens hook post --log ~/.agentlens/audit.jsonl"}
      ]}
    ]
  }
}

Now every tool call in Claude Code is audit-logged, and rm -rf /-class commands are denied before execution:

agentlens view   ~/.agentlens/audit.jsonl        # colorized event viewer
agentlens summary ~/.agentlens/audit.jsonl       # per-session stats
agentlens verify ~/.agentlens/audit.jsonl        # ✅ hash-chain integrity / ❌ tamper detected
agentlens feedback ~/.agentlens/audit.jsonl --emit-code   # suggest whitelist rules from suppressed violations (v0.8.0+)

feedback reads the accumulated log — including the suppressed_violations that the whitelist keeps instead of deleting — and proposes narrowly-scoped WhitelistRules for rules with a high false-positive rate. It is suggestion-only: it never rewrites your ruleset. A ruleset that auto-tunes from its own logs can be poisoned, so a human stays in the loop. Flags: --min-occurrences N (default 3), --threshold F (default 0.9), --emit-code.

Options: --block critical|high|off (default critical), --whitelist rules.json (false-positive suppression — suppressed violations stay in the log), --standalone (post-hook logs tool_use+result when no pre-hook is registered). Hooks are fail-open: the logger can never break your agent loop.

Design principles

  • Read-only interception — requests and responses are never altered
  • Append-only writes — log entries cannot be edited after creation
  • No AI in the logger — capture logic is deterministic code, not an LLM
  • Your data stays local — FileWriter (default) writes to your own machine; no data leaves your environment

Usage: SDK wrapper

from agentlens import AuditedAnthropic

# Drop-in replacement for anthropic.Anthropic()
client = AuditedAnthropic(log_path="./audit.jsonl")

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=[...],
    messages=[{"role": "user", "content": "..."}],
)
# Every tool_use and tool_result is now in audit.jsonl

Async (v0.7.0+)

AsyncAuditedAnthropic is the drop-in for anthropic.AsyncAnthropic — same audit logging and pre-execution blocking, awaited:

from agentlens import AsyncAuditedAnthropic

client = AsyncAuditedAnthropic(log_path="./audit.jsonl", block_on_critical=True)

response = await client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=[...],
    messages=[{"role": "user", "content": "..."}],
)
# Raises PreExecutionBlockedError before a critical tool call reaches you.

Streaming (v0.9.0+)

messages.stream() is wrapped too. Text passes through untouched; the audit and the pre-execution gate fire when the message completes — before your code reads the finished tool_use and acts on it. Works on the sync and async clients:

with client.messages.stream(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=[...],
    messages=[{"role": "user", "content": "..."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="")
    message = stream.get_final_message()  # tool_use audited + gated here
# block_on_critical raises PreExecutionBlockedError before you touch tool_use.

Log format (JSONL)

{"event_type": "tool_use", "tool_use_id": "toolu_01xxx", "tool_name": "bash", "tool_input": {"command": "ls -la"}, "model": "claude-opus-4-6", "timestamp": "2026-04-05T10:00:00+00:00", "session_id": "..."}
{"event_type": "tool_result", "tool_use_id": "toolu_01xxx", "result_content": "file1.txt\nfile2.txt", "is_error": false, "timestamp": "2026-04-05T10:00:01+00:00", "session_id": "..."}

Custom writer

from agentlens.writers import BaseWriter

class MyWriter(BaseWriter):
    def write(self, event) -> None:
        # send to your own DB, S3, SIEM, etc.
        my_db.insert(event.to_json())

client = AuditedAnthropic(writer=MyWriter())

Run tests

pip install -e ".[dev]"
pytest tests/

License

MIT

Release files for agentlens-io 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentlens-io 0.9.0
File Size Uploaded
agentlens_io-0.9.0.tar.gz 34.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agentlens-io 0.9.0
File Interpreter ABI Platform
agentlens_io-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 60.8 kB

Release files / agentlens_io-0.9.0.tar.gz

Download URL agentlens_io-0.9.0.tar.gz
Size 34.8 kB
Tags Source
SHA-256 checksum
How to use checksums
f2b6681239a7e38e3170d764f125d3c364eccd3d36002cea56b8f0f3c45b3d09
BLAKE2b-256 checksum
How to use checksums
1f190db5d968740fb59ef1505cd8792ad21cebd330312c2deb3388ac371d55ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / agentlens_io-0.9.0-py3-none-any.whl

Download URL agentlens_io-0.9.0-py3-none-any.whl
Size 26.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
75fefa3082a14f3fb8180303be26a4cc3868317bad81b90db0f07e2053f9fa52
BLAKE2b-256 checksum
How to use checksums
e55acf515370906150d3f430f869390ecff47edfa23bdcb91521bed9103e3fd3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.11.0

2 release files

This release

0.9.0 This release

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release 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