aer1-crewai
AER-1 verifiable workflow receipts for CrewAI.
Attach one listener and every crew run emits a hash-chained, offline-verifiable verifiable workflow receipt (AER-1, Section 8): what the crew did, in what order, with per-step hashes and a Merkle root over the whole run. It hooks CrewAI's own event bus, so there is no monkey-patching, no behavior change, and no network calls. The listener only observes. It complements CrewAI's built-in observability; it does not replace it.
The AER-1 framework collector family
The CrewAI collector in the AER-1 framework collector family. Any agent running on these frameworks can emit verifiable AER-1 execution receipts: every step recorded, hash-chained, one Merkle root over the whole run.
- aer1-smolagents ��� SmolAgents agents via step callbacks
- aer1-openai-agents ��� the OpenAI Agents SDK via its TracingProcessor
- aer1-crewai ��� CrewAI crews via the event bus
- aer1-langgraph ��� LangGraph swarms via callback handler
- aer1-autogen ��� AutoGen multi-agent chats
Plus the aer1 metapackage: pip install aer1, then import aer1; aer1.instrument(). One line, zero config, auto-detects your framework.
Install
pip install aer1-crewai
Use it (two lines)
# pip install aer1-crewai
from aer1_crewai import AER1CrewAIListener
from crewai import Agent, Task, Crew
listener = AER1CrewAIListener(goal="Research AER-1 adoption") # line 1
researcher = Agent(
role="Researcher",
goal="Find the latest AER-1 adoption numbers",
backstory="You are a diligent research analyst.",
)
writer = Agent(
role="Writer",
goal="Turn research into a crisp summary",
backstory="You write clearly and cite numbers.",
)
crew = Crew(
agents=[researcher, writer],
tasks=[
Task(description="Find the latest AER-1 adoption numbers",
expected_output="A bullet list of numbers with sources",
agent=researcher),
Task(description="Write a 3-sentence summary of the findings",
expected_output="Three sentences",
agent=writer),
],
)
result = crew.kickoff()
receipt = listener.finalize(final_answer=result.raw) # line 2
assert listener.verify(receipt) == [] # VALID
listener.save("receipt.json", workflow=receipt)
CrewAI needs an LLM API key in your environment (for example
OPENAI_API_KEY); the listener itself needs no model configuration.
Keep the listener referenced for the whole process. Constructing it
registers its handlers on CrewAI's global event bus; dropping the
reference can unregister them.
That is the whole integration. The receipt is a plain JSON object you can store, ship to an auditor, or render in a UI.
What gets recorded
One receipt step per observed event, in arrival order:
ToolUsageFinishedEventbecomes a step named for the tool, with the tool arguments, output, agent role, and task name hashed in, andstarted_at/ended_attaken from the event's own timestamps.ToolUsageErrorEventbecomes the same step withstatus: "error".TaskCompletedEvent/TaskFailedEventbecometool: "task"steps carrying the task name and output.
What the receipt contains
Workflow level (AER-1 Section 8, Table 2):
type,version,workflow_id,receipt_id,session_idgoal,statussteps: one record per observed event, seq 1..n in ordermerkle_root: Section 8.1 root over the ordered step receipt idsoutput_hash: SHA-256 of the final answerverify_url: where the verification procedure is documented
Step level (AER-1 Section 8, Table 3):
seq,receipt_id,tool,receipt_hash,started_at,ended_at,status
Each step receipt_hash is SHA-256 over the canonical JSON of what the
step actually did. The hash commits to the content; the receipt stays
compact.
Verification
listener.verify(receipt) runs the full offline check and returns a
list of failure reasons, empty when valid:
- all Table 2 / Table 3 members present and well-formed
seqvalues exactly 1..n in order, no gaps- no two steps share a
receipt_id(MM-1) merkle_rootmatches the recomputed Section 8.1 root- strict RFC 3339 timestamps, lowercase UUIDs, 64-char hex digests
Tamper with any field and verification fails. Try it:
receipt["steps"][0]["tool"] = ""
assert listener.verify(receipt) != [] # fails, as it should
Notes
- One listener observes every crew run in the process. Call
reset()between runs if you want one receipt per run from a shared listener. session_iddefaults to a fresh UUID per listener; pass your own to correlate receipts across runs.verify_urldefaults to the AER-1 specification page; point it at your own verifier in production.- If
final_answeris omitted,finalize()uses the output captured from the crew kickoff completed event when available.
Spec
AER-1: Agent Execution Receipts, draft-zambo-aer1,
https://datatracker.ietf.org/doc/draft-zambo-aer1/
License
Apache-2.0
Metadata
Release files for aer1-crewai 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aer1_crewai-0.1.2.tar.gz | 13.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aer1_crewai-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.3 kB
Release files / aer1_crewai-0.1.2.tar.gz
| Download URL | aer1_crewai-0.1.2.tar.gz |
|---|---|
| Size | 13.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
33f168aa360059c54bb8abc9e4706a1c79ff59655cc26cf3e2d59010b1830c17
|
|
BLAKE2b-256 checksum How to use checksums |
87eba9f3bf8fab3302870c1128c3512639af36e52b76ef51a40d7af11cd99e4d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
rambo-pypi-grab/0.1.0
|
Release files / aer1_crewai-0.1.2-py3-none-any.whl
| Download URL | aer1_crewai-0.1.2-py3-none-any.whl |
|---|---|
| Size | 9.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
301358a050df4d83e18e679245f277c451ebc0fca945b5453158bacc3f680011
|
|
BLAKE2b-256 checksum How to use checksums |
d381ce4d9083f4e8b128bb47ee365f4a12cd99b948cd675cd44c58d35622a3a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
rambo-pypi-grab/0.1.0
|