agenthooks

Named hook points for production AI agents.
Customers extend your agent — no fork, no source changes, nothing breaks.

PyPI Python Apache 2.0 Zero deps OTel
$ pip install agenthooks-py
GitHub Examples Architecture

The Problem

You build a production AI agent. A customer deploys it. They need approval gates, compliance enrichment, rate limiting, custom audit logging. Today they fork your code. You lose control of the release cycle.

# Without agenthooks — customer forks, patches, diverges forever
# With agenthooks — customer registers logic, agent stays canonical

class MyAgent(HookAgent):
    before_call = hookpoint("before_call")     # ← agent author declares this once

    async def run(self, query):
        ctx = HookContext.new(session_id="s1", tenant_id="acme", query=query)
        async with self.before_call.run(ctx) as ctx:
            return await llm.call(ctx.query)   # hooks have already enriched ctx

# Customer code — entirely separate file, separate team, separate deploy
registry = HookRegistry()

@registry.implement("before_call")
async def inject_context(ctx):
    return ctx.enrich("region", "EU").enrich("tier", "enterprise")

agent = MyAgent(registries=[registry])

What You Get

🪝

Named Hook Points

Declare extension points anywhere in your agent pipeline. Sequential or parallel execution. Per-hook timeouts and fallback behaviour.

🔒

Tenant Isolation

Filter conditions are enforced by the executor. A hook registered for tenant A never fires for tenant B. Multiple customers coexist on one agent.

📡

OTel-Native

Every hook execution emits a span parented to your active trace, 5 metric instruments, and trace-correlated structured logs. Zero config required.

📋

Append-Only Audit

Every execution — ok, timeout, error, blocked — writes a JSONL entry. Cannot be disabled. Redacted fields surface as [REDACTED].

🛡️

Security Guards

Sealed context fields prevent tenant impersonation. Prompt injection scanning on modified queries. Redaction API for credentials and PII.

Pattern Decorators

@inject, @block_if, @redact, @rate_limit, @require_tenant, @retry — compose freely, zero boilerplate.

OpenTelemetry — Automatic

Configure the OTel SDK once. Hook spans appear as children under your application trace in Jaeger, Tempo, Datadog APM, Honeycomb — any OTLP backend.

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)

# agenthooks picks this up automatically.
# Every hook execution becomes a child span with hook.name, hook.impl,
# hook.tenant_id, hook.status, hook.duration_ms attributes.
MetricTypeDimensions
agenthooks.hook.executionsCounterhook.name · hook.impl · hook.status
agenthooks.hook.duration_msHistogramhook.name · hook.impl
agenthooks.hook.errorsCounterhook.name · hook.impl
agenthooks.hook.timeoutsCounterhook.name
agenthooks.hook.blockedCounterhook.name · hook.impl

Documentation

Architecture →

Three-layer design, data model, execution flow, registry filter evaluation, and observability wiring.

Security →

Threat model, sealed fields, injection scanning, tenant isolation, redaction, audit invariant.

Execution Flow →

Step-by-step trace of a hook execution: span creation, metric recording, audit write, all failure paths.

Examples →

5 runnable examples: basic hooks, customer extensibility, multi-tenant isolation, resilience, OTel wiring.