kubiya-control-plane-api 0.9.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- control_plane_api/LICENSE +676 -0
- control_plane_api/README.md +350 -0
- control_plane_api/__init__.py +4 -0
- control_plane_api/__version__.py +8 -0
- control_plane_api/alembic/README +1 -0
- control_plane_api/alembic/env.py +121 -0
- control_plane_api/alembic/script.py.mako +28 -0
- control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
- control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
- control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
- control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
- control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
- control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
- control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
- control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
- control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
- control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
- control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
- control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
- control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
- control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
- control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
- control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
- control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
- control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
- control_plane_api/alembic.ini +148 -0
- control_plane_api/api/index.py +12 -0
- control_plane_api/app/__init__.py +11 -0
- control_plane_api/app/activities/__init__.py +20 -0
- control_plane_api/app/activities/agent_activities.py +384 -0
- control_plane_api/app/activities/plan_generation_activities.py +499 -0
- control_plane_api/app/activities/team_activities.py +424 -0
- control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
- control_plane_api/app/config/__init__.py +35 -0
- control_plane_api/app/config/api_config.py +469 -0
- control_plane_api/app/config/config_loader.py +224 -0
- control_plane_api/app/config/model_pricing.py +323 -0
- control_plane_api/app/config/storage_config.py +159 -0
- control_plane_api/app/config.py +115 -0
- control_plane_api/app/controllers/__init__.py +0 -0
- control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
- control_plane_api/app/database.py +135 -0
- control_plane_api/app/exceptions.py +408 -0
- control_plane_api/app/lib/__init__.py +11 -0
- control_plane_api/app/lib/environment.py +65 -0
- control_plane_api/app/lib/event_bus/__init__.py +17 -0
- control_plane_api/app/lib/event_bus/base.py +136 -0
- control_plane_api/app/lib/event_bus/manager.py +335 -0
- control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
- control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
- control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
- control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
- control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
- control_plane_api/app/lib/job_executor.py +330 -0
- control_plane_api/app/lib/kubiya_client.py +293 -0
- control_plane_api/app/lib/litellm_pricing.py +166 -0
- control_plane_api/app/lib/mcp_validation.py +163 -0
- control_plane_api/app/lib/nats/__init__.py +13 -0
- control_plane_api/app/lib/nats/credentials_manager.py +288 -0
- control_plane_api/app/lib/nats/listener.py +374 -0
- control_plane_api/app/lib/planning_prompt_builder.py +153 -0
- control_plane_api/app/lib/planning_tools/__init__.py +41 -0
- control_plane_api/app/lib/planning_tools/agents.py +409 -0
- control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
- control_plane_api/app/lib/planning_tools/base.py +119 -0
- control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
- control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
- control_plane_api/app/lib/planning_tools/environments.py +218 -0
- control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
- control_plane_api/app/lib/planning_tools/models.py +93 -0
- control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
- control_plane_api/app/lib/planning_tools/resources.py +242 -0
- control_plane_api/app/lib/planning_tools/teams.py +334 -0
- control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
- control_plane_api/app/lib/redis_client.py +803 -0
- control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
- control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
- control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
- control_plane_api/app/lib/storage/__init__.py +20 -0
- control_plane_api/app/lib/storage/base_provider.py +274 -0
- control_plane_api/app/lib/storage/provider_factory.py +157 -0
- control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
- control_plane_api/app/lib/supabase.py +71 -0
- control_plane_api/app/lib/supabase_utils.py +138 -0
- control_plane_api/app/lib/task_planning/__init__.py +138 -0
- control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
- control_plane_api/app/lib/task_planning/agents.py +389 -0
- control_plane_api/app/lib/task_planning/cache.py +218 -0
- control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
- control_plane_api/app/lib/task_planning/helpers.py +293 -0
- control_plane_api/app/lib/task_planning/hooks.py +474 -0
- control_plane_api/app/lib/task_planning/models.py +503 -0
- control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
- control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
- control_plane_api/app/lib/task_planning/runner.py +656 -0
- control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
- control_plane_api/app/lib/task_planning/workflow.py +424 -0
- control_plane_api/app/lib/templating/__init__.py +88 -0
- control_plane_api/app/lib/templating/compiler.py +278 -0
- control_plane_api/app/lib/templating/engine.py +178 -0
- control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
- control_plane_api/app/lib/templating/parsers/base.py +96 -0
- control_plane_api/app/lib/templating/parsers/env.py +85 -0
- control_plane_api/app/lib/templating/parsers/graph.py +112 -0
- control_plane_api/app/lib/templating/parsers/secret.py +87 -0
- control_plane_api/app/lib/templating/parsers/simple.py +81 -0
- control_plane_api/app/lib/templating/resolver.py +366 -0
- control_plane_api/app/lib/templating/types.py +214 -0
- control_plane_api/app/lib/templating/validator.py +201 -0
- control_plane_api/app/lib/temporal_client.py +232 -0
- control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
- control_plane_api/app/lib/temporal_credentials_service.py +203 -0
- control_plane_api/app/lib/validation/__init__.py +24 -0
- control_plane_api/app/lib/validation/runtime_validation.py +388 -0
- control_plane_api/app/main.py +531 -0
- control_plane_api/app/middleware/__init__.py +10 -0
- control_plane_api/app/middleware/auth.py +645 -0
- control_plane_api/app/middleware/exception_handler.py +267 -0
- control_plane_api/app/middleware/prometheus_middleware.py +173 -0
- control_plane_api/app/middleware/rate_limiting.py +384 -0
- control_plane_api/app/middleware/request_id.py +202 -0
- control_plane_api/app/models/__init__.py +40 -0
- control_plane_api/app/models/agent.py +90 -0
- control_plane_api/app/models/analytics.py +206 -0
- control_plane_api/app/models/associations.py +107 -0
- control_plane_api/app/models/auth_user.py +73 -0
- control_plane_api/app/models/context.py +161 -0
- control_plane_api/app/models/custom_integration.py +99 -0
- control_plane_api/app/models/environment.py +64 -0
- control_plane_api/app/models/execution.py +125 -0
- control_plane_api/app/models/execution_transition.py +50 -0
- control_plane_api/app/models/job.py +159 -0
- control_plane_api/app/models/llm_model.py +78 -0
- control_plane_api/app/models/orchestration.py +66 -0
- control_plane_api/app/models/plan_execution.py +102 -0
- control_plane_api/app/models/presence.py +49 -0
- control_plane_api/app/models/project.py +61 -0
- control_plane_api/app/models/project_management.py +85 -0
- control_plane_api/app/models/session.py +29 -0
- control_plane_api/app/models/skill.py +155 -0
- control_plane_api/app/models/system_tables.py +43 -0
- control_plane_api/app/models/task_planning.py +372 -0
- control_plane_api/app/models/team.py +86 -0
- control_plane_api/app/models/trace.py +257 -0
- control_plane_api/app/models/user_profile.py +54 -0
- control_plane_api/app/models/worker.py +221 -0
- control_plane_api/app/models/workflow.py +161 -0
- control_plane_api/app/models/workspace.py +50 -0
- control_plane_api/app/observability/__init__.py +177 -0
- control_plane_api/app/observability/context_logging.py +475 -0
- control_plane_api/app/observability/decorators.py +337 -0
- control_plane_api/app/observability/local_span_processor.py +702 -0
- control_plane_api/app/observability/metrics.py +303 -0
- control_plane_api/app/observability/middleware.py +246 -0
- control_plane_api/app/observability/optional.py +115 -0
- control_plane_api/app/observability/tracing.py +382 -0
- control_plane_api/app/policies/README.md +149 -0
- control_plane_api/app/policies/approved_users.rego +62 -0
- control_plane_api/app/policies/business_hours.rego +51 -0
- control_plane_api/app/policies/rate_limiting.rego +100 -0
- control_plane_api/app/policies/tool_enforcement/README.md +336 -0
- control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
- control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
- control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
- control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
- control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
- control_plane_api/app/policies/tool_restrictions.rego +86 -0
- control_plane_api/app/routers/__init__.py +4 -0
- control_plane_api/app/routers/agents.py +382 -0
- control_plane_api/app/routers/agents_v2.py +1598 -0
- control_plane_api/app/routers/analytics.py +1310 -0
- control_plane_api/app/routers/auth.py +59 -0
- control_plane_api/app/routers/client_config.py +57 -0
- control_plane_api/app/routers/context_graph.py +561 -0
- control_plane_api/app/routers/context_manager.py +577 -0
- control_plane_api/app/routers/custom_integrations.py +490 -0
- control_plane_api/app/routers/enforcer.py +132 -0
- control_plane_api/app/routers/environment_context.py +252 -0
- control_plane_api/app/routers/environments.py +761 -0
- control_plane_api/app/routers/execution_environment.py +847 -0
- control_plane_api/app/routers/executions/__init__.py +28 -0
- control_plane_api/app/routers/executions/router.py +286 -0
- control_plane_api/app/routers/executions/services/__init__.py +22 -0
- control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
- control_plane_api/app/routers/executions/services/status_service.py +420 -0
- control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
- control_plane_api/app/routers/executions/services/worker_health.py +514 -0
- control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
- control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
- control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
- control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
- control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
- control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
- control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
- control_plane_api/app/routers/executions.py +4888 -0
- control_plane_api/app/routers/health.py +165 -0
- control_plane_api/app/routers/health_v2.py +394 -0
- control_plane_api/app/routers/integration_templates.py +496 -0
- control_plane_api/app/routers/integrations.py +287 -0
- control_plane_api/app/routers/jobs.py +1809 -0
- control_plane_api/app/routers/metrics.py +517 -0
- control_plane_api/app/routers/models.py +82 -0
- control_plane_api/app/routers/models_v2.py +628 -0
- control_plane_api/app/routers/plan_executions.py +1481 -0
- control_plane_api/app/routers/plan_generation_async.py +304 -0
- control_plane_api/app/routers/policies.py +669 -0
- control_plane_api/app/routers/presence.py +234 -0
- control_plane_api/app/routers/projects.py +987 -0
- control_plane_api/app/routers/runners.py +379 -0
- control_plane_api/app/routers/runtimes.py +172 -0
- control_plane_api/app/routers/secrets.py +171 -0
- control_plane_api/app/routers/skills.py +1010 -0
- control_plane_api/app/routers/skills_definitions.py +140 -0
- control_plane_api/app/routers/storage.py +456 -0
- control_plane_api/app/routers/task_planning.py +611 -0
- control_plane_api/app/routers/task_queues.py +650 -0
- control_plane_api/app/routers/team_context.py +274 -0
- control_plane_api/app/routers/teams.py +1747 -0
- control_plane_api/app/routers/templates.py +248 -0
- control_plane_api/app/routers/traces.py +571 -0
- control_plane_api/app/routers/websocket_client.py +479 -0
- control_plane_api/app/routers/websocket_executions_status.py +437 -0
- control_plane_api/app/routers/websocket_gateway.py +323 -0
- control_plane_api/app/routers/websocket_traces.py +576 -0
- control_plane_api/app/routers/worker_queues.py +2555 -0
- control_plane_api/app/routers/worker_websocket.py +419 -0
- control_plane_api/app/routers/workers.py +1004 -0
- control_plane_api/app/routers/workflows.py +204 -0
- control_plane_api/app/runtimes/__init__.py +6 -0
- control_plane_api/app/runtimes/validation.py +344 -0
- control_plane_api/app/schemas/__init__.py +1 -0
- control_plane_api/app/schemas/job_schemas.py +302 -0
- control_plane_api/app/schemas/mcp_schemas.py +311 -0
- control_plane_api/app/schemas/template_schemas.py +133 -0
- control_plane_api/app/schemas/trace_schemas.py +168 -0
- control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
- control_plane_api/app/services/__init__.py +1 -0
- control_plane_api/app/services/agno_planning_strategy.py +233 -0
- control_plane_api/app/services/agno_service.py +838 -0
- control_plane_api/app/services/claude_code_planning_service.py +203 -0
- control_plane_api/app/services/context_graph_client.py +224 -0
- control_plane_api/app/services/custom_integration_service.py +415 -0
- control_plane_api/app/services/integration_resolution_service.py +345 -0
- control_plane_api/app/services/litellm_service.py +394 -0
- control_plane_api/app/services/plan_generator.py +79 -0
- control_plane_api/app/services/planning_strategy.py +66 -0
- control_plane_api/app/services/planning_strategy_factory.py +118 -0
- control_plane_api/app/services/policy_service.py +615 -0
- control_plane_api/app/services/state_transition_service.py +755 -0
- control_plane_api/app/services/storage_service.py +593 -0
- control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
- control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
- control_plane_api/app/services/trace_retention.py +354 -0
- control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
- control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
- control_plane_api/app/services/workflow_operations_service.py +611 -0
- control_plane_api/app/skills/__init__.py +100 -0
- control_plane_api/app/skills/base.py +239 -0
- control_plane_api/app/skills/builtin/__init__.py +37 -0
- control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
- control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
- control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
- control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
- control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
- control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
- control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
- control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
- control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
- control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
- control_plane_api/app/skills/builtin/docker/skill.py +104 -0
- control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
- control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
- control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
- control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
- control_plane_api/app/skills/builtin/python/__init__.py +4 -0
- control_plane_api/app/skills/builtin/python/skill.py +92 -0
- control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
- control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
- control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
- control_plane_api/app/skills/builtin/shell/skill.py +161 -0
- control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/app/skills/builtin/slack/skill.py +302 -0
- control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
- control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
- control_plane_api/app/skills/business_intelligence.py +189 -0
- control_plane_api/app/skills/config.py +63 -0
- control_plane_api/app/skills/loaders/__init__.py +14 -0
- control_plane_api/app/skills/loaders/base.py +73 -0
- control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
- control_plane_api/app/skills/registry.py +125 -0
- control_plane_api/app/utils/helpers.py +12 -0
- control_plane_api/app/utils/workflow_executor.py +354 -0
- control_plane_api/app/workflows/__init__.py +11 -0
- control_plane_api/app/workflows/agent_execution.py +520 -0
- control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
- control_plane_api/app/workflows/namespace_provisioning.py +326 -0
- control_plane_api/app/workflows/plan_generation.py +254 -0
- control_plane_api/app/workflows/team_execution.py +442 -0
- control_plane_api/scripts/seed_models.py +240 -0
- control_plane_api/scripts/validate_existing_tool_names.py +492 -0
- control_plane_api/shared/__init__.py +8 -0
- control_plane_api/shared/version.py +17 -0
- control_plane_api/test_deduplication.py +274 -0
- control_plane_api/test_executor_deduplication_e2e.py +309 -0
- control_plane_api/test_job_execution_e2e.py +283 -0
- control_plane_api/test_real_integration.py +193 -0
- control_plane_api/version.py +38 -0
- control_plane_api/worker/__init__.py +0 -0
- control_plane_api/worker/activities/__init__.py +0 -0
- control_plane_api/worker/activities/agent_activities.py +1585 -0
- control_plane_api/worker/activities/approval_activities.py +234 -0
- control_plane_api/worker/activities/job_activities.py +199 -0
- control_plane_api/worker/activities/runtime_activities.py +1167 -0
- control_plane_api/worker/activities/skill_activities.py +282 -0
- control_plane_api/worker/activities/team_activities.py +479 -0
- control_plane_api/worker/agent_runtime_server.py +370 -0
- control_plane_api/worker/binary_manager.py +333 -0
- control_plane_api/worker/config/__init__.py +31 -0
- control_plane_api/worker/config/worker_config.py +273 -0
- control_plane_api/worker/control_plane_client.py +1491 -0
- control_plane_api/worker/examples/analytics_integration_example.py +362 -0
- control_plane_api/worker/health_monitor.py +159 -0
- control_plane_api/worker/metrics.py +237 -0
- control_plane_api/worker/models/__init__.py +1 -0
- control_plane_api/worker/models/error_events.py +105 -0
- control_plane_api/worker/models/inputs.py +89 -0
- control_plane_api/worker/runtimes/__init__.py +35 -0
- control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
- control_plane_api/worker/runtimes/agno/__init__.py +34 -0
- control_plane_api/worker/runtimes/agno/config.py +248 -0
- control_plane_api/worker/runtimes/agno/hooks.py +385 -0
- control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
- control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
- control_plane_api/worker/runtimes/agno/utils.py +163 -0
- control_plane_api/worker/runtimes/base.py +979 -0
- control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
- control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
- control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
- control_plane_api/worker/runtimes/claude_code/config.py +829 -0
- control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
- control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
- control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
- control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
- control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
- control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
- control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
- control_plane_api/worker/runtimes/factory.py +173 -0
- control_plane_api/worker/runtimes/model_utils.py +107 -0
- control_plane_api/worker/runtimes/validation.py +93 -0
- control_plane_api/worker/services/__init__.py +1 -0
- control_plane_api/worker/services/agent_communication_tools.py +908 -0
- control_plane_api/worker/services/agent_executor.py +485 -0
- control_plane_api/worker/services/agent_executor_v2.py +793 -0
- control_plane_api/worker/services/analytics_collector.py +457 -0
- control_plane_api/worker/services/analytics_service.py +464 -0
- control_plane_api/worker/services/approval_tools.py +310 -0
- control_plane_api/worker/services/approval_tools_agno.py +207 -0
- control_plane_api/worker/services/cancellation_manager.py +177 -0
- control_plane_api/worker/services/code_ingestion_tools.py +465 -0
- control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
- control_plane_api/worker/services/data_visualization.py +834 -0
- control_plane_api/worker/services/event_publisher.py +531 -0
- control_plane_api/worker/services/jira_tools.py +257 -0
- control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
- control_plane_api/worker/services/runtime_analytics.py +328 -0
- control_plane_api/worker/services/session_service.py +365 -0
- control_plane_api/worker/services/skill_context_enhancement.py +181 -0
- control_plane_api/worker/services/skill_factory.py +471 -0
- control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
- control_plane_api/worker/services/team_executor.py +715 -0
- control_plane_api/worker/services/team_executor_v2.py +1866 -0
- control_plane_api/worker/services/tool_enforcement.py +254 -0
- control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
- control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
- control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
- control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
- control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
- control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/models.py +142 -0
- control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
- control_plane_api/worker/skills/__init__.py +12 -0
- control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
- control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
- control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
- control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
- control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
- control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
- control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
- control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
- control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
- control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
- control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
- control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
- control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
- control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
- control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
- control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
- control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
- control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
- control_plane_api/worker/skills/loaders/__init__.py +5 -0
- control_plane_api/worker/skills/loaders/base.py +23 -0
- control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
- control_plane_api/worker/skills/registry.py +208 -0
- control_plane_api/worker/tests/__init__.py +1 -0
- control_plane_api/worker/tests/conftest.py +12 -0
- control_plane_api/worker/tests/e2e/__init__.py +0 -0
- control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
- control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
- control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
- control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
- control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
- control_plane_api/worker/tests/integration/__init__.py +0 -0
- control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
- control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
- control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
- control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
- control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
- control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
- control_plane_api/worker/tests/unit/__init__.py +0 -0
- control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
- control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
- control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
- control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
- control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
- control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
- control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
- control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
- control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
- control_plane_api/worker/utils/__init__.py +1 -0
- control_plane_api/worker/utils/chunk_batcher.py +330 -0
- control_plane_api/worker/utils/environment.py +65 -0
- control_plane_api/worker/utils/error_publisher.py +260 -0
- control_plane_api/worker/utils/event_batcher.py +256 -0
- control_plane_api/worker/utils/logging_config.py +335 -0
- control_plane_api/worker/utils/logging_helper.py +326 -0
- control_plane_api/worker/utils/parameter_validator.py +120 -0
- control_plane_api/worker/utils/retry_utils.py +60 -0
- control_plane_api/worker/utils/streaming_utils.py +665 -0
- control_plane_api/worker/utils/tool_validation.py +332 -0
- control_plane_api/worker/utils/workspace_manager.py +163 -0
- control_plane_api/worker/websocket_client.py +393 -0
- control_plane_api/worker/worker.py +1297 -0
- control_plane_api/worker/workflows/__init__.py +0 -0
- control_plane_api/worker/workflows/agent_execution.py +909 -0
- control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
- control_plane_api/worker/workflows/team_execution.py +611 -0
- kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
- kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
- kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
- kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
- scripts/__init__.py +1 -0
- scripts/migrations.py +39 -0
- scripts/seed_worker_queues.py +128 -0
- scripts/setup_agent_runtime.py +142 -0
- worker_internal/__init__.py +1 -0
- worker_internal/planner/__init__.py +1 -0
- worker_internal/planner/activities.py +1499 -0
- worker_internal/planner/agent_tools.py +197 -0
- worker_internal/planner/event_models.py +148 -0
- worker_internal/planner/event_publisher.py +67 -0
- worker_internal/planner/models.py +199 -0
- worker_internal/planner/retry_logic.py +134 -0
- worker_internal/planner/worker.py +300 -0
- worker_internal/planner/workflows.py +970 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hook system for Claude Code runtime tool execution monitoring.
|
|
3
|
+
|
|
4
|
+
This module provides pre-tool and post-tool hooks for real-time event
|
|
5
|
+
publishing and monitoring of tool execution.
|
|
6
|
+
|
|
7
|
+
Hooks Implemented:
|
|
8
|
+
- PreToolUse: Called before tool execution (can block/modify)
|
|
9
|
+
- PostToolUse: Called after tool execution completes
|
|
10
|
+
- SubagentStop: Called when subagent (Task tool) completes
|
|
11
|
+
|
|
12
|
+
Why SubagentStop is Needed:
|
|
13
|
+
According to Claude SDK documentation, Task tools spawn subagents that execute
|
|
14
|
+
in separate contexts. PostToolUse hooks don't fire for subagent tools because
|
|
15
|
+
they run in isolated sessions. SubagentStop captures these completions.
|
|
16
|
+
|
|
17
|
+
Built-in Tool Behavior:
|
|
18
|
+
Some built-in tools (TodoWrite, Bash, Read, Write, Edit, Glob, Grep) may execute
|
|
19
|
+
through optimized SDK paths that skip hooks. This is expected behavior. The runtime's
|
|
20
|
+
fallback mechanism (runtime.py lines 1032-1105) publishes synthetic completion events
|
|
21
|
+
for these tools to ensure the frontend receives proper notifications.
|
|
22
|
+
|
|
23
|
+
Includes policy enforcement for tool executions.
|
|
24
|
+
|
|
25
|
+
BUG FIX #2: Replaced all print() statements with structured logging.
|
|
26
|
+
BUG FIX #8: Added SubagentStop hook to capture Task tool completions.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from typing import Dict, Any, Callable, Optional, TYPE_CHECKING
|
|
30
|
+
import structlog
|
|
31
|
+
import os
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from control_plane_api.worker.services.tool_enforcement import ToolEnforcementService
|
|
35
|
+
|
|
36
|
+
logger = structlog.get_logger(__name__)
|
|
37
|
+
|
|
38
|
+
# Check if verbose debug logging is enabled
|
|
39
|
+
# Support legacy CLAUDE_CODE_DEBUG for backward compatibility
|
|
40
|
+
if os.getenv("CLAUDE_CODE_DEBUG"):
|
|
41
|
+
logger.warning(
|
|
42
|
+
"deprecated_env_var",
|
|
43
|
+
old_var="CLAUDE_CODE_DEBUG",
|
|
44
|
+
new_var="KUBIYA_CLI_LOG_LEVEL",
|
|
45
|
+
message="CLAUDE_CODE_DEBUG is deprecated. Please use KUBIYA_CLI_LOG_LEVEL=DEBUG instead."
|
|
46
|
+
)
|
|
47
|
+
DEBUG_MODE = os.getenv("CLAUDE_CODE_DEBUG", "false").lower() == "true"
|
|
48
|
+
else:
|
|
49
|
+
DEBUG_MODE = os.getenv("KUBIYA_CLI_LOG_LEVEL", "INFO").upper() == "DEBUG"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def build_hooks(
|
|
53
|
+
execution_id: str,
|
|
54
|
+
event_callback: Optional[Callable[[Dict], None]],
|
|
55
|
+
active_tools: Dict[str, str],
|
|
56
|
+
completed_tools: set,
|
|
57
|
+
started_tools: set,
|
|
58
|
+
enforcement_context: Optional[Dict[str, Any]] = None,
|
|
59
|
+
enforcement_service: Optional["ToolEnforcementService"] = None,
|
|
60
|
+
) -> Dict[str, Any]:
|
|
61
|
+
"""
|
|
62
|
+
Build hooks for tool execution monitoring with policy enforcement.
|
|
63
|
+
|
|
64
|
+
Hooks intercept events like PreToolUse and PostToolUse to provide
|
|
65
|
+
real-time feedback and monitoring. Both hooks and ToolResultBlock
|
|
66
|
+
can publish tool_complete events, so we use completed_tools set
|
|
67
|
+
for deduplication. Uses started_tools set to prevent duplicate
|
|
68
|
+
tool_start events. Includes policy enforcement checks.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
execution_id: Execution ID for event tracking
|
|
72
|
+
event_callback: Callback for publishing events
|
|
73
|
+
active_tools: Shared dict mapping tool_use_id -> tool_name
|
|
74
|
+
completed_tools: Shared set of tool_use_ids that completed
|
|
75
|
+
started_tools: Shared set of tool_use_ids that started (prevents duplicate tool_start events)
|
|
76
|
+
enforcement_context: Optional context for policy enforcement
|
|
77
|
+
enforcement_service: Optional enforcement service for policy checks
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Dict of hook configurations
|
|
81
|
+
"""
|
|
82
|
+
from claude_agent_sdk import HookMatcher
|
|
83
|
+
|
|
84
|
+
async def pre_tool_hook(input_data, tool_use_id, tool_context):
|
|
85
|
+
"""
|
|
86
|
+
Hook called before tool execution.
|
|
87
|
+
|
|
88
|
+
BUG FIX #2: Uses logger.debug() instead of print().
|
|
89
|
+
"""
|
|
90
|
+
# BUG FIX #2: Use structured logging instead of print
|
|
91
|
+
if DEBUG_MODE:
|
|
92
|
+
logger.debug(
|
|
93
|
+
"pre_tool_hook_called",
|
|
94
|
+
tool_use_id=tool_use_id,
|
|
95
|
+
input_data_type=type(input_data).__name__,
|
|
96
|
+
input_data_keys=(
|
|
97
|
+
list(input_data.keys()) if isinstance(input_data, dict) else None
|
|
98
|
+
),
|
|
99
|
+
has_tool_context=bool(tool_context),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Try to extract tool name from input_data
|
|
103
|
+
tool_name = "unknown"
|
|
104
|
+
tool_args = {}
|
|
105
|
+
|
|
106
|
+
if isinstance(input_data, dict):
|
|
107
|
+
# Check if input_data has tool_name like output_data does
|
|
108
|
+
tool_name = input_data.get("tool_name", "unknown")
|
|
109
|
+
tool_args = input_data.get("tool_input", {})
|
|
110
|
+
|
|
111
|
+
# Always log MCP tool calls (not just in debug mode)
|
|
112
|
+
if tool_name.startswith("mcp__"):
|
|
113
|
+
logger.info(
|
|
114
|
+
"🔧 mcp_tool_starting",
|
|
115
|
+
tool_name=tool_name,
|
|
116
|
+
tool_use_id=tool_use_id[:12],
|
|
117
|
+
args=tool_args,
|
|
118
|
+
message=f"▶️ Executing {tool_name}"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if DEBUG_MODE:
|
|
122
|
+
if tool_name == "unknown":
|
|
123
|
+
logger.debug(
|
|
124
|
+
"pre_tool_hook_no_tool_name",
|
|
125
|
+
tool_use_id=tool_use_id,
|
|
126
|
+
input_data_keys=list(input_data.keys()),
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
logger.debug(
|
|
130
|
+
"pre_tool_hook_found_tool_name",
|
|
131
|
+
tool_use_id=tool_use_id,
|
|
132
|
+
tool_name=tool_name,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Enforcement check (non-blocking, async)
|
|
136
|
+
enforcement_allowed = True
|
|
137
|
+
enforcement_violation = None
|
|
138
|
+
enforcement_metadata = {}
|
|
139
|
+
|
|
140
|
+
if enforcement_service and enforcement_context and tool_name != "unknown":
|
|
141
|
+
try:
|
|
142
|
+
enforcement_allowed, enforcement_violation, enforcement_metadata = (
|
|
143
|
+
await enforcement_service.enforce_tool_execution(
|
|
144
|
+
tool_name=tool_name,
|
|
145
|
+
tool_args=tool_args,
|
|
146
|
+
enforcement_context={
|
|
147
|
+
**enforcement_context,
|
|
148
|
+
"execution_id": execution_id,
|
|
149
|
+
"tool_use_id": tool_use_id,
|
|
150
|
+
},
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Store enforcement result in tool context for post-hook
|
|
155
|
+
if not enforcement_allowed:
|
|
156
|
+
tool_context["enforcement_violation"] = enforcement_violation
|
|
157
|
+
tool_context["enforcement_metadata"] = enforcement_metadata
|
|
158
|
+
|
|
159
|
+
except Exception as e:
|
|
160
|
+
logger.error(
|
|
161
|
+
"enforcement_check_failed",
|
|
162
|
+
tool_name=tool_name,
|
|
163
|
+
error=str(e),
|
|
164
|
+
)
|
|
165
|
+
# Fail open - allow execution
|
|
166
|
+
enforcement_metadata = {"enforcer": "error", "error": str(e)}
|
|
167
|
+
|
|
168
|
+
# Publish tool_start event with enforcement metadata (with deduplication)
|
|
169
|
+
# Check if already started to prevent duplicate events
|
|
170
|
+
if event_callback and tool_name != "unknown" and tool_use_id not in started_tools:
|
|
171
|
+
try:
|
|
172
|
+
event_callback(
|
|
173
|
+
{
|
|
174
|
+
"type": "tool_start",
|
|
175
|
+
"tool_name": tool_name,
|
|
176
|
+
"tool_args": tool_args,
|
|
177
|
+
"tool_execution_id": tool_use_id,
|
|
178
|
+
"execution_id": execution_id,
|
|
179
|
+
"enforcement": enforcement_metadata, # Add enforcement metadata
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
# Mark as started to prevent duplicate events
|
|
183
|
+
started_tools.add(tool_use_id)
|
|
184
|
+
if DEBUG_MODE:
|
|
185
|
+
logger.debug(
|
|
186
|
+
"pre_tool_hook_published_tool_start",
|
|
187
|
+
tool_use_id=tool_use_id,
|
|
188
|
+
tool_name=tool_name,
|
|
189
|
+
)
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.error(
|
|
192
|
+
"failed_to_publish_tool_start",
|
|
193
|
+
tool_name=tool_name,
|
|
194
|
+
tool_use_id=tool_use_id,
|
|
195
|
+
error=str(e),
|
|
196
|
+
exc_info=True,
|
|
197
|
+
)
|
|
198
|
+
elif tool_use_id in started_tools:
|
|
199
|
+
if DEBUG_MODE:
|
|
200
|
+
logger.debug(
|
|
201
|
+
"tool_start_already_published",
|
|
202
|
+
tool_use_id=tool_use_id,
|
|
203
|
+
tool_name=tool_name,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return {}
|
|
207
|
+
|
|
208
|
+
async def post_tool_hook(output_data, tool_use_id, tool_context):
|
|
209
|
+
"""
|
|
210
|
+
Hook called after tool execution.
|
|
211
|
+
|
|
212
|
+
BUG FIX #2: Uses logger.debug() instead of print().
|
|
213
|
+
"""
|
|
214
|
+
# Extract tool name from output_data (provided by Claude Code SDK)
|
|
215
|
+
tool_name = "unknown"
|
|
216
|
+
if isinstance(output_data, dict):
|
|
217
|
+
# Claude SDK provides tool_name directly in output_data
|
|
218
|
+
tool_name = output_data.get("tool_name", "unknown")
|
|
219
|
+
|
|
220
|
+
# Check for errors from multiple sources:
|
|
221
|
+
# 1. tool_context.is_error (set by SDK)
|
|
222
|
+
# 2. output_data.isError (returned by tool wrapper when exception occurs)
|
|
223
|
+
is_error = (
|
|
224
|
+
(tool_context.get("is_error", False) if tool_context else False) or
|
|
225
|
+
(output_data.get("isError", False) if isinstance(output_data, dict) else False)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Check for enforcement violation and inject into output
|
|
229
|
+
enforcement_violation = tool_context.get("enforcement_violation") if tool_context else None
|
|
230
|
+
enforcement_metadata = tool_context.get("enforcement_metadata", {}) if tool_context else {}
|
|
231
|
+
|
|
232
|
+
if enforcement_violation:
|
|
233
|
+
# Inject violation into output_data
|
|
234
|
+
violation_message = (
|
|
235
|
+
f"\n{'='*60}\n"
|
|
236
|
+
f"⛔ POLICY VIOLATION DETECTED\n"
|
|
237
|
+
f"{'='*60}\n"
|
|
238
|
+
f"{enforcement_violation}\n"
|
|
239
|
+
f"{'='*60}\n\n"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if isinstance(output_data, dict):
|
|
243
|
+
existing_output = output_data.get("output", "")
|
|
244
|
+
output_data["output"] = violation_message + str(existing_output)
|
|
245
|
+
output_data["enforcement_violated"] = True
|
|
246
|
+
|
|
247
|
+
logger.warning(
|
|
248
|
+
"tool_execution_policy_violation",
|
|
249
|
+
tool_name=tool_name,
|
|
250
|
+
tool_use_id=tool_use_id,
|
|
251
|
+
enforcement_metadata=enforcement_metadata,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Always log MCP tool results (not just in debug mode)
|
|
255
|
+
if tool_name.startswith("mcp__"):
|
|
256
|
+
# Extract result/error from output_data
|
|
257
|
+
result_preview = None
|
|
258
|
+
if isinstance(output_data, dict):
|
|
259
|
+
result = output_data.get("result", output_data.get("output", output_data))
|
|
260
|
+
result_str = str(result)
|
|
261
|
+
result_preview = result_str[:500] + "..." if len(result_str) > 500 else result_str
|
|
262
|
+
|
|
263
|
+
if is_error:
|
|
264
|
+
logger.error(
|
|
265
|
+
"❌ mcp_tool_failed",
|
|
266
|
+
tool_name=tool_name,
|
|
267
|
+
tool_use_id=tool_use_id[:12],
|
|
268
|
+
error=result_preview,
|
|
269
|
+
message=f"Failed: {tool_name}"
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
logger.info(
|
|
273
|
+
"✅ mcp_tool_completed",
|
|
274
|
+
tool_name=tool_name,
|
|
275
|
+
tool_use_id=tool_use_id[:12],
|
|
276
|
+
result_preview=result_preview,
|
|
277
|
+
result_length=len(str(output_data)),
|
|
278
|
+
message=f"Completed: {tool_name}"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# BUG FIX #2: Use structured logging instead of print
|
|
282
|
+
if DEBUG_MODE:
|
|
283
|
+
logger.debug(
|
|
284
|
+
"post_tool_hook_called",
|
|
285
|
+
tool_use_id=tool_use_id,
|
|
286
|
+
tool_name=tool_name,
|
|
287
|
+
is_error=is_error,
|
|
288
|
+
status="failed" if is_error else "success",
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Publish tool_complete event (with deduplication)
|
|
292
|
+
# Both hooks and ToolResultBlock can publish, so check if already published
|
|
293
|
+
if event_callback and tool_use_id not in completed_tools:
|
|
294
|
+
try:
|
|
295
|
+
event_callback(
|
|
296
|
+
{
|
|
297
|
+
"type": "tool_complete",
|
|
298
|
+
"tool_name": tool_name,
|
|
299
|
+
"tool_execution_id": tool_use_id,
|
|
300
|
+
"status": "failed" if is_error else "success",
|
|
301
|
+
"output": str(output_data)[:1000] if output_data else None,
|
|
302
|
+
"error": str(output_data) if is_error else None,
|
|
303
|
+
"execution_id": execution_id,
|
|
304
|
+
"enforcement": enforcement_metadata, # Add enforcement metadata
|
|
305
|
+
}
|
|
306
|
+
)
|
|
307
|
+
# Mark as completed to prevent duplicate from ToolResultBlock
|
|
308
|
+
completed_tools.add(tool_use_id)
|
|
309
|
+
if DEBUG_MODE:
|
|
310
|
+
logger.debug(
|
|
311
|
+
"post_tool_hook_published_tool_complete",
|
|
312
|
+
tool_use_id=tool_use_id,
|
|
313
|
+
tool_name=tool_name,
|
|
314
|
+
is_error=is_error,
|
|
315
|
+
)
|
|
316
|
+
except Exception as e:
|
|
317
|
+
logger.error(
|
|
318
|
+
"failed_to_publish_tool_complete",
|
|
319
|
+
tool_name=tool_name,
|
|
320
|
+
tool_use_id=tool_use_id,
|
|
321
|
+
error=str(e),
|
|
322
|
+
exc_info=True,
|
|
323
|
+
)
|
|
324
|
+
elif tool_use_id in completed_tools:
|
|
325
|
+
if DEBUG_MODE:
|
|
326
|
+
logger.debug(
|
|
327
|
+
"tool_complete_already_published_via_stream",
|
|
328
|
+
tool_use_id=tool_use_id,
|
|
329
|
+
tool_name=tool_name,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
return {}
|
|
333
|
+
|
|
334
|
+
async def subagent_stop_hook(input_data, tool_use_id, tool_context):
|
|
335
|
+
"""
|
|
336
|
+
Hook called when a subagent (Task tool) completes.
|
|
337
|
+
|
|
338
|
+
This captures completions for Task tools that spawn subagents,
|
|
339
|
+
which don't fire PostToolUse hooks. According to Claude SDK docs,
|
|
340
|
+
subagent execution happens in separate contexts, so we need this
|
|
341
|
+
dedicated hook to track their completion.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
input_data: SubagentStopHookInput dict containing:
|
|
345
|
+
- session_id: Session ID for the subagent
|
|
346
|
+
- transcript_path: Path to subagent transcript
|
|
347
|
+
- cwd: Working directory
|
|
348
|
+
- stop_hook_active: Whether hook is active
|
|
349
|
+
- hook_event_name: Name of the hook event
|
|
350
|
+
tool_use_id: Unique identifier for this tool execution
|
|
351
|
+
tool_context: Additional context from SDK
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Empty dict (SDK requirement)
|
|
355
|
+
"""
|
|
356
|
+
tool_name = "Task" # Subagents are spawned by Task tool
|
|
357
|
+
|
|
358
|
+
# CRITICAL: Validate input_data is a dict (SDK passes SubagentStopHookInput)
|
|
359
|
+
if not isinstance(input_data, dict):
|
|
360
|
+
logger.error(
|
|
361
|
+
"subagent_stop_hook_invalid_input_type",
|
|
362
|
+
input_type=type(input_data).__name__,
|
|
363
|
+
note="Expected dict with session_id, transcript_path, etc."
|
|
364
|
+
)
|
|
365
|
+
return {}
|
|
366
|
+
|
|
367
|
+
# Defensive: Validate tool_use_id is present and valid
|
|
368
|
+
if not tool_use_id:
|
|
369
|
+
logger.error(
|
|
370
|
+
"subagent_stop_hook_missing_tool_use_id",
|
|
371
|
+
has_input_data=bool(input_data),
|
|
372
|
+
note="Cannot track completion without tool_use_id"
|
|
373
|
+
)
|
|
374
|
+
return {}
|
|
375
|
+
|
|
376
|
+
# Defensive: Ensure tool_use_id is string for string operations
|
|
377
|
+
if not isinstance(tool_use_id, str):
|
|
378
|
+
logger.warning(
|
|
379
|
+
"subagent_stop_hook_invalid_tool_use_id_type",
|
|
380
|
+
tool_use_id_type=type(tool_use_id).__name__,
|
|
381
|
+
note="Converting to string for safety"
|
|
382
|
+
)
|
|
383
|
+
try:
|
|
384
|
+
tool_use_id = str(tool_use_id)
|
|
385
|
+
except Exception as e:
|
|
386
|
+
logger.error(
|
|
387
|
+
"subagent_stop_hook_tool_use_id_conversion_failed",
|
|
388
|
+
error=str(e),
|
|
389
|
+
note="Cannot proceed without valid tool_use_id"
|
|
390
|
+
)
|
|
391
|
+
return {}
|
|
392
|
+
|
|
393
|
+
# Safe slicing for logging (check length)
|
|
394
|
+
tool_use_id_short = tool_use_id[:12] if len(tool_use_id) >= 12 else tool_use_id
|
|
395
|
+
|
|
396
|
+
if DEBUG_MODE:
|
|
397
|
+
logger.debug(
|
|
398
|
+
"subagent_stop_hook_called",
|
|
399
|
+
tool_use_id=tool_use_id_short,
|
|
400
|
+
has_input_data=bool(input_data),
|
|
401
|
+
input_data_type=type(input_data).__name__,
|
|
402
|
+
input_data_keys=list(input_data.keys()) if isinstance(input_data, dict) else None,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# Always log subagent completions (not just in debug mode)
|
|
406
|
+
logger.info(
|
|
407
|
+
"subagent_completed",
|
|
408
|
+
tool_use_id=tool_use_id_short,
|
|
409
|
+
tool_name=tool_name,
|
|
410
|
+
message=f"✅ Subagent (Task) completed: {tool_use_id_short}"
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# Check if already completed (deduplicate with PostToolUse, though unlikely)
|
|
414
|
+
if event_callback and tool_use_id not in completed_tools:
|
|
415
|
+
try:
|
|
416
|
+
# Extract meaningful info from SubagentStopHookInput
|
|
417
|
+
output_str = None
|
|
418
|
+
if input_data:
|
|
419
|
+
try:
|
|
420
|
+
session_id = input_data.get("session_id", "")
|
|
421
|
+
transcript_path = input_data.get("transcript_path", "")
|
|
422
|
+
output_str = f"Subagent completed (session: {session_id[:16] if session_id else 'unknown'})"
|
|
423
|
+
|
|
424
|
+
if transcript_path:
|
|
425
|
+
output_str += f" - transcript: {transcript_path}"
|
|
426
|
+
except Exception as conv_error:
|
|
427
|
+
logger.warning(
|
|
428
|
+
"subagent_input_extraction_failed",
|
|
429
|
+
tool_use_id=tool_use_id_short,
|
|
430
|
+
error=str(conv_error),
|
|
431
|
+
note="Using fallback output representation"
|
|
432
|
+
)
|
|
433
|
+
output_str = f"<Subagent completed - extraction failed: {type(input_data).__name__}>"
|
|
434
|
+
|
|
435
|
+
event_callback(
|
|
436
|
+
{
|
|
437
|
+
"type": "tool_complete",
|
|
438
|
+
"tool_name": tool_name,
|
|
439
|
+
"tool_execution_id": tool_use_id,
|
|
440
|
+
"status": "success", # Subagent completed successfully
|
|
441
|
+
"output": output_str,
|
|
442
|
+
"error": None,
|
|
443
|
+
"execution_id": execution_id,
|
|
444
|
+
}
|
|
445
|
+
)
|
|
446
|
+
completed_tools.add(tool_use_id)
|
|
447
|
+
|
|
448
|
+
if DEBUG_MODE:
|
|
449
|
+
logger.debug(
|
|
450
|
+
"subagent_completion_published",
|
|
451
|
+
tool_use_id=tool_use_id_short,
|
|
452
|
+
tool_name=tool_name,
|
|
453
|
+
)
|
|
454
|
+
except Exception as e:
|
|
455
|
+
# Non-fatal: Log but don't crash if event publishing fails
|
|
456
|
+
logger.error(
|
|
457
|
+
"failed_to_publish_subagent_completion",
|
|
458
|
+
tool_use_id=tool_use_id_short,
|
|
459
|
+
error=str(e),
|
|
460
|
+
error_type=type(e).__name__,
|
|
461
|
+
exc_info=True,
|
|
462
|
+
note="Completion tracking failed but execution succeeded"
|
|
463
|
+
)
|
|
464
|
+
elif tool_use_id in completed_tools:
|
|
465
|
+
if DEBUG_MODE:
|
|
466
|
+
logger.debug(
|
|
467
|
+
"subagent_completion_already_published",
|
|
468
|
+
tool_use_id=tool_use_id_short,
|
|
469
|
+
tool_name=tool_name,
|
|
470
|
+
note="Deduplicated - PostToolUse already published this"
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
return {}
|
|
474
|
+
|
|
475
|
+
# Build hook configuration
|
|
476
|
+
hooks = {
|
|
477
|
+
"PreToolUse": [HookMatcher(hooks=[pre_tool_hook])],
|
|
478
|
+
"PostToolUse": [HookMatcher(hooks=[post_tool_hook])],
|
|
479
|
+
"SubagentStop": [HookMatcher(hooks=[subagent_stop_hook])], # NEW: Track subagent (Task) completions
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return hooks
|