Commit Graph

17 Commits

Author SHA1 Message Date
ViditOstwal
ef97c33686 Replace cross-page doc links with plain-text section references.
Avoid internal hyperlinks between concept pages for SEO; point readers to section names in prose instead.
2026-08-06 20:38:28 +05:30
ViditOstwal
e5182e79a5 Restore cache_creation_tokens breakdown in UsageMetrics normalization.
Keep cache writes as a separate breakdown field while they remain folded into prompt_tokens for billed totals.
2026-08-06 20:35:07 +05:30
ViditOstwal
80d44851b9 Document Anthropic cache token accounting in LLM provider docs.
Explain how split Anthropic input counters map to UsageMetrics and link to the flows field semantics section.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 20:30:43 +05:30
ViditOstwal
a6161820d7 Document UsageMetrics token field semantics for flows and crews.
Clarify that total_tokens is prompt plus completion, breakdown fields are not additive, and Anthropic cache counters are folded into prompt_tokens.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 20:21:18 +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
Gui Vieira
3266932f00 [COR-636] Remove migrated AMP documentation (#6730) 2026-07-30 13:14:34 -03:00
João Moura
453676c61a feat(tools): surface tool failures instead of reporting them as success (#6712)
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
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(tools): surface tool failures instead of reporting them as success

A tool can finish without raising and still fail to do what it was asked.
Slack answers HTTP 200 with `{"ok": false, "error": "channel_not_found"}`;
an MCP server sets `isError`; a CrewAI AMP action returns
`API request failed: ...`. In every case the call "worked", so the error
text reached the agent as an ordinary result, the agent narrated the
problem in prose, and the run was recorded as a success.

Concretely: five failed `slackbot_send_message` calls each rendered as
"Tool Execution Completed", the task passed, and the crew passed -- with
the only evidence being a sentence in the final answer. Nothing
downstream could tell the difference, and an agent that keeps going on a
step that silently did nothing builds the rest of its work on it.

Give that outcome a type and a reaction:

- `ToolFailure` -- what a tool returns instead of an error string. The
  agent still reads prose via `as_agent_message()`, so model behavior is
  unchanged; the framework now knows the call failed.
- `ToolFailurePolicy` -- `ignore` (previous behavior), `warn` (default:
  record + emit, keep going), `raise` (abort with
  `ToolExecutionFailedError`). Resolved most-specific-first: tool, task,
  agent, crew.
- `ToolFailureDetectedEvent` -- emitted before a `raise` aborts, so
  subscribers always observe the failure. `ToolUsageFinishedEvent` also
  carries a `failure` field so a trace UI can mark the call failed
  without correlating two events.
- `tool_failures` on `TaskOutput`, `CrewOutput` and `LiteAgentOutput`,
  plus `has_tool_failures`, so consumers never parse a string.

Detection is strictly declarative -- no string sniffing, so a tool that
legitimately returns text about an error is never misread as failing.
Failures come from a returned `ToolFailure`, a raised exception, MCP
`isError`, a spent `max_usage_count`, or an unknown tool.

Wired into all four tool-execution paths (the ReAct path and the three
native function-calling implementations). Sources updated to report
structurally: `MCPClient.call_tool_result()` preserves `isError` that
`call_tool()` dropped, and `CrewAIPlatformActionTool` returns a
`ToolFailure` for non-2xx and for caught exceptions.

Two latent bugs fixed along the way: `ToolUsage` assumed every agent has
a `fingerprint` (LiteAgent does not), and policy resolution now tolerates
malformed values rather than letting telemetry take down a tool call.

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

* fix(tools): address review round 1 on tool-failure signalling

Five real defects from Bugbot, none of them cosmetic.

Tool-scoped policy never applied (high). `resolve_tool_failure_policy`
read `tool_failure_policy` off the object handed to it, but every
execution path passes the `CrewStructuredTool` wrapper, which never
carried the attribute -- and `BaseTool` never declared it in the first
place. A tool-scoped `raise`/`ignore` was silently ignored while the
docs and a unit test claimed otherwise; the test passed only because it
called the resolver directly with an authored tool. Declared the field on
`BaseTool`, propagated it through `to_structured_tool()` and
`CrewStructuredTool`, and made resolution fall back through
`_original_tool` so either shape works.

A failed call still printed the green "Completed" panel, then the red
one. That is the terminal version of the exact bug this PR is about.
Suppressed the success panel when the call reported failure.

A raised tool printed twice: `ToolUsageErrorEvent` already renders a red
panel, and the new failure panel repeated it. The event is still emitted
-- policy and traces need it -- but the duplicate console output is gone.
Both decisions now live in named predicates on `ConsoleFormatter` rather
than inline in the listener closure, so they are directly testable.

Unknown tools were reported on the ReAct path but silently ignored on all
three native paths, so the same miss was loud or silent depending on
executor style. Native paths now record `UNKNOWN_TOOL` too. This also
surfaced a live `NameError`: ruff had pruned `ToolFailureReason` from
`agent_utils` as unused, so the new branch would have crashed at runtime.

`LiteAgentOutput` had `tool_failures` but not `has_tool_failures`, which
the PR promised on all three output types -- an `AttributeError` for any
caller sharing one check across result types.

Testing: 16 further tests, 45 total. Two console tests were passing
vacuously because `emit()` dispatches sync handlers on a thread pool, so
the assertions raced the handler; they now assert on the predicates
directly, and the native-path test drains the bus with `flush()` and
checks the synchronously-written record. Full suite still matches
baseline exactly at 377 pre-existing failures.

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

* chore: update tool specifications

* fix(tools): address review round 2 and fix CI type failure

CI caught a type error I should have: widening `agent` to accept a
`LiteAgent` (so a standalone LiteAgent resolves its own policy) left the
declared signatures behind. Widened `execute_tool_and_check_finality`,
its async twin, and `ToolCallHookContext` to `Agent | BaseAgent |
LiteAgent | None`, which is what those actually receive now.

Seven CodeRabbit findings, all verified against the code first:

`raise` was being downgraded by three enclosing handlers. With
`max_execution_time` set, `_execute_with_timeout` wrapped every exception
in `RuntimeError`, so `_check_execution_error` no longer recognized the
passthrough and sent the task through the retry loop instead of aborting.
`StepExecutor.execute` turned it into `StepResult(success=False)` and let
the plan continue. `LiteAgent.kickoff` ran it through
`handle_unknown_error` and printed "This is likely a bug - please report
it" for what is a deliberate, configured stop.

Failure records were dropped on two paths. `reset_tool_failures()` only
ran in `_prepare_task_execution`, so `Agent.kickoff()` / `kickoff_async()`
— which enter through `_prepare_kickoff` — accumulated records across
runs. And a guardrail retry calls `execute_task` again, which resets the
agent, so a tool that failed on a blocked attempt vanished from the final
output entirely: a run could report zero failures having demonstrably
failed one. Failures now accumulate across guardrail attempts.

Writing the tests for that surfaced a further miss of my own:
`Agent.kickoff()` builds its `LiteAgentOutput` in `agent/core.py` via
`AgentExecutor`, not through `LiteAgent`, so `tool_failures` was always
empty there regardless of the recording fix. Wired up, and the LiteAgent
path now reads from whichever agent the executor was handed
(`original_agent` under kickoff, `self` standalone) rather than assuming.

`last_tool_failures` returns a copy, so a caller cannot mutate the
agent's record or watch it shift mid-run.

Testing: 7 further tests, 52 total, covering the timeout wrapper, the
retry limit, kickoff reset, the kickoff output path, copy semantics and
guardrail accumulation. Full suite matches baseline exactly at 377
pre-existing failures; mypy clean on every changed file.

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

* fix(tools): make crew-scoped policy real and close the last raise leak

Two findings, and the first was a documented feature that never worked.

`resolve_tool_failure_policy` consulted a crew, and the docs advertised
crew as a scope, but `Crew` had no `tool_failure_policy` field at all --
and even with one it was unreachable, because `BaseAgent` defaulted the
policy to `WARN` rather than `None`, so resolution always stopped at the
agent. Crew-level configuration was silently ignored.

Fixed by making "inherit" the default everywhere instead of baking `warn`
into one layer: `Crew` gains the field, and `BaseAgent`/`LiteAgent`
default to `None` like `Task` and `BaseTool` already did. The resolver
owns the single fallback, so the chain is genuinely
tool > task > agent > crew > warn and the effective default with nothing
configured is still `warn`. Reading `agent.tool_failure_policy` now
returns `None` (meaning "inherit") rather than `WARN`.

The other: `StepExecutor` re-raised `ToolExecutionFailedError` from its
outer handler, but the nested handler around the native-to-text tooling
fallback still caught it and returned `StepResult(success=False)`. An
agent whose LLM lacked native tool calling would therefore not abort
under `raise`. That is the third distinct place this exception was being
downgraded; it now re-raises there too.

Testing: 8 further tests, 60 total, including the full precedence chain
walked one level at a time and crew-scoped `raise`/`ignore` driven
end-to-end through `kickoff()` rather than only through the resolver --
the gap that let the original crew bug pass review. Full suite matches
baseline exactly at 377 pre-existing failures; mypy clean on every
changed file.

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

* docs: trim comments and docstrings on tool-failure signalling

Prose only -- no behavior change. Cut the module docstring, the longer
class and method docstrings, the multi-line inline comments, and the
verbose Field descriptions down to what actually earns its place. Net 87
lines lighter.

Kept the "why" in every case where the reason is non-obvious (why the
event fires before a raise, why the policy reads through the tool wrapper,
why the bus needs draining in tests) and dropped the restatements of what
the code already says.

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

* fix(tools): make ignore truly silent, stop caching failures, close 4 gaps

Six findings from the latest review round, all verified against the code
before touching it.

`ignore` was not silent. `ToolUsageFinishedEvent.failure` was set before
the policy ran, so traces still saw a failed call under a policy documented
as surfacing nothing. Worse, the console then showed *no* panel at all:
green was suppressed because `failure` was present, red was skipped because
`ignore` never emits `ToolFailureDetectedEvent`. New `reportable_failure()`
resolves the policy before the finished event and drops the flag under
`ignore`; wired into all four execution paths.

Failures were being cached. `CacheHandler.add` stored a `ToolFailure` like
any other result, so a transient error became permanent for the rest of the
run and every later hit re-reported a call that never re-ran. The cache now
refuses to store declared failures -- fixed at the single choke point rather
than at each of the four call sites.

A spent `max_usage_count` was invisible on the shared native path.
`BaseTool._claim_usage` returned a bare string that only the executors
recognising that exact message treated as a failure. It now returns a
`ToolFailure` with `USAGE_LIMIT`, so every path records it.

A guardrail returning a whole `TaskOutput` replaced the output without
carrying accumulated failures over, so earlier attempts vanished from
`CrewOutput.tool_failures`. New `merge_tool_failures()` combines and
deduplicates, and the retry-rebuild path uses it too.

A hook-blocked call inherited a cached failure and attributed it to a call
that never ran. Now cleared. Not reachable through the built-in cache once
failures stop being cached, so the test injects a custom cache handler that
does retain them -- verified to fail without the guard.

Also removed a `datetime` import left unused by the earlier console-test
rewrite.

Testing: 13 further tests, 73 total. Full suite matches baseline exactly at
377 pre-existing failures; the usage-limit suites that `_claim_usage`
touches pass unchanged; mypy clean on every changed file.

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

* fix(tools): let raise through the parallel native path, guard all handlers

Chasing down CodeRabbit's note about callers of
execute_single_native_tool_call turned up a fifth place this exception was
being downgraded: the experimental executor's parallel branch wrapped
future.result() in a broad except and folded the abort into a fake tool
result, so the remaining parallel calls carried on. The sequential path and
crew_agent_executor's parallel branch were already fine.

Five separate handlers have swallowed this during review, so added a guard
test asserting the passthrough at every site rather than trusting the next
one gets spotted.

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

* fix(tools): keep a failed tool out of the final answer, finish crew scope

Three more findings, all confirmed against the code.

A failed `result_as_answer` tool still became the task's output. The native
paths already excluded raised errors and hook blocks from short-circuiting,
but not declared failures -- so an error message silently became the answer,
which is the exact shape of bug this PR exists to prevent. Fixed on all
paths, and there were three independent override points, not one:
`ToolResult.result_as_answer` in tool_utils, the `execution_result`
finality checks in both executors, and `process_tool_results()`, which
reads `agent.tools_results` back separately. The first two fixes alone left
the behavior unchanged; only the third made the test pass.

`ToolUsage` never received a crew, so a crew-level `ignore` half-applied:
recording and `ToolFailureDetectedEvent` stayed quiet, but the flag was
still attached to `ToolUsageFinishedEvent`. It now takes and stores `crew`.

`CrewAgentExecutor.invoke`/`ainvoke` routed a deliberate stop through
`handle_unknown_error`, printing "An unknown error occurred" on verbose
runs. LiteAgent already special-cased this; both now do.

Testing: 5 further tests, 79 total, including that a *successful*
`result_as_answer` tool still short-circuits. Full suite matches baseline
exactly at 377 pre-existing failures; mypy clean on every changed file.

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

* fix(tools): report malformed tool args, correlate the failure event

Two findings from the latest round.

Malformed native tool arguments returned early with a plain error dict and
never reported a failure, so `ToolFailureReason.INVALID_INPUT` was declared
but unreferenced -- a bad tool call was absent from records, events and
`raise` aborts. `parse_tool_call_args` now carries an INVALID_INPUT failure
on the error dict and both executors report it before returning.

`ToolFailureDetectedEvent` never set `agent_id`, so a trace could not tie it
to a specific agent instance. Fixing that exposed the same gap running the
other way: `ToolUsage`'s own started/finished/error events never set
`agent_id` either, so on the ReAct path the paired finished event had
nothing to correlate against. Both now set it.

Set explicitly rather than via `from_agent`, which would also overwrite
`agent_role` and lose the `_original_role` preference those events already
apply -- a behavior change that has nothing to do with correlation.

Testing: 5 further tests, 84 total, asserting the ids match between the
failure event and its paired finished event. One existing test pinned the
exact key set of the parse-error dict and was updated for the new key. Full
suite matches baseline at 377 pre-existing failures; the one apparent
addition was the known `test_trace_enable_disable` order-flake, confirmed by
re-running rather than assumed.

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

* fix(tools): scope failure accumulation per execution, drop deprecated executor

Two review requests from @lorenzejay.

Accumulation no longer lives as mutable state on the shared agent. A
ContextVar collector is opened around each execution -- task, kickoff, and
each guardrail retry -- and the output reads that collector directly instead
of copying the agent's list. ContextVars are copied per asyncio task and per
thread, so concurrent executions cannot see each other's records, and
nesting is safe for retries. `last_tool_failures` prefers the active
collector and falls back to the last completed execution, so the accessor is
correct during a run too. The per-execution reset that caused the erasure is
gone.

Reproducing this took some digging and the finding is worth recording: crew
tasks *cannot* hit it, because `AgentExecutor` refuses concurrent reuse of
one instance and raises. `agent.kickoff()` has no such guard, and there the
bug reproduces exactly as reported -- two concurrent kickoffs each returned
two records. The regression test forces the overlap with a barrier so it is
deterministic rather than timing-dependent, and I verified it reports [2, 2]
against the old behavior and [1, 1] now.

Removed the tool-failure integration from `CrewAgentExecutor` entirely; that
file is back to its state on main. Note the shared ReAct helper it calls
still records failures, since that is common code rather than new behavior in
the deprecated file -- so a `raise` policy will be swallowed by that
executor's generic handler. Flagged on the PR rather than papered over.

Testing: 89 total. Two tests I wrote for this were vacuous on the first
attempt -- they passed against the simulated pre-fix code -- so each
concurrency test was checked against the old behavior before being kept.

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

* fix(tools): report malformed calls everywhere, drop the unused block reason

Four findings.

`execute_single_native_tool_call` swallowed a JSON decode error into an empty
args dict and ran the tool with no input at all -- worse than not reporting
it. It now routes through `parse_tool_call_args` like the executors do, so
the StepExecutor/planning path reports INVALID_INPUT and returns instead of
executing. That also removes a duplicated inline parse.

The ReAct path returned a `ToolUsageError` message as an ordinary result
without reporting it, so a malformed call there was invisible while the
equivalent native failure was recorded. Now reported as INVALID_INPUT too.

`Agent.kickoff` opened a collector but no longer reset the agent-level list,
so `last_tool_failures` grew across kickoffs. Reset restored, matching task
execution.

`ToolFailureReason.BLOCKED_BY_HOOK` was declared and never produced. Rather
than start reporting hook blocks as failures, the member is removed: a block
is a deliberate decision by the hook author, and treating it as a failure
would make `raise` abort on an intentional veto. Added a guard test that every
remaining reason is actually produced somewhere, so a dead member cannot
reappear -- the same smell that flagged INVALID_INPUT last round.

Also switched the deprecation guard test to a single import style.

Testing: 6 further tests, 95 total, including that the tool does not run when
its args fail to parse. Full suite matches baseline at 377 pre-existing
failures; mypy clean.

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

* fix(tools): merge failures across kickoff guardrail retries, cancel siblings

Kickoff guardrail retries discarded the blocked attempt's failures. Each
retry calls `_execute_and_build_output`, which opens a fresh collector and
builds a new output, so a run could report zero failures having demonstrably
failed one -- the same bug already fixed on the task guardrail path, which
merges. Now merged there too. Verified the test fails without the fix.

Under `raise`, one parallel native tool aborting left its siblings running:
the pool waited for them and pending ones still started. It now shuts the
pool down with `cancel_futures=True` so a not-yet-started sibling never runs.
Threads already in flight cannot be interrupted in Python, so a concurrent
tool may still complete before the abort surfaces; that is noted at the call
site rather than left implied.

Also satisfied CodeQL by materialising the enum in the guard test's loop.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-07-29 19:30:14 -07:00
Lucas Gomide
d52d0a1628 feat: emit FlowFailedEvent when a flow execution fails (#6718)
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
* feat: emit FlowFailedEvent when a flow execution fails

A failed flow never emitted a terminal lifecycle event, so the
`flow_started` scope stayed open and consumers such as tracing closed the
root span with a generic orphaned message instead of the real error.
`kickoff_async` and the resume path now emit `FlowFailedEvent`, paired
with `flow_started` and carrying the exception, after draining pending
handlers and background memory writes. The resume path also emits the
`MethodExecutionStartedEvent` it was missing for the method being resumed,
so its finished or failed event pairs with its own scope instead of
popping the flow's.

* fix: skip FlowFailedEvent when the run never opened a scope

The `kickoff_async` try block starts before `FlowStartedEvent` is emitted,
so an abort in the execution-start hooks, in input handling or in state
restore emitted a `flow_failed` with no opener, which pops an unrelated
scope and warns about an empty scope stack. The failure event is now
gated on the flow scope actually being open, either from this kickoff's
`flow_started` or from a restored deferred session scope.
2026-07-29 14:44:55 -04:00
Lorenze Jay
f15844b219 Lorenze/imp/skills progressive disclosure (#6675)
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
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
* skills progressive disclosure

* skills progressive disclosure

* improving progressive disclosure

* addressed comment

* fix test

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2026-07-28 09:33:43 -07:00
Ossama Alami
daa7019898 docs(llms): refresh model availability guidance (#6676)
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
* docs(llms): refresh model availability guidance

* docs: clarify structured output support

* docs: address LLM guide review feedback

* docs: refresh streaming model examples
2026-07-26 16:13:36 -07:00
João Moura
cc0759854d fix(skills): resolve registry skills through the runtime's CrewAI+ client (#6658)
* fix(skills): resolve registry skills through the installed AMP client

Skill downloads built their own `PlusAPI` and authenticated it from
`CREWAI_USER_PAT`, the platform integration token, or the saved CLI login.
Managed runtimes have no user credential to offer: they install a client of
their own, which `load_agent_from_repository` already resolves through, so
Agent Repository lookups worked while the skill downloads beside them failed
with 401.

Skills now resolve their client the same way, via `resolve_plus_client()` next
to the hook it reads. A client that can't fetch skills falls back to
environment credentials and warns, so older runtimes behave as they do today.
`resolve_plus_response()` shares the sync/async bridging both lookups need,
since `PlusAPI` is synchronous while managed clients are not.

Version pinning, which the same bug was hiding:

- Registry refs accept `@org/name@version`, and `@org/name@v1.2.0` since people
  write it both ways. `parse_skill_ref()` returns a `SkillRef(org, name,
  version)`; `parse_registry_ref()` keeps its `(org, name)` shape and drops the
  pin, so existing callers are unaffected
- Agent Repository agents record a version per skill, which was parsed off the
  response and dropped. Those pins now travel with the refs, so publishing a
  new version of a skill no longer changes every agent that uses it
- A pinned ref only accepts a project-local copy declaring that version in its
  `metadata.version` frontmatter, and the cache reports a miss when the version
  it recorded differs — so a pin re-resolves rather than loading another
  version. Unpinned refs keep hitting the cache as before
- An unknown pin fails instead of quietly falling back to the newest version

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

* fix(skills): reject a blank version pin instead of floating to latest

A blank `version` passed to `download_skill` read as "unpinned" and quietly
resolved the latest version, which is not what a caller supplying one asked
for — and it disagreed with `parse_skill_ref`, which already rejects empty
pins. Not reachable through `resolve_registry_ref` or the Agent Repository
auto-pinning, both of which only ever pass a non-empty version.

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

* fix(skills): carry the caller's context into the worker thread

When resolve_plus_response bridges an async client from inside a running loop
it runs the coroutine on a worker thread, which starts with empty ContextVars.
A client reading runtime state there — the platform integration token, flow
context — would see defaults rather than the caller's values, which is hard to
diagnose from the resulting auth or routing failure.

Copy the context across, matching how the parallel-summarization bridge in this
module already does it.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 13:27:29 -03: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
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
Lorenze Jay
b37505bcf9 Add streaming docs to the navigation (#6403) 2026-06-30 16:00:38 -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
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
Lucas Gomide
a237ebabba feat: adopt directory-based docs versioning with Edge channel (#6202)
* feat: adopt directory-based docs versioning with Edge channel

Switch docs.crewai.com from navigation-only versioning (every version
selector entry rendered the same docs/<lang>/* source files) to
Mintlify's directory-based versioning so each version selector entry
renders its own snapshot. Add an "Edge" channel under docs/edge/<lang>/*
that always reflects main HEAD for unreleased work, eliminating
pre-release leakage onto frozen release labels. External links to
canonical /<lang>/* URLs are preserved via wildcard redirects that
always land on the current default version.

Layout:
- docs/edge/<lang>/*         rolling source (you edit here)
- docs/edge/enterprise-api.*.yaml
- docs/v<X.Y.Z>/<lang>/*     frozen, immutable snapshots
- docs/v<X.Y.Z>/enterprise-api.*.yaml
- docs/images/               shared, append-only
- docs/docs.json             nav + redirects

URLs follow the Mintlify-idiomatic shape: /edge/<lang>/<page> for
Edge, /v<X.Y.Z>/<lang>/<page> for every frozen snapshot. The wildcard
redirects /<lang>/:slug* -> /<default>/<lang>/:slug* keep stale links
working, and every freeze rewrites them (plus all per-section/per-page
redirects) so destinations always resolve to the current default
without depending on a second redirect hop.

Release flow integration (devtools release):
- New module crewai_devtools.docs_versioning.freeze() materialises
  docs/v<X.Y.Z>/ from docs/edge/, rewrites openapi: refs inside the
  snapshot, inserts the version into every language block in
  docs.json, and refreshes all redirect destinations.
- _update_docs_and_create_pr() in cli.py now calls that freeze during
  Phase 2 of devtools release. Edge changelogs are updated first (so
  the snapshot freeze picks them up), then the snapshot is staged
  alongside docs.json, branched as docs/freeze-v<X.Y.Z>, and the PR
  is titled [docs-freeze] docs: snapshot and changelog for v<X.Y.Z>
  — the title prefix the new CI guard reads.
- The PR still gates tag, GitHub release, PyPI publish, and the
  enterprise release as before; no new PRs are added.
- Pre-releases (1.X.YaN, 1.X.YbN, ...) skip the snapshot — they ride
  Edge — and the docs PR title omits the [docs-freeze] prefix.
- docs_check (AI-generated docs scaffolding) writes to
  docs/edge/<lang>/* so newly-generated unreleased docs land in Edge
  and never accidentally touch a frozen snapshot.

Migration scripts (one-shot):
- scripts/docs/freeze_historical_versions.py reconstructs all 16
  historical snapshots (v1.10.0 .. v1.14.7) from git tags via
  git archive | tar, rewriting openapi: MDX refs so each snapshot
  reads its own enterprise-api YAML rather than the live one.
- scripts/docs/prefix_version_paths.py one-shot-migrates docs.json:
  rewrites every page path in 16 versioned blocks to point under
  docs/v<X.Y.Z>/, inserts a new Edge entry per language, tags
  v1.14.7 as Latest (default), prunes pages whose target file
  doesn't exist in the snapshot (e.g. docs/ar/ didn't exist before
  v1.12.0), and writes the wildcard + per-section redirects.
- scripts/docs/freeze_current_edge.py is now a thin CLI wrapper
  around docs_versioning.freeze for manual one-off freezes (e.g.
  retroactively snapshotting a forgotten release).

CI guards (.github/workflows/docs-snapshots.yml):
- Frozen snapshots under docs/v[0-9]*/ are immutable; only PRs whose
  title contains [docs-freeze] (i.e. release-cut PRs generated by
  devtools release or the manual wrapper) may modify them.
- Images under docs/images/ are append-only since snapshots share a
  single image directory. Deleting or renaming an image breaks every
  historical snapshot that still references it.

Restored docs/images/crewai-otel-export.png from PR #3673; it was
deleted in PR #4908 but v1.10.0 / v1.10.1 snapshots still reference
it. Restoring instead of editing the snapshots preserves historical
rendering fidelity and validates the new append-only rule
retroactively.

Tests:
- lib/devtools/tests/test_docs_versioning.py covers the freeze: file
  copy, openapi rewrite, version insertion, default demotion, redirect
  upserts, per-section redirect rewriting, idempotency, and invalid
  inputs.

Verified locally with mintlify broken-links: 0 broken links across
the full site (Edge + 16 frozen versions, 4 locales).

AGENTS.md (repo root) is the contributor guide for the new model;
RELEASING.md is the release-cut runbook; README's Contribution
section links to both.

Co-authored-by: Cursor <cursoragent@cursor.com>

* style: resolve linter issues

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 11:56:59 -04:00