Compare commits

..

7 Commits

Author SHA1 Message Date
Greyson LaLonde
aec7ad9731 chore: remove old a2a cassettes from lib/crewai (moved in previous commit) 2026-04-02 04:11:43 +08:00
Greyson LaLonde
bba48ec9df refactor: pin crewai-a2a version and move a2a tests to standalone package
Pin crewai-a2a to 1.13.0a6 to match workspace versioning convention.
Move all a2a tests and cassettes from lib/crewai to lib/crewai-a2a,
add crewai-a2a to devtools bump tooling, and update pytest/ruff/mypy configs.
2026-04-02 04:10:13 +08:00
Greyson LaLonde
2ada20e9c6 refactor: extract a2a module into standalone workspace package
Move lib/crewai/src/crewai/a2a to lib/crewai-a2a/src/crewai_a2a as a
separate workspace member. crewai's `[a2a]` optional extra now pulls in
crewai-a2a instead of listing raw deps. All imports updated from
crewai.a2a.* to crewai_a2a.*. Handle PydanticUndefinedAnnotation in
crewai/__init__.py model_rebuild to break circular import at load time.
2026-04-02 02:32:14 +08:00
João Moura
258f31d44c docs: update changelog and version for v1.13.0a6 (#5214) 2026-04-01 14:26:07 -03:00
João Moura
68720fd4e5 feat: bump versions to 1.13.0a6 (#5213) 2026-04-01 14:23:44 -03:00
alex-clawd
3132910084 perf: reduce framework overhead — lazy event bus, skip tracing when disabled (#5187)
* perf: reduce framework overhead for NVIDIA benchmarks

- Lazy initialize event bus thread pool and event loop on first emit()
  instead of at import time (~200ms savings)
- Skip trace listener registration (50+ handlers) when tracing disabled
- Skip trace prompt in non-interactive contexts (isatty check) to avoid
  20s timeout in CI/Docker/API servers
- Skip flush() when no events were emitted (avoids 30s timeout waste)
- Add _has_pending_events flag to track if any events were emitted
- Add _executor_initialized flag for lazy init double-checked locking

All existing behavior preserved when tracing IS enabled. No public APIs
changed - only conditional guards added.

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

* fix: address PR review comments — tracing override, executor init order, stdin guard, unused import

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

* style: fix ruff formatting in trace_listener.py and utils.py

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Iris Clawd <iris@crewai.com>
Co-authored-by: Greyson LaLonde <greyson.r.lalonde@gmail.com>
2026-04-01 14:17:57 -03:00
Lucas Gomide
c8f3a96779 docs: fix RBAC permission levels to match actual UI options (#5210)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Check Documentation Broken Links / Check broken links (push) Has been cancelled
2026-04-01 10:35:06 -04:00
86 changed files with 4797 additions and 836 deletions

View File

@@ -19,7 +19,7 @@ repos:
language: system
pass_filenames: true
types: [python]
exclude: ^(lib/crewai/src/crewai/cli/templates/|lib/crewai/tests/|lib/crewai-tools/tests/|lib/crewai-files/tests/)
exclude: ^(lib/crewai/src/crewai/cli/templates/|lib/crewai/tests/|lib/crewai-tools/tests/|lib/crewai-files/tests/|lib/crewai-a2a/tests/)
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.9.3
hooks:

View File

@@ -59,7 +59,11 @@ if _original_from_serialized_response is not None:
request: Any, serialized_response: Any, history: Any = None
) -> Any:
"""Patched version that ensures response._content is properly set."""
response = _original_from_serialized_response(request, serialized_response, history)
if not _original_from_serialized_response:
return None
response = _original_from_serialized_response(
request, serialized_response, history
)
# Explicitly set _content to avoid ResponseNotRead errors
# The content was passed to the constructor but the mocked read() prevents
# proper initialization of the internal state
@@ -255,7 +259,7 @@ def vcr_cassette_dir(request: Any) -> str:
for parent in test_file.parents:
if (
parent.name in ("crewai", "crewai-tools", "crewai-files")
parent.name in ("crewai", "crewai-tools", "crewai-files", "crewai-a2a")
and parent.parent.name == "lib"
):
package_root = parent

View File

@@ -4,6 +4,26 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
icon: "clock"
mode: "wide"
---
<Update label="1 أبريل 2026">
## v1.13.0a6
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a6)
## ما الذي تغير
### الوثائق
- إصلاح مستويات أذونات RBAC لتتوافق مع خيارات واجهة المستخدم الفعلية (#5210)
- تحديث سجل التغييرات والإصدار لـ v1.13.0a5 (#5200)
### الأداء
- تقليل عبء العمل على الإطار من خلال تنفيذ حافلة أحداث كسولة وتجاوز التتبع عند تعطيله (#5187)
## المساهمون
@alex-clawd, @joaomdmoura, @lucasgomide
</Update>
<Update label="31 مارس 2026">
## v1.13.0a5

View File

@@ -7,11 +7,13 @@ mode: "wide"
## نظرة عامة
يتيح RBAC في CrewAI AMP إدارة وصول آمنة وقابلة للتوسع من خلال مزيج من الأدوار على مستوى المؤسسة وعناصر التحكم في الرؤية على مستوى الأتمتة.
يتيح RBAC في CrewAI AMP إدارة وصول آمنة وقابلة للتوسع من خلال طبقتين:
1. **صلاحيات الميزات** — تتحكم في ما يمكن لكل دور القيام به عبر المنصة (إدارة، قراءة، أو بدون وصول)
2. **صلاحيات على مستوى الكيان** — وصول دقيق للأتمتات الفردية ومتغيرات البيئة واتصالات LLM ومستودعات Git
<Frame>
<img src="/images/enterprise/users_and_roles.png" alt="نظرة عامة على RBAC في CrewAI AMP" />
</Frame>
## المستخدمون والأدوار
@@ -39,6 +41,13 @@ mode: "wide"
</Step>
</Steps>
### الأدوار المحددة مسبقاً
| الدور | الوصف |
| :---------- | :-------------------------------------------------------------------- |
| **Owner** | وصول كامل لجميع الميزات والإعدادات. لا يمكن تقييده. |
| **Member** | وصول للقراءة لمعظم الميزات، وصول إدارة لمتغيرات البيئة واتصالات LLM ومشاريع Studio. لا يمكنه تعديل إعدادات المؤسسة أو الإعدادات الافتراضية. |
### ملخص التهيئة
| المجال | مكان التهيئة | الخيارات |
@@ -46,23 +55,80 @@ mode: "wide"
| المستخدمون والأدوار | Settings → Roles | محددة مسبقاً: Owner، Member؛ أدوار مخصصة |
| رؤية الأتمتة | Automation → Settings → Visibility | خاص؛ قائمة بيضاء للمستخدمين/الأدوار |
## التحكم في الوصول على مستوى الأتمتة
---
بالإضافة إلى الأدوار على مستوى المؤسسة، تدعم أتمتات CrewAI إعدادات رؤية دقيقة تتيح لك تقييد الوصول إلى أتمتات محددة حسب المستخدم أو الدور.
## مصفوفة صلاحيات الميزات
هذا مفيد لـ:
لكل دور مستوى صلاحية لكل منطقة ميزة. المستويات الثلاثة هي:
- **إدارة (Manage)** — وصول كامل للقراءة/الكتابة (إنشاء، تعديل، حذف)
- **قراءة (Read)** — وصول للعرض فقط
- **بدون وصول (No access)** — الميزة مخفية/غير قابلة للوصول
| الميزة | Owner | Member (افتراضي) | المستويات المتاحة | الوصف |
| :------------------------ | :------ | :--------------- | :--------------------------------- | :-------------------------------------------------------------- |
| `usage_dashboards` | Manage | Read | Manage / Read / No access | عرض مقاييس وتحليلات الاستخدام |
| `crews_dashboards` | Manage | Read | Manage / Read / No access | عرض لوحات النشر والوصول إلى تفاصيل الأتمتة |
| `invitations` | Manage | Read | Manage / Read / No access | دعوة أعضاء جدد إلى المؤسسة |
| `training_ui` | Manage | Read | Manage / Read / No access | الوصول إلى واجهات التدريب/الضبط الدقيق |
| `tools` | Manage | Read | Manage / Read / No access | إنشاء وإدارة الأدوات |
| `agents` | Manage | Read | Manage / Read / No access | إنشاء وإدارة الوكلاء |
| `environment_variables` | Manage | Manage | Manage / No access | إنشاء وإدارة متغيرات البيئة |
| `llm_connections` | Manage | Manage | Manage / No access | تهيئة اتصالات مزودي LLM |
| `default_settings` | Manage | No access | Manage / No access | تعديل الإعدادات الافتراضية على مستوى المؤسسة |
| `organization_settings` | Manage | No access | Manage / No access | إدارة الفوترة والخطط وتهيئة المؤسسة |
| `studio_projects` | Manage | Manage | Manage / No access | إنشاء وتعديل المشاريع في Studio |
<Tip>
عند إنشاء دور مخصص، يمكن ضبط معظم الميزات على **Manage** أو **Read** أو **No access**. ومع ذلك، فإن `environment_variables` و`llm_connections` و`default_settings` و`organization_settings` و`studio_projects` تدعم فقط **Manage** أو **No access** — لا يوجد خيار للقراءة فقط لهذه الميزات.
</Tip>
---
## النشر من GitHub أو Zip
من أكثر أسئلة RBAC شيوعاً: _"ما الصلاحيات التي يحتاجها عضو الفريق للنشر؟"_
### النشر من GitHub
لنشر أتمتة من مستودع GitHub، يحتاج المستخدم إلى:
1. **`crews_dashboards`**: على الأقل `Read` — مطلوب للوصول إلى لوحة الأتمتات حيث يتم إنشاء عمليات النشر
2. **الوصول إلى مستودع Git** (إذا كان RBAC على مستوى الكيان لمستودعات Git مفعلاً): يجب منح دور المستخدم الوصول إلى مستودع Git المحدد عبر صلاحيات مستوى الكيان
3. **`studio_projects`: `Manage`** — إذا كان يبني الطاقم في Studio قبل النشر
### النشر من Zip
لنشر أتمتة من ملف Zip، يحتاج المستخدم إلى:
1. **`crews_dashboards`**: على الأقل `Read` — مطلوب للوصول إلى لوحة الأتمتات
2. **تفعيل نشر Zip**: يجب ألا تكون المؤسسة قد عطلت نشر Zip في إعدادات المؤسسة
### مرجع سريع: الحد الأدنى من الصلاحيات للنشر
| الإجراء | صلاحيات الميزات المطلوبة | متطلبات إضافية |
| :------------------- | :----------------------------------- | :----------------------------------------------- |
| النشر من GitHub | `crews_dashboards: Read` | وصول كيان مستودع Git (إذا كان Git RBAC مفعلاً) |
| النشر من Zip | `crews_dashboards: Read` | يجب تفعيل نشر Zip على مستوى المؤسسة |
| البناء في Studio | `studio_projects: Manage` | — |
| تهيئة مفاتيح LLM | `llm_connections: Manage` | — |
| ضبط متغيرات البيئة | `environment_variables: Manage` | وصول مستوى الكيان (إذا كان RBAC الكيان مفعلاً) |
---
## التحكم في الوصول على مستوى الأتمتة (صلاحيات الكيان)
بالإضافة إلى الأدوار على مستوى المؤسسة، يدعم CrewAI صلاحيات دقيقة على مستوى الكيان تقيد الوصول إلى موارد فردية.
### رؤية الأتمتة
تدعم الأتمتات إعدادات رؤية تقيد الوصول حسب المستخدم أو الدور. هذا مفيد لـ:
- الحفاظ على خصوصية الأتمتات الحساسة أو التجريبية
- إدارة الرؤية عبر الفرق الكبيرة أو المتعاونين الخارجيين
- اختبار الأتمتات في سياقات معزولة
يمكن تهيئة عمليات النشر كخاصة، مما يعني أن المستخدمين والأدوار المدرجين في القائمة البيضاء فقط سيتمكنون من:
- عرض عملية النشر
- تشغيلها أو التفاعل مع API الخاص بها
- الوصول إلى سجلاتها ومقاييسها وإعداداتها
يتمتع مالك المؤسسة دائماً بالوصول، بغض النظر عن إعدادات الرؤية.
يمكن تهيئة عمليات النشر كخاصة، مما يعني أن المستخدمين والأدوار المدرجين في القائمة البيضاء فقط سيتمكنون من التفاعل معها.
يمكنك تهيئة التحكم في الوصول على مستوى الأتمتة في Automation → Settings → علامة تبويب Visibility.
@@ -99,9 +165,92 @@ mode: "wide"
<Frame>
<img src="/images/enterprise/visibility.png" alt="إعدادات رؤية الأتمتة في CrewAI AMP" />
</Frame>
### أنواع صلاحيات النشر
عند منح وصول على مستوى الكيان لأتمتة محددة، يمكنك تعيين أنواع الصلاحيات التالية:
| الصلاحية | ما تسمح به |
| :------------------- | :-------------------------------------------------- |
| `run` | تنفيذ الأتمتة واستخدام API الخاص بها |
| `traces` | عرض تتبعات التنفيذ والسجلات |
| `manage_settings` | تعديل، إعادة نشر، استرجاع، أو حذف الأتمتة |
| `human_in_the_loop` | الرد على طلبات الإنسان في الحلقة (HITL) |
| `full_access` | جميع ما سبق |
### RBAC على مستوى الكيان لموارد أخرى
عند تفعيل RBAC على مستوى الكيان، يمكن أيضاً التحكم في الوصول لهذه الموارد حسب المستخدم أو الدور:
| المورد | يتم التحكم فيه بواسطة | الوصف |
| :-------------------- | :--------------------------------- | :------------------------------------------------------------- |
| متغيرات البيئة | علامة ميزة RBAC الكيان | تقييد أي الأدوار/المستخدمين يمكنهم عرض أو إدارة متغيرات بيئة محددة |
| اتصالات LLM | علامة ميزة RBAC الكيان | تقييد الوصول لتهيئات مزودي LLM محددة |
| مستودعات Git | إعداد RBAC لمستودعات Git بالمؤسسة | تقييد أي الأدوار/المستخدمين يمكنهم الوصول لمستودعات متصلة محددة |
---
## أنماط الأدوار الشائعة
بينما يأتي CrewAI بدوري Owner وMember، تستفيد معظم الفرق من إنشاء أدوار مخصصة. إليك الأنماط الشائعة:
### دور المطور
دور لأعضاء الفريق الذين يبنون وينشرون الأتمتات لكن لا يديرون إعدادات المؤسسة.
| الميزة | الصلاحية |
| :------------------------ | :---------- |
| `usage_dashboards` | Read |
| `crews_dashboards` | Manage |
| `invitations` | Read |
| `training_ui` | Read |
| `tools` | Manage |
| `agents` | Manage |
| `environment_variables` | Manage |
| `llm_connections` | Manage |
| `default_settings` | No access |
| `organization_settings` | No access |
| `studio_projects` | Manage |
### دور المشاهد / أصحاب المصلحة
دور للمعنيين غير التقنيين الذين يحتاجون لمراقبة الأتمتات وعرض النتائج.
| الميزة | الصلاحية |
| :------------------------ | :---------- |
| `usage_dashboards` | Read |
| `crews_dashboards` | Read |
| `invitations` | No access |
| `training_ui` | Read |
| `tools` | Read |
| `agents` | Read |
| `environment_variables` | No access |
| `llm_connections` | No access |
| `default_settings` | No access |
| `organization_settings` | No access |
| `studio_projects` | No access |
### دور مسؤول العمليات / المنصة
دور لمشغلي المنصة الذين يديرون إعدادات البنية التحتية لكن قد لا يبنون الوكلاء.
| الميزة | الصلاحية |
| :------------------------ | :---------- |
| `usage_dashboards` | Manage |
| `crews_dashboards` | Manage |
| `invitations` | Manage |
| `training_ui` | Read |
| `tools` | Read |
| `agents` | Read |
| `environment_variables` | Manage |
| `llm_connections` | Manage |
| `default_settings` | Manage |
| `organization_settings` | Read |
| `studio_projects` | No access |
---
<Card title="تحتاج مساعدة؟" icon="headset" href="mailto:support@crewai.com">
تواصل مع فريق الدعم للمساعدة في أسئلة RBAC.
</Card>

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,26 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
icon: "clock"
mode: "wide"
---
<Update label="Apr 01, 2026">
## v1.13.0a6
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a6)
## What's Changed
### Documentation
- Fix RBAC permission levels to match actual UI options (#5210)
- Update changelog and version for v1.13.0a5 (#5200)
### Performance
- Reduce framework overhead by implementing a lazy event bus and skipping tracing when disabled (#5187)
## Contributors
@alex-clawd, @joaomdmoura, @lucasgomide
</Update>
<Update label="Mar 31, 2026">
## v1.13.0a5

View File

@@ -46,7 +46,7 @@ You can configure users and roles in Settings → Roles.
| Role | Description |
| :--------- | :-------------------------------------------------------------------------- |
| **Owner** | Full access to all features and settings. Cannot be restricted. |
| **Member** | Read access to most features, manage access to Studio projects. Cannot modify organization or default settings. |
| **Member** | Read access to most features, manage access to environment variables, LLM connections, and Studio projects. Cannot modify organization or default settings. |
### Configuration summary
@@ -65,22 +65,22 @@ Every role has a permission level for each feature area. The three levels are:
- **Read** — view-only access
- **No access** — feature is hidden/inaccessible
| Feature | Owner | Member (default) | Description |
| :------------------------ | :------ | :--------------- | :-------------------------------------------------------------- |
| `usage_dashboards` | Manage | Read | View usage metrics and analytics |
| `crews_dashboards` | Manage | Read | View deployment dashboards, access automation details |
| `invitations` | Manage | Read | Invite new members to the organization |
| `training_ui` | Manage | Read | Access training/fine-tuning interfaces |
| `tools` | Manage | Read | Create and manage tools |
| `agents` | Manage | Read | Create and manage agents |
| `environment_variables` | Manage | Read | Create and manage environment variables |
| `llm_connections` | Manage | Read | Configure LLM provider connections |
| `default_settings` | Manage | No access | Modify organization-wide default settings |
| `organization_settings` | Manage | No access | Manage billing, plans, and organization configuration |
| `studio_projects` | Manage | Manage | Create and edit projects in Studio |
| Feature | Owner | Member (default) | Available levels | Description |
| :------------------------ | :------ | :--------------- | :------------------------ | :-------------------------------------------------------------- |
| `usage_dashboards` | Manage | Read | Manage / Read / No access | View usage metrics and analytics |
| `crews_dashboards` | Manage | Read | Manage / Read / No access | View deployment dashboards, access automation details |
| `invitations` | Manage | Read | Manage / Read / No access | Invite new members to the organization |
| `training_ui` | Manage | Read | Manage / Read / No access | Access training/fine-tuning interfaces |
| `tools` | Manage | Read | Manage / Read / No access | Create and manage tools |
| `agents` | Manage | Read | Manage / Read / No access | Create and manage agents |
| `environment_variables` | Manage | Manage | Manage / No access | Create and manage environment variables |
| `llm_connections` | Manage | Manage | Manage / No access | Configure LLM provider connections |
| `default_settings` | Manage | No access | Manage / No access | Modify organization-wide default settings |
| `organization_settings` | Manage | No access | Manage / No access | Manage billing, plans, and organization configuration |
| `studio_projects` | Manage | Manage | Manage / No access | Create and edit projects in Studio |
<Tip>
When creating a custom role, you can set each feature independently to **Manage**, **Read**, or **No access** to match your team's needs.
When creating a custom role, most features can be set to **Manage**, **Read**, or **No access**. However, `environment_variables`, `llm_connections`, `default_settings`, `organization_settings`, and `studio_projects` only support **Manage** or **No access** — there is no read-only option for these features.
</Tip>
---
@@ -208,7 +208,7 @@ A role for team members who build and deploy automations but don't manage organi
| `tools` | Manage |
| `agents` | Manage |
| `environment_variables` | Manage |
| `llm_connections` | Read |
| `llm_connections` | Manage |
| `default_settings` | No access |
| `organization_settings` | No access |
| `studio_projects` | Manage |
@@ -229,7 +229,7 @@ A role for non-technical stakeholders who need to monitor automations and view r
| `llm_connections` | No access |
| `default_settings` | No access |
| `organization_settings` | No access |
| `studio_projects` | Read |
| `studio_projects` | No access |
### Ops / Platform Admin Role
@@ -247,7 +247,7 @@ A role for platform operators who manage infrastructure settings but may not bui
| `llm_connections` | Manage |
| `default_settings` | Manage |
| `organization_settings` | Read |
| `studio_projects` | Read |
| `studio_projects` | No access |
---

View File

@@ -4,6 +4,26 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
icon: "clock"
mode: "wide"
---
<Update label="2026년 4월 1일">
## v1.13.0a6
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a6)
## 변경 사항
### 문서
- 실제 UI 옵션에 맞게 RBAC 권한 수준 수정 (#5210)
- v1.13.0a5에 대한 변경 로그 및 버전 업데이트 (#5200)
### 성능
- 지연 이벤트 버스를 구현하고 비활성화 시 추적을 건너뛰어 프레임워크 오버헤드 감소 (#5187)
## 기여자
@alex-clawd, @joaomdmoura, @lucasgomide
</Update>
<Update label="2026년 3월 31일">
## v1.13.0a5

View File

@@ -1,108 +1,260 @@
---
title: "역할 기반 접근 제어 (RBAC)"
description: "역할과 자동화별 가시성으로 crews, 도구, 데이터 접근을 제어합니다."
description: "역할, 범위, 세분화된 권한으로 crews, 도구, 데이터 접근을 제어합니다."
icon: "shield"
mode: "wide"
---
## 개요
CrewAI AOP의 RBAC는 **조직 수준 역할**과 **자동화(Automation) 수준 가시성**을 결합하여 안전하고 확장 가능한 접근 제어를 제공합니다.
CrewAI AMP의 RBAC는 두 가지 계층을 통해 안전하고 확장 가능한 접근 관리를 제공합니다:
1. **기능 권한** — 플랫폼 전반에서 각 역할이 수행할 수 있는 작업을 제어합니다 (관리, 읽기 또는 접근 불가)
2. **엔티티 수준 권한** — 개별 자동화, 환경 변수, LLM 연결, Git 저장소에 대한 세분화된 접근 제어
<Frame>
<img src="/images/enterprise/users_and_roles.png" alt="CrewAI AMP RBAC 개요" />
</Frame>
## 사용자와 역할
워크스페이스의 각 구성원 역할이 있으며, 이는 기능 접근 범위 결정니다.
CrewAI 워크스페이스의 각 구성원에게는 역할이 할당되며, 이를 통해 다양한 기능에 대한 접근 범위 결정니다.
가능한 작업:
- 사전 정의된 역할 사용 (Owner, Member)
- 권한을 세분화한 커스텀 역할 생성
- 설정 화면에서 언제든 역할 할당/변경
- 특정 권한에 맞춘 커스텀 역할 생성
- 설정 패널에서 언제든 역할 할당
설정 위치: Settings → Roles
<Steps>
<Step title="Roles 열기">
<b>Settings → Roles</b>로 이동합니다.
<Step title="Roles 설정 열기">
CrewAI AMP에서 <b>Settings → Roles</b>로 이동합니다.
</Step>
<Step title="역할 선택">
<b>Owner</b> 또는 <b>Member</b> 사용하거나 <b>Create role</b>로 커스텀
역할을 만듭니다.
<Step title="역할 유형 선택">
사전 정의된 역할(<b>Owner</b>, <b>Member</b>)을 사용하거나{" "}
<b>Create role</b>을 클릭하여 커스텀 역할을 만듭니다.
</Step>
<Step title="멤버에 할당">
사용자들을 선택하 역할을 지정합니다. 언제든 변경할 수 있습니다.
사용자 선택하 역할을 할당합니다. 언제든 변경할 수 있습니다.
</Step>
</Steps>
### 사전 정의된 역할
| 역할 | 설명 |
| :--------- | :------------------------------------------------------------------- |
| **Owner** | 모든 기능 및 설정에 대한 전체 접근 권한. 제한할 수 없습니다. |
| **Member** | 대부분의 기능에 대한 읽기 접근, 환경 변수, LLM 연결, Studio 프로젝트에 대한 관리 접근. 조직 설정이나 기본 설정은 수정할 수 없습니다. |
### 구성 요약
| 영역 | 위치 | 옵션 |
| 영역 | 설정 위치 | 옵션 |
| :------------ | :--------------------------------- | :-------------------------------- |
| 사용자 & 역할 | Settings → Roles | Owner, Member; 커스텀 역할 |
| 사용자 & 역할 | Settings → Roles | 사전 정의: Owner, Member; 커스텀 역할 |
| 자동화 가시성 | Automation → Settings → Visibility | Private; 사용자/역할 화이트리스트 |
## 자동화 수준 접근 제어
---
조직 역할과 별개로, **Automations**는 사용자/역할별로 특정 자동화 접근을 제한하는 가시성 설정을 제공합니다.
## 기능 권한 매트릭스
유용한 경우:
각 역할에는 기능 영역별 권한 수준이 있습니다. 세 가지 수준은 다음과 같습니다:
- 민감/실험 자동화를 비공개로 유지
- 대규모 팀/외부 협업에서 가시성 관리
- **Manage** — 전체 읽기/쓰기 접근 (생성, 편집, 삭제)
- **Read** — 읽기 전용 접근
- **No access** — 기능이 숨겨지거나 접근 불가
| 기능 | Owner | Member (기본값) | 사용 가능한 수준 | 설명 |
| :-------------------------- | :------ | :--------------- | :------------------------- | :------------------------------------------------------------- |
| `usage_dashboards` | Manage | Read | Manage / Read / No access | 사용 메트릭 및 분석 보기 |
| `crews_dashboards` | Manage | Read | Manage / Read / No access | 배포 대시보드 보기, 자동화 세부 정보 접근 |
| `invitations` | Manage | Read | Manage / Read / No access | 조직에 새 멤버 초대 |
| `training_ui` | Manage | Read | Manage / Read / No access | 훈련/파인튜닝 인터페이스 접근 |
| `tools` | Manage | Read | Manage / Read / No access | 도구 생성 및 관리 |
| `agents` | Manage | Read | Manage / Read / No access | 에이전트 생성 및 관리 |
| `environment_variables` | Manage | Manage | Manage / No access | 환경 변수 생성 및 관리 |
| `llm_connections` | Manage | Manage | Manage / No access | LLM 제공자 연결 구성 |
| `default_settings` | Manage | No access | Manage / No access | 조직 전체 기본 설정 수정 |
| `organization_settings` | Manage | No access | Manage / No access | 결제, 플랜 및 조직 구성 관리 |
| `studio_projects` | Manage | Manage | Manage / No access | Studio에서 프로젝트 생성 및 편집 |
<Tip>
커스텀 역할을 만들 때 대부분의 기능은 **Manage**, **Read** 또는 **No access**로 설정할 수 있습니다. 그러나 `environment_variables`, `llm_connections`, `default_settings`, `organization_settings`, `studio_projects`는 **Manage** 또는 **No access**만 지원합니다 — 이 기능들에는 읽기 전용 옵션이 없습니다.
</Tip>
---
## GitHub 또는 Zip에서 배포
가장 흔한 RBAC 질문 중 하나: _"팀원이 배포하려면 어떤 권한이 필요한가요?"_
### GitHub에서 배포
GitHub 저장소에서 자동화를 배포하려면 사용자에게 다음이 필요합니다:
1. **`crews_dashboards`**: 최소 `Read` — 배포가 생성되는 자동화 대시보드에 접근하는 데 필요
2. **Git 저장소 접근** (Git 저장소에 대한 엔티티 수준 RBAC가 활성화된 경우): 사용자의 역할에 엔티티 수준 권한을 통해 특정 Git 저장소에 대한 접근이 부여되어야 함
3. **`studio_projects`: `Manage`** — 배포 전에 Studio에서 crew를 빌드하는 경우
### Zip에서 배포
Zip 파일 업로드로 자동화를 배포하려면 사용자에게 다음이 필요합니다:
1. **`crews_dashboards`**: 최소 `Read` — 자동화 대시보드에 접근하는 데 필요
2. **Zip 배포 활성화**: 조직이 조직 설정에서 Zip 배포를 비활성화하지 않아야 함
### 빠른 참조: 배포에 필요한 최소 권한
| 작업 | 필요한 기능 권한 | 추가 요구사항 |
| :------------------- | :----------------------------------- | :----------------------------------------------- |
| GitHub에서 배포 | `crews_dashboards: Read` | Git 저장소 엔티티 접근 (Git RBAC 활성화 시) |
| Zip에서 배포 | `crews_dashboards: Read` | 조직 수준에서 Zip 배포가 활성화되어야 함 |
| Studio에서 빌드 | `studio_projects: Manage` | — |
| LLM 키 구성 | `llm_connections: Manage` | — |
| 환경 변수 설정 | `environment_variables: Manage` | 엔티티 수준 접근 (엔티티 RBAC 활성화 시) |
---
## 자동화 수준 접근 제어 (엔티티 권한)
조직 전체 역할 외에도, CrewAI는 개별 리소스에 대한 접근을 제한하는 세분화된 엔티티 수준 권한을 지원합니다.
### 자동화 가시성
자동화는 사용자 또는 역할별로 접근을 제한하는 가시성 설정을 지원합니다. 다음과 같은 경우에 유용합니다:
- 민감하거나 실험적인 자동화를 비공개로 유지
- 대규모 팀이나 외부 협업자의 가시성 관리
- 격리된 컨텍스트에서 자동화 테스트
Private 모드에서는 화이트리스트에 포함된 사용자/역할만 다음 작업이 가능합니다:
배포를 비공개로 구성할 수 있으며, 이 경우 화이트리스트에 포함된 사용자역할만 상호작용할 수 있습니다.
- 자동화 보기
- 실행/API 사용
- 로그, 메트릭, 설정 접근
조직 Owner는 항상 접근 가능하며, 가시성 설정에 영향을 받지 않습니다.
설정 위치: Automation → Settings → Visibility
설정 위치: Automation → Settings → Visibility 탭
<Steps>
<Step title="Visibility 탭 열기">
<b>Automation → Settings → Visibility</b>로 이동합니다.
</Step>
<Step title="가시성 설정">
<b>Private</b>를 선택합니다. Owner는 항상 접근 가능합니다.
접근을 제한하려면 <b>Private</b>를 선택합니다. 조직 Owner는 항상
접근 권한을 유지합니다.
</Step>
<Step title="허용 대상 추가">
보기/실행/로그·메트릭·설정 접근이 가능한 사용자/역할을 추가합니다.
<Step title="접근 허용 대상 추가">
보기, 실행, 로그/메트릭/설정 접근이 허용된 특정 사용자역할을
추가합니다.
</Step>
<Step title="저장 및 확인">
저장 후, 목록에 없는 사용자가 보거나 실행할 수 없는지 확인합니다.
변경 사항을 저장 후, 화이트리스트에 없는 사용자가 자동화를 보거나 실행할 수
없는지 확인합니다.
</Step>
</Steps>
### Private 모드 접근 결과
### Private 가시성: 접근 결과
| 동작 | Owner | 화이트리스트 사용자/역할 | 비포함 |
| :--------------- | :---- | :----------------------- | :----- |
| 자동화 보기 | ✓ | ✓ | ✗ |
| 실행/API | ✓ | ✓ | ✗ |
| 로그/메트릭/설정 | ✓ | ✓ | ✗ |
| 동작 | Owner | 화이트리스트 사용자/역할 | 비포함 |
| :--------------------- | :---- | :----------------------- | :----- |
| 자동화 보기 | ✓ | ✓ | ✗ |
| 자동화/API 실행 | ✓ | ✓ | ✗ |
| 로그/메트릭/설정 접근 | ✓ | ✓ | ✗ |
<Tip>
Owner는 항상 접근 가능하며, Private 모드에서는 화이트리스트에 포함된
사용자/역할만 권한이 부여됩니다.
조직 Owner는 항상 접근 권한이 있습니다. Private 모드에서는 화이트리스트에 포함된
사용자/역할만 보기, 실행, 로그/메트릭/설정에 접근할 수 있습니다.
</Tip>
<Frame>
<img src="/images/enterprise/visibility.png" alt="CrewAI AMP 가시성 설정" />
<img src="/images/enterprise/visibility.png" alt="CrewAI AMP 자동화 가시성 설정" />
</Frame>
### 배포 권한 유형
특정 자동화에 엔티티 수준 접근을 부여할 때 다음 권한 유형을 할당할 수 있습니다:
| 권한 | 허용 범위 |
| :------------------- | :-------------------------------------------------- |
| `run` | 자동화 실행 및 API 사용 |
| `traces` | 실행 트레이스 및 로그 보기 |
| `manage_settings` | 자동화 편집, 재배포, 롤백 또는 삭제 |
| `human_in_the_loop` | HITL(human-in-the-loop) 요청에 응답 |
| `full_access` | 위의 모든 권한 |
### 기타 리소스에 대한 엔티티 수준 RBAC
엔티티 수준 RBAC가 활성화되면 다음 리소스에 대한 접근도 사용자 또는 역할별로 제어할 수 있습니다:
| 리소스 | 제어 방식 | 설명 |
| :----------------- | :---------------------------------- | :------------------------------------------------------------ |
| 환경 변수 | 엔티티 RBAC 기능 플래그 | 특정 환경 변수를 보거나 관리할 수 있는 역할/사용자 제한 |
| LLM 연결 | 엔티티 RBAC 기능 플래그 | 특정 LLM 제공자 구성에 대한 접근 제한 |
| Git 저장소 | Git 저장소 RBAC 조직 설정 | 특정 연결된 저장소에 접근할 수 있는 역할/사용자 제한 |
---
## 일반적인 역할 패턴
CrewAI는 Owner와 Member 역할을 기본 제공하지만, 대부분의 팀은 커스텀 역할을 만들어 활용합니다. 일반적인 패턴은 다음과 같습니다:
### Developer 역할
자동화를 빌드하고 배포하지만 조직 설정을 관리하지 않는 팀원을 위한 역할입니다.
| 기능 | 권한 |
| :-------------------------- | :---------- |
| `usage_dashboards` | Read |
| `crews_dashboards` | Manage |
| `invitations` | Read |
| `training_ui` | Read |
| `tools` | Manage |
| `agents` | Manage |
| `environment_variables` | Manage |
| `llm_connections` | Manage |
| `default_settings` | No access |
| `organization_settings` | No access |
| `studio_projects` | Manage |
### Viewer / Stakeholder 역할
자동화를 모니터링하고 결과를 확인해야 하는 비기술 이해관계자를 위한 역할입니다.
| 기능 | 권한 |
| :-------------------------- | :---------- |
| `usage_dashboards` | Read |
| `crews_dashboards` | Read |
| `invitations` | No access |
| `training_ui` | Read |
| `tools` | Read |
| `agents` | Read |
| `environment_variables` | No access |
| `llm_connections` | No access |
| `default_settings` | No access |
| `organization_settings` | No access |
| `studio_projects` | No access |
### Ops / Platform Admin 역할
인프라 설정을 관리하지만 에이전트를 빌드하지 않을 수 있는 플랫폼 운영자를 위한 역할입니다.
| 기능 | 권한 |
| :-------------------------- | :---------- |
| `usage_dashboards` | Manage |
| `crews_dashboards` | Manage |
| `invitations` | Manage |
| `training_ui` | Read |
| `tools` | Read |
| `agents` | Read |
| `environment_variables` | Manage |
| `llm_connections` | Manage |
| `default_settings` | Manage |
| `organization_settings` | Read |
| `studio_projects` | No access |
---
<Card
title="도움이 필요하신가요?"
icon="headset"
href="mailto:support@crewai.com"
>
RBAC 구성과 점검에 대한 지원이 필요하면 연락해 주세요.
RBAC 관련 질문은 지원팀에 문의해 주세요.
</Card>

View File

@@ -4,6 +4,26 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
icon: "clock"
mode: "wide"
---
<Update label="01 abr 2026">
## v1.13.0a6
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a6)
## O que Mudou
### Documentação
- Corrigir níveis de permissão RBAC para corresponder às opções reais da interface do usuário (#5210)
- Atualizar changelog e versão para v1.13.0a5 (#5200)
### Desempenho
- Reduzir a sobrecarga do framework implementando um barramento de eventos preguiçoso e pulando o rastreamento quando desativado (#5187)
## Contributors
@alex-clawd, @joaomdmoura, @lucasgomide
</Update>
<Update label="31 mar 2026">
## v1.13.0a5

View File

@@ -1,22 +1,24 @@
---
title: "Controle de Acesso Baseado em Funções (RBAC)"
description: "Controle o acesso a crews, ferramentas e dados com funções e visibilidade por automação."
description: "Controle o acesso a crews, ferramentas e dados com funções, escopos e permissões granulares."
icon: "shield"
mode: "wide"
---
## Visão Geral
O RBAC no CrewAI AMP permite gerenciar acesso de forma segura e escalável combinando **funções em nível de organização** com **controles de visibilidade em nível de automação**.
O RBAC no CrewAI AMP permite gerenciamento de acesso seguro e escalável através de duas camadas:
1. **Permissões de funcionalidade** — controlam o que cada função pode fazer na plataforma (gerenciar, ler ou sem acesso)
2. **Permissões em nível de entidade** — acesso granular em automações individuais, variáveis de ambiente, conexões LLM e repositórios Git
<Frame>
<img src="/images/enterprise/users_and_roles.png" alt="Visão geral de RBAC no CrewAI AMP" />
</Frame>
## Usuários e Funções
Cada membro da sua workspace possui uma função, que determina o acesso aos recursos.
Cada membro da sua workspace CrewAI recebe uma função, que determina seu acesso aos diversos recursos.
Você pode:
@@ -31,14 +33,21 @@ A configuração de usuários e funções é feita em Settings → Roles.
Vá em <b>Settings → Roles</b> no CrewAI AMP.
</Step>
<Step title="Escolher a função">
Use <b>Owner</b> ou <b>Member</b>, ou clique em <b>Create role</b> para
criar uma função personalizada.
Use uma função pré-definida (<b>Owner</b>, <b>Member</b>) ou clique em{" "}
<b>Create role</b> para criar uma personalizada.
</Step>
<Step title="Atribuir aos membros">
Selecione os usuários e atribua a função. Você pode alterar depois.
</Step>
</Steps>
### Funções Pré-definidas
| Função | Descrição |
| :--------- | :------------------------------------------------------------------------ |
| **Owner** | Acesso total a todas as funcionalidades e configurações. Não pode ser restrito. |
| **Member** | Acesso de leitura à maioria das funcionalidades, acesso de gerenciamento a variáveis de ambiente, conexões LLM e projetos Studio. Não pode modificar configurações da organização ou padrões. |
### Resumo de configuração
| Área | Onde configurar | Opções |
@@ -46,35 +55,93 @@ A configuração de usuários e funções é feita em Settings → Roles.
| Usuários & Funções | Settings → Roles | Pré-definidas: Owner, Member; Funções personalizadas |
| Visibilidade da automação | Automation → Settings → Visibility | Private; Lista de usuários/funções |
## Controle de Acesso em Nível de Automação
---
Além das funções na organização, as **Automations** suportam visibilidade refinada para restringir acesso por usuário ou função.
## Matriz de Permissões de Funcionalidades
Útil para:
Cada função possui um nível de permissão para cada área de funcionalidade. Os três níveis são:
- Manter automações sensíveis/experimentais privadas
- **Manage** — acesso total de leitura/escrita (criar, editar, excluir)
- **Read** — acesso somente leitura
- **No access** — funcionalidade oculta/inacessível
| Funcionalidade | Owner | Member (padrão) | Níveis disponíveis | Descrição |
| :------------------------ | :------ | :--------------- | :------------------------ | :-------------------------------------------------------------- |
| `usage_dashboards` | Manage | Read | Manage / Read / No access | Visualizar métricas e análises de uso |
| `crews_dashboards` | Manage | Read | Manage / Read / No access | Visualizar dashboards de deploy, acessar detalhes de automações |
| `invitations` | Manage | Read | Manage / Read / No access | Convidar novos membros para a organização |
| `training_ui` | Manage | Read | Manage / Read / No access | Acessar interfaces de treinamento/fine-tuning |
| `tools` | Manage | Read | Manage / Read / No access | Criar e gerenciar ferramentas |
| `agents` | Manage | Read | Manage / Read / No access | Criar e gerenciar agentes |
| `environment_variables` | Manage | Manage | Manage / No access | Criar e gerenciar variáveis de ambiente |
| `llm_connections` | Manage | Manage | Manage / No access | Configurar conexões de provedores LLM |
| `default_settings` | Manage | No access | Manage / No access | Modificar configurações padrão da organização |
| `organization_settings` | Manage | No access | Manage / No access | Gerenciar cobrança, planos e configuração da organização |
| `studio_projects` | Manage | Manage | Manage / No access | Criar e editar projetos no Studio |
<Tip>
Ao criar uma função personalizada, a maioria das funcionalidades pode ser definida como **Manage**, **Read** ou **No access**. No entanto, `environment_variables`, `llm_connections`, `default_settings`, `organization_settings` e `studio_projects` suportam apenas **Manage** ou **No access** — não há opção somente leitura para essas funcionalidades.
</Tip>
---
## Deploy via GitHub ou Zip
Uma das perguntas mais comuns sobre RBAC é: _"Quais permissões um membro da equipe precisa para fazer deploy?"_
### Deploy via GitHub
Para fazer deploy de uma automação a partir de um repositório GitHub, o usuário precisa de:
1. **`crews_dashboards`**: pelo menos `Read` — necessário para acessar o dashboard de automações onde os deploys são criados
2. **Acesso ao repositório Git** (se RBAC em nível de entidade para repositórios Git estiver habilitado): a função do usuário deve ter acesso ao repositório Git específico via permissões de entidade
3. **`studio_projects`: `Manage`** — se estiver construindo o crew no Studio antes do deploy
### Deploy via Zip
Para fazer deploy de uma automação via upload de arquivo Zip, o usuário precisa de:
1. **`crews_dashboards`**: pelo menos `Read` — necessário para acessar o dashboard de automações
2. **Deploys via Zip habilitados**: a organização não deve ter desabilitado deploys via Zip nas configurações da organização
### Referência Rápida: Permissões Mínimas para Deploy
| Ação | Permissões de funcionalidade necessárias | Requisitos adicionais |
| :------------------------- | :--------------------------------------- | :------------------------------------------------ |
| Deploy via GitHub | `crews_dashboards: Read` | Acesso à entidade do repositório Git (se habilitado) |
| Deploy via Zip | `crews_dashboards: Read` | Deploys via Zip devem estar habilitados na organização |
| Construir no Studio | `studio_projects: Manage` | — |
| Configurar chaves LLM | `llm_connections: Manage` | — |
| Definir variáveis de ambiente | `environment_variables: Manage` | Acesso em nível de entidade (se habilitado) |
---
## Controle de Acesso em Nível de Automação (Permissões de Entidade)
Além das funções em nível de organização, o CrewAI suporta permissões granulares em nível de entidade que restringem o acesso a recursos individuais.
### Visibilidade da Automação
Automações suportam configurações de visibilidade que restringem acesso por usuário ou função. Útil para:
- Manter automações sensíveis ou experimentais privadas
- Gerenciar visibilidade em equipes grandes ou colaboradores externos
- Testar automações em contexto isolado
Em modo privado, somente usuários/funções na whitelist poderão:
Deploys podem ser configurados como privados, significando que apenas usuários e funções na whitelist poderão interagir com eles.
- Ver a automação
- Executar/usar a API
- Acessar logs, métricas e configurações
O owner da organização sempre tem acesso, independente da visibilidade.
Configure em Automation → Settings → Visibility.
Configure em Automation → Settings → aba Visibility.
<Steps>
<Step title="Abrir a aba Visibility">
Acesse <b>Automation → Settings → Visibility</b>.
</Step>
<Step title="Definir visibilidade">
Selecione <b>Private</b> para restringir o acesso. O owner mantém acesso.
Selecione <b>Private</b> para restringir o acesso. O owner da organização
mantém acesso sempre.
</Step>
<Step title="Permitir acesso">
Adicione usuários e funções que poderão ver/executar e acessar
Adicione usuários e funções que poderão ver, executar e acessar
logs/métricas/configurações.
</Step>
<Step title="Salvar e verificar">
@@ -97,9 +164,92 @@ Configure em Automation → Settings → Visibility.
<Frame>
<img src="/images/enterprise/visibility.png" alt="Configuração de visibilidade no CrewAI AMP" />
</Frame>
### Tipos de Permissão de Deploy
Ao conceder acesso em nível de entidade a uma automação específica, você pode atribuir estes tipos de permissão:
| Permissão | O que permite |
| :------------------- | :-------------------------------------------------- |
| `run` | Executar a automação e usar sua API |
| `traces` | Visualizar traces de execução e logs |
| `manage_settings` | Editar, reimplantar, reverter ou excluir a automação |
| `human_in_the_loop` | Responder a solicitações human-in-the-loop (HITL) |
| `full_access` | Todos os anteriores |
### RBAC em Nível de Entidade para Outros Recursos
Quando o RBAC em nível de entidade está habilitado, o acesso a estes recursos também pode ser controlado por usuário ou função:
| Recurso | Controlado por | Descrição |
| :--------------------- | :------------------------------------- | :------------------------------------------------------------- |
| Variáveis de ambiente | Flag de funcionalidade RBAC de entidade | Restringir quais funções/usuários podem ver ou gerenciar variáveis específicas |
| Conexões LLM | Flag de funcionalidade RBAC de entidade | Restringir acesso a configurações de provedores LLM específicos |
| Repositórios Git | Configuração RBAC de repositórios Git | Restringir quais funções/usuários podem acessar repositórios conectados específicos |
---
## Padrões Comuns de Funções
Embora o CrewAI venha com as funções Owner e Member, a maioria das equipes se beneficia da criação de funções personalizadas. Aqui estão os padrões comuns:
### Função Developer
Uma função para membros da equipe que constroem e fazem deploy de automações, mas não gerenciam configurações da organização.
| Funcionalidade | Permissão |
| :------------------------ | :--------- |
| `usage_dashboards` | Read |
| `crews_dashboards` | Manage |
| `invitations` | Read |
| `training_ui` | Read |
| `tools` | Manage |
| `agents` | Manage |
| `environment_variables` | Manage |
| `llm_connections` | Manage |
| `default_settings` | No access |
| `organization_settings` | No access |
| `studio_projects` | Manage |
### Função Viewer / Stakeholder
Uma função para stakeholders não técnicos que precisam monitorar automações e visualizar resultados.
| Funcionalidade | Permissão |
| :------------------------ | :--------- |
| `usage_dashboards` | Read |
| `crews_dashboards` | Read |
| `invitations` | No access |
| `training_ui` | Read |
| `tools` | Read |
| `agents` | Read |
| `environment_variables` | No access |
| `llm_connections` | No access |
| `default_settings` | No access |
| `organization_settings` | No access |
| `studio_projects` | No access |
### Função Ops / Platform Admin
Uma função para operadores de plataforma que gerenciam configurações de infraestrutura, mas podem não construir agentes.
| Funcionalidade | Permissão |
| :------------------------ | :--------- |
| `usage_dashboards` | Manage |
| `crews_dashboards` | Manage |
| `invitations` | Manage |
| `training_ui` | Read |
| `tools` | Read |
| `agents` | Read |
| `environment_variables` | Manage |
| `llm_connections` | Manage |
| `default_settings` | Manage |
| `organization_settings` | Read |
| `studio_projects` | No access |
---
<Card title="Precisa de Ajuda?" icon="headset" href="mailto:support@crewai.com">
Fale com o nosso time para suporte em configuração e auditoria de RBAC.
Fale com o nosso time para suporte em configuração de RBAC.
</Card>

0
lib/crewai-a2a/README.md Normal file
View File

View File

@@ -0,0 +1,29 @@
[project]
name = "crewai-a2a"
dynamic = ["version"]
description = "Agent-to-Agent (A2A) protocol communication for CrewAI"
readme = "README.md"
authors = [
{ name = "Joao Moura", email = "joao@crewai.com" }
]
requires-python = ">=3.10, <3.14"
dependencies = [
"crewai==1.13.0a6",
"a2a-sdk~=0.3.10",
"httpx-auth~=0.23.1",
"httpx-sse~=0.4.0",
"aiocache[redis,memcached]~=0.12.3",
"pydantic~=2.11.9",
"pyjwt>=2.9.0,<3",
"httpx~=0.28.1",
]
[tool.uv.sources]
crewai = { workspace = true }
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.version]
path = "src/crewai_a2a/__init__.py"

View File

@@ -0,0 +1,12 @@
"""Agent-to-Agent (A2A) protocol communication for CrewAI."""
__version__ = "1.13.0a6"
from crewai_a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
__all__ = [
"A2AClientConfig",
"A2AConfig",
"A2AServerConfig",
]

View File

@@ -1,6 +1,6 @@
"""A2A authentication schemas."""
from crewai.a2a.auth.client_schemes import (
from crewai_a2a.auth.client_schemes import (
APIKeyAuth,
AuthScheme,
BearerTokenAuth,
@@ -11,7 +11,7 @@ from crewai.a2a.auth.client_schemes import (
OAuth2ClientCredentials,
TLSConfig,
)
from crewai.a2a.auth.server_schemes import (
from crewai_a2a.auth.server_schemes import (
AuthenticatedUser,
EnterpriseTokenAuth,
OIDCAuth,

View File

@@ -0,0 +1,71 @@
"""Deprecated: Authentication schemes for A2A protocol agents.
This module is deprecated. Import from crewai_a2a.auth instead:
- crewai_a2a.auth.ClientAuthScheme (replaces AuthScheme)
- crewai_a2a.auth.BearerTokenAuth
- crewai_a2a.auth.HTTPBasicAuth
- crewai_a2a.auth.HTTPDigestAuth
- crewai_a2a.auth.APIKeyAuth
- crewai_a2a.auth.OAuth2ClientCredentials
- crewai_a2a.auth.OAuth2AuthorizationCode
"""
from __future__ import annotations
from typing_extensions import deprecated
from crewai_a2a.auth.client_schemes import (
APIKeyAuth as _APIKeyAuth,
BearerTokenAuth as _BearerTokenAuth,
ClientAuthScheme as _ClientAuthScheme,
HTTPBasicAuth as _HTTPBasicAuth,
HTTPDigestAuth as _HTTPDigestAuth,
OAuth2AuthorizationCode as _OAuth2AuthorizationCode,
OAuth2ClientCredentials as _OAuth2ClientCredentials,
)
@deprecated("Use ClientAuthScheme from crewai_a2a.auth instead", category=FutureWarning)
class AuthScheme(_ClientAuthScheme):
"""Deprecated: Use ClientAuthScheme from crewai_a2a.auth instead."""
@deprecated("Import from crewai_a2a.auth instead", category=FutureWarning)
class BearerTokenAuth(_BearerTokenAuth):
"""Deprecated: Import from crewai_a2a.auth instead."""
@deprecated("Import from crewai_a2a.auth instead", category=FutureWarning)
class HTTPBasicAuth(_HTTPBasicAuth):
"""Deprecated: Import from crewai_a2a.auth instead."""
@deprecated("Import from crewai_a2a.auth instead", category=FutureWarning)
class HTTPDigestAuth(_HTTPDigestAuth):
"""Deprecated: Import from crewai_a2a.auth instead."""
@deprecated("Import from crewai_a2a.auth instead", category=FutureWarning)
class APIKeyAuth(_APIKeyAuth):
"""Deprecated: Import from crewai_a2a.auth instead."""
@deprecated("Import from crewai_a2a.auth instead", category=FutureWarning)
class OAuth2ClientCredentials(_OAuth2ClientCredentials):
"""Deprecated: Import from crewai_a2a.auth instead."""
@deprecated("Import from crewai_a2a.auth instead", category=FutureWarning)
class OAuth2AuthorizationCode(_OAuth2AuthorizationCode):
"""Deprecated: Import from crewai_a2a.auth instead."""
__all__ = [
"APIKeyAuth",
"AuthScheme",
"BearerTokenAuth",
"HTTPBasicAuth",
"HTTPDigestAuth",
"OAuth2AuthorizationCode",
"OAuth2ClientCredentials",
]

View File

@@ -42,7 +42,10 @@ logger = logging.getLogger(__name__)
try:
from fastapi import HTTPException, status as http_status
from fastapi import ( # type: ignore[import-not-found]
HTTPException,
status as http_status,
)
HTTP_401_UNAUTHORIZED = http_status.HTTP_401_UNAUTHORIZED
HTTP_500_INTERNAL_SERVER_ERROR = http_status.HTTP_500_INTERNAL_SERVER_ERROR

View File

@@ -20,7 +20,7 @@ from a2a.types import (
)
from httpx import AsyncClient, Response
from crewai.a2a.auth.client_schemes import (
from crewai_a2a.auth.client_schemes import (
APIKeyAuth,
BearerTokenAuth,
ClientAuthScheme,

View File

@@ -20,10 +20,10 @@ from pydantic import (
)
from typing_extensions import Self, deprecated
from crewai.a2a.auth.client_schemes import ClientAuthScheme
from crewai.a2a.auth.server_schemes import ServerAuthScheme
from crewai.a2a.extensions.base import ValidatedA2AExtension
from crewai.a2a.types import ProtocolVersion, TransportType, Url
from crewai_a2a.auth.client_schemes import ClientAuthScheme
from crewai_a2a.auth.server_schemes import ServerAuthScheme
from crewai_a2a.extensions.base import ValidatedA2AExtension
from crewai_a2a.types import ProtocolVersion, TransportType, Url
try:
@@ -36,8 +36,8 @@ try:
SecurityScheme,
)
from crewai.a2a.extensions.server import ServerExtension
from crewai.a2a.updates import UpdateConfig
from crewai_a2a.extensions.server import ServerExtension
from crewai_a2a.updates import UpdateConfig
except ImportError:
UpdateConfig: Any = Any # type: ignore[no-redef]
AgentCapabilities: Any = Any # type: ignore[no-redef]
@@ -50,7 +50,7 @@ except ImportError:
def _get_default_update_config() -> UpdateConfig:
from crewai.a2a.updates import StreamingConfig
from crewai_a2a.updates import StreamingConfig
return StreamingConfig()
@@ -360,8 +360,8 @@ class ClientTransportConfig(BaseModel):
@deprecated(
"""
`crewai.a2a.config.A2AConfig` is deprecated and will be removed in v2.0.0,
use `crewai.a2a.config.A2AClientConfig` or `crewai.a2a.config.A2AServerConfig` instead.
`crewai_a2a.config.A2AConfig` is deprecated and will be removed in v2.0.0,
use `crewai_a2a.config.A2AClientConfig` or `crewai_a2a.config.A2AServerConfig` instead.
""",
category=FutureWarning,
)

View File

@@ -13,13 +13,13 @@ via the X-A2A-Extensions header.
See: https://a2a-protocol.org/latest/topics/extensions/
"""
from crewai.a2a.extensions.base import (
from crewai_a2a.extensions.base import (
A2AExtension,
ConversationState,
ExtensionRegistry,
ValidatedA2AExtension,
)
from crewai.a2a.extensions.server import (
from crewai_a2a.extensions.server import (
ExtensionContext,
ServerExtension,
ServerExtensionRegistry,

View File

@@ -19,7 +19,6 @@ from pydantic import BeforeValidator
if TYPE_CHECKING:
from a2a.types import Message
from crewai.agent.core import Agent

View File

@@ -18,8 +18,8 @@ from a2a.extensions.common import (
)
from a2a.types import AgentCard, AgentExtension
from crewai.a2a.config import A2AClientConfig, A2AConfig
from crewai.a2a.extensions.base import ExtensionRegistry
from crewai_a2a.config import A2AClientConfig, A2AConfig
from crewai_a2a.extensions.base import ExtensionRegistry
def get_extensions_from_config(

View File

@@ -18,13 +18,12 @@ from a2a.types import (
TaskStatusUpdateEvent,
TextPart,
)
from typing_extensions import NotRequired
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
A2AConnectionErrorEvent,
A2AResponseReceivedEvent,
)
from typing_extensions import NotRequired
if TYPE_CHECKING:

View File

@@ -16,7 +16,7 @@ from typing_extensions import NotRequired
try:
from crewai.a2a.updates import (
from crewai_a2a.updates import (
PollingConfig,
PollingHandler,
PushNotificationConfig,

View File

@@ -1,6 +1,6 @@
"""A2A update mechanism configuration types."""
from crewai.a2a.updates.base import (
from crewai_a2a.updates.base import (
BaseHandlerKwargs,
PollingHandlerKwargs,
PushNotificationHandlerKwargs,
@@ -8,12 +8,12 @@ from crewai.a2a.updates.base import (
StreamingHandlerKwargs,
UpdateHandler,
)
from crewai.a2a.updates.polling.config import PollingConfig
from crewai.a2a.updates.polling.handler import PollingHandler
from crewai.a2a.updates.push_notifications.config import PushNotificationConfig
from crewai.a2a.updates.push_notifications.handler import PushNotificationHandler
from crewai.a2a.updates.streaming.config import StreamingConfig
from crewai.a2a.updates.streaming.handler import StreamingHandler
from crewai_a2a.updates.polling.config import PollingConfig
from crewai_a2a.updates.polling.handler import PollingHandler
from crewai_a2a.updates.push_notifications.config import PushNotificationConfig
from crewai_a2a.updates.push_notifications.handler import PushNotificationHandler
from crewai_a2a.updates.streaming.config import StreamingConfig
from crewai_a2a.updates.streaming.handler import StreamingHandler
UpdateConfig = PollingConfig | StreamingConfig | PushNotificationConfig

View File

@@ -28,8 +28,8 @@ if TYPE_CHECKING:
from a2a.client import Client
from a2a.types import AgentCard, Message, Task
from crewai.a2a.task_helpers import TaskStateResult
from crewai.a2a.updates.push_notifications.config import PushNotificationConfig
from crewai_a2a.task_helpers import TaskStateResult
from crewai_a2a.updates.push_notifications.config import PushNotificationConfig
class BaseHandlerKwargs(TypedDict, total=False):

View File

@@ -18,17 +18,6 @@ from a2a.types import (
TaskState,
TextPart,
)
from typing_extensions import Unpack
from crewai.a2a.errors import A2APollingTimeoutError
from crewai.a2a.task_helpers import (
ACTIONABLE_STATES,
TERMINAL_STATES,
TaskStateResult,
process_task_state,
send_message_and_get_task_id,
)
from crewai.a2a.updates.base import PollingHandlerKwargs
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
A2AConnectionErrorEvent,
@@ -36,6 +25,17 @@ from crewai.events.types.a2a_events import (
A2APollingStatusEvent,
A2AResponseReceivedEvent,
)
from typing_extensions import Unpack
from crewai_a2a.errors import A2APollingTimeoutError
from crewai_a2a.task_helpers import (
ACTIONABLE_STATES,
TERMINAL_STATES,
TaskStateResult,
process_task_state,
send_message_and_get_task_id,
)
from crewai_a2a.updates.base import PollingHandlerKwargs
if TYPE_CHECKING:

View File

@@ -7,8 +7,8 @@ from typing import Annotated
from a2a.types import PushNotificationAuthenticationInfo
from pydantic import AnyHttpUrl, BaseModel, BeforeValidator, Field
from crewai.a2a.updates.base import PushNotificationResultStore
from crewai.a2a.updates.push_notifications.signature import WebhookSignatureConfig
from crewai_a2a.updates.base import PushNotificationResultStore
from crewai_a2a.updates.push_notifications.signature import WebhookSignatureConfig
def _coerce_signature(

View File

@@ -16,19 +16,6 @@ from a2a.types import (
TaskState,
TextPart,
)
from typing_extensions import Unpack
from crewai.a2a.task_helpers import (
TaskStateResult,
process_task_state,
send_message_and_get_task_id,
)
from crewai.a2a.updates.base import (
CommonParams,
PushNotificationHandlerKwargs,
PushNotificationResultStore,
extract_common_params,
)
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
A2AConnectionErrorEvent,
@@ -36,6 +23,19 @@ from crewai.events.types.a2a_events import (
A2APushNotificationTimeoutEvent,
A2AResponseReceivedEvent,
)
from typing_extensions import Unpack
from crewai_a2a.task_helpers import (
TaskStateResult,
process_task_state,
send_message_and_get_task_id,
)
from crewai_a2a.updates.base import (
CommonParams,
PushNotificationHandlerKwargs,
PushNotificationResultStore,
extract_common_params,
)
if TYPE_CHECKING:

View File

@@ -22,18 +22,6 @@ from a2a.types import (
TaskStatusUpdateEvent,
TextPart,
)
from typing_extensions import Unpack
from crewai.a2a.task_helpers import (
ACTIONABLE_STATES,
TERMINAL_STATES,
TaskStateResult,
process_task_state,
)
from crewai.a2a.updates.base import StreamingHandlerKwargs, extract_common_params
from crewai.a2a.updates.streaming.params import (
process_status_update,
)
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
A2AArtifactReceivedEvent,
@@ -42,6 +30,18 @@ from crewai.events.types.a2a_events import (
A2AStreamingChunkEvent,
A2AStreamingStartedEvent,
)
from typing_extensions import Unpack
from crewai_a2a.task_helpers import (
ACTIONABLE_STATES,
TERMINAL_STATES,
TaskStateResult,
process_task_state,
)
from crewai_a2a.updates.base import StreamingHandlerKwargs, extract_common_params
from crewai_a2a.updates.streaming.params import (
process_status_update,
)
logger = logging.getLogger(__name__)

View File

@@ -16,15 +16,6 @@ from a2a.client.errors import A2AClientHTTPError
from a2a.types import AgentCapabilities, AgentCard, AgentSkill
from aiocache import cached # type: ignore[import-untyped]
from aiocache.serializers import PickleSerializer # type: ignore[import-untyped]
import httpx
from crewai.a2a.auth.client_schemes import APIKeyAuth, HTTPDigestAuth
from crewai.a2a.auth.utils import (
_auth_store,
configure_auth_client,
retry_on_401,
)
from crewai.a2a.config import A2AServerConfig
from crewai.crew import Crew
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
@@ -32,13 +23,23 @@ from crewai.events.types.a2a_events import (
A2AAuthenticationFailedEvent,
A2AConnectionErrorEvent,
)
import httpx
from crewai_a2a.auth.client_schemes import APIKeyAuth, HTTPDigestAuth
from crewai_a2a.auth.utils import (
_auth_store,
configure_auth_client,
retry_on_401,
)
from crewai_a2a.config import A2AServerConfig
if TYPE_CHECKING:
from crewai.a2a.auth.client_schemes import ClientAuthScheme
from crewai.agent import Agent
from crewai.task import Task
from crewai_a2a.auth.client_schemes import ClientAuthScheme
def _get_tls_verify(auth: ClientAuthScheme | None) -> ssl.SSLContext | bool | str:
"""Get TLS verify parameter from auth scheme.
@@ -495,7 +496,7 @@ def _agent_to_agent_card(agent: Agent, url: str) -> AgentCard:
Returns:
AgentCard describing the agent's capabilities.
"""
from crewai.a2a.utils.agent_card_signing import sign_agent_card
from crewai_a2a.utils.agent_card_signing import sign_agent_card
server_config = _get_server_config(agent) or A2AServerConfig()
@@ -529,7 +530,7 @@ def _agent_to_agent_card(agent: Agent, url: str) -> AgentCard:
capabilities = server_config.capabilities
if server_config.server_extensions:
from crewai.a2a.extensions.server import ServerExtensionRegistry
from crewai_a2a.extensions.server import ServerExtensionRegistry
registry = ServerExtensionRegistry(server_config.server_extensions)
ext_list = registry.get_agent_extensions()

View File

@@ -5,7 +5,7 @@ JSON Web Signatures (JWS) as per RFC 7515. Signed agent cards allow clients
to verify the authenticity and integrity of agent card information.
Example:
>>> from crewai.a2a.utils.agent_card_signing import sign_agent_card
>>> from crewai_a2a.utils.agent_card_signing import sign_agent_card
>>> signature = sign_agent_card(agent_card, private_key_pem, key_id="key-1")
>>> card_with_sig = card.model_copy(update={"signatures": [signature]})
"""

View File

@@ -10,7 +10,6 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING, Annotated, Final, Literal, cast
from a2a.types import Part
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import A2AContentTypeNegotiatedEvent

View File

@@ -23,48 +23,6 @@ from a2a.types import (
Role,
TextPart,
)
import httpx
from pydantic import BaseModel
from crewai.a2a.auth.client_schemes import APIKeyAuth, HTTPDigestAuth
from crewai.a2a.auth.utils import (
_auth_store,
configure_auth_client,
validate_auth_against_agent_card,
)
from crewai.a2a.config import ClientTransportConfig, GRPCClientConfig
from crewai.a2a.extensions.registry import (
ExtensionsMiddleware,
validate_required_extensions,
)
from crewai.a2a.task_helpers import TaskStateResult
from crewai.a2a.types import (
HANDLER_REGISTRY,
HandlerType,
PartsDict,
PartsMetadataDict,
TransportType,
)
from crewai.a2a.updates import (
PollingConfig,
PushNotificationConfig,
StreamingHandler,
UpdateConfig,
)
from crewai.a2a.utils.agent_card import (
_afetch_agent_card_cached,
_get_tls_verify,
_prepare_auth_headers,
)
from crewai.a2a.utils.content_type import (
DEFAULT_CLIENT_OUTPUT_MODES,
negotiate_content_types,
)
from crewai.a2a.utils.transport import (
NegotiatedTransport,
TransportNegotiationError,
negotiate_transport,
)
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
A2AConversationStartedEvent,
@@ -72,6 +30,48 @@ from crewai.events.types.a2a_events import (
A2ADelegationStartedEvent,
A2AMessageSentEvent,
)
import httpx
from pydantic import BaseModel
from crewai_a2a.auth.client_schemes import APIKeyAuth, HTTPDigestAuth
from crewai_a2a.auth.utils import (
_auth_store,
configure_auth_client,
validate_auth_against_agent_card,
)
from crewai_a2a.config import ClientTransportConfig, GRPCClientConfig
from crewai_a2a.extensions.registry import (
ExtensionsMiddleware,
validate_required_extensions,
)
from crewai_a2a.task_helpers import TaskStateResult
from crewai_a2a.types import (
HANDLER_REGISTRY,
HandlerType,
PartsDict,
PartsMetadataDict,
TransportType,
)
from crewai_a2a.updates import (
PollingConfig,
PushNotificationConfig,
StreamingHandler,
UpdateConfig,
)
from crewai_a2a.utils.agent_card import (
_afetch_agent_card_cached,
_get_tls_verify,
_prepare_auth_headers,
)
from crewai_a2a.utils.content_type import (
DEFAULT_CLIENT_OUTPUT_MODES,
negotiate_content_types,
)
from crewai_a2a.utils.transport import (
NegotiatedTransport,
TransportNegotiationError,
negotiate_transport,
)
logger = logging.getLogger(__name__)
@@ -80,7 +80,7 @@ logger = logging.getLogger(__name__)
if TYPE_CHECKING:
from a2a.types import Message
from crewai.a2a.auth.client_schemes import ClientAuthScheme
from crewai_a2a.auth.client_schemes import ClientAuthScheme
_DEFAULT_TRANSPORT: Final[TransportType] = "JSONRPC"
@@ -771,7 +771,7 @@ def _create_grpc_channel_factory(
auth_metadata: list[tuple[str, str]] = []
if auth is not None:
from crewai.a2a.auth.client_schemes import (
from crewai_a2a.auth.client_schemes import (
APIKeyAuth,
BearerTokenAuth,
HTTPBasicAuth,

View File

@@ -103,7 +103,7 @@ class LogContext:
_log_context.reset(self._token)
def configure_json_logging(logger_name: str = "crewai.a2a") -> None:
def configure_json_logging(logger_name: str = "crewai_a2a") -> None:
"""Configure JSON logging for the A2A module.
Args:

View File

@@ -4,10 +4,10 @@ from __future__ import annotations
from typing import TypeAlias
from crewai.types.utils import create_literals_from_strings
from pydantic import BaseModel, Field, create_model
from crewai.a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
from crewai.types.utils import create_literals_from_strings
from crewai_a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
A2AConfigTypes: TypeAlias = A2AConfig | A2AServerConfig | A2AClientConfig

View File

@@ -37,10 +37,6 @@ from a2a.utils import (
)
from a2a.utils.errors import ServerError
from aiocache import SimpleMemoryCache, caches # type: ignore[import-untyped]
from pydantic import BaseModel
from crewai.a2a.utils.agent_card import _get_server_config
from crewai.a2a.utils.content_type import validate_message_parts
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
A2AServerTaskCanceledEvent,
@@ -50,12 +46,17 @@ from crewai.events.types.a2a_events import (
)
from crewai.task import Task
from crewai.utilities.pydantic_schema_utils import create_model_from_schema
from pydantic import BaseModel
from crewai_a2a.utils.agent_card import _get_server_config
from crewai_a2a.utils.content_type import validate_message_parts
if TYPE_CHECKING:
from crewai.a2a.extensions.server import ExtensionContext, ServerExtensionRegistry
from crewai.agent import Agent
from crewai_a2a.extensions.server import ExtensionContext, ServerExtensionRegistry
logger = logging.getLogger(__name__)

View File

@@ -11,7 +11,6 @@ from dataclasses import dataclass
from typing import Final, Literal
from a2a.types import AgentCard, AgentInterface
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import A2ATransportNegotiatedEvent

View File

@@ -15,33 +15,6 @@ from types import MethodType
from typing import TYPE_CHECKING, Any, NamedTuple
from a2a.types import Role, TaskState
from pydantic import BaseModel, ValidationError
from crewai.a2a.config import A2AClientConfig, A2AConfig
from crewai.a2a.extensions.base import (
A2AExtension,
ConversationState,
ExtensionRegistry,
)
from crewai.a2a.task_helpers import TaskStateResult
from crewai.a2a.templates import (
AVAILABLE_AGENTS_TEMPLATE,
CONVERSATION_TURN_INFO_TEMPLATE,
PREVIOUS_A2A_CONVERSATION_TEMPLATE,
REMOTE_AGENT_RESPONSE_NOTICE,
UNAVAILABLE_AGENTS_NOTICE_TEMPLATE,
)
from crewai.a2a.types import AgentResponseProtocol
from crewai.a2a.utils.agent_card import (
afetch_agent_card,
fetch_agent_card,
inject_a2a_server_methods,
)
from crewai.a2a.utils.delegation import (
aexecute_a2a_delegation,
execute_a2a_delegation,
)
from crewai.a2a.utils.response_model import get_a2a_agents_and_response_model
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
A2AConversationCompletedEvent,
@@ -49,11 +22,37 @@ from crewai.events.types.a2a_events import (
)
from crewai.lite_agent_output import LiteAgentOutput
from crewai.task import Task
from pydantic import BaseModel, ValidationError
from crewai_a2a.config import A2AClientConfig, A2AConfig
from crewai_a2a.extensions.base import (
A2AExtension,
ConversationState,
ExtensionRegistry,
)
from crewai_a2a.task_helpers import TaskStateResult
from crewai_a2a.templates import (
AVAILABLE_AGENTS_TEMPLATE,
CONVERSATION_TURN_INFO_TEMPLATE,
PREVIOUS_A2A_CONVERSATION_TEMPLATE,
REMOTE_AGENT_RESPONSE_NOTICE,
UNAVAILABLE_AGENTS_NOTICE_TEMPLATE,
)
from crewai_a2a.types import AgentResponseProtocol
from crewai_a2a.utils.agent_card import (
afetch_agent_card,
fetch_agent_card,
inject_a2a_server_methods,
)
from crewai_a2a.utils.delegation import (
aexecute_a2a_delegation,
execute_a2a_delegation,
)
from crewai_a2a.utils.response_model import get_a2a_agents_and_response_model
if TYPE_CHECKING:
from a2a.types import AgentCard, Message
from crewai.agent.core import Agent
from crewai.tools.base_tool import BaseTool

View File

@@ -3,14 +3,12 @@ from __future__ import annotations
import os
import uuid
import pytest
import pytest_asyncio
from a2a.client import ClientFactory
from a2a.types import AgentCard, Message, Part, Role, TaskState, TextPart
from crewai.a2a.updates.polling.handler import PollingHandler
from crewai.a2a.updates.streaming.handler import StreamingHandler
from crewai_a2a.updates.polling.handler import PollingHandler
from crewai_a2a.updates.streaming.handler import StreamingHandler
import pytest
import pytest_asyncio
A2A_TEST_ENDPOINT = os.getenv("A2A_TEST_ENDPOINT", "http://localhost:9999")
@@ -162,7 +160,7 @@ class TestA2APushNotificationHandler:
)
@pytest.fixture
def mock_task(self) -> "Task":
def mock_task(self) -> Task:
"""Create a minimal valid task for testing."""
from a2a.types import Task, TaskStatus
@@ -182,11 +180,12 @@ class TestA2APushNotificationHandler:
from unittest.mock import AsyncMock, MagicMock
from a2a.types import Task, TaskStatus
from crewai_a2a.updates.push_notifications.config import PushNotificationConfig
from crewai_a2a.updates.push_notifications.handler import (
PushNotificationHandler,
)
from pydantic import AnyHttpUrl
from crewai.a2a.updates.push_notifications.config import PushNotificationConfig
from crewai.a2a.updates.push_notifications.handler import PushNotificationHandler
completed_task = Task(
id="task-123",
context_id="ctx-123",
@@ -246,11 +245,12 @@ class TestA2APushNotificationHandler:
from unittest.mock import AsyncMock, MagicMock
from a2a.types import Task, TaskStatus
from crewai_a2a.updates.push_notifications.config import PushNotificationConfig
from crewai_a2a.updates.push_notifications.handler import (
PushNotificationHandler,
)
from pydantic import AnyHttpUrl
from crewai.a2a.updates.push_notifications.config import PushNotificationConfig
from crewai.a2a.updates.push_notifications.handler import PushNotificationHandler
mock_store = MagicMock()
mock_store.wait_for_result = AsyncMock(return_value=None)
@@ -303,7 +303,9 @@ class TestA2APushNotificationHandler:
"""Test that push handler fails gracefully without config."""
from unittest.mock import MagicMock
from crewai.a2a.updates.push_notifications.handler import PushNotificationHandler
from crewai_a2a.updates.push_notifications.handler import (
PushNotificationHandler,
)
mock_client = MagicMock()

View File

@@ -2,9 +2,9 @@
from unittest.mock import MagicMock, patch
from crewai_a2a.config import A2AConfig
import pytest
from crewai.a2a.config import A2AConfig
try:
from a2a.types import Message, Role
@@ -27,9 +27,8 @@ def _create_mock_agent_card(name: str = "Test", url: str = "http://test-endpoint
@pytest.mark.skipif(not A2A_SDK_INSTALLED, reason="Requires a2a-sdk to be installed")
def test_trust_remote_completion_status_true_returns_directly():
"""When trust_remote_completion_status=True and A2A returns completed, return result directly."""
from crewai.a2a.wrapper import _delegate_to_a2a
from crewai.a2a.types import AgentResponseProtocol
from crewai import Agent, Task
from crewai_a2a.wrapper import _delegate_to_a2a
a2a_config = A2AConfig(
endpoint="http://test-endpoint.com",
@@ -51,8 +50,8 @@ def test_trust_remote_completion_status_true_returns_directly():
a2a_ids = ["http://test-endpoint.com/"]
with (
patch("crewai.a2a.wrapper.execute_a2a_delegation") as mock_execute,
patch("crewai.a2a.wrapper._fetch_agent_cards_concurrently") as mock_fetch,
patch("crewai_a2a.wrapper.execute_a2a_delegation") as mock_execute,
patch("crewai_a2a.wrapper._fetch_agent_cards_concurrently") as mock_fetch,
):
mock_card = _create_mock_agent_card()
mock_fetch.return_value = ({"http://test-endpoint.com/": mock_card}, {})
@@ -83,8 +82,8 @@ def test_trust_remote_completion_status_true_returns_directly():
@pytest.mark.skipif(not A2A_SDK_INSTALLED, reason="Requires a2a-sdk to be installed")
def test_trust_remote_completion_status_false_continues_conversation():
"""When trust_remote_completion_status=False and A2A returns completed, ask server agent."""
from crewai.a2a.wrapper import _delegate_to_a2a
from crewai import Agent, Task
from crewai_a2a.wrapper import _delegate_to_a2a
a2a_config = A2AConfig(
endpoint="http://test-endpoint.com",
@@ -116,8 +115,8 @@ def test_trust_remote_completion_status_false_continues_conversation():
return "unexpected"
with (
patch("crewai.a2a.wrapper.execute_a2a_delegation") as mock_execute,
patch("crewai.a2a.wrapper._fetch_agent_cards_concurrently") as mock_fetch,
patch("crewai_a2a.wrapper.execute_a2a_delegation") as mock_execute,
patch("crewai_a2a.wrapper._fetch_agent_cards_concurrently") as mock_fetch,
):
mock_card = _create_mock_agent_card()
mock_fetch.return_value = ({"http://test-endpoint.com/": mock_card}, {})
@@ -152,4 +151,4 @@ def test_default_trust_remote_completion_status_is_false():
endpoint="http://test-endpoint.com",
)
assert a2a_config.trust_remote_completion_status is False
assert a2a_config.trust_remote_completion_status is False

View File

@@ -4,10 +4,9 @@ from __future__ import annotations
import os
import pytest
from crewai import Agent
from crewai.a2a.config import A2AClientConfig
from crewai_a2a.config import A2AClientConfig
import pytest
A2A_TEST_ENDPOINT = os.getenv(
@@ -50,9 +49,7 @@ class TestAgentA2AKickoff:
@pytest.mark.skip(reason="VCR cassette matching issue with agent card caching")
@pytest.mark.vcr()
def test_agent_kickoff_with_calculator_skill(
self, researcher_agent: Agent
) -> None:
def test_agent_kickoff_with_calculator_skill(self, researcher_agent: Agent) -> None:
"""Test that agent can delegate calculation to A2A server."""
result = researcher_agent.kickoff(
"Ask the remote A2A agent to calculate 25 times 17."
@@ -149,9 +146,7 @@ class TestAgentA2AKickoff:
@pytest.mark.skip(reason="VCR cassette matching issue with agent card caching")
@pytest.mark.vcr()
def test_agent_kickoff_with_list_messages(
self, researcher_agent: Agent
) -> None:
def test_agent_kickoff_with_list_messages(self, researcher_agent: Agent) -> None:
"""Test that agent.kickoff() works with list of messages."""
messages = [
{

View File

@@ -1,14 +1,12 @@
"""Test A2A wrapper is only applied when a2a is passed to Agent."""
from unittest.mock import patch
from crewai import Agent
from crewai_a2a.config import A2AConfig
import pytest
from crewai import Agent
from crewai.a2a.config import A2AConfig
try:
import a2a # noqa: F401
import a2a
A2A_SDK_INSTALLED = True
except ImportError:
@@ -106,6 +104,9 @@ def test_wrapper_is_applied_differently_per_instance():
a2a=a2a_config,
)
assert agent_without_a2a.execute_task.__func__ is not agent_with_a2a.execute_task.__func__
assert (
agent_without_a2a.execute_task.__func__
is not agent_with_a2a.execute_task.__func__
)
assert not hasattr(agent_without_a2a.execute_task, "__wrapped__")
assert hasattr(agent_with_a2a.execute_task, "__wrapped__")

View File

@@ -3,10 +3,9 @@
from __future__ import annotations
from a2a.types import AgentCard, AgentSkill
from crewai import Agent
from crewai.a2a.config import A2AClientConfig, A2AServerConfig
from crewai.a2a.utils.agent_card import inject_a2a_server_methods
from crewai_a2a.config import A2AClientConfig, A2AServerConfig
from crewai_a2a.utils.agent_card import inject_a2a_server_methods
class TestInjectA2AServerMethods:

View File

@@ -6,13 +6,12 @@ import asyncio
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
import pytest_asyncio
from a2a.server.agent_execution import RequestContext
from a2a.server.events import EventQueue
from a2a.types import Message, Task as A2ATask, TaskState, TaskStatus
from crewai.a2a.utils.task import cancel, cancellable, execute
from crewai_a2a.utils.task import cancel, cancellable, execute
import pytest
import pytest_asyncio
@pytest.fixture
@@ -85,8 +84,11 @@ class TestCancellableDecorator:
assert call_count == 1
@pytest.mark.asyncio
async def test_executes_function_with_context(self, mock_context: MagicMock) -> None:
async def test_executes_function_with_context(
self, mock_context: MagicMock
) -> None:
"""Function executes normally with RequestContext when not cancelled."""
@cancellable
async def my_func(context: RequestContext) -> str:
await asyncio.sleep(0.01)
@@ -134,6 +136,7 @@ class TestCancellableDecorator:
@pytest.mark.asyncio
async def test_extracts_context_from_kwargs(self, mock_context: MagicMock) -> None:
"""Context can be passed as keyword argument."""
@cancellable
async def my_func(value: int, context: RequestContext | None = None) -> int:
return value + 1
@@ -156,8 +159,8 @@ class TestExecute:
) -> None:
"""Execute completes successfully and enqueues completed task."""
with (
patch("crewai.a2a.utils.task.Task", return_value=mock_task),
patch("crewai.a2a.utils.task.crewai_event_bus") as mock_bus,
patch("crewai_a2a.utils.task.Task", return_value=mock_task),
patch("crewai_a2a.utils.task.crewai_event_bus") as mock_bus,
):
await execute(mock_agent, mock_context, mock_event_queue)
@@ -175,8 +178,8 @@ class TestExecute:
) -> None:
"""Execute emits A2AServerTaskStartedEvent."""
with (
patch("crewai.a2a.utils.task.Task", return_value=mock_task),
patch("crewai.a2a.utils.task.crewai_event_bus") as mock_bus,
patch("crewai_a2a.utils.task.Task", return_value=mock_task),
patch("crewai_a2a.utils.task.crewai_event_bus") as mock_bus,
):
await execute(mock_agent, mock_context, mock_event_queue)
@@ -197,8 +200,8 @@ class TestExecute:
) -> None:
"""Execute emits A2AServerTaskCompletedEvent on success."""
with (
patch("crewai.a2a.utils.task.Task", return_value=mock_task),
patch("crewai.a2a.utils.task.crewai_event_bus") as mock_bus,
patch("crewai_a2a.utils.task.Task", return_value=mock_task),
patch("crewai_a2a.utils.task.crewai_event_bus") as mock_bus,
):
await execute(mock_agent, mock_context, mock_event_queue)
@@ -221,8 +224,8 @@ class TestExecute:
mock_agent.aexecute_task = AsyncMock(side_effect=ValueError("Test error"))
with (
patch("crewai.a2a.utils.task.Task", return_value=mock_task),
patch("crewai.a2a.utils.task.crewai_event_bus") as mock_bus,
patch("crewai_a2a.utils.task.Task", return_value=mock_task),
patch("crewai_a2a.utils.task.crewai_event_bus") as mock_bus,
):
with pytest.raises(Exception):
await execute(mock_agent, mock_context, mock_event_queue)
@@ -245,8 +248,8 @@ class TestExecute:
mock_agent.aexecute_task = AsyncMock(side_effect=asyncio.CancelledError())
with (
patch("crewai.a2a.utils.task.Task", return_value=mock_task),
patch("crewai.a2a.utils.task.crewai_event_bus") as mock_bus,
patch("crewai_a2a.utils.task.Task", return_value=mock_task),
patch("crewai_a2a.utils.task.crewai_event_bus") as mock_bus,
):
with pytest.raises(asyncio.CancelledError):
await execute(mock_agent, mock_context, mock_event_queue)
@@ -354,6 +357,7 @@ class TestExecuteAndCancelIntegration:
mock_task: MagicMock,
) -> None:
"""Calling cancel stops a running execute."""
async def slow_task(**kwargs: Any) -> str:
await asyncio.sleep(2.0)
return "should not complete"
@@ -361,8 +365,8 @@ class TestExecuteAndCancelIntegration:
mock_agent.aexecute_task = slow_task
with (
patch("crewai.a2a.utils.task.Task", return_value=mock_task),
patch("crewai.a2a.utils.task.crewai_event_bus"),
patch("crewai_a2a.utils.task.Task", return_value=mock_task),
patch("crewai_a2a.utils.task.crewai_event_bus"),
):
execute_task = asyncio.create_task(
execute(mock_agent, mock_context, mock_event_queue)
@@ -372,4 +376,4 @@ class TestExecuteAndCancelIntegration:
await cancel(mock_context, mock_event_queue)
with pytest.raises(asyncio.CancelledError):
await execute_task
await execute_task

View File

@@ -152,4 +152,4 @@ __all__ = [
"wrap_file_source",
]
__version__ = "1.13.0a5"
__version__ = "1.13.0a6"

View File

@@ -11,7 +11,7 @@ dependencies = [
"pytube~=15.0.0",
"requests~=2.32.5",
"docker~=7.1.0",
"crewai==1.13.0a5",
"crewai==1.13.0a6",
"tiktoken~=0.8.0",
"beautifulsoup4~=4.13.4",
"python-docx~=1.2.0",

View File

@@ -309,4 +309,4 @@ __all__ = [
"ZapierActionTools",
]
__version__ = "1.13.0a5"
__version__ = "1.13.0a6"

View File

@@ -54,7 +54,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
[project.optional-dependencies]
tools = [
"crewai-tools==1.13.0a5",
"crewai-tools==1.13.0a6",
]
embeddings = [
"tiktoken~=0.8.0"
@@ -98,10 +98,7 @@ anthropic = [
"anthropic~=0.73.0",
]
a2a = [
"a2a-sdk~=0.3.10",
"httpx-auth~=0.23.1",
"httpx-sse~=0.4.0",
"aiocache[redis,memcached]~=0.12.3",
"crewai-a2a==1.13.0a6",
]
file-processing = [
"crewai-files",
@@ -136,6 +133,7 @@ torchvision = [
{ index = "pytorch", marker = "python_version < '3.13'" },
]
crewai-files = { workspace = true }
crewai-a2a = { workspace = true }
[build-system]

View File

@@ -4,7 +4,7 @@ from typing import Any
import urllib.request
import warnings
from pydantic import PydanticUserError
from pydantic import PydanticUndefinedAnnotation, PydanticUserError
from crewai.agent.core import Agent
from crewai.agent.planning_config import PlanningConfig
@@ -44,7 +44,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
_suppress_pydantic_deprecation_warnings()
__version__ = "1.13.0a5"
__version__ = "1.13.0a6"
_telemetry_submitted = False
@@ -119,11 +119,11 @@ try:
"ToolResult": _ToolResult,
},
)
except (ImportError, PydanticUserError):
except (ImportError, PydanticUserError, PydanticUndefinedAnnotation):
import logging as _logging
_logging.getLogger(__name__).warning(
"AgentExecutor.model_rebuild() failed; forward refs may be unresolved.",
_logging.getLogger(__name__).debug(
"AgentExecutor.model_rebuild() deferred; forward refs may be unresolved.",
exc_info=True,
)

View File

@@ -1,10 +0,0 @@
"""Agent-to-Agent (A2A) protocol communication module for CrewAI."""
from crewai.a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
__all__ = [
"A2AClientConfig",
"A2AConfig",
"A2AServerConfig",
]

View File

@@ -1,71 +0,0 @@
"""Deprecated: Authentication schemes for A2A protocol agents.
This module is deprecated. Import from crewai.a2a.auth instead:
- crewai.a2a.auth.ClientAuthScheme (replaces AuthScheme)
- crewai.a2a.auth.BearerTokenAuth
- crewai.a2a.auth.HTTPBasicAuth
- crewai.a2a.auth.HTTPDigestAuth
- crewai.a2a.auth.APIKeyAuth
- crewai.a2a.auth.OAuth2ClientCredentials
- crewai.a2a.auth.OAuth2AuthorizationCode
"""
from __future__ import annotations
from typing_extensions import deprecated
from crewai.a2a.auth.client_schemes import (
APIKeyAuth as _APIKeyAuth,
BearerTokenAuth as _BearerTokenAuth,
ClientAuthScheme as _ClientAuthScheme,
HTTPBasicAuth as _HTTPBasicAuth,
HTTPDigestAuth as _HTTPDigestAuth,
OAuth2AuthorizationCode as _OAuth2AuthorizationCode,
OAuth2ClientCredentials as _OAuth2ClientCredentials,
)
@deprecated("Use ClientAuthScheme from crewai.a2a.auth instead", category=FutureWarning)
class AuthScheme(_ClientAuthScheme):
"""Deprecated: Use ClientAuthScheme from crewai.a2a.auth instead."""
@deprecated("Import from crewai.a2a.auth instead", category=FutureWarning)
class BearerTokenAuth(_BearerTokenAuth):
"""Deprecated: Import from crewai.a2a.auth instead."""
@deprecated("Import from crewai.a2a.auth instead", category=FutureWarning)
class HTTPBasicAuth(_HTTPBasicAuth):
"""Deprecated: Import from crewai.a2a.auth instead."""
@deprecated("Import from crewai.a2a.auth instead", category=FutureWarning)
class HTTPDigestAuth(_HTTPDigestAuth):
"""Deprecated: Import from crewai.a2a.auth instead."""
@deprecated("Import from crewai.a2a.auth instead", category=FutureWarning)
class APIKeyAuth(_APIKeyAuth):
"""Deprecated: Import from crewai.a2a.auth instead."""
@deprecated("Import from crewai.a2a.auth instead", category=FutureWarning)
class OAuth2ClientCredentials(_OAuth2ClientCredentials):
"""Deprecated: Import from crewai.a2a.auth instead."""
@deprecated("Import from crewai.a2a.auth instead", category=FutureWarning)
class OAuth2AuthorizationCode(_OAuth2AuthorizationCode):
"""Deprecated: Import from crewai.a2a.auth instead."""
__all__ = [
"APIKeyAuth",
"AuthScheme",
"BearerTokenAuth",
"HTTPBasicAuth",
"HTTPDigestAuth",
"OAuth2AuthorizationCode",
"OAuth2ClientCredentials",
]

View File

@@ -102,16 +102,16 @@ from crewai.utilities.training_handler import CrewTrainingHandler
try:
from crewai.a2a.types import AgentResponseProtocol
from crewai_a2a.types import AgentResponseProtocol
except ImportError:
AgentResponseProtocol = None # type: ignore[assignment, misc]
if TYPE_CHECKING:
from crewai_a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
from crewai_files import FileInput
from crewai_tools import CodeInterpreterTool
from crewai.a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
from crewai.agents.agent_builder.base_agent import PlatformAppOrAction
from crewai.task import Task
from crewai.tools.base_tool import BaseTool
@@ -1790,7 +1790,7 @@ class Agent(BaseAgent):
try:
from crewai.a2a.config import (
from crewai_a2a.config import (
A2AClientConfig as _A2AClientConfig,
A2AConfig as _A2AConfig,
A2AServerConfig as _A2AServerConfig,

View File

@@ -58,10 +58,10 @@ class AgentMeta(ModelMetaclass):
a2a_value = getattr(self, "a2a", None)
if a2a_value is not None:
from crewai.a2a.extensions.registry import (
from crewai_a2a.extensions.registry import (
create_extension_registry_from_config,
)
from crewai.a2a.wrapper import wrap_agent_with_a2a_instance
from crewai_a2a.wrapper import wrap_agent_with_a2a_instance
extension_registry = create_extension_registry_from_config(
a2a_value

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]==1.13.0a5"
"crewai[tools]==1.13.0a6"
]
[project.scripts]

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]==1.13.0a5"
"crewai[tools]==1.13.0a6"
]
[project.scripts]

View File

@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
readme = "README.md"
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]==1.13.0a5"
"crewai[tools]==1.13.0a6"
]
[tool.crewai]

View File

@@ -85,6 +85,8 @@ class CrewAIEventsBus:
_shutting_down: bool
_pending_futures: set[Future[Any]]
_futures_lock: threading.Lock
_executor_initialized: bool
_has_pending_events: bool
def __new__(cls) -> Self:
"""Create or return the singleton instance.
@@ -102,8 +104,9 @@ class CrewAIEventsBus:
def _initialize(self) -> None:
"""Initialize the event bus internal state.
Creates handler dictionaries and starts a dedicated background
event loop for async handler execution.
Creates handler dictionaries. The thread pool executor and event loop
are lazily initialized on first emit() to avoid overhead when events
are never emitted.
"""
self._shutting_down = False
self._rwlock = RWLock()
@@ -115,19 +118,37 @@ class CrewAIEventsBus:
type[BaseEvent], dict[Handler, list[Depends[Any]]]
] = {}
self._execution_plan_cache: dict[type[BaseEvent], ExecutionPlan] = {}
self._sync_executor = ThreadPoolExecutor(
max_workers=10,
thread_name_prefix="CrewAISyncHandler",
)
self._console = ConsoleFormatter()
# Lazy initialization flags - executor and loop created on first emit
self._executor_initialized = False
self._has_pending_events = False
self._loop = asyncio.new_event_loop()
self._loop_thread = threading.Thread(
target=self._run_loop,
name="CrewAIEventsLoop",
daemon=True,
)
self._loop_thread.start()
def _ensure_executor_initialized(self) -> None:
"""Lazily initialize the thread pool executor and event loop.
Called on first emit() to avoid startup overhead when events are never used.
Thread-safe via double-checked locking.
"""
if self._executor_initialized:
return
with self._instance_lock:
if self._executor_initialized:
return
self._sync_executor = ThreadPoolExecutor(
max_workers=10,
thread_name_prefix="CrewAISyncHandler",
)
self._loop = asyncio.new_event_loop()
self._loop_thread = threading.Thread(
target=self._run_loop,
name="CrewAIEventsLoop",
daemon=True,
)
self._loop_thread.start()
self._executor_initialized = True
def _track_future(self, future: Future[Any]) -> Future[Any]:
"""Track a future and set up automatic cleanup when it completes.
@@ -431,6 +452,15 @@ class CrewAIEventsBus:
sync_handlers = self._sync_handlers.get(event_type, frozenset())
async_handlers = self._async_handlers.get(event_type, frozenset())
# Skip executor initialization if no handlers exist for this event
if not sync_handlers and not async_handlers:
return None
# Lazily initialize executor and event loop only when handlers exist
self._ensure_executor_initialized()
# Track that we have pending events for flush optimization
self._has_pending_events = True
if has_dependencies:
return self._track_future(
asyncio.run_coroutine_threadsafe(
@@ -474,6 +504,10 @@ class CrewAIEventsBus:
Returns:
True if all handlers completed, False if timeout occurred.
"""
# Skip flush entirely if no events were ever emitted
if not self._has_pending_events:
return True
with self._futures_lock:
futures_to_wait = list(self._pending_futures)
@@ -629,6 +663,9 @@ class CrewAIEventsBus:
with self._rwlock.w_locked():
self._shutting_down = True
# Check if executor was ever initialized (lazy init optimization)
if not self._executor_initialized:
return
loop = getattr(self, "_loop", None)
if loop is None or loop.is_closed():

View File

@@ -17,7 +17,10 @@ from crewai.events.listeners.tracing.first_time_trace_handler import (
from crewai.events.listeners.tracing.trace_batch_manager import TraceBatchManager
from crewai.events.listeners.tracing.types import TraceEvent
from crewai.events.listeners.tracing.utils import (
is_tracing_enabled_in_context,
safe_serialize_to_dict,
should_auto_collect_first_time_traces,
should_enable_tracing,
)
from crewai.events.types.a2a_events import (
A2AAgentCardFetchedEvent,
@@ -198,6 +201,17 @@ class TraceCollectionListener(BaseEventListener):
if self._listeners_setup:
return
# Skip registration entirely if tracing is disabled and not first-time user
# This avoids overhead of 50+ handler registrations when tracing won't be used
# Also check is_tracing_enabled_in_context() so per-run overrides (Crew(tracing=True)) still work
if (
not should_enable_tracing()
and not is_tracing_enabled_in_context()
and not should_auto_collect_first_time_traces()
):
self._listeners_setup = True
return
self._register_env_event_handlers(crewai_event_bus)
self._register_flow_event_handlers(crewai_event_bus)
self._register_context_event_handlers(crewai_event_bus)

View File

@@ -481,6 +481,26 @@ def should_auto_collect_first_time_traces() -> bool:
return is_first_execution()
def _is_interactive_terminal() -> bool:
"""Check if stdin is an interactive terminal.
Returns False in non-interactive contexts (CI, API servers, Docker, etc.)
to avoid blocking on prompts that no one can respond to.
"""
import sys
try:
stdin = getattr(sys, "stdin", None)
if stdin is None:
return False
isatty = getattr(stdin, "isatty", None)
if not callable(isatty):
return False
return bool(isatty())
except Exception:
return False
def prompt_user_for_trace_viewing(timeout_seconds: int = 20) -> bool:
"""
Prompt user if they want to see their traces with timeout.
@@ -492,6 +512,11 @@ def prompt_user_for_trace_viewing(timeout_seconds: int = 20) -> bool:
if should_suppress_tracing_messages():
return False
# Skip prompt in non-interactive contexts (CI, API servers, Docker, etc.)
# This avoids blocking for 20 seconds when no one can respond
if not _is_interactive_terminal():
return False
try:
import threading

View File

@@ -30,10 +30,9 @@ from typing_extensions import Self
if TYPE_CHECKING:
from crewai_a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
from crewai_files import FileInput
from crewai.a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
from crewai.agents.cache.cache_handler import CacheHandler
@@ -119,8 +118,9 @@ def _kickoff_with_a2a_support(
Returns:
LiteAgentOutput from either local execution or A2A delegation.
"""
from crewai.a2a.utils.response_model import get_a2a_agents_and_response_model
from crewai.a2a.wrapper import _execute_task_with_a2a
from crewai_a2a.utils.response_model import get_a2a_agents_and_response_model
from crewai_a2a.wrapper import _execute_task_with_a2a
from crewai.task import Task
a2a_agents, agent_response_model = get_a2a_agents_and_response_model(agent.a2a)
@@ -318,11 +318,11 @@ class LiteAgent(FlowTrackable, BaseModel):
def setup_a2a_support(self) -> Self:
"""Setup A2A extensions and server methods if a2a config exists."""
if self.a2a:
from crewai.a2a.config import A2AClientConfig, A2AConfig
from crewai.a2a.extensions.registry import (
from crewai_a2a.config import A2AClientConfig, A2AConfig
from crewai_a2a.extensions.registry import (
create_extension_registry_from_config,
)
from crewai.a2a.utils.agent_card import inject_a2a_server_methods
from crewai_a2a.utils.agent_card import inject_a2a_server_methods
configs = self.a2a if isinstance(self.a2a, list) else [self.a2a]
client_configs = [
@@ -994,7 +994,7 @@ class LiteAgent(FlowTrackable, BaseModel):
try:
from crewai.a2a.config import (
from crewai_a2a.config import (
A2AClientConfig as _A2AClientConfig,
A2AConfig as _A2AConfig,
A2AServerConfig as _A2AServerConfig,

View File

@@ -793,6 +793,10 @@ class TestTraceListenerSetup:
"crewai.events.listeners.tracing.utils._is_test_environment",
return_value=False,
),
patch(
"crewai.events.listeners.tracing.utils._is_interactive_terminal",
return_value=True,
),
patch("threading.Thread") as mock_thread,
):
from crewai.events.listeners.tracing.utils import (

View File

@@ -1,3 +1,3 @@
"""CrewAI development tools."""
__version__ = "1.13.0a5"
__version__ = "1.13.0a6"

View File

@@ -187,6 +187,7 @@ _DEFAULT_WORKSPACE_PACKAGES: Final[list[str]] = [
"crewai",
"crewai-tools",
"crewai-devtools",
"crewai-a2a",
]

View File

@@ -107,6 +107,7 @@ ignore-decorators = ["typing.overload"]
"lib/crewai/tests/**/*.py" = ["S101", "RET504", "S105", "S106"] # Allow assert statements, unnecessary assignments, and hardcoded passwords in tests
"lib/crewai-tools/tests/**/*.py" = ["S101", "RET504", "S105", "S106", "RUF012", "N818", "E402", "RUF043", "S110", "B017"] # Allow various test-specific patterns
"lib/crewai-files/tests/**/*.py" = ["S101", "RET504", "S105", "S106", "B017", "F841"] # Allow assert statements and blind exception assertions in tests
"lib/crewai-a2a/tests/**/*.py" = ["S101", "RET504", "S105", "S106", "RUF012", "F821", "F401", "B017"] # Allow test-specific patterns
[tool.mypy]
@@ -119,7 +120,7 @@ warn_return_any = true
show_error_codes = true
warn_unused_ignores = true
python_version = "3.12"
exclude = "(?x)(^lib/crewai/src/crewai/cli/templates/|^lib/crewai/tests/|^lib/crewai-tools/tests/|^lib/crewai-files/tests/)"
exclude = "(?x)(^lib/crewai/src/crewai/cli/templates/|^lib/crewai/tests/|^lib/crewai-tools/tests/|^lib/crewai-files/tests/|^lib/crewai-a2a/tests/)"
plugins = ["pydantic.mypy"]
@@ -135,6 +136,7 @@ testpaths = [
"lib/crewai/tests",
"lib/crewai-tools/tests",
"lib/crewai-files/tests",
"lib/crewai-a2a/tests",
]
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "function"
@@ -179,6 +181,7 @@ members = [
"lib/crewai-tools",
"lib/devtools",
"lib/crewai-files",
"lib/crewai-a2a",
]
@@ -187,3 +190,4 @@ crewai = { workspace = true }
crewai-tools = { workspace = true }
crewai-devtools = { workspace = true }
crewai-files = { workspace = true }
crewai-a2a = { workspace = true }

37
uv.lock generated
View File

@@ -15,6 +15,7 @@ resolution-markers = [
[manifest]
members = [
"crewai",
"crewai-a2a",
"crewai-devtools",
"crewai-files",
"crewai-tools",
@@ -1160,10 +1161,7 @@ dependencies = [
[package.optional-dependencies]
a2a = [
{ name = "a2a-sdk" },
{ name = "aiocache", extra = ["memcached", "redis"] },
{ name = "httpx-auth" },
{ name = "httpx-sse" },
{ name = "crewai-a2a" },
]
anthropic = [
{ name = "anthropic" },
@@ -1220,9 +1218,7 @@ watson = [
[package.metadata]
requires-dist = [
{ name = "a2a-sdk", marker = "extra == 'a2a'", specifier = "~=0.3.10" },
{ name = "aiobotocore", marker = "extra == 'aws'", specifier = "~=2.25.2" },
{ name = "aiocache", extras = ["memcached", "redis"], marker = "extra == 'a2a'", specifier = "~=0.12.3" },
{ name = "aiosqlite", specifier = "~=0.21.0" },
{ name = "anthropic", marker = "extra == 'anthropic'", specifier = "~=0.73.0" },
{ name = "appdirs", specifier = "~=1.4.4" },
@@ -1231,13 +1227,12 @@ requires-dist = [
{ name = "boto3", marker = "extra == 'bedrock'", specifier = "~=1.40.45" },
{ name = "chromadb", specifier = "~=1.1.0" },
{ name = "click", specifier = "~=8.1.7" },
{ name = "crewai-a2a", marker = "extra == 'a2a'", editable = "lib/crewai-a2a" },
{ name = "crewai-files", marker = "extra == 'file-processing'", editable = "lib/crewai-files" },
{ name = "crewai-tools", marker = "extra == 'tools'", editable = "lib/crewai-tools" },
{ name = "docling", marker = "extra == 'docling'", specifier = "~=2.75.0" },
{ name = "google-genai", marker = "extra == 'google-genai'", specifier = "~=1.65.0" },
{ name = "httpx", specifier = "~=0.28.1" },
{ name = "httpx-auth", marker = "extra == 'a2a'", specifier = "~=0.23.1" },
{ name = "httpx-sse", marker = "extra == 'a2a'", specifier = "~=0.4.0" },
{ name = "ibm-watsonx-ai", marker = "extra == 'watson'", specifier = "~=1.3.39" },
{ name = "instructor", specifier = ">=1.3.3" },
{ name = "json-repair", specifier = "~=0.25.2" },
@@ -1274,6 +1269,32 @@ requires-dist = [
]
provides-extras = ["a2a", "anthropic", "aws", "azure-ai-inference", "bedrock", "docling", "embeddings", "file-processing", "google-genai", "litellm", "mem0", "openpyxl", "pandas", "qdrant", "qdrant-edge", "tools", "voyageai", "watson"]
[[package]]
name = "crewai-a2a"
source = { editable = "lib/crewai-a2a" }
dependencies = [
{ name = "a2a-sdk" },
{ name = "aiocache", extra = ["memcached", "redis"] },
{ name = "crewai" },
{ name = "httpx" },
{ name = "httpx-auth" },
{ name = "httpx-sse" },
{ name = "pydantic" },
{ name = "pyjwt" },
]
[package.metadata]
requires-dist = [
{ name = "a2a-sdk", specifier = "~=0.3.10" },
{ name = "aiocache", extras = ["memcached", "redis"], specifier = "~=0.12.3" },
{ name = "crewai", editable = "lib/crewai" },
{ name = "httpx", specifier = "~=0.28.1" },
{ name = "httpx-auth", specifier = "~=0.23.1" },
{ name = "httpx-sse", specifier = "~=0.4.0" },
{ name = "pydantic", specifier = "~=2.11.9" },
{ name = "pyjwt", specifier = ">=2.9.0,<3" },
]
[[package]]
name = "crewai-devtools"
source = { editable = "lib/devtools" }