125 Commits

Author SHA1 Message Date
Vidit Ostwal
977aa0c6ec feat(cli): improve platform integration setup UX (#7453)
* fix(cli): silence tool import warnings during platform setup

* feat(cli): validate platform integrations concurrently

* fix(cli): persist platform token during crew setup

* fix(core): make settings write probe concurrency-safe

* test(cli): simplify event loop fallback stub
2026-09-17 11:35:32 -07:00
mairaarshad19
b9629cfb8f feat: add native Gemini 3.8 Flash support (#7284)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
* feat(llm): add Gemini 3.8 Flash support

- Register Gemini 3.8 Flash in CrewAI and CLI model catalogs
- Add 1,048,576-token context window mapping
- Add native Gemini provider support
- Add Gemini 3.8 Flash to the CLI model picker
- Add native model detection coverage for gemini/ and google/ formats
- Add context window regression coverage

Closes #7241

* test: add VCR cassette for gemini-3.8-flash

* chore: stop tracking .env.test

* Bringing back .env.test

---------

Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
Co-authored-by: ViditOstwal <viditostwal@gmail.com>
2026-09-17 08:59:36 -07:00
Lorenze Jay
b0343eb75f feat: bump versions to 1.15.22 (#7520) 2026-09-16 22:04:49 +00:00
João Moura
5990ead5ec feat(cli): record why a deployment create failed (#7451)
* feat(cli): record why a deployment create failed

`crewai deploy create` counts every attempt (`Create Crew Deployment`) and every
success (`Crew Deployment Created`), but the gap between them carried no cause:
among clients able to emit the success span, the CLI succeeds 96.7% of the time
and the run TUI 36.4%, and nothing said why. A third span, `Crew Deployment
Failed`, now fires for every failure after the attempt is counted, with a closed
vocabulary `reason` (api_4xx, api_5xx, invalid_response, network_error,
zip_error, user_declined, unexpected), the HTTP `status_code` when the API
answered, and the existing `source`. Never the error message.

The request path is factored into `_request_crew_creation`; every exception is
classified, reported and re-raised unchanged, so CLI and TUI behaviour is the
same as before. HTTP failures are classified before `_validate_response`, which
still prints and exits as it did.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(cli): classify deploy create failures by status and by stage

Review fixes on the failure span. Check the HTTP class before the body so a
gateway's HTML page counts as api_4xx / api_5xx with its code. Treat a 2xx
whose body is not a JSON object carrying uuid and status as invalid_response
and exit cleanly, instead of emitting a success span and crashing in the
display step. Recognise archive failures by a dedicated ArchiveError
(a ValueError) raised from create_project_zip, so the git helpers' own
ValueErrors no longer read as zip_error; a failed ZIP write is wrapped and
its partial file removed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(cli): treat staging and temp-file failures as archive errors

`create_project_zip` only wrapped the ZIP write, so an `OSError` while
staging files or creating the temporary archive escaped as a bare
`OSError` and the deploy command recorded it as `unexpected` instead of
`zip_error`. The archive boundary now covers staging, temp-file creation
and the write; the staging directory is removed on every path, and no
partial archive is left behind.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(cli): tell a non-JSON 2xx apart from a non-creation 2xx

A proxy's 200 HTML page and a JSON body missing the creation fields were
both recorded as `invalid_response`. They are different failures, one in
the network path and one in the API contract, so the deploy failure span
now records `invalid_json` for the first and `invalid_creation_response`
for the second. Requested in review.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-16 23:37:14 +05:30
João Moura
6b6a39d503 fix(events): Improve coding agents instructions (#7447)
* feat: scaffold project with assistant instruction files

- Updated project creation to include `CLAUDE.md` and `GEMINI.md` that import `AGENTS.md`, ensuring consistent guidance across coding assistants.
- Implemented utility functions to copy assistant instruction files during project setup.
- Enhanced documentation in `AGENTS.md` to emphasize the importance of keeping telemetry enabled for optimal performance.
- Added tests to verify the correct scaffolding of assistant instruction files and their contents.

* fix(cli): neutral observability guidance in scaffolded AGENTS.md

- State the observability rule as the user's decision, never a fix for
  console warnings, speed, or a "clean" configuration
- Rewrite the AMP section as built-in capabilities: no "free",
  "proactively", "sales pitch", or scripted pitches
- Turn the research mandate into a list of sources to consult when
  version details matter
- Retarget the scaffold tests to the new wording

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(cli): scaffold assistant files for JSON crews and harden telemetry tests

- create_json_crew, the default `crewai create crew` path, now copies
  AGENTS.md, CLAUDE.md and GEMINI.md; AGENTS.md documents the JSON layout
- span helper no longer depends on OTEL_SDK_DISABLED being popped by an
  earlier test; thread-scope test stops its worker before leaving the mock
- single import style in the shutdown test

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 02:07:57 -03:00
Lorenze Jay
993a96c4e0 feat(tracing): port trace events sessions to OSS (#7464)
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
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
Nightly Canary Release / Check for new commits (push) Has been cancelled
Nightly Canary Release / Build nightly packages (push) Has been cancelled
Nightly Canary Release / Publish nightly to PyPI (push) Has been cancelled
* feat(tracing): port enterprise event sessions to OSS

* fix(tracing): address review findings and verify concurrent exports

* fix(tracing): keep redactor ownership in enterprise integrations

* test(tracing): isolate intentional failures from cleanup assertions
2026-09-15 13:09:40 -07:00
Sharoon Sharif
756d8d33c8 fix(cli): read json checkpoints as utf-8 (#7491)
JsonProvider writes checkpoints with encoding="utf-8" and the runtime
serialises non-ASCII text verbatim, but the checkpoint CLI still opened
them with the platform default encoding. On Windows (cp1252) `crewai
checkpoint info` raised UnicodeDecodeError and `list` showed a 0-byte
entry for any checkpoint containing non-ASCII text. Open the files as
UTF-8 and add regression tests for the three readers.
2026-09-15 16:52:54 +00:00
Sharoon Sharif
0e2fa7ec24 fix(cli): overwrite stale poetry.lock backup on windows (#7463)
Some checks failed
CodeQL Advanced / Analyze (python) (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
* fix(cli): overwrite stale poetry.lock backup on windows

os.rename raises FileExistsError on Windows when poetry-old.lock already
exists from a previous run, so a second `crewai update` crashed there
while POSIX silently replaced the file. Use os.replace, which overwrites
on every platform, and add a regression test.

* test(cli): add docstrings to update_crew tests

---------

Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-15 15:37:03 +00:00
theater
a225c1b373 fix(cli): don't crash the run TUI when streamed output contains a literal [...] (#7435)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
* fix(cli): reject non-serializable literal_eval results in TUI JSON formatting

_try_parse_structured() accepted any dict/list coming out of
ast.literal_eval(), including values json.dumps() cannot encode such as
[Ellipsis] from a literal [...]. _format_json_in_text() then raised
TypeError: Object of type ellipsis is not JSON serializable, which
propagated through _tick and cancelled the whole crew run.

Validate the parsed object with json.dumps() inside
_try_parse_structured() so only JSON-serializable dict/list values are
returned; anything else falls back to the original text. Fixes #7434.

* fix(cli): contain RecursionError in the TUI JSON formatting boundary

A streamed structure nested deeper than the JSON backend can walk could
raise RecursionError out of _try_parse_structured (from json.loads) or
out of the render-path dumps, escaping _tick and losing the TUI update.

Catch RecursionError when loading, and validate literal_eval results
with the exact kwargs the render path uses, so any structure the render
cannot encode is rejected at the boundary and the raw text renders
instead.

---------

Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-14 20:41:33 +05:30
Vidit Ostwal
93ad4d67c4 feat: validate platform integrations during crew setup (#7385)
* feat: validate platform integrations during crew setup

* fix: clarify platform integration validation

* refactor: split platform authentication workflow

* feat: list connected platform integrations
2026-09-11 12:20:33 -07:00
Vidit Ostwal
d20845f0a3 feat: add platform tools to JSON crew wizard (#7384)
* feat: support platform tools in JSON crews

* style: clarify platform integration labels

* fix: avoid repeating tool picker title

* fix(platform): surface JSON tool discovery errors
2026-09-11 12:13:55 -07:00
Lorenze Jay
d469e9fb2b feat: bump versions to 1.15.21 (#7362) 2026-09-09 22:29:18 +00:00
Vidit Ostwal
4ed4aba6cd fix(cli): keep deploy push on the AMP create source (#7345)
Push was choosing ZIP vs git from a local origin remote, so adding origin later rebuilt the last ZIP with no files. Prefer AMP zip_deployment from status, and fall back to the old origin heuristic when that field is missing.
2026-09-10 00:23:40 +05:30
Lorenze Jay
b0fd0a9fa3 feat(telemetry): track checkpoint runtime and CLI usage (#7348)
* feat(cli): track checkpoint command and TUI usage

* feat(telemetry): track runtime checkpoint operations

* fix(telemetry): count prune usage after argument validation

* style(cli): format checkpoint prune command

---------

Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-09 22:13:11 +05:30
João Moura
1457740528 feat: bump versions to 1.15.20 (#7270) 2026-09-04 09:40:31 -03:00
João Moura
227844ef86 feat: bump versions to 1.15.19 (#7265) 2026-09-04 08:26:06 -03:00
Lorenze Jay
917b9df6d7 Validate JSON crews in project environments (#7171)
* Validate JSON crews in project environments

* fix(cli): address standalone deploy review feedback

---------

Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-01 16:00:59 +00:00
Lorenze Jay
5fd7c47669 feat: bump versions to 1.15.18 (#7136) 2026-08-27 18:00:28 +00:00
João Moura
fcdeb3d98d feat(events): record a created deployment with the uuid it was given (#7115)
`Create Crew Deployment` fires before the API call that creates the
deployment, so it counts creation ATTEMPTS and cannot carry the uuid - the
call that creates the deployment is the call that returns it. Live effect:
`create_deployment` reads 76,015 events with 0 carrying a uuid (0.000%), so
deployments cannot be joined to anything.

Moving the existing span after the response would fix the uuid and silently
redefine the metric, turning attempts into successes; the deployment churn
figures are built on attempts. So this adds a second span rather than moving
the first.

`Crew Deployment Created` fires after `_validate_response`, which raises
SystemExit on failure - a failed create therefore still counts as an attempt
and reports no creation. Both creation paths, git remote and zip upload,
converge on that line and both return the uuid.

Emits no `deploy:created` feature count: the attempt span already does, and a
second emit would double the deployment count that origin-independent
aggregation depends on.

Tests cover the emitter (uuid carried, distinct span name, no second feature
count, absent-vs-empty uuid) and the call site across both creation paths plus
the failure path.

The warehouse consumer must be widened BEFORE this merges or it delivers
nothing: `mv_span_fanout_forward` filters on a hard-coded 13-name allowlist,
and `mv_fanout_deployment_spans`'s `multiIf` ends in a catch-all `remove_crew`
arm that would mislabel the new span. Runbook prepared separately.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 17:13:17 -07:00
Vidit Ostwal
039f6ff5f6 fix(llms): raise Anthropic default max_tokens so large tool calls survive (#7077)
* fix(llms): raise Anthropic default max_tokens so large tool calls survive

* fix(llms): default Anthropic to sonnet-4-6 and drop retired models

---------

Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-08-26 19:01:31 +00:00
Vidit Ostwal
d0e9208627 [OSS-129] Map GPT-5.6 family to the official 1.05M context window (#7012)
* fix(core): map GPT-5.6 to the official 1.05M context window

LiteLLM fallback treated Sol, Terra, Luna, and the gpt-5.6 alias as unknown and used the 8k default.

* fix(openai): give GPT-5.6 its own 1.05M window

Native lookup matched gpt-5 first, so Sol, Terra, and Luna inherited 1,047,576. Longest-prefix matching keeps gpt-5 and gpt-5.4-mini on their own sizes.

* fix(azure): map GPT-5.6 deployments to the official 1.05M window

Azure had no gpt-5 / gpt-5.6 entry, so Sol, Terra, and Luna fell back to 8k.

* feat(cli): list GPT-5.6 Sol, Terra, and Luna in curated catalogs

The family is generally available; keep gpt-5.5 as the offline default.

* refactor: keep context-window tables in longest-prefix order

Drop the runtime sort and document that new keys must be inserted longest-first so startswith matching stays correct.

* fix(core): resolve prefixed LiteLLM models to the GPT-5.6 window

openai/gpt-5.6-luna kept its provider prefix on self.model, so startswith matching missed the 1.05M mapping. Strip recognized prefixes for lookup and leave unknown ones intact.
2026-08-24 19:03:48 +00:00
João Moura
9e9a8577be feat(events): report project creation with the id minted for it (#7074)
* feat(telemetry): report project creation with the id minted for it

Acquisition was only observable from a project's first run. That misses every
project created and never run, and dates the rest to the wrong day.

All three scaffolding paths already mint a project_id into the new
pyproject.toml. None of them reported it, and two of them - create_crew and
create_json_crew, which is the default `crewai create crew` path - emitted no
telemetry at all.

`Project Created` carries the kind (crew, json_crew, flow) and the id that was
just minted, and is emitted after the mint so it can carry it.

The attribute is `created_project_id`, not `project_id`, because those are two
different things. CommonAttributesSpanProcessor stamps `project_id` on every
span from get_project_id(), which reads the current working directory and is
cached for the life of the process - during `crewai create` that describes the
directory the command was run from, not the project being created. Reusing the
name would have given one column two meanings depending on span type.

Nothing is emitted for `create_crew(parent_folder=...)`: that adds a crew to a
project which already exists, mints no id, and is not an acquisition.

The existing `Flow Creation` span is left exactly as it is. Note for whoever
reads it: it is emitted from two places with two different meanings - CLI
scaffolding (create_flow.py) and runtime flow construction (event_listener.py on
FlowCreatedEvent) - so it cannot separate acquisition from usage. Not changed
here because it is a live series and renaming it would break continuity.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(telemetry): stop the creation-span tests exporting to the real collector

The recorded_spans fixture has to enable telemetry for its assertions to mean
anything - a disabled Telemetry never builds self.provider, so every assertion
would pass vacuously against a non-recording span. But enabling it is exactly
what makes __init__ wire a BatchSpanProcessor around the real
SafeOTLPSpanExporter, pointed at the production collector.

Measured, with a spy on both SafeOTLPSpanExporter classes reporting at
interpreter exit: before this change the file made 3 real export calls, handed 3
synthetic Project Created spans with invented created_project_id values to the
production exporter, and completed 3 connects to the collector. After: 0, 0, 0.

Sampling at pytest_sessionfinish reports 0 either way and is how this was missed
- BatchSpanProcessor flushes on a background timer, and with no
provider.shutdown() the flush lands in the atexit handler, which runs after
sessionfinish.

--block-network does not prevent it: it is function-scoped and only swaps
socket.connect, which a background batch thread outlives.

Follows telemetry_with_exporter in tests/telemetry/test_tracer_isolation.py:
_NullExporter swapped in before construction, _register_shutdown_handlers
suppressed so no atexit hook is left behind, and provider.shutdown() in finally.
Patch target is crewai_core because that is where this Telemetry comes from.

Also disambiguates the docs rows: the minted ID belongs to the new project, not
to the directory the command ran in, and the two can differ. Reworded in all four
languages rather than renaming the token to created_project_id - this table
documents data, never span-attribute keys (kind and crewai_version on the same
row are unnamed), and `project_id` is already the page's name for the
pyproject.toml key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* docs(ar): use tanwin fath on the letter, not on the alif

مشروعًا / جديدًا rather than مشروعاً / جديداً, in the row added by this PR.

Both forms appear in docs/edge/ar (3 each), so this is not a house convention
being broken either way; the corrected form is the more standard one and the text
is mine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* docs: stop the creation row implying the span's project_id holds the minted value

CodeRabbit re-raised this as Major after I rejected the first version, and it was
right to. My rejection argued the page never names wire keys, so introducing
created_project_id would be its only one. That part still holds -- grep finds no
attribute key anywhere in the four files. But it was the wrong conclusion: the row
still used the token `project_id` for a value the span does NOT carry under that
key, while the same span's real project_id holds the cwd-derived value. The row
also already exposes literal wire values (`crew`, `json_crew`, `flow` are the
actual kind values), so "this page has no wire detail" was overstated.

Dropping the token resolves the ambiguity without adding the page's only key
name: the row now says "the project ID minted for that new project", and names
`project_id` only to say the minted value is recorded separately from it.

All four languages. No docs/v*/ touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* docs: use an em dash in the creation row, matching the rest of the page

The two ASCII `--` occurrences in these files were both mine, introduced by this
PR: the page otherwise uses em dashes throughout (en 4, ar 4, ko 2, pt-BR 4).

CodeRabbit flagged pt-BR; the same slip was in en, so both are fixed. Now zero
ASCII `--` across all four language files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-08-23 15:59:56 -03:00
João Moura
4718b190d1 fix(cli): open the conversational TUI for a declarative chat flow (#7060)
* fix(cli): open the conversational TUI for a declarative chat flow

`crewai run` refused a declarative conversational flow and told the user to
drive it from Python. That was wrong: the conversational TUI already exists and
already does this job. `CrewRunApp(conversational=True)` renders a chat pane and
drives `handle_turn` per message (crew_run_tui.py:833-935), and
`kickoff_flow._run_conversational_flow_tui` launches it for a Python
conversational Flow.

A declaration-built flow satisfies everything that TUI needs -- `handle_turn`,
a settable `defer_trace_finalization`, and `finalize_session_traces()` -- so it
now routes there instead of exiting.

A chat loop still needs a terminal. A headless run (`is_interactive()` false,
which folds in CREWAI_DMN) says what it would have needed rather than kicking
off a single turn and presenting that as the whole conversation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cli): do not send a human-feedback chat flow to the Textual TUI

A declaration can carry both a `conversational:` block and a method with
`human_feedback:` -- verified, both predicates return True on the same flow.
Routing it to the chat TUI hangs: the runtime collects feedback with a blocking
`input()` (flow/runtime/__init__.py:3719) that Textual cannot service, so the
prompt is never shown. The STEPS TUI already declines these for exactly this
reason. Such a flow now falls back to the terminal REPL, which can prompt.

Also updates the guide in en/ar/ko/pt-BR: it still said `crewai run` has no
chat loop and exits, which is now the opposite of what the CLI does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cli): reject --inputs on a conversational flow instead of dropping it

The conversational branch returns before `_resolve_flow_inputs`, and the TUI
calls `handle_turn(message)` -- which owns the kickoff inputs, passing
`{"id": session_id}` itself. So any `--inputs` value was silently discarded and
the conversation ran as if it had been applied. It now errors, and says that
resuming a session by id is not wired up yet rather than implying it worked.

Also corrects the Arabic guide: `مُوجّه محجوز` reads as "reserved router", not
the blocking prompt it describes.

Both found by CodeRabbit on #7060.

* fix(cli): document the conversational routing exceptions

Three review follow-ups:

- The Arabic guide read خدمته (masculine) against the feminine
  مُطالبة introduced by the last fix.
- Both docstrings described a routing path that now has exceptions: a
  conversational declaration rejects --inputs and skips state-schema
  resolution, and a human-feedback one uses the terminal REPL.
- The --inputs rejection test accepted SystemExit(0); it now pins code 1.

Found by CodeRabbit on #7060.

* fix(cli): reject --inputs on a chat flow even when it parses empty

parse_inputs_json returns {} both when the option is absent and when the user
passes --inputs "{}", so the falsy check started the TUI for the second case
while the docs said it was unsupported. The conversational path now takes
whether the option was supplied, not what it parsed to.

Documents the restriction in en, ar, ko and pt-BR.

Found by CodeRabbit on #7060.

* test(cli): pin the headless conversational exit status

pytest.raises(SystemExit) also accepts SystemExit(0), so the error path could
regress to a successful exit unnoticed.

Found by CodeRabbit on #7060.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 14:36:40 +05:30
João Moura
0c2bcb510c feat(cli): backfill project_id from every user-invoked project command (#7057)
* feat(cli): backfill project_id from every user-invoked project command

`crewai run` has always backfilled: a project declaring [tool.crewai] without a
project_id gets one minted the first time it runs. No other command did, so a
project driven entirely through `crewai test`, `crewai deploy` or
`crewai traces enable` never acquired an id and every one of its runs stayed
unattributable - which is the denominator problem, not a cosmetic gap.

Adds the same call to train, replay, test, login, deploy create, deploy push,
flow add-crew, enterprise configure and traces enable. Every one is an action the
user explicitly invoked, which is the condition run_crew already relies on, so this
is the existing principle applied evenly rather than a new policy. It is still never
called from the SDK during kickoff, and get_or_create_project_id still refuses to
create the [tool.crewai] table, so an unrelated directory is never rewritten.

`crewai flow kickoff` is deliberately untouched: it delegates to run_crew and
already inherits the backfill. A test pins that so the delegation is not
accidentally duplicated. There is no `crewai evaluate` command - `crewai test` is
that path.

The call is the first statement in each command so a command that later fails still
leaves the project with an id. The tests patch the backfill to raise, which proves
the call happened and guarantees nothing after it runs, so no test touches user
settings, spawns a subprocess or reaches the network. Verified they fail against the
unpatched module: 9 command tests fail, the 2 guard tests still pass.

Tests live under lib/crewai/tests/cli/ because that is the path the required CI job
runs; nothing runs lib/cli/tests/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(cli): assert the backfill at runtime instead of reading source text

Addresses CodeRabbit and github-code-quality on #7057. The two guard tests
grepped module source for a call string, which asserts on formatting rather than
behavior: a reformat would break them and a real regression could slip past.

They now invoke the commands in an isolated project and assert on observed calls.
The flow-kickoff test patches the two distinct import sites separately and asserts
run_crew's is called exactly once while cli's is not called at all, which is what
makes 'delegates' and 'duplicates' distinguishable at runtime rather than by
reading the file.

Verified both catch what they claim: injecting a duplicate call into flow_run
fails the delegation test, and removing run_crew's own call fails the run test.

This also drops the module-level 'import crewai_cli.cli as cli_module' that mixed
import styles with the existing 'from crewai_cli.cli import crewai', which is the
code-quality finding - the rewrite removes the need for it entirely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(cli): make the exact-once assertion observable

Addresses CodeRabbit on #7057, and the finding was correct: with
side_effect=_BackfillReached the mock raised on first use, so call_count == 1 was
guaranteed by the mock rather than by the code. A second backfill call inside the
same run_crew execution could never have been observed.

Both backfill mocks now return normally and execution is stopped at the first call
AFTER the backfill (configured_project_json_crew), so the recorded count is real.

Verified the difference this makes: injecting a duplicate get_or_create_project_id()
INSIDE run_crew now fails both tests, which the previous version could not detect at
all. The flow_run duplicate case is still caught.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(cli): assert flow kickoff reaches the post-backfill boundary

Addresses CodeRabbit on #7057, and the finding was right: the flow-kickoff test
discarded the runner.invoke() result, so if the path returned or raised after one
backfill call but before configured_project_json_crew, both call-count assertions
would still have passed - for the wrong reason.

test_run_still_backfills already asserted the boundary; this makes the pair
consistent.

Verified it earns its place: injecting an early return after the backfill and
before the boundary now fails both tests, and previously would have failed
neither.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(cli): pin that the backfill precedes command-specific work

Addresses CodeRabbit on #7057. The finding is valid: the parametrized test proves
the backfill is reached, not that nothing ran before it, so its assertion message
claimed more than the test established.

Fixed in two parts rather than as proposed. The message now states what the test
actually proves, and a new test pins the ordering on login: , whose first action
goes through a module-level name that can be patched without reaching into the
command.

Deliberately not parameterized across all nine commands, which is what the finding
suggested: that would mean naming each command's current first action, and those
change as commands evolve, so the suite would end up tracking their internals
rather than this ordering property. One representative command establishes it, and
placement is visible in the diff for the rest.

Verified it catches the regression: swapping login's first two statements so its
own work runs before the backfill fails the new test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 23:14:09 -07:00
João Moura
5ddf62ae7b feat: bump versions to 1.15.17 (#7054) 2026-08-20 00:16:11 +00:00
João Moura
4dfd074fae docs(flow): document declarative conversational flows (#7035)
* test(flow): unskip the conversational end-to-end suite

The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".

That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.

Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(flow): make conversational opt-in unmistakable

Opting a Flow into chat took two statements, and forgetting one failed
silently. With `@ConversationConfig(...)` but no `conversational = True`:
`FlowDefinition.conversational` came back `None`, the built-in graph never
registered, and `handle_turn()` returned `None` without appending a message
or raising — while `chat()` reported "only available on conversational flows"
on a class that was literally decorated with a conversational config.

Three changes:

- `ConversationConfig.__call__` now also sets `conversational = True`. Every
  field on the config is consumed only by the conversational graph, so a
  decorated non-conversational Flow could only ever discard it.
- `FlowConversationalDefinition.enabled` defaults to True. The block is absent
  on non-conversational flows, so declaring it is the opt-in; `enabled: false`
  remains an explicit opt-out.
- `handle_turn()` raises like `chat()` and `stream_turn()` already do instead
  of silently returning `None`.

Setting `conversational = True` by hand still works and is still the way to
opt in without a config.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(flow): let a declaration drive conversational mode

`FlowDefinition.conversational` was written by the DSL projection and read by
nothing: every conversational gate resolved through `type(self).flow_definition()`
— the class projection — instead of `self._definition`, the declaration a flow
was actually built from. So `Flow.from_declaration()` on a definition with a
conversational block produced a flow that reported itself non-conversational,
dropped the user message, and never registered its declared routes.

Resolution rules, applied consistently:

- Structure (enabled, methods, route labels, builtin/internal routes) comes
  from `self._definition`, which is the loaded declaration for a declarative
  flow and the class projection otherwise. Both paths now agree.
- Behavior (`conversational_config`) still prefers the class attribute, which
  can hold live objects — a configured LLM, a custom BaseLLM, a response_format
  model class — that the serializable definition degrades to a config dict or a
  `module:qualname` ref. Reading the definition first would silently downgrade
  every decorated Python flow. A declaration-built flow has no class config, so
  `_config_from_definition` supplies one, cached for stable identity.
- A declared `state:` block is never replaced. `_create_default_extension_state`
  is consulted before `_create_definition_state`, so returning `ConversationState`
  there discarded every field the declaration asked for. It now yields to a
  declared state and only supplies the default when nothing else does.

The class-scoped `_is_conversational` / `_conversational_definition`
classmethods are gone; the existing instance-scoped `_is_conversational_enabled`
is the single gate.

A router `response_format` that survived serialization as a ref or schema dict
is dropped with a warning rather than handed to `llm.call()`, which needs a
real class.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(flow): synthesize the built-in conversational methods for declarations

A declaration carrying `conversational: {}` loaded clean and then ran zero
methods and returned `None`, because the four built-in graph handlers are
inherited from `_ConversationalMixin` and a declaration has nothing to inherit
from. Authors had to name `crewai.experimental.conversational_mixin:_Conversational
Mixin.route_conversation` and three siblings by hand.

`Flow._extend_definition` is a new runtime extension hook, called once
`_definition` is resolved and before methods are bound. The conversational
mixin overrides it to fill in `route_conversation`, `converse_turn`,
`end_conversation` and `answer_from_history_turn` when they are missing, using
the same code refs the DSL projection already emits so a declaration and a
class projection of the same flow produce identical method definitions.

Synthesis is deliberately a runtime concern, not a contract one:
`FlowDefinition` stays independent of the authoring layer and of the engine,
as `test_flow_definition_contract_is_dsl_agnostic` requires, and a loaded
declaration still serializes back to exactly what its author wrote.

Route descriptions are now carried by the contract. The DSL projects a handler
docstring's first line into `FlowMethodDefinition.description`, and the router
catalog reads that before falling back to the live docstring. This also fixes
a real defect: for a declarative flow `getattr(type(self), handler_name, None)`
is `None`, and the old code read `None.__doc__` — so the router LLM was told a
route's description was "The type of the None singleton."

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(flow): let an agent or crew handler reply in a conversation (#7034)

`handle_turn` promotes a handler's return value to the assistant message when
the handler did not append one itself, but the check required `isinstance(result,
str)`. Declarative `agent` and `crew` actions return `LiteAgentOutput` and
`CrewOutput`, whose text lives on `.raw` — so the most natural declarative
handler was exactly the one whose reply never reached the transcript.

`_is_public_turn_result` now unwraps `.raw` before deciding, matching
`_stringify_result`, which already did. The routing-artefact guards are applied
to the unwrapped text, so an output echoing a route label or this turn's intent
is still not promoted.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(flow): keep privately recorded agent results out of the transcript

Unwrapping `.raw` in `_is_public_turn_result` made the end-of-turn fallback
promote `LiteAgentOutput` / `CrewOutput` objects that the handler had already
recorded via `append_agent_result` with the default private visibility. That
call does not set `_assistant_reply_appended`, so the fallback republished the
very object the handler asked to keep private — defeating
`visible_agent_outputs`.

Reproduced against `main` for contrast: no leak before the unwrap, leak after.

`append_agent_result` now remembers the object it recorded for the duration of
the turn, and the fallback skips anything already routed that way. The check is
identity-based on purpose: a handler that records scratch work privately and
then returns a user-facing summary still gets that summary promoted, which a
simple "handler already handled it" flag would have broken.

Found by Cursor Bugbot on #7033.

* feat(flow): mark a declarative chat flow conversational on the instance

A declaration enables chat through `conversational.enabled`, without the
`conversational = True` class attribute. Callers outside this package
capability-check that attribute -- the AG-UI serving guide states it as a
requirement -- so it disagreed with `_is_conversational_enabled()` and a
declarative conversational flow looked non-conversational from outside.

`_extend_definition` now sets it on the instance when the definition enables
chat. Instance-only on purpose: the DSL projection reads the attribute off the
*class* to decide whether to emit a conversational block, so setting it there
would make every later subclass look conversational.

Verified on a real declarative flow: `conversational` and `stream_turn` both
now satisfy the documented capability check, while `Flow.conversational` and
any later subclass stay False.

* refactor(flow): derive routing-artefact labels from the effective routes

`_is_public_turn_result` matched a literal set of route labels, duplicating
knowledge that `_effective_builtin_routes()` already owns. A declaration that
adds a builtin route was not covered, so a handler echoing that label could be
promoted into the transcript -- the same class of divergence already fixed for
`route_turn`.

Verified the derived set is byte-identical to the old literal one for a
class-based flow, so this is a pure generalization: `conversation` and
`route_to_flow` stay explicit because neither is a route.

Also replaces a tuple-index lambda in the chat REPL test with a named
`input_fn`; it relied on tuple evaluation order and on the list being mutated
before its length was read.

Both found by CodeRabbit on #7033.

* docs(flow): document declarative conversational flows

The authoring skill told LLM authors "use top-level `conversational` only when
the user asks for a chat flow" while documenting none of its 19 fields — there
was no ModelSpec for either conversational model, so the API reference appendix
skipped them entirely.

- Adds both conversational models to the skill reference, with field
  descriptions, and registers them under the existing `conversational` skip so
  `skills(skips=["conversational"])` still suppresses the whole block.
- Adds authoring rules: do not declare the built-in graph, do not name a
  handler after the route it listens to, do not declare state unless it needs
  extra fields, and give every route handler a description.
- Documents the declarative form in the conversational-flows guide across en,
  ar, ko and pt-BR, including what is supplied automatically, how to run it,
  and what a declaration cannot express (live LLM objects, a response_format
  class, route_turn overrides).
- `crewai run` on a conversational declaration now says it has no chat loop and
  points at handle_turn/chat, instead of quietly running a single turn and
  exiting. It fails closed: a flow that cannot be inspected runs normally.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(flow): render the conversational router section in the skill

Both conversational models shared one `Conversational` section, and the
template renders only the first model of a non-union section. The router's
fields were therefore dropped from the API reference and the generated link to
them pointed at a heading that did not exist.

Also softens the built-in-handler rule: `_extend_definition` keeps an
author-supplied entry and the guide documents that override, so the skill
should say to omit those handlers by default rather than never declare them.

Adds regression tests for both sections rendering, for every field of both
models appearing, and for `skips=["conversational"]` suppressing both.

Both found by CodeRabbit on #7035.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(flow): correct the route-description rule in the authoring skill

The rule said every route handler must define `description`, but
`conversational.router.route_descriptions` is the higher-precedence source --
`_build_route_catalog` checks the overrides before falling back to the method
description. Either one describes a route; the rule now says so, and says what
happens when a route has neither.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: ViditOstwal <viditostwal@gmail.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-08-19 20:18:18 +05:30
Lorenze Jay
852916c8bf feat: bump versions to 1.15.16 (#6990) 2026-08-14 00:06:10 +00:00
João Moura
93d7a07422 feat(telemetry): count deployments from any origin and record where they started (#6974)
Deployments were only countable through two span types that no aggregation
reads, and cli_usage:deploy counted the TUI button rather than deployments.
Emit deploy:created and deploy:pushed alongside the existing spans so a
deployment is countable from the feature-usage aggregation no matter how it
was started, and tag Create Crew Deployment / Start Deployment with
source=cli|tui so the two origins stay distinguishable.

Separately, the TUI's `t` and `d` key bindings dispatch straight to
action_view_traces / action_deploy_crew, which never recorded anything -
only on_button_pressed did. Every keyboard-driven trace view and deploy was
therefore invisible. Move the recording into the actions, which both input
paths funnel through, and past the completed guard so a mid-run keypress
that does nothing is not counted as usage.


Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 15:41:59 +00:00
João Moura
7d2437cf85 feat: bump versions to 1.15.15 (#6962)
* feat(flow): report flow outcome and human-in-the-loop signals

A flow reported only that it started. FlowFinishedEvent, FlowFailedEvent,
MethodExecutionFailedEvent, MethodExecutionPausedEvent and FlowPausedEvent all
reached the console formatter and stopped there, and FlowInputRequestedEvent,
FlowInputReceivedEvent and ConversationTurnFailedEvent had no listener at all -
so success rate, failure rate and every HITL pause were unmeasurable.

Adds flow:completed, flow:failed, flow:method_failed, flow:paused,
flow:hitl_paused, flow:input_requested, flow:input_received and
flow:conversation_turn_failed as feature-usage spans, which the existing
feature-usage aggregation already reads.

Deliberately does not hold the Flow Execution span open to measure duration:
flow_executions_daily_target counts those spans at start, so a run that never
finishes would disappear from the count entirely. Duration needs its own span.

Counts only - flow names, method names, error text and flow state are never
recorded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* feat(flow): record how long a flow ran

Adds a Flow Completed span carrying flow_name, duration_ms and outcome,
emitted when a flow finishes or fails. Elapsed time comes from a monotonic
stamp taken at flow start and cleared on use.

Kept separate from the Flow Execution span rather than holding that one open:
it is emitted and closed at start and the daily aggregate counts it, so
holding it would drop every run that is killed or crashes from the execution
count. A killed run now simply has no Flow Completed row, and the count is
unaffected.

Elapsed time is an explicit duration_ms attribute rather than the span's own
duration, which the ingestion pipeline stores as a suffixed string
("0.0000184s") that downstream aggregation parses to zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* feat(flow): tag flow origin and report resumed runs

Two gaps found while testing the pause/resume path end to end.

Resumed runs were invisible. There is no resume event: a restored run re-enters
through kickoff(), so it looked identical to a fresh start. flow:resumed is
derived from _is_execution_resuming at flow start, which makes
flow:paused - flow:resumed the abandonment rate.

Flow counts are dominated by CrewAI's own AgentExecutor, which is itself a Flow
and runs once per agent execution - it is the top flow in the warehouse by a
wide margin. Nothing distinguished it from a user's flows except guessing at the
name. Both Flow Execution and Flow Completed now carry origin: "internal" when
the flow class is defined under crewai.*, "user" otherwise. Tagging only the new
span would have left the existing daily count unsplittable.

Both span methods take origin with a default, so their signatures stay
backward compatible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* fix(flow): scope outcome and resume signals to user flows

Two findings from review, both confirmed against the code.

Outcome features counted CrewAI's own flows. The agent executor, memory
encoding and memory recall are all Flows and all set suppress_flow_events;
they run far more often than anything a user wrote, so flow:completed,
flow:failed and flow:method_failed were mostly bookkeeping. Those three are now
emitted only for flows the caller wrote. Internal outcomes are still recorded
on the Flow Completed span, which carries origin.

flow:resumed counted checkpoint restores. _is_execution_resuming is set both by
from_pending (a human pause) and by a checkpoint restore that never paused for
anyone, so resumes could exceed pauses and the abandonment rate was unusable.
Keyed off _pending_feedback_context instead, which only from_pending sets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* fix(flow): declare internal flows instead of inferring them

Three findings from review, all confirmed against the code.

Gating on suppress_flow_events was wrong. That flag asks for console quiet and
is a public field, so a caller who set it on their own flow silently lost
flow:completed, flow:failed and flow:method_failed.

Deciding origin from the defining module was also wrong. Flow.from_declaration()
returns a Flow typed in crewai.flow.flow, so a caller's declarative flow was
reported as one of CrewAI's own - the inversion this split exists to prevent.

Both had the same root cause: the discriminator was inferred. Flow now declares
is_crewai_internal, set on the agent executor and the memory encoding/recall
flows, and one helper serves both origin and the outcome gate.

A failed conversational session was reported as completed. Its session closes
with FlowFinishedEvent whatever happened, so a failed turn produced
flow:conversation_turn_failed and flow:completed together. The turn failure is
now recorded on the flow and read back when the session finishes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* refactor(flow): report flow lifecycle as spans, not feature usage

Flow start, completion, pause and method failure are lifecycle facts, and the
lifecycle is reported as spans everywhere else. Reporting them through
feature usage put them in a table that aggregates on the feature string alone -
it cannot carry origin, duration or outcome, so those signals could never be
split between a user's flows and the ones CrewAI runs for itself.

Adds Flow Paused and Flow Method Failed spans, and a resumed marker on Flow
Execution so a run restored from a pause is not counted as a second fresh
start. Removes the duplicate feature rows for completed, failed, method_failed,
paused and resumed - every one of those facts is now on a span, with more
attached to it than the feature row ever carried.

Feature usage keeps only genuine adoption signals: flow:hitl_paused,
flow:input_requested, flow:input_received and flow:conversation_turn_failed.

Also clears the conversational turn-failure flag on every terminal path. A turn
that failed without deferred finalization ends via FlowFailedEvent, and the flag
left set there marked the next run on that instance as failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* test(flow): update the flow_execution_span caller for the resumed argument

Adding the resumed marker changed a signature that tests/utilities/test_events.py
asserts on exactly, and that assertion was not re-run before pushing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* test(flow): make the checkpoint-restore guard actually guard

The test asserted that flow:resumed was absent from feature usage, but that
signal moved onto the Flow Execution span. The assertion could no longer fail,
so a regression that mis-tagged checkpoint restores as resumes would have gone
unnoticed.

Now asserts the resumed attribute, and waits for the handlers: the manual emit
dispatches asynchronously, so the previous shape also read its result before the
listener had run.

Confirmed it discriminates - keying resumed off _is_execution_resuming again
fails it with [('RestoredFlow', True)] == [('RestoredFlow', False)].

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* fix(telemetry): record the resumed marker as a string

Verified end to end against the live collector and ClickHouse: the pipeline
encodes a boolean attribute as the presence of a vBool key, so false arrives as
the key simply being absent. That is invisible in the schema and easy to read
wrongly - crew_memory is extracted as "the attribute exists" and consequently
reports 1 for 99.8% of crews against a field that defaults to False.

A string leaves nothing to infer. Confirmed in the warehouse: the emitted span
reads resumed = "false".

Adds direct coverage for the attributes each flow span records, including both
resumed values, and resets the Telemetry singleton in the helper so more than
one span method can be exercised per session.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH

* feat: bump versions to 1.15.15

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 22:13:41 -03:00
Vidit Ostwal
11890e6701 Standardize CLI flags to kebab-case (#6880)
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
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
* Standardize CLI flags to kebab-case with deprecated snake_case aliases.

Unify active long-option naming across create, train, test, and replay while keeping hidden backward-compatible aliases and documenting the migration in edge docs and AGENTS.md.

* Emit deprecation warnings when snake_case CLI flag aliases are used.

Route hidden legacy flags through separate internal params so warnings fire only when the alias is supplied, and extend CLI tests for create, replay, and --help coverage.

* Merge CLI deprecation warn helpers into warn_deprecated(kind=...).

Replace warn_deprecated_command and warn_deprecated_flag with one helper that accepts kind="command" or kind="flag".
2026-08-11 22:17:24 +05:30
João Moura
752a2a9c0a feat: bump versions to 1.15.14 (#6876) 2026-08-08 19:55:33 -03:00
João Moura
42e041146e feat: bump versions to 1.15.13 (#6865) 2026-08-07 21:10:19 +00:00
João Moura
10583d6ee3 feat: bump versions to 1.15.12 (#6835) 2026-08-05 19:49:52 -03:00
Vidit Ostwal
319a20c028 docs: update scaffold AGENTS.md for unified create CLI (#6829)
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
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
Nightly Canary Release / Check for new commits (push) Has been cancelled
Nightly Canary Release / Build nightly packages (push) Has been cancelled
Nightly Canary Release / Publish nightly to PyPI (push) Has been cancelled
Document crewai create tool/skill/template, lifecycle commands, and
deprecated-but-supported scaffolding aliases in the project template
AGENTS.md. Remove the no-op --skip_provider flag from the flow example.
2026-08-06 01:10:19 +05:30
Vidit Ostwal
8320178697 fix(flow): clarify conversational route/handler name collision errors (#6825)
* fix(flow): clarify route/handler name collision validation errors

When @listen(...) includes the handler's own name, FlowDefinition validation
fails. This change improves the error text and how it surfaces for Python
Flow classes.

What changed
- _self_listen_error in flow_definition.py: two message variants (conversational
  vs default), both include the listen condition
- build_flow_definition in dsl/_utils.py: wraps FlowDefinition ValidationError
  with the Python Flow class name
- Tests for declarative and DSL-built flows; docs follow in a separate commit

When each error surfaces

1. Conversational message — FlowDefinition validation when
   conversational.enabled is true and listen references the handler name.
   Example: @listen("create_video") on def create_video in a conversational flow.
   Surfaces via:
   - FlowDefinition.from_declaration(dict/yaml) → pydantic ValidationError for
     FlowDefinition (Value error, methods.create_video.listen listen condition...)
   - MyFlow.flow_definition() / MyFlow() → ValueError Invalid flow definition
     for MyFlow: ... (wrapped by pydantic as ValidationError for MyFlow on
     instantiation)

2. Default (non-conversational) message — same trigger check when the flow is
   not conversational. Example: @listen("publish") on def publish.
   Surfaces via the same paths as (1).

3. Class-name wrapper — only on the Python DSL path when build_flow_definition
   catches FlowDefinition ValidationError. Prepends Invalid flow definition for
   {ClassName}: to the underlying message from (1) or (2). Does not apply to
   from_declaration without a Flow class.

* docs(flow): explain conversational handler naming vs route labels

Document why @listen route labels must differ from handler method names and
recommend the handle_* naming pattern.

* docs(cli): warn against matching @listen labels to handler names

Add AGENTS.md guidance for crew and flow scaffolding so coding assistants
do not name handlers the same as their @listen route or event labels.

* docs(cli): clarify @listen self-reference fails at validation

Document that matching @listen labels to handler names raises a validation
error at flow instantiation, and that the runtime loop only occurs if
validation is bypassed.
2026-08-06 00:14:55 +05:30
Vidit Ostwal
fef32f43a0 feat(cli): unify scaffolding under crewai create <resource> (#6821)
* feat(cli): add canonical `crewai create tool` command

Unify tool scaffolding under the create verb and deprecate
`crewai tool create` with a yellow warning while keeping backward
compatibility.

* feat(cli): add canonical `crewai create skill` command

Unify skill scaffolding under the create verb and deprecate
`crewai skill create` with a yellow warning while keeping backward
compatibility.

* feat(cli): add canonical `crewai create template` command

Unify template scaffolding under the create verb and deprecate
`crewai template add` with a yellow warning while keeping backward
compatibility.

* docs: document unified `crewai create` scaffolding commands

Document canonical create forms for tool, skill, and template projects,
note deprecated aliases, and update skills and agents-md guides.

* feat(cli): extend create picker and DMN guidance for all types

Show tool, skill, and template in the interactive create picker and
list every supported type in the CREWAI_DMN usage error.

* fix(cli): allow create tool/skill/template in CREWAI_DMN mode

Only set skip_provider in DMN mode for crew creation, since tool,
skill, and template paths reject that flag as a crew-only option.

* test(cli): patch TemplateCommand at cli lookup site in DMN test

create() resolves TemplateCommand from crewai_cli.cli, not from
remote_template.main directly.
2026-08-05 16:41:27 +00:00
João Moura
a60d36a8df feat: bump versions to 1.15.11 (#6816) 2026-08-04 22:52:54 -07:00
João Moura
b10c4ffcdc feat: add project_id to link OSS usage to an enterprise account (#6791)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
Nightly Canary Release / Check for new commits (push) Has been cancelled
Nightly Canary Release / Build nightly packages (push) Has been cancelled
Nightly Canary Release / Publish nightly to PyPI (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* feat: add project_id to link OSS usage to an enterprise account

Adds a stable per-project identifier so a project's OSS traces and runs can
be attributed to an account after signup. There was no such identifier
before: [tool.crewai] held only `type`, the deploy UUID was printed to the
console but never persisted, Settings.org_uuid is global rather than
per-project, and trace batches carried only crew_fingerprint/crew_name.

The id lives in the project's pyproject.toml, so it is committed with the
repository and stays stable across machines, teammates, CI, and containers -
unlike a machine- or user-derived identifier, which is unstable in exactly
the containerized production environments that matter most.

crewai-core:
- get_project_id(): read-only lookup of [tool.crewai].project_id. Safe for
  library code; never creates or modifies anything.
- get_or_create_project_id(): mints a uuid4 and persists it, returning
  (id, created) so callers can tell the user. Best-effort - returns
  (None, False) for a missing, malformed, or read-only pyproject.toml rather
  than raising.
- Insertion edits the raw TOML text instead of round-tripping through a
  writer, so comments, key order, and formatting elsewhere survive. The key
  is placed at the end of the [tool.crewai] table, before the next table
  header, so it cannot land in a neighbouring section.
- LoginPayload and TraceExecutionContext gain optional project_id.

Sent on two paths:
- Traces: project_id is added to execution_context, which is sent on both
  the ephemeral and authenticated paths, so a project's traces remain
  attributable before and after the user creates an account.
- Login: `crewai login` already sends the pseudonymous user_identifier on an
  authenticated request; adding project_id means one request carries account
  + user + project, which is the link itself.

Minting is restricted to CLI commands the user explicitly invoked - `crewai
create` for new projects and `crewai run` to backfill existing ones - and is
announced when it happens. Library code only ever reads. Silently rewriting
a user's pyproject.toml during Crew.kickoff() would be surprising.

Privacy: project_id is a random uuid4 in a file the user commits. It is
visible in a diff, contains nothing personal, and identifies a project
rather than a person - so this needs none of the notice changes that
attaching a user identifier to all telemetry would require.

Tests: 18 new tests covering minting, stability, table placement, comment
and formatting preservation, five pyproject layouts, the neighbouring-table
regression, and graceful handling of missing/malformed/read-only files.
Verified end-to-end that both create paths mint distinct ids, that the trace
payload carries project_id on both the ephemeral and authenticated paths,
and that the login payload carries user_identifier and project_id together.

Follow-ups, deliberately not included: adding project_id to telemetry spans,
and backend persistence of the (account, user_identifier, project_id) triple.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t

* refactor: drop the console announcement when minting project_id

Minting now happens silently. With no message to print, the (id, created)
tuple had no consumer, so simplify the API rather than keep the flag around
for a hypothetical caller:

- get_or_create_project_id() returns `str | None` instead of
  `tuple[str | None, bool]`.
- Remove crewai_cli.utils.ensure_project_id, which existed only to print the
  message and discard the flag. The four call sites (crewai create crew,
  crewai create flow, crewai run, and tool-repository login) now call
  get_or_create_project_id directly.
- Update tests for the simplified signature; still 18 tests covering minting,
  stability, table placement, formatting preservation, five pyproject
  layouts, and missing/malformed/read-only handling.

Behaviour is otherwise unchanged: minting stays restricted to CLI commands
the user invoked, library code still only reads via get_project_id, and a
missing or read-only pyproject.toml still returns None rather than raising.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t

* fix: harden project_id minting against TOML corruption; address review

Several reviewers found ways the raw-text edit could produce invalid TOML.
Each is now fixed and covered by a test that fails without the fix.

Duplicate project_id key (Cursor bugbot, Copilot x2):
- get_project_id() reports a blank or non-string value as "absent", so a file
  containing `project_id = ""` took the insert path and gained a second
  project_id line - a duplicate key, and therefore invalid TOML that no
  tomli-based tool could read afterwards.
- _insert_project_id is now _set_project_id: it replaces an existing
  assignment inside [tool.crewai] instead of appending unconditionally.

Table header with a trailing comment (CodeRabbit major, Cursor bugbot):
- `[tool.crewai]  # config` is valid TOML but failed exact string equality,
  so the fallback appended a second [tool.crewai] header - a redefined table,
  also invalid TOML, and silent because get_project_id swallows the resulting
  decode error.
- Added _is_table_header(), which tolerates a trailing comment and does not
  match similar names such as [tool.crewai-extra].

Writing into malformed TOML (Cursor bugbot, Copilot):
- get_or_create_project_id relied on get_project_id, which cannot distinguish
  "no id" from "unparsable file", so it appended to files it could not parse.
- The locked path now parses explicitly and bails on a decode error, and
  re-parses the updated content before writing, so this feature can never be
  the reason a project's pyproject.toml stops parsing.

Concurrency and atomicity (CodeRabbit major):
- Two CLI processes could both see no id, mint different uuids, and clobber
  each other, leaving a caller holding an id that is not on disk. Minting now
  takes the existing crewai_core cross-process lock, re-reads under it, and
  returns the id that persists.
- Writes go through a temp file in the same directory plus os.replace, so an
  interruption cannot truncate pyproject.toml. File mode is copied across, and
  the temp file is removed on failure.
- os.replace only needs a writable directory, which would have let an atomic
  write silently overwrite a file the user marked read-only; writability is
  now checked explicitly so that case still returns None.

Line endings (CodeRabbit):
- Path.read_text/write_text normalized CRLF to LF, so minting would rewrite a
  CRLF-committed file entirely. Read and write now use newline="" and the
  inserted line ending is derived from the existing content.

Default create path skipped minting (Cursor bugbot):
- `crewai create crew` defaults to create_json_crew; only the --classic and
  flow paths minted, so most new projects had no id until a later command.
  Wired into create_json_crew as well. Verified all three paths now mint
  distinct ids.

Do not mint during login (CodeRabbit major):
- ToolCommand.login ran get_or_create_project_id, which is outside the
  sanctioned minting commands and is invoked by `crewai tools create` from a
  freshly scaffolded directory before the project is persisted. It now uses
  the read-only get_project_id. Verified login leaves pyproject.toml
  untouched.

Not applied: Copilot asked for a console message when an id is written, in
create_crew and create_flow. Minting was made deliberately silent in the
previous commit, so the (id, created) tuple and the announcement are both
gone by design.

Tests: 32 in test_project_id.py, up from 18. New cases cover blank and
non-string existing ids, three commented-header forms, similar table names,
malformed input, CRLF and LF preservation, concurrent minting convergence,
file-mode preservation, and temp-file cleanup. Confirmed the header and
duplicate-key tests fail when the fixes are reverted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t

* fix: never create [tool.crewai], treat whitespace ids as absent, harden test

`crewai run` could rewrite unrelated projects (Cursor bugbot, high):
- get_or_create_project_id ran before the cwd was established as a CrewAI
  project, and _set_project_id appended a [tool.crewai] table when none
  existed. Any directory with a pyproject.toml could therefore gain one -
  including on `crewai run --definition`, which may otherwise succeed.
- _set_project_id no longer creates the table; it returns None when
  [tool.crewai] is absent, so a key is only ever added to a table the project
  already declares. The templates all ship the table, so no create path needs
  the old fallback.
- The minting call in run_crew moved after the --definition early return, so
  an explicit-flow run does not touch the cwd at all.
- Presence is checked, not truthiness: an empty [tool.crewai] is still a
  CrewAI marker, and get_crewai_project_config returns {} both for that and
  for an absent table.
- Verified an unrelated project's pyproject.toml is byte-identical after a
  mint attempt.

Whitespace-only project_id accepted as valid (CodeRabbit):
- `project_id = "   "` is truthy, so it was returned as an identity and would
  have propagated into login payloads and tracing context. It also meant the
  '"   "' parameter of the replacement test asserted nothing.
- Added _usable_project_id, which strips before deciding, used by both
  get_project_id and the locked mint path.

Concurrency test could hang CI (CodeRabbit, major):
- Neither the barrier nor the joins had timeouts, so a thread dying early or
  blocking on the lock would hang the job rather than fail it. The result
  count was also unchecked, so a dead thread still passed.
- Added timeouts, an explicit liveness assertion, a result-count assertion, a
  lock around the shared result list, and corrected the docstring: this covers
  the read-modify-write race with threads, not the cross-process backend.

Tests: 35, up from 32. New coverage for the absent-table refusal and three
whitespace forms; the blank-id replacement case now asserts a real uuid
replaced the blank value.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t

* chore(deps): force gitpython 3.1.57+ for GHSA-p538-c434-8v24 and GHSA-3f7w-8rr8-f37f

Unrelated to project_id; bundled here only because it blocks this PR's
vulnerability scan. Two advisories were published for gitpython 3.1.55 after
main last passed the scan:

- GHSA-p538-c434-8v24: arbitrary file truncation via `git rev-list --output`
  argument injection. Fixed in 3.1.56.
- GHSA-3f7w-8rr8-f37f: unguarded git option forwarding in
  IndexFile.checkout() and TagReference. Fixed in 3.1.57.

- Bump the override floor to gitpython>=3.1.57 and declare the same floor in
  crewai-tools, so consumers installing the published package are covered and
  not only this repo's lock.
- 3.1.57 was published 2026-07-26, past gitpython's exclude-newer-package
  cutoff of 2026-07-24, so that cutoff moves to 2026-07-27. Without it the
  floor is unresolvable.

pip-audit against the updated lock reports no known vulnerabilities.
Verified gitpython 3.1.57 resolves and that crewai_tools and crewai_cli.git
still import.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 15:25:06 -07:00
João Moura
766d71aefb feat: surface AMP in AGENTS.md and detect coding agents in telemetry (#6779) 2026-08-03 12:13:05 -07:00
João Moura
f262ac214e feat: bump versions to 1.15.10 (#6753) 2026-07-31 14:45:28 +00:00
João Moura
bfe8df4471 feat: bump versions to 1.15.9 (#6725) 2026-07-29 22:40:32 -07:00
João Moura
133baf39b8 feat: bump versions to 1.15.8 (#6702) 2026-07-28 14:59:36 +00:00
João Moura
38ca5edce2 feat: bump versions to 1.15.7 (#6672) 2026-07-26 18:07:46 +00:00
Lorenze Jay
c459d01c35 feat: bump versions to 1.15.7a1 (#6661) 2026-07-26 13:47:11 -03:00
Lorenze Jay
a4cbeacca5 feat: bump versions to 1.15.6 (#6631) 2026-07-24 20:21:58 +00:00
Lorenze Jay
40279e3152 fix dep resolution (#6605)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
2026-07-21 08:37:08 -03:00
Vinicius Brasil
4c7e483936 feat: bump versions to 1.15.5 (#6601) 2026-07-20 16:20:33 +00:00
Vinicius Brasil
e0bd967484 feat: bump versions to 1.15.4 (#6582) 2026-07-17 14:27:15 +00:00
João Moura
f0704ebb22 feat(skills)!: promote Skills Repository out of experimental (#6579)
* feat(skills)!: promote Skills Repository out of experimental

The registry-backed Skills Repository (crewai skill create/publish/
install/list, @org/name refs, global cache) is now mainline:

- CLI: `crewai skill ...` is a top-level group; the CREWAI_EXPERIMENTAL
  gate and the now-empty `crewai experimental` group are removed.
- Runtime: registry.py, cache.py, and events.py move from
  crewai.experimental.skills into crewai.skills next to the loader;
  the require_experimental_skills() gate is gone.
  crewai.experimental.skills remains as a deprecated re-export shim.
- Docs: concepts/skills now leads with the CLI workflow and documents
  the create -> publish -> install lifecycle.

Linear: n/a (requested promotion)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(skills): org-scoped publish only + docs in all languages

Skills are always scoped to the publishing organization, like tools:
drop the --public/--private flags from `crewai skill publish` and
always send is_public=False to the registry. CLI tests assert the flag
is rejected and the API never receives a public publish.

Translate the new CLI-first Quick Start and the create -> publish ->
install lifecycle section into ar, pt-BR, and ko concepts/skills docs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(skills): address review comments on the promotion PR

- Back-compat shim now aliases the old submodules in sys.modules so
  `crewai.experimental.skills.registry/cache/events` imports (and patch
  targets) resolve to the real crewai.skills modules, not just the
  package-root re-exports.
- `crewai skill publish` actually enforces the git-state check that
  --force claims to skip: unsynced repos block publishing (mirroring
  tool publish); standalone skill dirs outside any git repo publish
  without a check.
- Explicit UTF-8 encoding on SKILL.md and cache-metadata reads/writes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(skills): fail closed when git state cannot be validated on publish

Follow deploy's pattern: construct git.Repository(fetch=False) and only
treat "not a Git repository" as skippable — any other git error
(fetch/auth/misconfiguration) now blocks publish with a --force escape
hatch instead of silently bypassing the sync check.

Also single-style imports in the shim test (CodeQL) with the dotted
shim import covered via importlib.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(skills): fetch before sync check on publish; bump mcp past advisories

Publish now refreshes remote-tracking refs (repository.fetch()) before
is_synced(), so ahead/behind is judged against the actual remote rather
than stale local refs; a failing fetch blocks publish with the --force
escape hatch. Adds a fail-closed test for fetch errors.

Raise mcp to >=1.28.1,<2 (locks 1.28.1): the ~=1.26.0 pin blocked
GHSA-hvrp-rf83-w775 / GHSA-jpw9-pfvf-9f58 (fixed 1.27.2) and
GHSA-vj7q-gjh5-988w (fixed 1.28.1), which were failing pip-audit on
this PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Vinicius Brasil <vini@hey.com>
2026-07-17 14:21:15 +00:00
Vinicius Brasil
cbb8c982f8 feat: bump versions to 1.15.3 (#6576) 2026-07-16 19:26:14 +00:00