- New lib/crewai-core/ package: version, paths, constants, lock_store, user_data,
printer, telemetry. Pure leaf — depends only on appdirs/portalocker/rich/otel.
- crewai now depends on crewai-core; old crewai.utilities.{version,paths,printer,
lock_store} and the user-data block of events/listeners/tracing/utils.py become
one-shot DeprecationWarning shims that re-export from crewai_core.
- crewai-cli drops its hard dep on crewai and depends only on crewai-core. CLI
imports for telemetry/version/printer/constants now point at crewai_core.
- tools/main.py lazy-imports project_utils + get_user_id; the publish/login
subcommands print a friendly "requires crewai" error if it's missing.
- crewai-cli is now genuinely standalone: 'crewai --help', 'version', 'login',
'config', 'traces', 'create', 'template' all work without crewai installed.
- 351 CLI tests + 9 crewai-core smoke tests + crewai's full mypy (471 files) clean.
- Re-export get_crewai_version explicitly so consumers stop getting
attr-defined.
- Drop the telemetry call in TemplateCommand.add_template; the
standalone CLI's BaseCommand intentionally has no _telemetry, matching
the choice already made for DeployCommand.
- Add the user_identifier kwarg to crewai_cli's
PlusAPI.login_to_tool_repository so tools.main.login keeps working
and the surface matches crewai.plus_api.
- Update the lib/cli login tests for the new json={} payload.
Mirrors the parameter origin/main added on crewai.cli.plus_api so the
relocated crewai.plus_api stays in sync. Also fix a stale crewai_cli
patch target in the lib/crewai plus_api test.
These tests targeted crewai.auth.oauth2.AuthenticationCommand but exercise
_login_to_tool_repository, which lives only on the standalone
crewai_cli.authentication.main.AuthenticationCommand. The same tests
already exist in lib/cli/tests/authentication/test_auth_main.py against
the correct class.
These functions live in crewai_cli.utils, not crewai.utilities.project_utils.
The same tests already exist in lib/cli/tests/test_utils.py against the
correct module.
Resolve conflicts from origin/main: relocate new CLI additions
(checkpoint_tui, deploy/validate, remote_template, content_crew
templates) into lib/cli, rewrite imports for the standalone
crewai-cli package, port main's trained_agents_file param and
predeploy validation, and bump python-dotenv/pydantic in
crewai-cli to match crewai's constraints. Add the new
mark_ephemeral_trace_batch_as_failed method to the relocated
crewai.plus_api. Update tests for the new payload field, deploy
--skip-validate kwarg, and crewai_cli import paths.
In `_execute_task_with_a2a` and its async variant, the try body
sets `task.output_pydantic = None` before returning an A2A
response. The finally block then checks
`if task.output_pydantic is not None` before restoring the
original value — but since it was just set to None, the condition
is always False and the original value is never restored. This
permanently mutates the Task object.
Remove the guard so `output_pydantic` is unconditionally restored,
matching the unconditional restoration of `description` and
`response_model` in the same block.
Co-authored-by: Greyson LaLonde <greyson.r.lalonde@gmail.com>
When a tool with result_as_answer=True raises an exception, the agent
was receiving result_as_answer=True and returning the error string as
the final answer. Now we set result_as_answer=False when an error event
is emitted, allowing the agent to reflect and retry.
FixescrewAIInc/crewAI#5156
---------
Co-authored-by: NIK-TIGER-BILL <nik.tiger.bill@github.com>
Co-authored-by: Greyson LaLonde <greyson.r.lalonde@gmail.com>
## Summary
- Reverts `b0e2fda` ("fix(flow): add execution_id separate from state.id", COR-48): removes `Flow.execution_id` and points `current_flow_id` / `current_flow_request_id` back at `flow_id` (i.e. `state.id`). The separate per-run tracking id was no longer the right abstraction once `restore_from_state_id` reshapes how `state.id` is assigned;
- Adds an optional `restore_from_state_id` kwarg to `Flow.kickoff` / `Flow.kickoff_async` that hydrates state from a previously-persisted flow's latest snapshot
- Reassigns `state.id` to a fresh value (or `inputs["id"]` if pinned) so the new run's `@persist` writes don't extend the source's history
- Existing `inputs["id"]` resume, `@persist`, and `from_checkpoint` paths are unchanged
## Problem
`@persist` only supports *resume* today: `kickoff(inputs={"id": <uuid>})` hydrates state and continues writing under the same `flow_uuid`. There's no way to **fork** — hydrate from a snapshot but persist under a separate key, leaving the source's history intact. This PR adds that.
| | `state.id` after kickoff | `@persist` writes land under |
|---|---|---|
| `inputs["id"]` (resume) | supplied id | supplied id (extends history) |
| `restore_from_state_id` (fork) | fresh id, or `inputs["id"]` if pinned | new id (source preserved) |
## Behavior
| `inputs.id` | `restore_from_state_id` | Effect |
|---|---|---|
| — | — | Fresh kickoff |
| set | — | Existing resume |
| — | UUID | Fork — new `state.id`, hydrated from source |
| set | UUID | Fork into a pinned `state.id`, hydrated from source |
- Source not found → silent fallback (mirrors existing resume)
- Both `from_checkpoint` and `restore_from_state_id` set → `ValueError`
- `restore_from_state_id=None` → byte-identical to current main
## Design
Fork hydration runs before the existing `inputs` block in `kickoff_async`. On a hit, it calls the same `_restore_state` primitive used by resume, then overwrites `state.id` with a fresh UUID (or `inputs["id"]`). A `fork_succeeded` flag gates the existing `inputs["id"]` path so we don't double-load. `_completed_methods` / `_is_execution_resuming` are intentionally untouched — skip-completed-methods remains the territory of `apply_checkpoint` and `from_pending`.
## Test plan
- [ ] `pytest tests/test_flow_persistence.py` — 5 new tests (four-row matrix, not-found fallback, default no-op, conflict raise) + 6 existing as regression
- [ ] `pytest tests/test_flow.py` — broader flow suite
- [ ] Manual end-to-end against an HITL `@persist` flow
* feat(crewai-tools): add highlights to ExaSearchTool, rename from EXASearchTool
- Add a highlights init param so agents can get token-efficient excerpts instead of full pages
- Rename EXASearchTool to ExaSearchTool; keep EXASearchTool as a deprecated alias so existing imports keep working
- Update the docs and example to use highlights as the recommended option
- Add a small note that says Exa is the fastest and most accurate web search API
- Add tests for the new highlights param and the deprecation alias
* fix(crewai-tools): import order and module-level Exa for tests
- Reorder std-lib imports so ruff is happy with force-sort-within-sections.
- Import Exa at module level (with a fallback) so the existing test mocks resolve.
The lazy install prompt still works if exa_py is missing.
- Allow content and summary to be a dict, matching highlights.
- Trim test file to the cases this PR introduces (highlights param and the
EXASearchTool deprecation alias). Existing init-shape tests stay.
Co-Authored-By: ishan <ishan@exa.ai>
* chore(crewai-tools): drop self-explanatory comment on schema alias
Co-Authored-By: ishan <ishan@exa.ai>
* docs(crewai-tools): default highlights to True, drop summary from examples
Co-Authored-By: ishan <ishan@exa.ai>
* docs(crewai-tools): simplify highlights examples to highlights=True
Co-Authored-By: ishan <ishan@exa.ai>
* feat(crewai-tools): add x-exa-integration header for usage tracking
Co-Authored-By: ishan <ishan@exa.ai>
* docs(crewai-tools): add Exa MCP section and resources links
Co-Authored-By: ishan <ishan@exa.ai>
---------
Co-authored-by: ishan <ishan@exa.ai>
Co-authored-by: Greyson LaLonde <greyson.r.lalonde@gmail.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* feat(azure): forward credential_scopes to Azure AI Inference client
Adds a credential_scopes field to the native Azure AI Inference
provider and a matching AZURE_CREDENTIAL_SCOPES env var
(comma-separated). The value is forwarded to ChatCompletionsClient /
AsyncChatCompletionsClient when set, letting keyless / Entra-based
callers target a specific Azure AD audience (e.g.
https://cognitiveservices.azure.com/.default) without subclassing the
provider. Matches the upstream azure.ai.inference SDK kwarg of the
same name.
Lazy build re-reads the env var so an LLM constructed at module
import (before deployment env vars are set) still picks up scopes —
same pattern as the existing AZURE_API_KEY / AZURE_ENDPOINT lazy
reads. to_config_dict round-trips the field.
* refactor(azure): tighten credential_scopes env handling
Address review feedback:
- Move os.getenv into the helper so AZURE_CREDENTIAL_SCOPES appears once
- Match the surrounding api_key/endpoint `or` style in the validator
- Drop the list() defensive copy in to_config_dict — every other field
in that method (and the base class's `stop`) is assigned by reference