Metadata-Version: 2.4
Name: aer1-crewai
Version: 0.1.2
Summary: AER-1 verifiable workflow receipts for CrewAI
License: Apache-2.0
Project-URL: Homepage, https://datatracker.ietf.org/doc/draft-zambo-aer1/
Keywords: aer-1,crewai,ai-agents,verifiable-receipts,audit,tracing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: crewai>=1.0.0

# aer1-crewai

AER-1 verifiable workflow receipts for [CrewAI](https://github.com/crewAIInc/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](https://pypi.org/project/aer1-smolagents/) — SmolAgents agents via step callbacks
- [aer1-openai-agents](https://pypi.org/project/aer1-openai-agents/) — the OpenAI Agents SDK via its TracingProcessor
- [aer1-crewai](https://pypi.org/project/aer1-crewai/) — CrewAI crews via the event bus
- [aer1-langgraph](https://pypi.org/project/aer1-langgraph/) — LangGraph swarms via callback handler
- [aer1-autogen](https://pypi.org/project/aer1-autogen/) — AutoGen multi-agent chats

Plus the [`aer1`](https://pypi.org/project/aer1/) metapackage: `pip install aer1`, then `import aer1; aer1.instrument()`. One line, zero config, auto-detects your framework.

## Install

```bash
pip install aer1-crewai
```

## Use it (two lines)

```python
# 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:

- `ToolUsageFinishedEvent` becomes a step named for the tool, with the
  tool arguments, output, agent role, and task name hashed in, and
  `started_at` / `ended_at` taken from the event's own timestamps.
- `ToolUsageErrorEvent` becomes the same step with `status: "error"`.
- `TaskCompletedEvent` / `TaskFailedEvent` become `tool: "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_id`
- `goal`, `status`
- `steps`: one record per observed event, seq 1..n in order
- `merkle_root`: Section 8.1 root over the ordered step receipt ids
- `output_hash`: SHA-256 of the final answer
- `verify_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
- `seq` values exactly 1..n in order, no gaps
- no two steps share a `receipt_id` (MM-1)
- `merkle_root` matches 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:

```python
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_id` defaults to a fresh UUID per listener; pass your own to
  correlate receipts across runs.
- `verify_url` defaults to the AER-1 specification page; point it at
  your own verifier in production.
- If `final_answer` is 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
