CrewAI Adapter¶
The CrewAIAdapter registers global before/after tool-call hooks with the
CrewAI framework. Every tool call across all agents in a crew passes through
these hooks.
Installation¶
Setup¶
from edictum import Edictum, Principal
from edictum.adapters.crewai import CrewAIAdapter
guard = Edictum.from_yaml("contracts.yaml")
adapter = CrewAIAdapter(
guard=guard,
session_id="crew-session-01",
principal=Principal(user_id="ops-crew", role="devops"),
)
# Register global hooks -- this must be called before the crew runs
adapter.register()
The register() method imports CrewAI's before_tool_call and
after_tool_call decorators and registers the adapter's hook functions as
global handlers. After this call, every tool invocation in the CrewAI runtime
passes through Edictum governance.
Full Example¶
from edictum import Edictum, Principal
from edictum.adapters.crewai import CrewAIAdapter
from crewai import Agent, Crew, Task
# Configure governance
guard = Edictum.from_yaml("contracts.yaml")
adapter = CrewAIAdapter(
guard=guard,
principal=Principal(user_id="deploy-crew", role="ci"),
)
adapter.register()
# Build crew as usual -- hooks are global
researcher = Agent(
role="Researcher",
goal="Find deployment status",
tools=[status_tool, log_reader_tool],
)
task = Task(
description="Check the health of the staging deployment",
agent=researcher,
)
crew = Crew(agents=[researcher], tasks=[task])
result = crew.kickoff()
Hook Behavior¶
-
Before hook:
async _before_hook(context) -> bool | None- The
contextobject has.tool_nameand.tool_inputattributes. - Returns
Noneto allow the tool call to proceed. - Returns
Falseto deny. CrewAI interpretsFalseas a signal to skip tool execution.
- The
-
After hook:
async _after_hook(context)- The
contextobject has.tool_resultwith the tool's return value. - Runs postconditions and records the execution in the session.
- Does not return a value.
- The
Notes¶
-
Sequential execution model: CrewAI executes tools sequentially within a crew run. The adapter uses a single-pending slot (not a dict keyed by call ID) to correlate before/after events. This is correct for sequential execution but would need adaptation if CrewAI ever supports parallel tool calls.
-
Global hooks:
register()modifies global state in the CrewAI runtime. Call it once before any crew runs. If you create multiple adapters, only the last one registered will be active.
Observe Mode¶
guard = Edictum.from_yaml("contracts.yaml", mode="observe")
adapter = CrewAIAdapter(guard=guard)
adapter.register()
Denials are logged as CALL_WOULD_DENY audit events but tool calls proceed
normally.