Commit Graph

338 Commits

Author SHA1 Message Date
Lucas Gomide
0e5d0ecfb9 feat: add step interception points and rework execution hooks docs around @on (#6518)
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 / 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 pre_step and post_step interception points on task execution

Introduces `StepContext` and the two step points in the dispatcher, and
wires them around agent execution in `task.py` (sync and async paths):
`pre_step` fires after `TaskStartedEvent` with the task context as
payload, `post_step` fires before `TaskCompletedEvent` with the
`TaskOutput`, and hook replacements are rebound in both directions.

* feat: wire pre_step and post_step on flow method execution

Dispatches the step points around each flow method with
kind="flow_method": `pre_step` receives the dumped call params and maps
returned edits back onto args/kwargs, `post_step` can rewrite the method
result before it is recorded. Conformance tests cover per-method firing
and output rewriting.

* docs: rework execution hooks page around the @on api

Replaces the standalone interception hooks catalog with a single
`execution-hooks.mdx` page that teaches `@on` as the primary way to
write hooks, covering the full ten-point catalog across task, flow, and
LLM execution. The legacy per-point decorators stay documented in a
closing section, and the `docs.json` navigation drops the removed page.
2026-07-14 14:47:18 -04:00
Lucas Gomide
a194f3867a feat: wire execution-boundary interception points (#6517)
* feat: wire execution-boundary interception points

Adds the typed interception contexts (`crewai/hooks/contexts.py`) and wires
the `execution_start`, `input`, `output`, and `execution_end` points for both
crews and flows through the dispatcher. `prepare_kickoff` and
`Flow.kickoff_async` fire `execution_start`/`input` so a hook can rewrite
resolved inputs before the run, while `Crew._create_crew_output` and the flow
tail fire `output`/`execution_end` so the final result can be observed or
replaced. Closes the eight critical-path points without touching the legacy
hooks.

* fix: correct execution-boundary hook ordering and input aliasing

Reworks the crew and flow boundary seams flagged in review. `OUTPUT` and
`EXECUTION_END` now run before the completion event (`CrewKickoffCompletedEvent`
and `FlowFinishedEvent`) so a `HookAborted` no longer leaves a spurious
completed signal and a returned payload replacement is honored on the emitted
and returned result. Boundary contexts alias `inputs` to the same object as
`payload` instead of a fresh dict from `or`, so in-place edits survive
read-back. Flows re-publish the resolved inputs into `flow_inputs` baggage
after the `INPUT` hook so trigger-payload injection observes hook rewrites, and
a resumed flow now dispatches `OUTPUT`/`EXECUTION_END` on its completion path.

* chore: drop redundant seam comments from execution-boundary wiring

Removes two inline comments narrating the OUTPUT/EXECUTION_END dispatch
ordering in `crew.py` and the flow runtime, plus a stray sentence about
enterprise adapters in the conformance-suite docstring. Comment-only
cleanup, no behavior change.

* fix: keep crew output typed across boundary hook dispatch

`_create_crew_output` reassigned `crew_output` from the hook contexts'
`payload`, which is typed `Any`, so mypy flagged `no-any-return` at the
function's return. Cast the payload back to `CrewOutput` after each
dispatch and split the `ExecutionEndContext` construction to satisfy
`ruff format`'s line-length limit.
2026-07-14 10:43:18 -04:00
Lucas Gomide
7d21283630 feat: add generic interception-hook dispatcher (#6516)
* feat: add generic interception-hook dispatcher

Introduces `crewai/hooks/dispatch.py` as a single engine behind every
interception point: a hook receives a typed context, may mutate or replace
its `payload`, or raise `HookAborted(reason, source)` to stop the operation.
The full `InterceptionPoint` catalog is frozen from day zero, with global and
contextvar-scoped registries, an `@on` decorator, a no-op fast path, and a
`HookDispatchedEvent` for telemetry. The four existing `before/after_llm_call`
and `before/after_tool_call` hooks become adapters over the dispatcher, so the
legacy dialect and `return False` semantics keep working unchanged while
gaining the new contract.

* fix: harden interception dispatcher against review findings

Corrects several dispatcher edge cases surfaced in review. `_default_reducer`
now reports a modification only when a `payload` is actually applied, the
`agents=` filter falls back to `agent_role` for contexts without an `agent`
object, and `unregister` resolves the filter wrapper stashed by `on` so a
filtered hook can be removed. The tool-hook runners honor the executing
agent's `verbose` flag instead of silently swallowing hook errors, and the
ReAct tool path now runs `POST_TOOL_CALL` on blocked calls to match the
native paths. Also adds abort-telemetry coverage and replaces the flaky
absolute no-op timing budget with a relative one.

* fix: honor scoped hooks on direct llm calls and register @on crew methods

Direct agent-less LLM calls short-circuited on the empty global hook list,
so hooks registered only for the current `scoped_hooks()` context never
ran; the direct-call helpers now defer to `dispatch`, which resolves
scoped hooks behind its own no-op fast path. `CrewBase` likewise only
scanned the legacy `is_*_hook` markers, so `@on(InterceptionPoint.X)`
methods were silently dropped — it now registers them on the dispatcher
with filters applied and `self` bound. Also tightens result typing across
the tool-call seams so `mypy` stays green.

* refactor: scope InterceptionPoint to the points this layer wires

The dispatcher only fires the model- and tool-call boundaries, so
`InterceptionPoint` now lists just those four rather than the full future
catalog. New points are introduced alongside the seams that dispatch them,
keeping every layer free of enum members with no live consumer. The
dispatcher unit tests that borrowed unused points as generic examples are
remapped onto the four kept points.

* test: pin per-hook fail-open at the LLM and tool seams

The dispatcher swallows a hook's exception per hook rather than around the
whole loop, so one buggy hook no longer silently skips every hook registered
after it. These seam-level tests pin that behavior through
`_setup_before_llm_call_hooks` and `run_before/after_tool_call_hooks`, and
confirm an intentional `return False` block still short-circuits later hooks.

* fix: run execution-scoped hooks on the agent executor model seams

`_setup_before/after_llm_call_hooks` only ran the executor's snapshot
hook lists, so hooks registered via `scoped_hooks()` never fired on
`PRE/POST_MODEL_CALL` during normal agent execution, while the tool
seams (which go through `dispatch`) merged them. The seams now append
the current scope's hooks after the snapshot via `get_scoped_hooks`,
matching dispatch's global-then-scoped ordering, and a scoped-only
registration no longer short-circuits the seam.
2026-07-14 10:27:34 -04:00
Lucas Gomide
6452608724 fix: after_llm_call hooks no longer break native tool execution (#6531)
* fix: don't clobber native tool-call responses in after-LLM hooks

Registering any `after_llm_call` hook broke native tool execution: the
executor invokes `_setup_after_llm_call_hooks` on the intermediate
response that carries the model's tool calls, the non-str payload was
stringified for the response-rewrite pass, and the executor then treated
that string as a final answer instead of executing the tools. Structured
payloads (neither `str` nor `BaseModel`) now pass through untouched,
mirroring the isinstance guard `_invoke_after_llm_call_hooks` already
applies on the direct-call path; hooks still fire on the follow-up
textual response.

Fixes #6529

* style: shorten the tool-call guard comment
2026-07-14 09:49:21 -04:00
Vinicius Brasil
9d72e269e4 Remove redundant CEL text helper (#6528)
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
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
This commit removes the redundant CEL text helper, in favor of the
easier interpolation syntax.
2026-07-13 14:29:36 -04:00
João Moura
fb8e93be25 fix(flow): don't double-append the turn reply when a handler trims history (#6510)
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
Mark stale issues and pull requests / stale (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
handle_turn() (and stream_turn) decided "did the handler append its
reply?" by snapshotting the assistant-message count before kickoff and
appending the stringified result when the count came back unchanged. A
handler that appends its reply and then trims state.messages to a cap —
a normal bounded-context pattern — left the count unchanged, so the
fallback appended the reply a second time on every turn once trimming
engaged, and the duplicates then crowded real turns out of the capped
window.

Replace the count heuristic with an explicit per-turn flag:
append_assistant_message() sets _assistant_reply_appended, handle_turn
and stream_turn clear it before kickoff and only fall back when no
assistant message was appended during the turn. The now-unused
_assistant_message_count() helper is removed.

Fixes EPD-181.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 20:01:07 -07:00
João Moura
4fdb7f2bfb fix(tools)!: make tool-result caching opt-in instead of on by default (#6509)
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
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
* fix(tools)!: make tool-result caching opt-in instead of on by default

Tool-result caching defaulted to on (Crew.cache=True, and standalone
agents self-wired a CacheHandler at construction), so an LLM calling
the same tool with identical arguments twice in one run silently got
the first result back without the tool executing. For live-data tools
that is a confidently stale answer; for state-mutating tools the second
action is silently dropped.

Caching is now opt-in with the machinery unchanged:
- Crew.cache defaults to False; Crew(cache=True) restores today's
  behavior exactly (agents still default to participating when a crew
  offers its handler, and Agent(cache=False) still opts an agent out).
- Standalone agents no longer self-wire a cache; Agent(cache=True) or
  an explicit cache_handler opts in. Previously even Crew(cache=False)
  agents cached via this self-wired handler.
- Per-tool cache_function write gating is unchanged once opted in.

Existing tests that exercised the caching machinery now opt in
explicitly; new regression tests cover the default (both identical
calls execute), crew-level opt-in dedup, and agent-level wiring.

Fixes EPD-180.

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

* fix(agent): don't let copy() turn the cache default into an explicit opt-in

Agent.copy() rebuilds from model_dump(), which includes the field
default cache=True, so the copy's model_fields_set contained "cache"
and _setup_agent_executor wired a CacheHandler the source agent never
opted into (Bugbot review finding). Drop "cache" from the dump when it
was not explicitly set on the source; explicit opt-ins still survive
copying.

Also sync the Crew and BaseAgent class docstrings with the new opt-in
cache semantics (CodeRabbit review findings).

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

* fix(agent): preserve cache_handler-only opt-in across Agent.copy()

copy() excludes cache_handler from the rebuilt agent, so an agent that
opted into tool-result caching solely via an explicit cache_handler
lost caching after copy() (Bugbot review finding). Carry the consent as
cache=True on the copy when the source has a handler wired and hasn't
explicitly disabled caching — the copy wires its own fresh handler,
matching pre-change copy semantics (copies never shared the source's
handler instance).

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

* fix(crew): offer the crew cache handler to the hierarchical manager

The hierarchical manager agent is created in _create_manager_agent,
outside the validation-time agents loop that offers the crew's cache
handler — and managers no longer self-wire a handler — so
Crew(cache=True) hierarchical runs never cached the manager's
delegation tool calls (Bugbot review finding). Offer the shared crew
handler when the crew opted in; a user-provided manager with
cache=False stays excluded via the existing set_cache_handler gate.

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

* fix(agent): only construction-time cache opt-ins survive Agent.copy()

The previous copy() fix treated any wired cache_handler as consent, but
agents that merely received the crew's shared handler at kickoff
(set_cache_handler from Crew(cache=True)) never opted in themselves —
their copies must not become standalone cachers (Bugbot review
finding). Record the opt-in signal in _setup_agent_executor, which runs
at construction before any crew wiring can happen, and have copy()
consult that flag instead of inspecting cache_handler after the fact.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:37:36 -07:00
João Moura
bfa652a7be fix(tools): stop rewriting the authored tool description at construction (#6508)
* fix(tools): stop rewriting the authored tool description at construction

BaseTool.model_post_init silently replaced the public description field
with the LLM-facing composite ("Tool Name: ...\nTool Arguments: ...\n
Tool Description: <authored>"), breaking equality assertions on authored
text and hiding the extra prompt tokens from token-careful authors.

The authored description now survives construction as written. The
composite is composed on demand via a new formatted_description property
on BaseTool and CrewStructuredTool (shared format_description_for_llm
helper), and every prompt path that relied on the baked-in composite —
render_text_description_and_args, ToolUsage._render, and tool-usage
error messages — now renders through it, so the text the LLM sees is
unchanged.

The helper strips any pre-existing composite block before composing, so
tools deserialized from old checkpoints and adapters that still bake the
composite into the field (e.g. the crewai-tools MCP adapter) don't get
double-wrapped. BaseTool._generate_description remains as a no-op hook
because subclasses override it and model_post_init still calls it.

Fixes EPD-179.

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

* fix(tools): harden composite-description handling after review

- Anchor the pre-baked-composite check to the actual three-line block
  shape instead of a naive substring match, so authored prose that
  merely mentions "Tool Description:" is never truncated (CodeRabbit /
  Bugbot review finding). Shared as
  strip_composite_description_prefix() and reused by the function-
  calling schema builder, which had the same naive split.
- Make render_text_description_and_args tolerate duck-typed tools
  without a real formatted_description string (fixes CI: step-executor
  tests pass Mock tools whose auto-created attribute is not a str).

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-07-10 20:33:57 -03:00
João Moura
b65c8487d2 fix(output): expose token usage under both names on agent and crew results (#6507)
Agent.kickoff() returned LiteAgentOutput with a plain dict at
.usage_metrics and no token_usage attribute, while Crew.kickoff()
returned CrewOutput with a UsageMetrics object at .token_usage and no
usage_metrics attribute — so a usage accessor written for one path
raised AttributeError on the other, and every consumer had to
duck-type both shapes.

Give both result types both surfaces, each name with one consistent
shape everywhere: .token_usage is a UsageMetrics object and
.usage_metrics is a plain dict, on both LiteAgentOutput and CrewOutput.
Added as read-only properties, so existing fields, serialization, and
constructors are unchanged.

Fixes EPD-178.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 20:29:34 -03:00
João Moura
a8b3ecb723 fix(agent): report per-call usage metrics on kickoff results (#6506)
* fix(agent): report per-call usage metrics on kickoff results

Agent.kickoff() populated result.usage_metrics from the LLM instance's
lifetime token accumulator, so counts grew across calls and pooled
across agents sharing one LLM object — a second agent's first turn
appeared to cost the whole preceding session.

Snapshot the accumulator when a kickoff starts and report the delta on
the result (guardrail retries included), via the new
UsageMetrics.delta_since(). The LLM instance's cumulative counters are
untouched: get_token_usage_summary() keeps lifetime totals for
crew-level aggregation, and its docstring now states that scope
explicitly. Applies to both Agent and the deprecated LiteAgent, sync
and async paths.

Fixes EPD-177.

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

* refactor(agent): drop lite_agent.py diff, add guardrail-retry usage test

Per review: LiteAgent's kickoff path is no longer used, so the per-call
usage snapshot only needs to live in agent/core.py — revert the
lite_agent.py changes entirely. This also removes the duplicated
_current_usage_summary helper and the instance-attr baseline CodeRabbit
flagged.

Add the requested guardrail-retry regression test: a guardrail that
rejects the first attempt and accepts the second must yield
usage_metrics covering both attempts (2x a single-attempt kickoff).

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:17:45 -07:00
João Moura
7967b19057 fix(flow): stop replaying previous turn's intent when route_turn() returns falsy (#6505)
In conversational flows, a falsy return from an overridden route_turn()
fell back to the sticky state.last_intent from a previous turn, silently
re-running the prior turn's handler for an unhandled input.

The fallback exists for the legacy default_intents path, where
receive_user_message() classifies the intent fresh each turn. Track that
per-turn classification in _turn_classified_intent (cleared on every turn
reset) and route on it instead, so a falsy route_turn() now falls through
to the built-in answer_from_history/converse defaults and never reuses
stale routing state.

Fixes EPD-176.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-07-10 11:44:57 -07:00
João Moura
85c467dfe2 feat(cli): run declarative flows on the TUI (headless terminal fallback) (#6484)
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
* feat(cli): run declarative flows on the TUI with a headless terminal fallback

Declarative flows now run on the CrewRunApp TUI when interactive, matching
declarative crews and conversational flows. Headless contexts — CREWAI_DMN
(deploy), piped output, CI, any non-TTY — fall back to the direct-terminal
kickoff, gated by is_interactive() (folds in the CREWAI_DMN check and requires
a real TTY).

The TUI shows per-method progress: a new STEPS panel driven by flow method
events (FlowStarted / MethodExecutionStarted/Finished/Failed), each labeled
with its declarative call type (crew/agent/expression/…) read from the flow
definition. Crews/agents inside a method keep streaming in the main panel via
the existing crew/task/LLM handlers.

- crew_run_tui.py: _run_flow_worker (flow.kickoff in a thread worker; reuses
  _on_crew_done/_on_crew_failed + _stringify_output), _is_flow_run gate so crew
  rendering is byte-identical, flow-event subscriptions building _flow_steps,
  and the STEPS sidebar + flow-aware header.
- run_declarative_flow.py: is_interactive() branch → _run_declarative_flow_tui
  (EventListener, method-type map from flow._definition, crew-parity exit codes
  and deploy chaining) or the existing terminal path.

Deviation from the approved plan: gate on is_interactive() rather than
is_dmn_mode_enabled() alone, so non-TTY runs (CI/pipes/CliRunner) never launch
a TUI — this also keeps existing headless flow tests green.

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

* fix(cli): force flow events on for the TUI so STEPS renders under suppress_flow_events

Review follow-up: the STEPS panel and header are driven by flow method events
(FlowStarted / MethodExecution*), but the declarative runtime skips emitting
those when the flow declared config.suppress_flow_events. Interactive TUI runs
would then keep STEPS on "waiting…" and the header on "Starting flow…" while
nested crews still execute.

_run_declarative_flow_tui now forces flow.suppress_flow_events = False for the
interactive run (mirroring how the conversational path mutates the flow for the
TUI). The headless/terminal path never reaches this and keeps the flow's
declared setting. Regression test: test_run_declarative_flow_tui_enables_flow_events.

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

* fix(cli): clear flow header's current method when a method ends

Review follow-up: the flow header keys off _current_method, which was set on
MethodExecutionStarted but never cleared on Finished/Failed. Between steps (or
after a failed method before kickoff exits) the header kept spinning the old
method name while the STEPS sidebar already showed it done/failed.

_clear_current_method now drops the header's active method when it ends,
falling back to another still-active step (methods can overlap) or none. The
header's idle fallback shows "Working…" once a step has run and "Starting
flow…" only before the first method.

Tests: test_current_method_clears_and_falls_back_across_overlap, plus a
_current_method assertion in test_flow_method_events_build_steps.

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

* fix: suppress flow console panels in TUI mode; clear header agent on method change

Two review follow-ups:

1) Method panels break Textual TUI (Cursor): forcing suppress_flow_events off
   so the STEPS panel receives events also un-gated the EventListener's Rich
   flow/method panels (ConsoleFormatter.print_panel prints is_flow=True panels
   regardless of verbose), which interleave with Textual and corrupt the TUI.
   print_panel now skips is_flow panels when is_tui_mode() is set (the same
   context the TUI worker already establishes and the tracing listeners already
   honor). Non-TUI/headless flow runs are unaffected. Test:
   test_console_formatter_tui_mode.

2) Flow header showed a stale agent (CodeRabbit): _current_agent persisted
   across methods. It's now cleared when a method starts and when the active
   method changes, so the header never shows the previous method's agent until
   a new agent event arrives. Test: test_flow_method_transitions_clear_current_agent.

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

* fix(cli): keep flow name over nested crews; show paused flow methods

Two review follow-ups on the flow TUI:

1) Crew kickoff renamed the flow (Cursor): CrewKickoffStartedEvent overwrote
   _crew_name / the app title with a nested `call: crew` step's crew name, so
   the post-run summary could be labeled with a child crew. The rename is now
   gated on `not _is_flow_run`, preserving the flow's name; crew runs still
   adopt the crew name. Tests: test_crew_kickoff_does_not_rename_flow_run,
   test_crew_kickoff_renames_in_crew_mode.

2) Paused methods showed active (Cursor): the TUI didn't handle
   MethodExecutionPausedEvent, so a @human_feedback pause left the STEPS
   spinner running (flow status panels are suppressed in TUI mode). It now
   marks the step "paused" (⏸, teal) and the header shows "waiting for
   feedback" instead of a spinner. Test: test_method_paused_marks_step_paused.

Note: interactively *providing* human feedback from the flow TUI is a separate
follow-up; this only makes the pause visible instead of a silent stuck spinner.

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

* fix(cli): run human-feedback declarative flows on the terminal, not the TUI

Two review follow-ups, both rooted in @human_feedback methods:

- Paused flow marked complete (Cursor): async human feedback makes kickoff
  RETURN a HumanFeedbackPending marker (not raise), which _run_flow_worker
  would stringify and report as a successful completion with exit 0.
- Sync feedback breaks TUI (Cursor): default (sync) @human_feedback collects
  input via the flow runtime's Rich console.print + blocking input(), which
  interleaves with Textual and leaves the user unable to review output or
  submit feedback.

run_declarative_flow now routes any flow whose declarative definition declares
human feedback (_flow_uses_human_feedback) to the terminal path, where blocking
input and Rich prompts work natively — regardless of interactivity. Non-feedback
flows still get the TUI. Tests: test_flow_uses_human_feedback_detection,
test_human_feedback_flow_uses_terminal_even_when_interactive.

Fully interactive human feedback inside the TUI remains a separate follow-up.

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

* refactor(cli): address review — Flow typing, debug logging, flow-vs-crew naming

Review follow-ups from @lucasgomide:

- Type flow helpers as Flow[Any] (via TYPE_CHECKING import) instead of Any and
  drop the defensive getattr chains — _definition is a typed PrivateAttr and
  name/suppress_flow_events are typed fields, so attribute access is safe.
- Replace the silent `except Exception: pass` blocks with logger.debug(...,
  exc_info=True) so unexpected failures are diagnosable in the field
  (_flow_method_types, _flow_uses_human_feedback, suppress_flow_events toggle).
- Flow-vs-crew naming: the flow worker now uses group="flow" (was the
  misleading "crew"), and the shared completion/failure handlers report the
  run with an entity-aware noun ("flow" vs "crew") via _run_noun.

Deferred (separate PR): the os._exit(130) hard-kill on user quit is kept as-is
to match the existing crew convention (run_crew._run_json_crew).

Tests: test_flow_done_uses_flow_wording_for_unfinished_tool; existing crew
wording tests unchanged.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 11:42:12 -03:00
Lorenze Jay
7baf8f9ba1 improving custom OpenAI urls (#6490)
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
Check Documentation Broken Links / Check broken links (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
* Support legacy OpenAI base URL env var

* Add custom OpenAI-compatible endpoint support

* Refactor OpenAI completion module test to restore original module state

- Added logic to save and restore the original OpenAI completion module during the test to prevent issues with class re-imports affecting subsequent tests.
- Ensured that the test checks for the presence of the module and its attributes only after the module is properly reloaded.
- Improved test reliability by avoiding potential failures due to module state changes across tests.

* addressing comments
2026-07-09 15:30:16 -07:00
Lucas Gomide
860817cbcd Drain memory writes before kickoff and flow completion events (#6497)
* fix: drain memory writes before kickoff and flow completion events

Background memory saves from the final task could still be in flight
when `CrewKickoffCompletedEvent`/`FlowFinishedEvent` fired, so telemetry
listeners tore down before `MemorySaveCompletedEvent` arrived and the
save span surfaced as "Span orphaned" errors in traces despite the
record persisting. `Crew` now drains all pending saves — including
per-agent `agent.memory` pools, which the old `finally`-only drain
missed entirely — before emitting the completion event, with the same
ordering applied to both `FlowFinishedEvent` emit paths in the flow
runtime.

* fix: address review findings on the memory drain paths

Bugbot and CodeRabbit flagged gaps in the drain coverage: the
hierarchical `manager_agent` memory pool was never drained,
`Crew.akickoff` lacked the exception-path safety net that sync
`kickoff` has, and `finalize_session_traces` emitted the deferred
session-end `FlowFinishedEvent` without draining first. Also offloads
the pre-emit drains in the flow runtime to `asyncio.to_thread` so the
blocking wait doesn't stall other coroutines sharing the event loop.

* fix: flush event bus after memory drain in flow completion paths

Bugbot flagged that flow paths went straight from the memory drain to
`FlowFinishedEvent`, while crew kickoff flushes the bus in between.
Save completion events emitted during the drain could still have
pending async handlers when flow-finished triggered trace teardown.
Adds a `crewai_event_bus.flush()` after the drain at both flow runtime
emit sites and in `finalize_session_traces`, mirroring
`Crew._create_crew_output`.
2026-07-09 15:01:14 -04:00
João Moura
3246cb30f5 fix(cli): unify crewai run flow input resolution and prompt from the state schema (#6466)
* fix(cli): unify `crewai run` flow input resolution; prompt from state schema

`crewai run` resolved the configured [tool.crewai] flow, but `--inputs` was
hard-gated behind `--definition` and routed through a separate branch — the two
ways of pointing at the same flow didn't share resolution, and required inputs
were never detected, prompted, or validated (a missing field only blew up at
runtime).

Now inputs and definition come from one place:

- Remove the "--inputs requires --definition" gate (cli.py, run_crew.py,
  run_declarative_flow.py). `--inputs` alone resolves the configured flow,
  exactly like a bare `crewai run`; `--definition` is purely an override. The
  project-env re-exec forwards `--inputs` instead of rejecting it.
- Read the flow's state schema from the runtime Flow instance
  (`type(flow.state).model_json_schema()`), which is reliable for both inline
  `json_schema` and ref-imported `pydantic` state (the static definition's
  json_schema is None for the common ref case).
- Plain `crewai run` detects required state fields (minus those satisfied by
  state defaults) and prompts for them interactively, showing each field's
  description; skipped in non-interactive / CREWAI_DMN mode.
- Validate against the schema before kickoff: pointed
  "Missing required input 'x' — <description>" errors, and warn on unknown keys
  with a did-you-mean suggestion (catches typos like `prospect_emai`).

`--inputs` on a non-flow project now errors clearly ("only supported for
declarative flows") instead of the old confusing gate.

Tests: schema-driven prompt/validate/override paths, unknown-key warning,
defaults-satisfy-required, type validation, and re-exec input forwarding.

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

* fix(cli): forward reserved `id` input to flow kickoff; ruff format

- Cursor: the schema filter treated an `id` key in --inputs as unknown and
  dropped it, regressing kickoff's persistence-restore support (inputs["id"]).
  Let `id` pass through untouched (test: reserved_id_input_is_forwarded).
- Apply ruff format to run_declarative_flow.py (fixes the lint-run
  `ruff format --check` step).

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

* fix(cli): don't block persistence-restore resume on schema validation

Cursor (High): `crewai run --inputs '{"id":"…"}'` is a persistence resume —
kickoff hydrates full state from storage, so schema-required fields may come
from the restored state rather than --inputs. The new required-field
prompt/validation was erroring/prompting before kickoff, breaking resume. When
`id` is present in --inputs, forward the inputs unchanged and skip the
prompt/validation. Test: test_id_only_input_skips_required_validation.

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

* fix(cli): load project .env in the declarative-flow runner

The declarative-flow path never loaded .env — flow projects (type = "flow")
missed API keys/config that crew projects pick up. The JSON-crew path loads
Path.cwd()/.env with override=True (run_crew._run_json_crew); mirror that at
the top of run_declarative_flow() so flow projects behave the same regardless
of where crewai is installed. Test: run_declarative_flow_loads_project_env.

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

* feat(cli): unify runtime-input prompting across declarative flows and crews

Declarative (JSON) crews now resolve inputs the same way declarative flows
do, via a shared crewai_cli.input_prompt module (prompt_for_inputs,
parse_inputs_json, closest_name, is_interactive):

- accept --inputs (previously rejected for crews), forwarded to the crew
  subprocess via CREWAI_JSON_CREW_INPUTS and validated before spinning up uv
- layer --inputs over the crew's declared `inputs` defaults
- prompt for missing {placeholder}s with the same UX as flows, and error
  cleanly with a pointed per-name message when non-interactive
- warn on unknown keys with a "did you mean" suggestion

Unlike flows — whose state schema is authoritative, so unknown keys are
dropped — the crew placeholder scan is heuristic (agent/task text fields
only), so unrecognized keys are warned about but kept, to avoid discarding a
value a field the scan doesn't cover may rely on.

--inputs remains rejected for classic (Python/YAML) crews, which take their
inputs from main.py. run_declarative_flow's private input helpers move to the
shared module with no behavior change.

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

* test(crewai): update mirrored CLI test after run_crew input refactor

lib/crewai/tests/cli/test_run_crew.py imports crewai_cli internals and is
collected by the lib/crewai test job (Run Tests). It still imported
_prompt_for_missing_inputs, which was replaced by _resolve_crew_inputs, so
the module failed to import — erroring pytest at collection and cancelling
the rest of the matrix via fail-fast.

Point it at _resolve_crew_inputs and patch the prompt in the shared
crewai_cli.input_prompt module where prompting now lives.

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

* fix(cli): filter unknown --inputs keys even on flow persistence restore

Review follow-up: the `id` (persistence-restore) branch of
_resolve_flow_inputs returned the raw payload, so typo keys passed alongside
`id` skipped the unknown-key warning/drop and reached kickoff — which can
fail strict (extra="forbid") flow state models. The restore path now still
warns on and drops unknown keys (keeping `id` and known state fields); it
only skips the required-field prompt and pre-kickoff validation, which
persistence hydrates. Regression test: test_id_restore_still_drops_unknown_keys.

Also drop the duplicate module import in test_input_prompt.py (both `import`
and `from ... import` of crewai_cli.input_prompt) flagged by the code-quality
bot; monkeypatching now uses the string target form.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 22:17:09 -03:00
Lorenze Jay
799ab0f548 ensure we are writing version for flows (#6467)
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
Mark stale issues and pull requests / stale (push) Has been cancelled
2026-07-06 18:47:27 -07:00
Lorenze Jay
e55e710df0 Implement message setup and feedback handling in AgentExecutor (#6465)
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
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
* Implement message setup and feedback handling in AgentExecutor

- Added  method to streamline message preparation for agent execution, allowing for integration with human input providers.
- Introduced  and  methods to manage the state during feedback processing.
- Enhanced  and  methods to re-run the executor flow using existing feedback messages.
- Updated tests to verify the new message setup and feedback handling functionality, ensuring compatibility with human input providers.

* dont commit runner

* Remove xfail marker from test_crew_train_success as training feedback migration to AgentExecutor is complete.

* fix runtype errors

* fix test

* revert

* mypy fix

* handled reset iterations
2026-07-06 15:28:37 -07:00
Vinicius Brasil
2b90117e88 Add repository agents to flow definitions (#6437)
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
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
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
Inline agent and crew actions can now use repository-backed agents
without duplicating role, goal, and backstory in each definition.

Examples:
* `agent.with.from_repository: support_specialist`
* `crew.with.agents.researcher.from_repository: researcher`

`PlusAPI.get_agent` now uses the shared synchronous request path so
project loaders can fetch repository agents without nested event loops.
2026-07-02 14:28:01 -07:00
Vinicius Brasil
24901cd4f6 Support templated Flow action inputs (#6426)
Flow action inputs now support `${...}` inside strings, not only
strings that are fully wrapped in one expression. This lets authored
flows use simple prompt-like values such as:

* `query: "News about ${state.topic}"`
* `input: "Ticket ${text(state, "ticket.id", "unknown")}"`
* `sources: ["${state.primary_source}", "archive-${state.topic}"]`

Whole-expression values still preserve their runtime type, so
`${state.limit}` remains a number and `${state.domains}` remains a list.
Mixed literal and expression strings render as text.

This removes the need to build labeled strings with CEL concatenation,
which was hard to read, easy to quote incorrectly in YAML, and a poor
fit for the Flow authoring skill examples.
2026-07-02 08:57:50 -07:00
Vinicius Brasil
f630c471cf Document flow agent options (#6420)
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
* Document flow agent options

Document and type inline Flow agent options so authored flows can set:
* `llm.model`, `llm.max_tokens`, and `llm.max_completion_tokens`
* `planning_config.max_attempts`
* `allow_delegation`
* `max_iter`
* `max_rpm`
* `max_execution_time` in seconds

Also tell the flow skill to omit optional fields unless needed.

* Potential fix for pull request finding 'Unused import'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-07-01 14:08:44 -07:00
Vinicius Brasil
1452ee2021 Add text helper to flow skill example (#6406)
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
Check Documentation Broken Links / Check broken links (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
2026-06-30 21:18:58 -07:00
Vinicius Brasil
629f5d537b Reject self-listening flow methods (#6405)
A method that listens to its own name can re-trigger itself or collide
with router events. Rejecting that definition keeps declarative and
Python-authored flows aligned before kickoff.
2026-06-30 19:42:01 -07:00
Vinicius Brasil
ba2dafdeda Add text helper for flow CEL prompts (#6404)
CEL string concatenation currently fails when prompt builders read
missing or null fields. This commit adds `text(root, "path", "default")`
custom CEL helper so prompt text can safely read nested state/output
values.
2026-06-30 16:45:39 -07:00
Daniel Barreto
ba855bae2b feat: repoint template commands to crewAIInc-fde org (#6394)
Point `crewai template list`/`template add` at the crewAIInc-fde GitHub
org so the FDE template_* repos are listed and installed instead of the
crewAIInc ones.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-06-30 11:32:54 -07:00
Vinicius Brasil
c157199065 Support inline skill definitions (#6396)
* Support inline skill definitions

This commit adds inline skill loading without a need for a file. It also
DRYs the skill loading feature.

* Address code review suggestions
2026-06-30 11:22:05 -07:00
Lorenze Jay
8eed457e70 Define stream frame protocol for flows (#6391)
* Define stream frame protocol for flows

* Add direct LLM streaming helpers

* Unify flow streaming frame items

* Update flow streaming integration properties

* Drop stream frame debug runner example

* Address streaming contract review feedback

* Replay cached stream frame projections

* Remove stream frame version field

* Fix streaming contract docs link

* Preserve LLM instance state for stream events

* Address streaming review cleanup
2026-06-30 10:53:48 -07:00
Vinicius Brasil
04fec31f1e Type tool and app in CrewDefinition (#6395)
* Type tool and app in CrewDefinition

This commit fixes a bug in the CrewDefinition class where the tool and
app were not being added.

* Type mcps= parameter
2026-06-30 10:24:21 -07:00
Vinicius Brasil
1556dbea3e Add generated Flow Definition authoring skill (#6393)
* Add generated Flow Definition authoring skill

Generate a portable skill from the Flow Definition schema so agents can
author valid declarative flows with the same reference CrewAI uses to
validate them. New declarative flow projects now write this skill.

```python
from crewai.flow.flow_definition import FlowDefinition
skill = FlowDefinition.skill(skips=(), examples_format="yaml")
```

* `examples_format` accepts `"yaml"` or `"json"`.
* Supported skips: `conversational`, `non_linear_flows`, `each`, `hitl`, `persistence`, `config`, `expression_action`, `script_action`, `tool_action`

The generated skill includes authoring rules, a routed crew example, and
an API reference extracted from the Flow, action, state, agent, crew,
and task Pydantic schemas.

* Fix declarative flow scaffold without framework import

* Fix skipped expression action guidance

* Fix markdown links in skill
2026-06-30 09:13:33 -07:00
Vinicius Brasil
596150188b Require explicit CrewAI project definitions (#6358)
* Require explicit CrewAI project definitions

JSON crews and declarative flows now resolve from `[tool.crewai]`
metadata instead of implicit filename discovery. This makes project type
selection deterministic, prevents stray `crew.json(c)` files from changing
CLI behavior, and centralizes definition path validation for run, install,
deploy validation, plotting, and memory reset paths.

`[tool.crewai].definition` must be a project-local file path. Absolute
paths, `~`, missing files, directories, and paths escaping the project root
are rejected so deploy and runtime commands use the same contract.

Breaking changes and migration paths:

* JSON crew projects are no longer discovered from `crew.json` or
  `crew.jsonc` alone. Add explicit metadata:

  ```toml
  [tool.crewai]
  type = "crew"
  definition = "crew.jsonc"
  ```

* Declarative flow projects must use a valid project-local definition path:

  ```toml
  [tool.crewai]
  type = "flow"
  definition = "flows/research.yaml"
  ```

* `Flow.from_definition(definition)` is removed. Use:

  ```python
  Flow.from_declaration(contents=definition)
  ```

* `FlowDefinition.to_json()` and `FlowDefinition.to_yaml()` are removed.
  Use `FlowDefinition.to_dict()` and serialize with the caller's JSON or
  YAML library.

* `FlowDefinition.from_dict()` is removed. Use:

  ```python
  FlowDefinition.from_declaration(contents=data)
  ```

* `FlowDefinition.json_schema()` is removed. Use Pydantic's schema API only
  where schema generation is intentionally needed:

  ```python
  FlowDefinition.model_json_schema(by_alias=True)
  ```

* `crewai_cli.run_crew.find_crew_json_file()` and `_has_json_crew()` are
  removed. Use `configured_project_json_crew()` or the shared
  `crewai_core.project.configured_project_definition("crew")` helper.

* `crewai reset-memories` now only loads JSON crews declared through
  `[tool.crewai].definition`, and invalid declared JSON crew definitions
  fail instead of silently falling back to classic crew discovery.

* Address code review comments
2026-06-26 12:07:03 -07:00
Lorenze Jay
9b31226494 Track conversational flow turn usage in telemetry (#6324)
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
* Track conversational flow turn usage in telemetry

* adjusted name to flow:conversation_turn

* only mark on turn completed event

* ensure tui also emits these events
2026-06-25 11:02:07 -07:00
Vinicius Brasil
340d23ae5d Remove StateProxy from flow state access (#6327)
`StateProxy` looked like a thread-safety boundary, but it only protected
a small slice of state operations. Some examples of operations that were
not covered:

- `self.state.counter += 1`, `self.state["counter"] += 1` (increments)
- `self.state.user.profile.score += 1` (nested object mutations)
- `self.state.config["limits"]["max"] = 10` (mutation through model fields)
- `self.state.items[0].status = "done"` (list/container mutations)

This commit decided to remove it completely for simplicity and
performance:

- Simpler runtime code
- attr read: 24x faster, attr write: 27x faster, list append: 19x faster (local benchmark)
- Clearer concurrency contract (lifecycle locks remain, but arbitrary
  shared state mutation is not presented as thread-safe)
2026-06-24 16:37:51 -07:00
Vinicius Brasil
7738a1d30c Make declarative refs work across flows and crews (#6326)
Declarative flows already used `module:qualname` refs for runtime
objects, but crew JSON tools still had their own lookup path. That meant
examples like `project_tools:LookupTool` were treated as named
`crewai_tools` lookups and failed with guidance that only mentioned
`SerperDevTool` or `custom:<name>`. Invalid refs such as
`not_tools:NotATool` also missed the same BaseTool validation used by
flow tool actions.

Move ref resolution into a shared declarative helper, use it from flow
tool actions and crew JSON loading, and require tool refs to resolve to
`BaseTool` classes before instantiation. Validation still checks tool
refs structurally, so validating a crew does not import or execute
project code.
2026-06-24 15:11:59 -07:00
Vinicius Brasil
156b3500b4 Fix JSON schema flow state kickoff inputs (#6325)
Allow required JSON schema state fields to be supplied by kickoff inputs
instead of requiring every field to exist in state.default before
runtime.

Example: a flow with required lead_name and no state.default can now run
with kickoff inputs={"lead_name": "Ada Lovelace"}.
2026-06-24 13:55:38 -07:00
Rip&Tear
fac3e3579b Fix symlink path traversal in skill archive extraction (#6235)
* Fix symlink path traversal in skill archive extraction

`_safe_extractall` (the Python < 3.12 fallback used by `crewai skills`
archive unpacking) validated each member's *name* against the destination
but never validated symlink/hardlink *targets*. A malicious skill tarball
could plant a symlink escaping the destination (e.g. `link -> /home/user/.ssh`)
followed by a regular member written through it (`link/authorized_keys`),
escaping `dest` even though every member name resolves inside it — the
classic symlink-extraction traversal.

The 3.12+ path (`extractall(..., filter="data")`) already blocks this; the
fallback now mirrors it by rejecting absolute link targets and any link
target that resolves outside the destination directory.

Adds regression tests covering absolute and relative escaping symlinks plus
benign in-tree symlinks and ordinary archives.

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

* Harden skill cache archive extraction

* Reject special skill archive members

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:50:41 -07:00
Vinicius Brasil
3452e5c187 Add unified declarative flow loading (#6308)
Add a single declaration loader shared by API and CLI callers.

- Add FlowDefinition.from_declaration for FlowDefinition instances, dictionaries, YAML/JSON strings, and file paths
- Add Flow.from_declaration to build runnable flows directly from the same inputs
- Route declarative flow CLI loading through Flow.from_declaration so path handling and validation stay centralized

```
# Load just the serializable definition when you do not need to run it yet.
definition = FlowDefinition.from_declaration(path="flows/research.crewai")
definition = FlowDefinition.from_declaration(contents=flow_yaml)
definition = FlowDefinition.from_declaration(contents=flow_dict)

# Build a runnable flow directly from the same declaration inputs.
flow = Flow.from_declaration(path="flows/research.crewai")
flow = Flow.from_declaration(contents=flow_yaml)
flow = Flow.from_declaration(contents=flow_dict)
flow = Flow.from_declaration(contents=definition)

# Run it like any other flow.
result = flow.kickoff(inputs={"topic": "AI agents"})

# The CLI now goes through the same path-based loader.
# crewai run --definition flows/research.crewai
```
2026-06-23 12:02:18 -07:00
Vinicius Brasil
221dfdb08e Consolidate crewai run and crewai flow kickoff (#6296)
Make `crewai run` the single execution path for crews and flows, with
`crewai flow kickoff` kept as a deprecated compatibility alias.
2026-06-22 20:44:08 -07:00
Vinicius Brasil
720a4c7216 Keep flow method progress visible for nested crews (#6295)
Inline crews default to `verbose=False`. They set the shared formatter's
`verbose` value in `lib/crewai/src/crewai/crew.py`, which could hide
flow method status from `lib/crewai/src/crewai/events/utils/
console_formatter.py`.

Remove that `verbose` check for flow method status. Flow output is still
controlled by `suppress_flow_events`.

Normal quiet crews are unchanged because crew, task, and agent logs
still use their own `verbose` checks.
2026-06-22 20:37:16 -07:00
Vinicius Brasil
4b2ce00a09 Add declarative Flow CLI support (#6294)
* Add declarative Flow CLI support

Currently, declarative flows can be loaded by the runtime, but the CLI
still treats them as an experimental definition file instead of a
first-class Flow project shape.

With this PR, `crewai create flow --declarative` scaffolds a YAML-backed
Flow project, and `crewai run`, `crewai flow kickoff`, and `crewai flow
plot` can run against the configured definition.

This also lets crew actions reference reusable crew definition files or
folders and override their inputs from the Flow definition, so
declarative flows can compose existing declarative crews without
inlining everything.

* Address code review comments
2026-06-22 19:58:17 -07:00
Vinicius Brasil
0391febc6c Allow @router() as start method of a flow (#6288)
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
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
This commit fixes a bug where a router method could not be the start
method of a flow.

This is useful when you want to route against the initial state, or even
stack two routers.
2026-06-22 14:04:45 -07:00
Vinicius Brasil
9db2d44766 Add typed output schemas for CrewAI tools (#6236)
Some checks failed
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
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 / pip-audit (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
Currently, tools have a strong input contract through `args_schema`, but no
output contract. This means that anything a tool outputs is converted to
string.

Not only the contract is weak, but the "invisible" conversion to string can
have unexpected effects when the tool returns complex objects like dicts and
arrays.

With this PR, a tool can _optionally_ define an output contract with
`output_schema`. CrewAI validates the raw result and sends the agent JSON.

```python
class ProductResult(BaseModel):
    sku: str
    name: str
    in_stock: bool

class ProductLookupTool(BaseTool):
    name: str = "Product Lookup"
    description: str = "Look up product availability by SKU."

    def _run(self, sku: str) -> ProductResult:
        return ProductResult(sku=sku, name="USB-C dock", in_stock=True)
```

If the result does not match the schema, CrewAI warns and falls back to
`str(raw_result)` instead of failing the run:

```python
@tool("Product Lookup", output_schema=ProductResult)
def product_lookup(sku: str) -> dict[str, object]:
    return {"sku": sku, "name": "USB-C dock", "in_stock": True}

#=> RuntimeWarning: Failed to validate or serialize output from tool 'Bad Product Lookup' using output_schema 'ProductResult'... Falling back to str(raw_result).
```

This is additive and non-breaking. Existing tools do not need to change. Tools
without `output_schema` keep the old string behavior. Invalid typed outputs
warn and fall back to the old formatting path.
2026-06-19 14:33:51 -07:00
Vinicius Brasil
bc2c2a858c Add single agent action to Flow definitions (#6226)
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 / 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 / Build nightly packages (push) Has been cancelled
Nightly Canary Release / Check for new commits (push) Has been cancelled
Nightly Canary Release / Publish nightly to PyPI (push) Has been cancelled
* Add single agent action to Flow definitions

Lets a flow method build and run a single CrewAI agent directly, without
wrapping it in a crew. Same idea as the existing `crew` action, but for
one agent.

  methods:
    answer:
      do:
        call: agent
        with:
          role: Analyst
          goal: Answer questions
          backstory: Knows things.
          input: "${state.question}"
      start: true

* `input` is required and interpolated from flow state, like
  `${state.question}` or `${item}` inside an `each` loop
* optional `response_format` points at a Pydantic model (`{"python":
  "models.AnswerModel"}`) to get structured output
* `input` must be a string and its CEL is validated at load time, so bad
  expressions like `${state.}` fail early

* Simplify test code
2026-06-18 14:53:33 -07:00
Vinicius Brasil
b0816e00b6 Validate flow CEL expressions at definition load time (#6224)
* Validate flow CEL expressions at definition load time

Promote CEL expression handling to a public Expression API and validate expressions when a FlowDefinition is built instead of when it executes.

Invalid CEL syntax or unknown roots now raise ValidationError from FlowDefinition.from_yaml() and FlowDefinition.from_dict(). Expressions may reference state and outputs, plus item inside each.do; bare identifiers are rejected as unknown roots.

For with values, the CEL contract is intentionally simple: after trimming whitespace, a string is evaluated as CEL only if it starts with ${ and ends with }. Anything else is treated as a literal value, so partial interpolation is not supported. If the content inside the wrapper is not valid CEL, validation fails.

Examples:

```text
"${state.topic}"          -> evaluated, returns state.topic
"topic is ${state.topic}" -> literal string
"${state.topic} suffix"   -> literal string
"${'a'}${'b'}"              -> invalid CEL
```

* Honor explicit empty-context overrides in evaluate() / render_template()
2026-06-18 12:18:22 -07:00
Vinicius Brasil
b5e23a87f2 Add optional if expression to each.do steps (#6214)
* Use explicit name/action shape for each.do steps

* Add optional `if` expression to `each.do` steps

Lets a step inside an `each` action run conditionally based on a CEL
expression evaluated against `item` and prior step `outputs`.
2026-06-18 10:33:13 -07:00
João Moura
504c5c9b04 JSON crew fixes (#6217)
* feat: update pyproject.toml to specify wheel targets

Added a new section to the pyproject.toml file to include only specific files in the wheel build, enhancing the packaging process. Updated tests to verify the inclusion of these targets.

* feat: add memory save event handling to activity log

Implemented event handlers for MemorySaveStartedEvent, MemorySaveCompletedEvent, and MemorySaveFailedEvent in the crew_run_tui module. This allows the application to log memory save operations, capturing their status and details in the activity log. Added corresponding tests to verify the correct logging behavior for successful and failed memory saves.

* feat: enhance memory save event handling in activity log

Added functionality to suppress nested memory save events and updated the handling of MemorySaveStartedEvent, MemorySaveCompletedEvent, and MemorySaveFailedEvent to improve logging accuracy. Introduced new tests to verify the correct behavior of memory save events, including scenarios for nested events and completion updates for timed-out entries.

* Fix memory save activity log handling

* Normalize alpha package versions

* Update scaffolded crew dependency

* feat: add button to copy setup instructions for CrewAI coding agents

Introduced a button in the documentation that allows users to easily copy setup instructions for CrewAI coding agents. The instructions include installation steps, environment setup, and best practices for using the CrewAI CLI. This enhancement aims to streamline the onboarding process for new users.

* Improve missing CrewAI install guidance

* fix: address pr review feedback

* fix: avoid mismatched memory save rows

* fix: wait for queued memory save events

* fix: avoid matching memory saves on missing ids

* chore: normalize prerelease version to 1.14.8a1
2026-06-18 14:14:54 -03:00
Vinicius Brasil
218dc82bf7 Replace flow diagnostics with logging (#6212)
This commit removes flow diagnostics from the definition. These were
used for logging only, and should not be coupled to the definition.
2026-06-17 19:37:52 -07:00
Vinicius Brasil
7374486f00 Document FlowDefinition fields in the JSON schema (#6198)
Add a description and examples to every FlowDefinition field and
standardize on `typing.Literal`, so the generated JSON schema documents
itself — each action discriminator, state branch, and config option
explains what it is and shows a realistic value.

Examples live on individual fields only, never at the model level, which
keeps the schema readable for tooling that renders field-level help.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 18:49:01 -07:00
Vinicius Brasil
5bd10ee2c4 Add script/code block action to FlowDefinition (#6197)
* Add script/code blocks to FlowDefinition

Let a Flow method run trusted inline Python with `call: script`. The code
is compiled once into a generated function and receives the runtime
values as arguments.

```yaml
methods:
  normalize:
    start: true
    do:
      call: script
      code: |
        import math
        state["rounded"] = math.ceil(state["raw_score"])
        return f"rounded:{state['rounded']}"
```

Even though this shares the same surface of tools (custom code), I
decided to make it opt-in for now, using
`CREWAI_ALLOW_FLOW_SCRIPT_EXECUTION=1`.

* Address code review comments
2026-06-17 18:38:41 -07:00
João Moura
d3c37c4a40 Enhance memory reset functionality and JSON crew handling (#6195)
* Enhance memory reset functionality and JSON crew handling

- Added `reset_all` method to the `Memory` class to reset the entire memory store, ignoring `root_scope`.
- Updated the `Crew` class to utilize `reset_all` when resetting memory.
- Enhanced the `_reset_flow_memory` function to check for `Memory` instances and call `reset_all` accordingly.
- Introduced helper functions to load JSON crew configurations and handle project declarations, improving the reset command's flexibility.
- Added tests to validate the new JSON crew memory reset behavior and ensure proper handling of declared flow projects.

* Fix memory reset review issues

* Bump litellm for security advisory
2026-06-17 12:14:50 -03:00
Vinicius Brasil
7bb9bc7e1a Discriminate FlowDefinition state types (#6196)
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 / pip-audit (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Replace the single FlowStateDefinition model with a `type`-discriminated
union of FlowDictStateDefinition, FlowPydanticStateDefinition,
FlowJsonSchemaStateDefinition, and FlowUnknownStateDefinition.

Each branch only carries the fields it actually uses and forbids extras,
so an invalid combination like a `dict` state with a `ref` now fails
validation instead of being silently accepted. The runtime reads `ref`
and `json_schema` defensively since they no longer exist on every branch.

```yaml
state:
  type: json_schema
  json_schema:
    type: object
    properties:
      topic:
        type: string
```

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 21:31:07 -07:00
João Moura
06ada68083 Enhance crew loading and validation logic (#6182)
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
Nightly Canary Release / Build nightly packages (push) Has been cancelled
Nightly Canary Release / Check for new commits (push) Has been cancelled
Nightly Canary Release / Publish nightly to PyPI (push) Has been cancelled
* Enhance crew loading and validation logic

- Updated `crew_loader.py` to pass the project root when loading task and agent definitions, improving the handling of Python references.
- Refactored `json_loader.py` to include additional validation for Python references, ensuring they are resolved within the project root and enforcing depth limits.
- Added tests in `test_crew_loader.py` and `test_json_loader.py` to validate rejection of unsafe Python references and input files outside the project root.
- Improved error handling for JSON project validation, ensuring clearer feedback for invalid configurations.

* Refactor tests for hierarchical verbose manager agent

- Removed `@pytest.mark.vcr()` decorators from `test_hierarchical_verbose_manager_agent` and `test_hierarchical_verbose_false_manager_agent`.
- Introduced mocking for task outputs in both tests to simulate execution without relying on external dependencies.
- Ensured that the `crew.kickoff()` method is called within a context that patches the `Task.execute_sync` method, improving test isolation and reliability.

* Fix JSON loader PR review comments

* Fix JSON loader project root after rebase

* Handle UNC paths in JSON input files
2026-06-16 18:45:26 -03:00