From 9d659a644bdda32bd14d9857df30ad8d718c6f13 Mon Sep 17 00:00:00 2001 From: Lucas Gomide Date: Tue, 4 Aug 2026 14:13:24 -0300 Subject: [PATCH 1/2] feat: track interception-hook dispatches in telemetry (#6805) `HookDispatchedEvent` was already emitted from the dispatcher but never landed in Feature Usage. Wire it through `hook_dispatched_span` so hook adoption and abort outcomes (e.g. policy checks) show up in the same ClickHouse aggregation as other features. --- .../src/crewai/events/event_listener.py | 8 +++ lib/crewai/src/crewai/telemetry/telemetry.py | 18 ++++- lib/crewai/tests/telemetry/test_telemetry.py | 70 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/lib/crewai/src/crewai/events/event_listener.py b/lib/crewai/src/crewai/events/event_listener.py index 1ee18bc4f..d4b6a2d3f 100644 --- a/lib/crewai/src/crewai/events/event_listener.py +++ b/lib/crewai/src/crewai/events/event_listener.py @@ -54,6 +54,7 @@ from crewai.events.types.flow_events import ( MethodExecutionPausedEvent, MethodExecutionStartedEvent, ) +from crewai.events.types.hook_events import HookDispatchedEvent from crewai.events.types.knowledge_events import ( KnowledgeQueryCompletedEvent, KnowledgeQueryFailedEvent, @@ -875,5 +876,12 @@ class EventListener(BaseEventListener): if has_hooks: self._telemetry.feature_usage_span("hooks:registered") + @crewai_event_bus.on(HookDispatchedEvent) + def on_hook_dispatched(_: Any, event: HookDispatchedEvent) -> None: + self._telemetry.hook_dispatched_span( + interception_point=event.interception_point, + outcome=event.outcome, + ) + event_listener = EventListener() diff --git a/lib/crewai/src/crewai/telemetry/telemetry.py b/lib/crewai/src/crewai/telemetry/telemetry.py index b952f3756..ddc5bcfd3 100644 --- a/lib/crewai/src/crewai/telemetry/telemetry.py +++ b/lib/crewai/src/crewai/telemetry/telemetry.py @@ -1148,7 +1148,8 @@ class Telemetry: Args: feature: Feature identifier, e.g. "planning:creation", - "mcp:connection", "a2a:delegation". + "mcp:connection", "a2a:delegation", + "hooks:pre_tool_call", "hooks:aborted". """ def _operation() -> None: @@ -1160,6 +1161,21 @@ class Telemetry: self._safe_telemetry_operation(_operation) + def hook_dispatched_span( + self, + interception_point: str, + outcome: str, + ) -> None: + """Records an interception-hook dispatch via Feature Usage. + + Emits ``hooks:`` on every dispatch, plus ``hooks:aborted`` when + a hook aborted the operation (e.g. a policy check). No reasons, + payloads, or other user content are recorded. + """ + self.feature_usage_span(f"hooks:{interception_point}") + if outcome == "aborted": + self.feature_usage_span("hooks:aborted") + def coding_agent_span(self) -> None: """Records which AI coding assistant (if any) is running this process. diff --git a/lib/crewai/tests/telemetry/test_telemetry.py b/lib/crewai/tests/telemetry/test_telemetry.py index eed582421..5b565371f 100644 --- a/lib/crewai/tests/telemetry/test_telemetry.py +++ b/lib/crewai/tests/telemetry/test_telemetry.py @@ -230,3 +230,73 @@ def test_no_signal_handler_traceback_in_non_main_thread(): mock_holder["logger"].debug.assert_any_call( "Skipping signal handler registration: not running in main thread" ) + + +def test_hook_dispatched_span_counts_point_usage(): + with ( + patch.dict( + os.environ, + { + "CREWAI_DISABLE_TELEMETRY": "false", + "CREWAI_DISABLE_TRACKING": "false", + "OTEL_SDK_DISABLED": "false", + }, + ), + patch("crewai.telemetry.telemetry.TracerProvider"), + ): + telemetry = Telemetry() + with patch.object(telemetry, "feature_usage_span") as feature_usage_span: + telemetry.hook_dispatched_span("pre_tool_call", "proceeded") + + feature_usage_span.assert_called_once_with("hooks:pre_tool_call") + + +def test_hook_dispatched_span_counts_aborts(): + with ( + patch.dict( + os.environ, + { + "CREWAI_DISABLE_TELEMETRY": "false", + "CREWAI_DISABLE_TRACKING": "false", + "OTEL_SDK_DISABLED": "false", + }, + ), + patch("crewai.telemetry.telemetry.TracerProvider"), + ): + telemetry = Telemetry() + with patch.object(telemetry, "feature_usage_span") as feature_usage_span: + telemetry.hook_dispatched_span("pre_tool_call", "aborted") + + feature_usage_span.assert_any_call("hooks:pre_tool_call") + feature_usage_span.assert_any_call("hooks:aborted") + assert feature_usage_span.call_count == 2 + + +def test_event_listener_tracks_hook_dispatched_events(): + from crewai.events.event_bus import crewai_event_bus + from crewai.events.event_listener import event_listener + from crewai.events.types.hook_events import HookDispatchedEvent + + with ( + crewai_event_bus.scoped_handlers(), + patch.object( + event_listener._telemetry, + "hook_dispatched_span", + ) as hook_dispatched_span, + ): + event_listener.setup_listeners(crewai_event_bus) + crewai_event_bus.emit( + "test", + HookDispatchedEvent( + interception_point="pre_tool_call", + outcome="aborted", + hook_count=1, + duration_ms=1.5, + ), + ) + crewai_event_bus.flush() + + hook_dispatched_span.assert_called_once_with( + interception_point="pre_tool_call", + outcome="aborted", + ) From 7accafbaf4dd6554a813b48e9523c6bcfe83cb38 Mon Sep 17 00:00:00 2001 From: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:04:20 +0530 Subject: [PATCH 2/2] chore(ci): group uv patch/minor Dependabot updates (#6807) Add a patch-minor-updates group for routine version bumps while keeping the existing security-updates grouping. Ignore semver-major updates so breaking upgrades stay manual. --- .github/dependabot.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2a52b9257..c989a1d7a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,3 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 @@ -8,9 +5,22 @@ updates: - package-ecosystem: uv directory: "/" schedule: - interval: "weekly" + interval: weekly + day: monday + open-pull-requests-limit: 10 groups: security-updates: applies-to: security-updates patterns: - "*" + patch-minor-updates: + applies-to: version-updates + patterns: + - "*" + update-types: + - patch + - minor + ignore: + - dependency-name: "*" + update-types: + - version-update:semver-major