Skip to content

Conversation

@theonlypal
Copy link

@theonlypal theonlypal commented Dec 29, 2025

Summary

  • SwiftAPI integration for cryptographic attestation of all CrewAI tool invocations and multi-agent orchestration
  • Every action requires authorization before execution — no attestation, no execution (fail-closed)
  • JTI-based audit trail for compliance and forensics

What this adds

Component Purpose
SwiftAPIStructuredTool Wraps CrewStructuredTool with attestation verification
SwiftAPICrew Wraps Crew for multi-agent attestation with audit trails
SwiftAPIConfig Configuration with fail-closed defaults
MockAttestationProvider For testing without live API

Why this matters

Agentic AI needs governance. SwiftAPI provides:

  • Cryptographic proof of authorization (Ed25519 signatures)
  • Immutable audit trail (unique JTI per action)
  • Fail-closed execution (no attestation = blocked, not logged and continued)
  • Policy enforcement before execution, not after

Usage

from crewai import Crew
from crewai.swiftapi_integration import SwiftAPICrew

crew = Crew(agents=[...], tasks=[...])
swiftapi_crew = SwiftAPICrew(
    crew=crew,
    swiftapi_key="swiftapi_live_..."  # or SWIFTAPI_KEY env var
)
result = swiftapi_crew.kickoff()

Or wrap individual tools:

from crewai.swiftapi_integration import SwiftAPIStructuredTool

tool = SwiftAPIStructuredTool.wrap(
    existing_tool,
    swiftapi_key="swiftapi_live_..."
)

Test plan

  • Direct API connection test — verifies attestation flow
  • Crew kickoff attestation — verifies multi-agent wrapping
  • Multi-action sequence — verifies JTI audit trail accumulation
  • All tests pass against live SwiftAPI endpoint

Files

lib/crewai/src/crewai/swiftapi_integration/
├── __init__.py        # Module exports
├── config.py          # SwiftAPIConfig dataclass
├── attestation.py     # HTTP client + attestation provider
├── tools.py           # SwiftAPIStructuredTool wrapper
├── crew.py            # SwiftAPICrew wrapper
├── demo.py            # Standalone test script
└── README.txt         # Usage documentation

Get a key

https://getswiftapi.com


🤖 Generated with Claude Code


Note

Introduces SwiftAPI-based authorization to gate CrewAI actions before execution, providing auditability (JTIs) and optional tool/crew wrappers.

  • Adds crewai/swiftapi_integration/ with config.py, attestation.py, tools.py, crew.py, __init__.py, demo.py, and README.txt
  • SwiftAPIStructuredTool: wraps CrewStructuredTool to call /verify prior to execution (sync/async), builds intent/context, handles 401/403/429/timeouts, supports shared provider and optional fail_open
  • SwiftAPICrew: wraps Crew to attest kickoff (sync/async), optionally wrap all agent tools, and record an audit log with JTI metadata
  • SwiftAPIConfig: env-driven configuration (SWIFTAPI_KEY, SWIFTAPI_URL) with validation and fail-closed defaults
  • Attestation interfaces: AttestationProvider, SwiftAPIAttestationProvider, MockAttestationProvider for testing
  • demo.py: standalone tests for direct API, crew kickoff, and multi-action sequences; README.txt with usage

Written by Cursor Bugbot for commit 7fbfaaa. This will update automatically on new commits. Configure here.

SwiftAPI integration provides cryptographic attestation for all CrewAI tool
invocations and multi-agent orchestration. Every action requires authorization
before execution - no attestation, no execution.

## What this adds

- `SwiftAPIStructuredTool`: Wraps CrewStructuredTool with attestation verification
- `SwiftAPICrew`: Wraps Crew for multi-agent attestation with audit trails
- `SwiftAPIConfig`: Configuration with fail-closed defaults
- Full async support via httpx
- JTI-based audit trail for compliance

## Why this matters

Agentic AI needs governance. SwiftAPI provides:
- Cryptographic proof of authorization (Ed25519 signatures)
- Immutable audit trail (JTI per action)
- Fail-closed execution (no attestation = blocked)
- Policy enforcement before execution, not after

## Usage

```python
from crewai import Crew
from crewai.swiftapi_integration import SwiftAPICrew

crew = Crew(agents=[...], tasks=[...])
swiftapi_crew = SwiftAPICrew(
    crew=crew,
    swiftapi_key="swiftapi_live_..."  # or SWIFTAPI_KEY env var
)
result = swiftapi_crew.kickoff()
```

Tested against live SwiftAPI endpoint. All tests pass.

Get a key: https://getswiftapi.com

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

403 = explicit policy denial. Must never be bypassed regardless of fail_open.

Previously, if a 403 response had malformed JSON, response.json() would
throw json.JSONDecodeError, falling through to the generic exception
handler. With fail_open=True, this would approve an explicitly denied action.

Now: JSON parsing errors on 403 responses are caught locally. The denial
still raises PolicyViolationError with whatever body text is available.
fail_open is for network failures, not explicit denials.
"User-Agent": "CrewAI-SwiftAPI/1.0",
},
)
return self._client
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AsyncClient reused across event loops causes runtime errors

The _get_client method stores an httpx.AsyncClient in self._client and reuses it across calls. However, the synchronous wrappers in invoke and _sync_attest use asyncio.run() which creates a temporary event loop that is closed when the call finishes. When the stored AsyncClient is reused in a subsequent asyncio.run() call with a different event loop, it causes "RuntimeError: Event loop is closed" because the client's internal state is bound to the original (now closed) loop. The is_closed check only detects explicit aclose() calls, not event loop closure. This will fail on the second and subsequent synchronous tool invocations or crew kickoffs.

Additional Locations (2)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant