SakshiCrewListener once before crew.kickoff() and every model call and tool
call inside a witness session is recorded on the chain. The listener does not
import crewai at module load, so it degrades cleanly and carries no version lock.
Install
Register the listener
Create the listener once (it registers on CrewAI’s event bus), then run your crew inside a witness session.What gets witnessed
As the crew runs, the listener records onto the active witness session:- Model calls become
llm_callsteps with the served model, token usage, and finish reason. - Tool calls become
tool_callsteps with the tool name, the argument keys (never the values, which are tokenized at ingest anyway), the latency, and the role of the agent that called it. A tool that errors is recorded as such. - Crew, agent, and task boundaries are bracketed, so a multi-agent crew reads as structured control flow on the chain.
Enforcement in CrewAI
CrewAI’s event bus is observe-only, so this middleware witnesses; it does not block a tool the way the ADK or MCP tool-path enforcement does. To gate a consequential action inside CrewAI, callenforce at
the top of the tool’s own function and act on the outcome.
Recording is fail-open: a problem writing a step never breaks the crew, and with
no active witness session the listener is a no-op. CrewAI dispatches event
handlers asynchronously and flushes them before finishing kickoff, so every model
and tool call is recorded; the final crew close-bracket is best-effort. Call
crewai_event_bus.flush() after kickoff if you want it guaranteed before the
witness session closes.Full SDK reference
The client, the witness session, and enforcement.

