Commit Graph

2705 Commits

Author SHA1 Message Date
lorenzejay
5007af866e refactor(llm): remove dead provider helpers 2026-08-04 09:53:29 -07:00
Vidit Ostwal
c5b9d9a4c9 fix(deps): bump aiohttp and cryptography to clear six GHSA advisories (#6802)
Raise override floors to aiohttp>=3.14.3 and cryptography>=50.0.0 so pip-audit
no longer reports WebSocket smuggling, C parser OOB read, wildcard cert, path
building, and PKCS#7 oracle issues in the locked dependency tree.
2026-08-04 19:33:54 +05:30
Rip&Tear
98668c6f17 fix(ci): correctly skip code CI for docs-only PRs (#6800)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
dorny/paths-filter treats exclusion-only patterns as matching every
non-excluded path under the default "some" quantifier, so docs-only
PRs still set code=true. Add an explicit "**" include and use
predicate-quantifier: every in tests, type-checker, and linter.

Also gate Vulnerability Scan the same way on pull_request while
keeping schedule and main push runs unconditional.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
2026-08-04 20:30:13 +08:00
João Moura
b10c4ffcdc feat: add project_id to link OSS usage to an enterprise account (#6791)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
Nightly Canary Release / Check for new commits (push) Has been cancelled
Nightly Canary Release / Build nightly packages (push) Has been cancelled
Nightly Canary Release / Publish nightly to PyPI (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* feat: add project_id to link OSS usage to an enterprise account

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

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

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

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

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

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

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

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

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

* refactor: drop the console announcement when minting project_id

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 15:25:06 -07:00
Gabe Milani
26518e0dec fix(flow): report the real CEL error for failures inside map literals (#6793)
Some checks failed
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.10) (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
Vulnerability Scan / pip-audit (push) Has been cancelled
2026-08-03 19:47:02 +00:00
João Moura
766d71aefb feat: surface AMP in AGENTS.md and detect coding agents in telemetry (#6779) 2026-08-03 12:13:05 -07:00
PawanThakurIBM
c8f441cffa feat(crewai-tools): add IBM Db2 search tool (#5885)
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.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
Build uv cache / build-cache (3.10) (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(crewai-tools): add db2 search tool

* refactor(crewai-tools): improve db2 search tool implementation

* feat(tools): improve DB2VectorSearchTool validation, security, and configurability

* docs: add DB2SearchTool documentation

* feat: add DB2 search tool

* docs: update DB2SearchTool documentation

* fix: address CodeRabbit review feedback

* fix: validate non-empty filter_by in DB2ToolSchema

* chore: trigger CodeRabbit re-review

* feat: fortify DB2 tool; fixed JSON response shape, added input guards and config validation

* refactor(db2): replace DB2Config with connection_string field

* refactor(db2): remove dead _setup_db2 validator and importlib import

* refactor(db2): remove dead guard in _connect as  _disconnect() is called at the end of every
_run, so self.connection is always None when _connect is called next.
The 'if not self.connection' guard was dead code.

* fix(db2): tighten _validate_identifier regex. Old regex allowed leading digits, multiple periods and dot-only strings (e.g. '.....' passed).

* fix(db2): replace __import__ with importlib.import_module in _generate_embedding as keeping openai as a lazy optional import since it is not always required.

* perf(db2): cache OpenAI client in _openai_client to avoid re-instantiation as OpenAI(api_key=...) was recreated on every _generate_embedding call. Extract into _get_openai_client() which lazily initialises and caches self._openai_client on first use, reusing it for all subsequent queries.

* docs(db2): clarify tool description to mention embedding fallback

* docs(db2): update README supported features to clarify embedding behaviour. 'OpenAI embedding fallback' implied it was
optional. Replaced with 'Uses a custom embedding function if supplied,
otherwise OpenAI embeddings.'

* updated both code examples to use the correct import path and public run() method.

* feat(crewai-tools): add db2 search tool

* refactor(crewai-tools): improve db2 search tool implementation

* feat(tools): improve DB2VectorSearchTool validation, security, and configurability

* docs: add DB2SearchTool documentation

* feat: add DB2 search tool

* docs: update DB2SearchTool documentation

* fix: address CodeRabbit review feedback

* fix: validate non-empty filter_by in DB2ToolSchema

* chore: trigger CodeRabbit re-review

* feat: fortify DB2 tool; fixed JSON response shape, added input guards and config validation

* fix(db2): address ruff and mypy linter errors

* style(db2): apply ruff format to db2_search_tool.py

* fix(db2-search-tool): address PR review comments

- Restore DirectoryReadTool export accidentally removed; add DB2VectorSearchTool
  and DB2ToolSchema to crewai_tools.tools __init__ and __all__
- Align _ALLOWED_METRICS whitelist with Db2 VECTOR_DISTANCE API:
  replace DOT_PRODUCT/L2_DISTANCE with EUCLIDEAN_SQUARED/DOT/HAMMING/MANHATTAN
- Replace ImportString fields for db2_package/db2_dbi_package with plain Any +
  lazy importlib.import_module in new _resolve_db2_packages() to avoid Pydantic
  default-validation gap where strings were never resolved at construction time
- Move docs from frozen docs/v1.13.0/ snapshot to docs/edge/en/tools/database-data/
  and register in docs/docs.json; update examples to match actual API
  (connection_string constructor, not DB2Config), correct return format, and
  align documented distance metrics with the whitelist

* fix(db2-search-tool): resolve default and string db2 package imports dynamically

* fix(db2-search-tool): export DB2VectorSearchTool and DB2ToolSchema from package-level crewai_tools

* docs(db2-search-tool): fix installation command and import path in README

---------

Co-authored-by: priyanshu-krishnan1 <priyanshu.krishnan1@ibm.com>
Co-authored-by: GeetikaChugh24 <geetika@ibm.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
Co-authored-by: Dhruv Chaturvedi <dhruv_insights@Dhruvs-MacBook-Pro.local>
2026-07-31 09:13:33 -07:00
João Moura
3932d3fea6 [docs-freeze] docs: snapshot and changelog for v1.15.10 (#6756) 1.15.10 2026-07-31 14:57:21 +00:00
João Moura
f262ac214e feat: bump versions to 1.15.10 (#6753) 2026-07-31 14:45:28 +00:00
João Moura
ebe0082aca feat(tracing): collect skill usage events (#6727)
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(tracing): collect skill usage events

PR #6652 added SkillUsedEvent but deliberately shipped no listener wiring,
so the event reached no collector. The trace listener subscribed to the five
setup events -- discovery, load, activation, failure -- and none of them can
answer the question skills observability is for: activation is idempotent and
fires once at setup, so an agent using a skill across twenty turns produces
exactly one event.

SkillUsedEvent is the only runtime signal and the only one that re-fires per
execution. Subscribing to it lets a trace attribute skill usage to an agent
and a task.

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

* fix(tests): scope the trace-listener handlers, assert the forwarded event

CrewAIEventsBus is a singleton, so constructing one in the fixture still
registered against the process-wide bus. _register_action_event_handlers
attached every action handler with no cleanup, leaving them live after the
patch ended -- firing against a listener built with __new__, which has no
batch_manager, in whatever test ran next. scoped_handlers clears them.

Also assert the event object itself is forwarded, not just its type: the
collector serializes the event, so dropping or replacing it would lose every
attribution field while still passing a type-only check.

Both raised in review on #6727.

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

* test: assert the forwarded skill event by identity

Comparing field values would still pass if a handler forwarded a
reconstructed copy rather than the event itself. Bind the event and assert
`forwarded is event`.

Raised in review on #6727.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:17:13 +00:00
Gui Vieira
3266932f00 [COR-636] Remove migrated AMP documentation (#6730) 2026-07-30 13:14:34 -03:00
Rip&Tear
ceed4a3ff7 Update security reporting guidelines (#6728) 2026-07-30 21:46:28 +08:00
João Moura
112762a7fa [docs-freeze] docs: snapshot and changelog for v1.15.9 (#6726)
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
1.15.9
2026-07-30 05:45:17 +00:00
João Moura
bfe8df4471 feat: bump versions to 1.15.9 (#6725) 2026-07-29 22:40:32 -07:00
João Moura
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
João Moura
e9caf1e1b8 [docs-freeze] docs: snapshot and changelog for v1.15.8 (#6703) 1.15.8 2026-07-28 15:05:44 +00:00
João Moura
133baf39b8 feat: bump versions to 1.15.8 (#6702) 2026-07-28 14:59:36 +00:00
João Moura
2e95bfb4e8 fix(tools): sandbox FileWriterTool writes and fix file tool rough edges (#6692)
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
* fix(tools): sandbox FileWriterTool writes and fix file tool rough edges

FileReadTool confined reads to the working directory, but FileWriterTool
only checked that `filename` stayed inside `directory` — and `directory`
itself is an LLM-supplied schema field. An agent could therefore write
anywhere the process had permission to, including ~/.ssh and site-packages,
while the reader refused to read back what the writer had just written.
FileWriterTool was the only filesystem tool in the package that did not go
through validate_file_path; files_compressor_tool validates even its
output path.

Writes are now confined to base_dir (the working directory by default):
the resolved directory must sit inside base_dir, and the resolved file
must sit inside that directory. The pre-existing filename containment
check is kept as-is and still applies even when the unsafe-paths escape
hatch is on, so no existing guarantee is weakened.

Both tools gain a base_dir field so a developer can widen the sandbox
deliberately instead of reaching for the process-wide
CREWAI_TOOLS_ALLOW_UNSAFE_PATHS kill switch. FileReadTool also stops
rejecting a file_path given to its own constructor: that is
developer-declared intent, and declaring one file does not expose its
siblings.

Also fixed:

- FileReadTool scanned the whole file when reading a line window; it now
  stops via islice once the requested lines are collected.
- FileWriterTool._run(**kwargs) made the documented positional call
  signature raise TypeError and turned a missing overwrite into
  "error accessing key". It now takes named parameters in the documented
  (filename, content, directory) order.
- A directory naming an existing file reported "already exists and
  overwrite option was not passed" even with overwrite=True; it now
  explains the real problem.
- Subdirectories inside filename are created, matching what passing
  directory already did.
- Both tools now write and decode UTF-8 by default instead of the
  platform locale encoding, with an encoding field to override. The docs
  already claimed UTF-8 and recommended the writer to Windows users.
- The writer's schema fields had no descriptions for the LLM.
- Docs claimed FileReadTool parses JSON into a dict (it never has),
  shipped a snippet that raised TypeError, and did not mention the path
  sandbox. The writer README also began with a stray "Here's the
  rewritten README" preamble.

BREAKING CHANGE: FileWriterTool no longer writes outside the working
directory. Pass base_dir to authorize a different tree.

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

* chore: update tool specifications

* fix(tools): make the declared FileReadTool file reachable by agents

Addresses review feedback on #6692.

The constructor-path exemption did not actually work the way an agent
calls the tool. The description only advertises a redacted label (the
basename, when the file sits outside the sandbox), but resolution
required the exact absolute path, so the model's call was sandboxed and
the declared file was never read. Worse, file_path was a required schema
field, so the long-documented "call with no arguments to read the default
file" raised a validation error instead:

    FileReadTool(file_path="/outside/declared.txt")
    .run()                          -> ValueError: validation failed
    .run(file_path="declared.txt")  -> Error: File not found
    .run(file_path="/outside/declared.txt") -> works, but the model was
                                               never told this path

file_path is now optional in the schema, so omitting it reads the default,
and the declared file is addressable by the label the description shows
the model as well as by its real path. Declaring one file still does not
expose its siblings.

The declared path is also pinned to its real path at construction, so a
later chdir cannot silently repoint it at a different file — previously a
relative constructor path re-resolved against the new working directory
on every call.

Also guards the writer's filepath resolution, which could raise
ValueError out of _run for a filename containing a null byte, breaking
the contract of always returning a descriptive string. The directory and
read paths were already guarded.

Adds docstrings to strtobool and both _run methods, corrects an Arabic
tanween spelling and a kaf-as-descriptor calque in the localized read
docs, and regenerates tool.specs.json for the schema change.

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

* fix(tools): anchor the declared read path to base_dir, not the cwd

Addresses the second round of review feedback on #6692.

The previous commit pinned a relative constructor file_path with
os.path.realpath, which anchors to the working directory, while both
format_path_for_display and validate_file_path anchor a relative path to
base_dir. With the two roots disagreeing, the same relative string meant
two different files — and the tool served the cwd one under a label that
looks like it belongs to the sandbox:

    FileReadTool(file_path="data.txt", base_dir="/allowed")   # cwd=/work
    label advertised to the model -> "data.txt"
    run(file_path="data.txt")     -> contents of /work/data.txt

That reads a file from outside base_dir, so it was a sandbox escape
introduced by the exemption itself, not just a wrong-file bug.

Resolution now goes through a single _resolve_against_base helper that
anchors relative paths exactly the way the sandbox does, so the pinned
path, the advertised label and the containment check all agree. Covered
by test_relative_declared_path_anchors_to_base_dir.

Also softens "always readable" to "always allowed past the containment
check" in the docstring, README and docs, since bypassing containment
does not guarantee the read succeeds — it can still fail on a missing
file, a directory, or permissions.

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

* fix(tools): tell the LLM about the path sandbox in tool descriptions

Addresses the low-confidence notes from the Copilot review on #6692.

Both tools' descriptions were pre-sandbox wording, so the model learned
about containment only by attempting a path and reading the error back.
Both now state that access is confined to the tool's allowed directory
and that a path resolving outside it is rejected.

The wording deliberately says "the tool's allowed directory" rather than
"the working directory", because the root is base_dir when one is set,
and naming the absolute root would leak it into the prompt — the same
reason paths are redacted in errors.

Not changed: the notes also suggested advertising `encoding`. That is a
constructor-only field the model cannot set, so describing it to the LLM
would be misleading.

Also fixes a test docstring that contradicted its own assertion — the
public run() path does raise on schema validation failure, which is what
the test asserts.

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

* fix(tools): anchor base_dir at construction so the sandbox cannot move

Addresses the third review round on #6692.

Both remaining findings came from the same habit: storing an unanchored
string and re-resolving it later.

A relative base_dir was kept verbatim and re-resolved against getcwd() on
every call, while the declared file was pinned once at construction. After
a chdir the sandbox root moved but the declared default did not, so one
tool applied two different roots. base_dir is now resolved once — in the
reader's __init__, and via a field_validator on the writer so it also
applies on the model_validate path.

That also covers the serialization concern. model_dump drops the private
pin, and __init__ re-runs on restore, so a relative file_path was
re-anchored against whatever the working directory happened to be at load
time. With base_dir anchored, restore rebuilds the identical pin.

The residual case is a relative file_path with no base_dir, where the
sandbox root is the working directory too — so both move together and the
tool stays self-consistent. Covered by
test_declared_path_survives_a_serialization_round_trip and
test_relative_base_dir_is_anchored_at_construction on both tools.

Also corrects the writer's 'directory' description, README and docs: the
default resolves inside the tool's allowed directory, which is base_dir
when one is set, not always the working directory.

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

---------

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>
2026-07-28 02:20:26 -07:00
João Moura
97981ed31b feat(tools): add WaitTool for pausing on long-running jobs (#6690)
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): add WaitTool for pausing on long-running jobs

Agents that kick off out-of-band work (a sandbox build, a deployment, an
async API job) have no way to let clock time pass: they either poll in a
tight loop or give up before the work finishes.

WaitTool pauses for a given number of seconds, with an optional reason
echoed back for traces. A single call waits at most max_seconds (default
300, configurable). Longer requests are clamped to the cap and the result
says so, so the model calls again rather than failing. Sync and async
execution are both implemented; stdlib only, no new dependencies.

The tool description spells out when to reach for it (builds, deploys,
batch jobs, async polling, backoff) and when not to, so models pick it up
for the right reason.

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

* fix(tools): enforce non-negative wait on positional calls, fix doc snippets

BaseTool.run() skips args_schema validation when called with positional
arguments, so tool.run(-5) reached time.sleep(-5) and failed with an
unrelated error. _resolve_duration now enforces the seconds >= 0 contract
itself, covered for both run() and arun().

Docs and README examples are now self-contained: check_build_status_tool
is defined with the @tool decorator instead of referenced out of nowhere,
and the async example awaits inside asyncio.run() rather than at top level.

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

* docs: point the wait tool card at the edge path

Unprefixed links resolve against the default docs version (v1.15.7),
where the wait tool page does not exist, so the card 404'd in the broken
link check. Prefixing with /edge matches how other edge pages link.

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

* fix(tools): never cache waits and keep the advertised cap accurate

Two issues from review, both confirmed against the code.

Waits inherited the default cache_function, which always allows caching.
With crew cache enabled, a repeat call with the same arguments returned
"Waited N seconds." straight from the cache without sleeping, turning a
poll-wait-check loop into a busy loop. WaitTool now declares a
cache_function that always refuses.

The description advertising the cap was only rebuilt when max_seconds
reached __init__ without an explicit description. Passing both (as a
platform building from tool.specs.json init params would), calling
model_validate, or assigning max_seconds left the text claiming 300
seconds while clamping to something else. A model_validator now derives
the description from max_seconds on construction, validation, and
assignment, and leaves a caller-supplied description untouched.

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

* fix(tools): reject NaN waits and pluralize single-second results

_resolve_duration now rejects NaN with its own message instead of letting
time.sleep raise "Invalid value NaN (not a number)" from a positional
call. Infinity keeps clamping to the cap like any other oversized wait.

Result and description text no longer says "1 seconds". Tests use the
public WaitTool().description as the baseline rather than reaching for
module-private helpers.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 17:12:27 -07:00
Thiago Moretto
e2c8d7ca88 fix: mark E2B_API_KEY as a required env var for E2B tools (#6688)
E2B_API_KEY was declared with required=False on the shared E2B tool
base (E2BExecTool, E2BFileTool, E2BPythonTool), even though none of
the three tools can create or attach to a sandbox without it.
E2B_DOMAIN stays optional since it genuinely defaults to e2b.dev.

Regenerated lib/crewai-tools/tool.specs.json via
generate_tool_specs.py to reflect the change.
2026-07-27 18:31:51 +00:00
Lucas Gomide
ca5ef810be ci: check doc links only on edge and latest versions (#6633)
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
`mintlify broken-links` walks every frozen version snapshot under
`docs/` (currently 22 of them), pushing docs PR checks past 14 minutes
and growing with each release. Prune the immutable snapshots — keeping
`edge` and the default (latest) version, which unprefixed links resolve
against — before running the checker, cutting the run to ~30 seconds.
`workflow_dispatch` still checks the full tree, and the deprecated
`mintlify` CLI is swapped for `mint`, which drops the `yes` prompt hack.
2026-07-27 08:22:01 -04: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
1870b444e7 [docs-freeze] docs: snapshot and changelog for v1.15.7 (#6673) 1.15.7 2026-07-26 11:19:47 -07:00
João Moura
38ca5edce2 feat: bump versions to 1.15.7 (#6672) 2026-07-26 18:07:46 +00:00
Lorenze Jay
213d9485fe docs: snapshot and changelog for v1.15.7a1 (#6665)
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
1.15.7a1
2026-07-26 14:04:16 -03:00
Lorenze Jay
c459d01c35 feat: bump versions to 1.15.7a1 (#6661) 2026-07-26 13:47:11 -03: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
alex-clawd
bd2cb0f23e fix(openai): recover from the GPT-5.6 tools + reasoning_effort 400 (#6660)
An ordinary agent with a tool fails on the whole GPT-5.6 family:

    Agent(role=..., goal=..., backstory=...,
          llm=LLM(model="openai/gpt-5.6-sol"), tools=[multiply])

    Function tools with reasoning_effort are not supported for gpt-5.6-sol in
    /v1/chat/completions. To use function tools, use /v1/responses or set
    reasoning_effort to 'none'.

Nothing sets reasoning_effort -- not the user, not CrewAI. The family applies a
server-side default and then refuses it once tools are present. Confirmed with
raw HTTP, no CrewAI involved, on a payload with no reasoning_effort key at all:

    gpt-5.6-sol  tools, no reasoning_effort key   -> 400
    gpt-5.6-sol  tools, reasoning_effort="none"   -> OK
    gpt-5.5      tools, no reasoning_effort key   -> OK
    gpt-5.4      tools, no reasoning_effort key   -> OK
    gpt-5.2      tools, no reasoning_effort key   -> OK

So this is GPT-5.6 only, and it needs an explicit "none" -- dropping the key is
what the rejected request already looked like.

Recovered from the error rather than a model list: catch the 400, resend with
reasoning_effort="none", once. No model names, so a family OpenAI restricts later
works without a release here. Detection matches the structured `param` field plus
the message, so the unrelated "Unsupported value" 400 that o1/o3 return for
"none" isn't mistaken for this one, and the retry can't loop.

Verified against main with real agents (no reasoning_effort anywhere):

    model          no tools   tools        tools + reasoning=True
    gpt-5.6-sol    ok / ok    400 / ok     hang / ok
    gpt-5.6-terra  ok / ok    400 / ok        - / ok
    gpt-5.6-luna   ok / ok    400 / ok        - / ok
    gpt-5.5        ok         ok              -
    gpt-5.2        ok         ok           ok / ok
    gpt-4o         ok         ok              -

On main, tools + Agent(reasoning=True) produced no output and no error and was
killed at 420s; gpt-5.2 with the same config finishes in ~40s. Both the 400 and
that hang are fixed.

Tests: 15 cases, including agent definitions with tools, with tools plus
reasoning=True, and without tools. tests/llms + tests/agents -> 961 passed
(1 pre-existing unrelated failure from a local OLLAMA_API_KEY env leak).
Ruff + mypy clean.
2026-07-26 12:48:17 -03:00
alex-clawd
c52d0d9530 fix(openai): make tool calling work on the Responses API path (#6657)
* fix(openai): make tool calling work on the Responses API path

An agent with tools on api="responses" never produced an answer. It returned the
raw tool-call list instead:

    [{'id': 'call_...', 'name': 'multiply', 'arguments': '{"a":17,"b":23}'}]

Three defects in the chain, all on the Responses side only:

1. `is_tool_call_list()` knew the OpenAI-nested, Anthropic, Bedrock and Gemini
   shapes but not the Responses one ({"id", "name", "arguments"} -- no nested
   "function", no "input"). The list wasn't recognized as tool calls, so the
   executor handed it back verbatim as the final answer.

2. `extract_tool_call_info()` read "arguments" only from a nested "function"
   object, falling back to "input". For the Responses shape both missed and the
   arguments silently became {}, so the tool would have run with no input.

3. With those fixed the tool ran, then the follow-up request 400'd:

       Invalid type for 'input[1].content': expected one of an array of objects
       or string, but got null instead.

   Tool calling is expressed differently by the two APIs. Chat Completions uses an
   assistant message carrying `tool_calls` with content: None, then role: "tool"
   results. The Responses API uses flat function_call / function_call_output items
   keyed by call_id. Those messages were passed through untranslated.

`_to_responses_input()` now converts them. Messages without tool calls pass
through unchanged, so nothing else moves.

Verified end to end against the live API:

    api="responses" + tools           -> 391   (was raw tool-call JSON)
    chained multi-step tool calls     -> 400   (17*23, then +9)
    completions path (control)        -> 391   (unchanged)

The generated `input` payload was also posted to /v1/responses directly and
accepted, and the pre-fix chat-shaped payload confirmed as a 400.

This is why api="responses" never worked for agents: the provider side has had a
full Responses implementation since #4258/c4c9208, but the executor never learned
the shape it emits. Fixing it also unblocks routing gpt-5.4+ tool calls to the
Responses API instead of dropping reasoning_effort.

Tests: 12 cases covering recognition, extraction (including that the Chat
Completions and Bedrock shapes are unaffected), translation of assistant/tool
messages, parallel calls, assistant text alongside tool calls, non-string tool
output, and the full prepared `input` list.

* fix(openai): prefer Responses "call_id" over the item's own "id"

Per CodeRabbit review. A raw Responses function_call item carries both keys with
different values, confirmed against the live API:

    keys    ['arguments', 'call_id', 'id', 'name', 'status', 'type']
    id      fc_0adeb715c5d740c7006a65ccb72b948199872ad8b5a5c53108
    call_id call_dEoHFrYnOgWYvk17FymdcDZ5

function_call_output must reference call_id. Reading the item's own "id" would
produce a tool result the model can't correlate back to its invocation.

Our own _extract_function_calls_from_response already maps item.call_id into "id",
so the normal path was correct and the existing tests passed. But
extract_tool_call_info is a shared helper reached from every provider's tool loop,
and a raw Responses item is a plausible thing to hand it -- silently picking the
wrong identifier is a bad trap to leave in place for one line of guard.

Tests: raw item extraction asserting call_id is chosen over id, and a round-trip
check that the id extracted from a call is the one sent back with its result.
997 passed across tests/llms, test_agent_utils and tests/agents (1 pre-existing
unrelated failure from a local OLLAMA_API_KEY env leak). Real two-agent chained-tool
run still returns the correct answer.

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2026-07-26 06:18:20 -03:00
alex-clawd
b64c92c87b fix(openai): route responses-only models instead of failing with 404 (#6656)
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
Mark stale issues and pull requests / stale (push) Has been cancelled
The pro tier is not served by /v1/chat/completions. Probing the live endpoints:

    model         /v1/chat/completions   /v1/responses
    gpt-5-pro     404                    OK
    gpt-5.5-pro   404                    OK
    gpt-5.4-pro   404                    OK
    gpt-5.2-pro   404                    OK
    o1-pro        404                    OK
    o3-pro        404                    OK

Since api defaults to "completions", LLM(model="openai/gpt-5-pro") fails with
"Model ... not found", which is misleading -- the model exists, the endpoint is
wrong. OpenAI's own 404 text ("This is not a chat model") doesn't make the fix
obvious either.

These requests now route to the Responses API automatically, which is verified to
work for every model above. An explicit api= setting is always honoured.

Model matching normalizes the configured string first, so "openai/gpt-5-pro" and
"gpt-5-pro-2025-10-06" both resolve to "gpt-5-pro". It's an exact list rather
than a "-pro" substring, so a custom deployment named "gpt-4-pro-custom" isn't
swept up.

The chat-completions 404 handler also gained an actionable message: when the
response says responses-only, or the model is a known pro model, the error names
api="responses" instead of just reporting "not found".

Tests: 29 cases covering name normalization, detection, routing (including that
call() reaches the Responses handler), and both 404 message paths.
2026-07-26 06:03:09 -03:00
alex-clawd
80fa0295c4 Emit skill usage events at runtime for observability (#6652)
* feat: emit skill usage events at runtime

* test: cover skill events via execute_task paths
2026-07-26 05:04:19 -03:00
alex-clawd
728183e420 fix(deps): bump bedrock-agentcore to patch CVE-2026-16796 (#6654)
bedrock-agentcore 1.7.0 has GHSA-j6g5-3hh3-pgw8 (CVE-2026-16796, high):
argument-delimiter injection in CodeInterpreter.install_packages(). It fails
the pip-audit vulnerability scan on every PR in the repo.

The patch is 1.18.1, which requires boto3>=1.43.31. The old <1.8.0 cap plus
aiobotocore~=3.5.0 (botocore<1.42.92) made that unsatisfiable, so the AWS
stack moves together:

- bedrock-agentcore >=1.7.0,<1.8.0 -> >=1.18.1,<2.0.0
- boto3 ~=1.42.90 -> ~=1.43.46  (aws + bedrock extras)
- aiobotocore ~=3.5.0 -> ~=3.8.0 (aws + bedrock extras)

aiobotocore 3.8.0 allows botocore <1.43.47 and boto3 1.43.46 pins botocore
1.43.46, so the ranges overlap.

Verified: uv lock resolves, pip-audit reports no vulnerabilities (3 existing
ignores, none new), 48 bedrock tests pass, and both bedrock toolkits import
cleanly. BrowserClient.{start,stop,generate_ws_headers} and
CodeInterpreter.{start,stop,invoke} are unchanged in 1.18.1.
2026-07-26 04:55:05 -03:00
Lorenze Jay
b3aaaab023 [docs-freeze] docs: snapshot and changelog for v1.15.6 (#6632)
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
1.15.6
2026-07-24 20:30:21 +00:00
Lorenze Jay
a4cbeacca5 feat: bump versions to 1.15.6 (#6631) 2026-07-24 20:21:58 +00:00
alex-clawd
c528e8bdee fix: detect Anthropic preview tool-use blocks (#6629)
* fix: detect anthropic preview tool-use blocks

* fix: preserve typed Anthropic tool-use blocks

* fix: bump gitpython for vulnerability scan

* docs: clarify gitpython advisory ranges

---------

Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-07-24 15:32:38 -03:00
alex-clawd
c06043f7e8 fix: preserve strict tool schema property names (#6628) 2026-07-24 09:49:05 -07:00
Rip&Tear
b14d36bfe4 chore: bump json-repair to 0.60.1, drop fixed vuln ignores in scan (#6612)
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
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
* chore: bump json-repair to 0.60.1 and un-ignore fixed vulns in scan

- json-repair 0.25.3 -> 0.60.1 (fixes GHSA-xf7x-x43h-rpqh)
- pyOpenSSL already at 26.2.0 in lock (covers CVE-2026-27448, CVE-2026-27459)
- remove the corresponding --ignore-vuln flags from vulnerability-scan.yml

* fix: adapt _safe_repair_json to json-repair 0.60 semantics

json-repair >= 0.60 returns an empty string for plain-text input and
wraps brace-enclosed junk in a single-element list instead of the old
""/{} sentinel values. Treat both as unrepairable so the original
tool input is preserved.

* chore: fix CI - bump gitpython/pyasn1, drop stale type ignores

- gitpython 3.1.50 -> 3.1.52 (GHSA-2f96-g7mh-g2hx, GHSA-v396-v7q4-x2qj,
  GHSA-956x-8gvw-wg5v; fixed in 3.1.51)
- pyasn1 0.6.3 -> 0.6.4 (GHSA-8ppf-4f7h-5ppj, GHSA-hm4w-wwcw-mr6r)
- json-repair 0.60 ships type stubs; remove now-unused
  type: ignore[import-untyped] comments flagged by mypy
2026-07-22 16:47:33 -07:00
Lucas Gomide
3bb87532da fix: dispatch execution_end hook on failed crew and flow executions (#6607)
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
* fix: dispatch execution_end hook on failed crew and flow executions

The `execution_end` interception point only fired after a successful
kickoff, so consumers never learned about failed runs. Crew kickoff
paths (`kickoff`/`akickoff`) and the flow runtime (`kickoff_async`,
`resume_async`) now dispatch it on the failure path too, with new
additive `status` ("completed"/"failed") and `error` fields on
`ExecutionEndContext`. Pairing flags guarantee exactly-once dispatch,
keep the start/end pairing invariant, and the original exception
propagates unchanged.

* fix: track execution_end pairing per invocation for reentrant flows

Reentrant kickoffs on the same Flow instance are supported (usage
aggregation already accommodates them), but the instance-level pairing
booleans let an inner kickoff's completion mark the outer execution as
ended, skipping the outer failure's `execution_end`. The pairing state
now lives in each `kickoff_async` invocation's locals, and the resume
path passes a per-invocation holder into `_resume_async_body`. Crew
keeps its instance flags since crew kickoffs are not reentrant on the
same instance (`kickoff_for_each` copies the crew).
2026-07-21 15:25:06 -04:00
iris-clawd
6d496f799b fix: handle async get_agent in load_agent_from_repository (#6608)
* fix: handle async get_agent in load_agent_from_repository

The enterprise PlusClient.get_agent() is async, but
load_agent_from_repository() calls it synchronously. When the enterprise
client is hooked in, client.get_agent() returns a coroutine instead of a
response, causing "'coroutine' object has no attribute 'status_code'".

This adds an inspect.isawaitable() check after the call: if the response
is a coroutine, it is properly awaited via asyncio.run() (or via a
thread-pool executor if an event loop is already running).

Co-authored-by: Joe Moura <joao@crewai.com>

* fix: resolve mypy type-checker errors for async awaitable handling

* fix: remove unused type: ignore comment

---------

Co-authored-by: Joe Moura <joao@crewai.com>
2026-07-21 15:21:41 -03:00
Lorenze Jay
40279e3152 fix dep resolution (#6605)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
Build uv cache / build-cache (3.10) (push) Has been cancelled
Build uv cache / build-cache (3.11) (push) Has been cancelled
Build uv cache / build-cache (3.12) (push) Has been cancelled
Build uv cache / build-cache (3.13) (push) Has been cancelled
2026-07-21 08:37:08 -03:00
Vinicius Brasil
ce739e28c7 [docs-freeze] docs: snapshot and changelog for v1.15.5 (#6602)
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
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
1.15.5
2026-07-20 12:33:16 -04:00
Vinicius Brasil
4c7e483936 feat: bump versions to 1.15.5 (#6601) 2026-07-20 16:20:33 +00:00
Vinicius Brasil
fa255387a3 Authenticate skill registry downloads (#6600)
Registry downloads initialized PlusAPI without credentials, so uncached skills failed outside CLI-authenticated flows and were blocked entirely in non-interactive environments.

Use CREWAI_USER_PAT first, then the platform integration token, then the saved login token, and pass CREWAI_ORGANIZATION_UUID. Remove the non-interactive cache-only restriction so runtime downloads work.
2026-07-20 12:43:03 -03:00
Vinicius Brasil
69c0308f2c [docs-freeze] docs: snapshot and changelog for v1.15.4 (#6583)
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
1.15.4
2026-07-17 14:33:07 +00:00
Vinicius Brasil
e0bd967484 feat: bump versions to 1.15.4 (#6582) 2026-07-17 14:27:15 +00:00
João Moura
f0704ebb22 feat(skills)!: promote Skills Repository out of experimental (#6579)
* feat(skills)!: promote Skills Repository out of experimental

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

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

Linear: n/a (requested promotion)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Vinicius Brasil <vini@hey.com>
2026-07-17 14:21:15 +00:00
Vinicius Brasil
4e23bf6d45 Update dependencies with security fixes (#6580)
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
* Pillow 12.1.1 → 12.3.0 (CVE-2026-55798, CVE-2026-54059, CVE-2026-54060,
  CVE-2026-55379, CVE-2026-55380, CVE-2026-59197, CVE-2026-59203)
* mcp 1.26.0 → 1.28.1 (CVE-2026-59950)
* couchbase 4.3.5 → 4.6.0
2026-07-17 09:12:45 -03:00
Jesse Miller
df2e68fe0a docs: add Flows in Studio documentation (#6575)
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
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: add Flows in Studio documentation

Documents the new Flows build mode in Studio: why deterministic
workflows with agentic steps matter, the three core node types
(Single Agent, Crews, Router), and Agent Repository publish/pull
sync across organizations.

Includes a rollout banner for the week of July 20th, English source
plus pt-BR, Korean, and Arabic translations, and nav entries for
both edge and v1.15.2 (Crew Studio group renamed to Studio).

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

* docs: limit Flows in Studio docs to edge version

Versioned snapshots are updated by a separate script, so remove the
v1.15.2 copies and revert its nav changes; the page now lives only
under edge.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 20:05:59 +00:00