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.
Files changed (479) hide show
  1. control_plane_api/LICENSE +676 -0
  2. control_plane_api/README.md +350 -0
  3. control_plane_api/__init__.py +4 -0
  4. control_plane_api/__version__.py +8 -0
  5. control_plane_api/alembic/README +1 -0
  6. control_plane_api/alembic/env.py +121 -0
  7. control_plane_api/alembic/script.py.mako +28 -0
  8. control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
  9. control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
  10. control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
  11. control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
  12. control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
  13. control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
  14. control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
  15. control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
  16. control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
  17. control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
  18. control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
  19. control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
  20. control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
  21. control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
  22. control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
  23. control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
  24. control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
  25. control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
  26. control_plane_api/alembic.ini +148 -0
  27. control_plane_api/api/index.py +12 -0
  28. control_plane_api/app/__init__.py +11 -0
  29. control_plane_api/app/activities/__init__.py +20 -0
  30. control_plane_api/app/activities/agent_activities.py +384 -0
  31. control_plane_api/app/activities/plan_generation_activities.py +499 -0
  32. control_plane_api/app/activities/team_activities.py +424 -0
  33. control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
  34. control_plane_api/app/config/__init__.py +35 -0
  35. control_plane_api/app/config/api_config.py +469 -0
  36. control_plane_api/app/config/config_loader.py +224 -0
  37. control_plane_api/app/config/model_pricing.py +323 -0
  38. control_plane_api/app/config/storage_config.py +159 -0
  39. control_plane_api/app/config.py +115 -0
  40. control_plane_api/app/controllers/__init__.py +0 -0
  41. control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
  42. control_plane_api/app/database.py +135 -0
  43. control_plane_api/app/exceptions.py +408 -0
  44. control_plane_api/app/lib/__init__.py +11 -0
  45. control_plane_api/app/lib/environment.py +65 -0
  46. control_plane_api/app/lib/event_bus/__init__.py +17 -0
  47. control_plane_api/app/lib/event_bus/base.py +136 -0
  48. control_plane_api/app/lib/event_bus/manager.py +335 -0
  49. control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
  50. control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
  51. control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
  52. control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
  53. control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
  54. control_plane_api/app/lib/job_executor.py +330 -0
  55. control_plane_api/app/lib/kubiya_client.py +293 -0
  56. control_plane_api/app/lib/litellm_pricing.py +166 -0
  57. control_plane_api/app/lib/mcp_validation.py +163 -0
  58. control_plane_api/app/lib/nats/__init__.py +13 -0
  59. control_plane_api/app/lib/nats/credentials_manager.py +288 -0
  60. control_plane_api/app/lib/nats/listener.py +374 -0
  61. control_plane_api/app/lib/planning_prompt_builder.py +153 -0
  62. control_plane_api/app/lib/planning_tools/__init__.py +41 -0
  63. control_plane_api/app/lib/planning_tools/agents.py +409 -0
  64. control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
  65. control_plane_api/app/lib/planning_tools/base.py +119 -0
  66. control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
  67. control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
  68. control_plane_api/app/lib/planning_tools/environments.py +218 -0
  69. control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
  70. control_plane_api/app/lib/planning_tools/models.py +93 -0
  71. control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
  72. control_plane_api/app/lib/planning_tools/resources.py +242 -0
  73. control_plane_api/app/lib/planning_tools/teams.py +334 -0
  74. control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
  75. control_plane_api/app/lib/redis_client.py +803 -0
  76. control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
  77. control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
  78. control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
  79. control_plane_api/app/lib/storage/__init__.py +20 -0
  80. control_plane_api/app/lib/storage/base_provider.py +274 -0
  81. control_plane_api/app/lib/storage/provider_factory.py +157 -0
  82. control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
  83. control_plane_api/app/lib/supabase.py +71 -0
  84. control_plane_api/app/lib/supabase_utils.py +138 -0
  85. control_plane_api/app/lib/task_planning/__init__.py +138 -0
  86. control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
  87. control_plane_api/app/lib/task_planning/agents.py +389 -0
  88. control_plane_api/app/lib/task_planning/cache.py +218 -0
  89. control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
  90. control_plane_api/app/lib/task_planning/helpers.py +293 -0
  91. control_plane_api/app/lib/task_planning/hooks.py +474 -0
  92. control_plane_api/app/lib/task_planning/models.py +503 -0
  93. control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
  94. control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
  95. control_plane_api/app/lib/task_planning/runner.py +656 -0
  96. control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
  97. control_plane_api/app/lib/task_planning/workflow.py +424 -0
  98. control_plane_api/app/lib/templating/__init__.py +88 -0
  99. control_plane_api/app/lib/templating/compiler.py +278 -0
  100. control_plane_api/app/lib/templating/engine.py +178 -0
  101. control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
  102. control_plane_api/app/lib/templating/parsers/base.py +96 -0
  103. control_plane_api/app/lib/templating/parsers/env.py +85 -0
  104. control_plane_api/app/lib/templating/parsers/graph.py +112 -0
  105. control_plane_api/app/lib/templating/parsers/secret.py +87 -0
  106. control_plane_api/app/lib/templating/parsers/simple.py +81 -0
  107. control_plane_api/app/lib/templating/resolver.py +366 -0
  108. control_plane_api/app/lib/templating/types.py +214 -0
  109. control_plane_api/app/lib/templating/validator.py +201 -0
  110. control_plane_api/app/lib/temporal_client.py +232 -0
  111. control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
  112. control_plane_api/app/lib/temporal_credentials_service.py +203 -0
  113. control_plane_api/app/lib/validation/__init__.py +24 -0
  114. control_plane_api/app/lib/validation/runtime_validation.py +388 -0
  115. control_plane_api/app/main.py +531 -0
  116. control_plane_api/app/middleware/__init__.py +10 -0
  117. control_plane_api/app/middleware/auth.py +645 -0
  118. control_plane_api/app/middleware/exception_handler.py +267 -0
  119. control_plane_api/app/middleware/prometheus_middleware.py +173 -0
  120. control_plane_api/app/middleware/rate_limiting.py +384 -0
  121. control_plane_api/app/middleware/request_id.py +202 -0
  122. control_plane_api/app/models/__init__.py +40 -0
  123. control_plane_api/app/models/agent.py +90 -0
  124. control_plane_api/app/models/analytics.py +206 -0
  125. control_plane_api/app/models/associations.py +107 -0
  126. control_plane_api/app/models/auth_user.py +73 -0
  127. control_plane_api/app/models/context.py +161 -0
  128. control_plane_api/app/models/custom_integration.py +99 -0
  129. control_plane_api/app/models/environment.py +64 -0
  130. control_plane_api/app/models/execution.py +125 -0
  131. control_plane_api/app/models/execution_transition.py +50 -0
  132. control_plane_api/app/models/job.py +159 -0
  133. control_plane_api/app/models/llm_model.py +78 -0
  134. control_plane_api/app/models/orchestration.py +66 -0
  135. control_plane_api/app/models/plan_execution.py +102 -0
  136. control_plane_api/app/models/presence.py +49 -0
  137. control_plane_api/app/models/project.py +61 -0
  138. control_plane_api/app/models/project_management.py +85 -0
  139. control_plane_api/app/models/session.py +29 -0
  140. control_plane_api/app/models/skill.py +155 -0
  141. control_plane_api/app/models/system_tables.py +43 -0
  142. control_plane_api/app/models/task_planning.py +372 -0
  143. control_plane_api/app/models/team.py +86 -0
  144. control_plane_api/app/models/trace.py +257 -0
  145. control_plane_api/app/models/user_profile.py +54 -0
  146. control_plane_api/app/models/worker.py +221 -0
  147. control_plane_api/app/models/workflow.py +161 -0
  148. control_plane_api/app/models/workspace.py +50 -0
  149. control_plane_api/app/observability/__init__.py +177 -0
  150. control_plane_api/app/observability/context_logging.py +475 -0
  151. control_plane_api/app/observability/decorators.py +337 -0
  152. control_plane_api/app/observability/local_span_processor.py +702 -0
  153. control_plane_api/app/observability/metrics.py +303 -0
  154. control_plane_api/app/observability/middleware.py +246 -0
  155. control_plane_api/app/observability/optional.py +115 -0
  156. control_plane_api/app/observability/tracing.py +382 -0
  157. control_plane_api/app/policies/README.md +149 -0
  158. control_plane_api/app/policies/approved_users.rego +62 -0
  159. control_plane_api/app/policies/business_hours.rego +51 -0
  160. control_plane_api/app/policies/rate_limiting.rego +100 -0
  161. control_plane_api/app/policies/tool_enforcement/README.md +336 -0
  162. control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
  163. control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
  164. control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
  165. control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
  166. control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
  167. control_plane_api/app/policies/tool_restrictions.rego +86 -0
  168. control_plane_api/app/routers/__init__.py +4 -0
  169. control_plane_api/app/routers/agents.py +382 -0
  170. control_plane_api/app/routers/agents_v2.py +1598 -0
  171. control_plane_api/app/routers/analytics.py +1310 -0
  172. control_plane_api/app/routers/auth.py +59 -0
  173. control_plane_api/app/routers/client_config.py +57 -0
  174. control_plane_api/app/routers/context_graph.py +561 -0
  175. control_plane_api/app/routers/context_manager.py +577 -0
  176. control_plane_api/app/routers/custom_integrations.py +490 -0
  177. control_plane_api/app/routers/enforcer.py +132 -0
  178. control_plane_api/app/routers/environment_context.py +252 -0
  179. control_plane_api/app/routers/environments.py +761 -0
  180. control_plane_api/app/routers/execution_environment.py +847 -0
  181. control_plane_api/app/routers/executions/__init__.py +28 -0
  182. control_plane_api/app/routers/executions/router.py +286 -0
  183. control_plane_api/app/routers/executions/services/__init__.py +22 -0
  184. control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
  185. control_plane_api/app/routers/executions/services/status_service.py +420 -0
  186. control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
  187. control_plane_api/app/routers/executions/services/worker_health.py +514 -0
  188. control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
  189. control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
  190. control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
  191. control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
  192. control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
  193. control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
  194. control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
  195. control_plane_api/app/routers/executions.py +4888 -0
  196. control_plane_api/app/routers/health.py +165 -0
  197. control_plane_api/app/routers/health_v2.py +394 -0
  198. control_plane_api/app/routers/integration_templates.py +496 -0
  199. control_plane_api/app/routers/integrations.py +287 -0
  200. control_plane_api/app/routers/jobs.py +1809 -0
  201. control_plane_api/app/routers/metrics.py +517 -0
  202. control_plane_api/app/routers/models.py +82 -0
  203. control_plane_api/app/routers/models_v2.py +628 -0
  204. control_plane_api/app/routers/plan_executions.py +1481 -0
  205. control_plane_api/app/routers/plan_generation_async.py +304 -0
  206. control_plane_api/app/routers/policies.py +669 -0
  207. control_plane_api/app/routers/presence.py +234 -0
  208. control_plane_api/app/routers/projects.py +987 -0
  209. control_plane_api/app/routers/runners.py +379 -0
  210. control_plane_api/app/routers/runtimes.py +172 -0
  211. control_plane_api/app/routers/secrets.py +171 -0
  212. control_plane_api/app/routers/skills.py +1010 -0
  213. control_plane_api/app/routers/skills_definitions.py +140 -0
  214. control_plane_api/app/routers/storage.py +456 -0
  215. control_plane_api/app/routers/task_planning.py +611 -0
  216. control_plane_api/app/routers/task_queues.py +650 -0
  217. control_plane_api/app/routers/team_context.py +274 -0
  218. control_plane_api/app/routers/teams.py +1747 -0
  219. control_plane_api/app/routers/templates.py +248 -0
  220. control_plane_api/app/routers/traces.py +571 -0
  221. control_plane_api/app/routers/websocket_client.py +479 -0
  222. control_plane_api/app/routers/websocket_executions_status.py +437 -0
  223. control_plane_api/app/routers/websocket_gateway.py +323 -0
  224. control_plane_api/app/routers/websocket_traces.py +576 -0
  225. control_plane_api/app/routers/worker_queues.py +2555 -0
  226. control_plane_api/app/routers/worker_websocket.py +419 -0
  227. control_plane_api/app/routers/workers.py +1004 -0
  228. control_plane_api/app/routers/workflows.py +204 -0
  229. control_plane_api/app/runtimes/__init__.py +6 -0
  230. control_plane_api/app/runtimes/validation.py +344 -0
  231. control_plane_api/app/schemas/__init__.py +1 -0
  232. control_plane_api/app/schemas/job_schemas.py +302 -0
  233. control_plane_api/app/schemas/mcp_schemas.py +311 -0
  234. control_plane_api/app/schemas/template_schemas.py +133 -0
  235. control_plane_api/app/schemas/trace_schemas.py +168 -0
  236. control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
  237. control_plane_api/app/services/__init__.py +1 -0
  238. control_plane_api/app/services/agno_planning_strategy.py +233 -0
  239. control_plane_api/app/services/agno_service.py +838 -0
  240. control_plane_api/app/services/claude_code_planning_service.py +203 -0
  241. control_plane_api/app/services/context_graph_client.py +224 -0
  242. control_plane_api/app/services/custom_integration_service.py +415 -0
  243. control_plane_api/app/services/integration_resolution_service.py +345 -0
  244. control_plane_api/app/services/litellm_service.py +394 -0
  245. control_plane_api/app/services/plan_generator.py +79 -0
  246. control_plane_api/app/services/planning_strategy.py +66 -0
  247. control_plane_api/app/services/planning_strategy_factory.py +118 -0
  248. control_plane_api/app/services/policy_service.py +615 -0
  249. control_plane_api/app/services/state_transition_service.py +755 -0
  250. control_plane_api/app/services/storage_service.py +593 -0
  251. control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
  252. control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
  253. control_plane_api/app/services/trace_retention.py +354 -0
  254. control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
  255. control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
  256. control_plane_api/app/services/workflow_operations_service.py +611 -0
  257. control_plane_api/app/skills/__init__.py +100 -0
  258. control_plane_api/app/skills/base.py +239 -0
  259. control_plane_api/app/skills/builtin/__init__.py +37 -0
  260. control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
  261. control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
  262. control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
  263. control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
  264. control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
  265. control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
  266. control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
  267. control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
  268. control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
  269. control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
  270. control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
  271. control_plane_api/app/skills/builtin/docker/skill.py +104 -0
  272. control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
  273. control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
  274. control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
  275. control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
  276. control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
  277. control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
  278. control_plane_api/app/skills/builtin/python/__init__.py +4 -0
  279. control_plane_api/app/skills/builtin/python/skill.py +92 -0
  280. control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
  281. control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
  282. control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
  283. control_plane_api/app/skills/builtin/shell/skill.py +161 -0
  284. control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
  285. control_plane_api/app/skills/builtin/slack/skill.py +302 -0
  286. control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
  287. control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
  288. control_plane_api/app/skills/business_intelligence.py +189 -0
  289. control_plane_api/app/skills/config.py +63 -0
  290. control_plane_api/app/skills/loaders/__init__.py +14 -0
  291. control_plane_api/app/skills/loaders/base.py +73 -0
  292. control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
  293. control_plane_api/app/skills/registry.py +125 -0
  294. control_plane_api/app/utils/helpers.py +12 -0
  295. control_plane_api/app/utils/workflow_executor.py +354 -0
  296. control_plane_api/app/workflows/__init__.py +11 -0
  297. control_plane_api/app/workflows/agent_execution.py +520 -0
  298. control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
  299. control_plane_api/app/workflows/namespace_provisioning.py +326 -0
  300. control_plane_api/app/workflows/plan_generation.py +254 -0
  301. control_plane_api/app/workflows/team_execution.py +442 -0
  302. control_plane_api/scripts/seed_models.py +240 -0
  303. control_plane_api/scripts/validate_existing_tool_names.py +492 -0
  304. control_plane_api/shared/__init__.py +8 -0
  305. control_plane_api/shared/version.py +17 -0
  306. control_plane_api/test_deduplication.py +274 -0
  307. control_plane_api/test_executor_deduplication_e2e.py +309 -0
  308. control_plane_api/test_job_execution_e2e.py +283 -0
  309. control_plane_api/test_real_integration.py +193 -0
  310. control_plane_api/version.py +38 -0
  311. control_plane_api/worker/__init__.py +0 -0
  312. control_plane_api/worker/activities/__init__.py +0 -0
  313. control_plane_api/worker/activities/agent_activities.py +1585 -0
  314. control_plane_api/worker/activities/approval_activities.py +234 -0
  315. control_plane_api/worker/activities/job_activities.py +199 -0
  316. control_plane_api/worker/activities/runtime_activities.py +1167 -0
  317. control_plane_api/worker/activities/skill_activities.py +282 -0
  318. control_plane_api/worker/activities/team_activities.py +479 -0
  319. control_plane_api/worker/agent_runtime_server.py +370 -0
  320. control_plane_api/worker/binary_manager.py +333 -0
  321. control_plane_api/worker/config/__init__.py +31 -0
  322. control_plane_api/worker/config/worker_config.py +273 -0
  323. control_plane_api/worker/control_plane_client.py +1491 -0
  324. control_plane_api/worker/examples/analytics_integration_example.py +362 -0
  325. control_plane_api/worker/health_monitor.py +159 -0
  326. control_plane_api/worker/metrics.py +237 -0
  327. control_plane_api/worker/models/__init__.py +1 -0
  328. control_plane_api/worker/models/error_events.py +105 -0
  329. control_plane_api/worker/models/inputs.py +89 -0
  330. control_plane_api/worker/runtimes/__init__.py +35 -0
  331. control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
  332. control_plane_api/worker/runtimes/agno/__init__.py +34 -0
  333. control_plane_api/worker/runtimes/agno/config.py +248 -0
  334. control_plane_api/worker/runtimes/agno/hooks.py +385 -0
  335. control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
  336. control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
  337. control_plane_api/worker/runtimes/agno/utils.py +163 -0
  338. control_plane_api/worker/runtimes/base.py +979 -0
  339. control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
  340. control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
  341. control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
  342. control_plane_api/worker/runtimes/claude_code/config.py +829 -0
  343. control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
  344. control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
  345. control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
  346. control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
  347. control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
  348. control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
  349. control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
  350. control_plane_api/worker/runtimes/factory.py +173 -0
  351. control_plane_api/worker/runtimes/model_utils.py +107 -0
  352. control_plane_api/worker/runtimes/validation.py +93 -0
  353. control_plane_api/worker/services/__init__.py +1 -0
  354. control_plane_api/worker/services/agent_communication_tools.py +908 -0
  355. control_plane_api/worker/services/agent_executor.py +485 -0
  356. control_plane_api/worker/services/agent_executor_v2.py +793 -0
  357. control_plane_api/worker/services/analytics_collector.py +457 -0
  358. control_plane_api/worker/services/analytics_service.py +464 -0
  359. control_plane_api/worker/services/approval_tools.py +310 -0
  360. control_plane_api/worker/services/approval_tools_agno.py +207 -0
  361. control_plane_api/worker/services/cancellation_manager.py +177 -0
  362. control_plane_api/worker/services/code_ingestion_tools.py +465 -0
  363. control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
  364. control_plane_api/worker/services/data_visualization.py +834 -0
  365. control_plane_api/worker/services/event_publisher.py +531 -0
  366. control_plane_api/worker/services/jira_tools.py +257 -0
  367. control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
  368. control_plane_api/worker/services/runtime_analytics.py +328 -0
  369. control_plane_api/worker/services/session_service.py +365 -0
  370. control_plane_api/worker/services/skill_context_enhancement.py +181 -0
  371. control_plane_api/worker/services/skill_factory.py +471 -0
  372. control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
  373. control_plane_api/worker/services/team_executor.py +715 -0
  374. control_plane_api/worker/services/team_executor_v2.py +1866 -0
  375. control_plane_api/worker/services/tool_enforcement.py +254 -0
  376. control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
  377. control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
  378. control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
  379. control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
  380. control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
  381. control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
  382. control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
  383. control_plane_api/worker/services/workflow_executor/models.py +142 -0
  384. control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
  385. control_plane_api/worker/skills/__init__.py +12 -0
  386. control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
  387. control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
  388. control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
  389. control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
  390. control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
  391. control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
  392. control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
  393. control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
  394. control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
  395. control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
  396. control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
  397. control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
  398. control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
  399. control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
  400. control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
  401. control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
  402. control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
  403. control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
  404. control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
  405. control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
  406. control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
  407. control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
  408. control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
  409. control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
  410. control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
  411. control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
  412. control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
  413. control_plane_api/worker/skills/loaders/__init__.py +5 -0
  414. control_plane_api/worker/skills/loaders/base.py +23 -0
  415. control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
  416. control_plane_api/worker/skills/registry.py +208 -0
  417. control_plane_api/worker/tests/__init__.py +1 -0
  418. control_plane_api/worker/tests/conftest.py +12 -0
  419. control_plane_api/worker/tests/e2e/__init__.py +0 -0
  420. control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
  421. control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
  422. control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
  423. control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
  424. control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
  425. control_plane_api/worker/tests/integration/__init__.py +0 -0
  426. control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
  427. control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
  428. control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
  429. control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
  430. control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
  431. control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
  432. control_plane_api/worker/tests/unit/__init__.py +0 -0
  433. control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
  434. control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
  435. control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
  436. control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
  437. control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
  438. control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
  439. control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
  440. control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
  441. control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
  442. control_plane_api/worker/utils/__init__.py +1 -0
  443. control_plane_api/worker/utils/chunk_batcher.py +330 -0
  444. control_plane_api/worker/utils/environment.py +65 -0
  445. control_plane_api/worker/utils/error_publisher.py +260 -0
  446. control_plane_api/worker/utils/event_batcher.py +256 -0
  447. control_plane_api/worker/utils/logging_config.py +335 -0
  448. control_plane_api/worker/utils/logging_helper.py +326 -0
  449. control_plane_api/worker/utils/parameter_validator.py +120 -0
  450. control_plane_api/worker/utils/retry_utils.py +60 -0
  451. control_plane_api/worker/utils/streaming_utils.py +665 -0
  452. control_plane_api/worker/utils/tool_validation.py +332 -0
  453. control_plane_api/worker/utils/workspace_manager.py +163 -0
  454. control_plane_api/worker/websocket_client.py +393 -0
  455. control_plane_api/worker/worker.py +1297 -0
  456. control_plane_api/worker/workflows/__init__.py +0 -0
  457. control_plane_api/worker/workflows/agent_execution.py +909 -0
  458. control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
  459. control_plane_api/worker/workflows/team_execution.py +611 -0
  460. kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
  461. kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
  462. kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
  463. kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
  464. kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
  465. kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
  466. scripts/__init__.py +1 -0
  467. scripts/migrations.py +39 -0
  468. scripts/seed_worker_queues.py +128 -0
  469. scripts/setup_agent_runtime.py +142 -0
  470. worker_internal/__init__.py +1 -0
  471. worker_internal/planner/__init__.py +1 -0
  472. worker_internal/planner/activities.py +1499 -0
  473. worker_internal/planner/agent_tools.py +197 -0
  474. worker_internal/planner/event_models.py +148 -0
  475. worker_internal/planner/event_publisher.py +67 -0
  476. worker_internal/planner/models.py +199 -0
  477. worker_internal/planner/retry_logic.py +134 -0
  478. worker_internal/planner/worker.py +300 -0
  479. worker_internal/planner/workflows.py +970 -0
@@ -0,0 +1,908 @@
1
+ """
2
+ Agent Communication Tools - Enable agents to call other agents or teams.
3
+
4
+ Provides hierarchical agent execution capabilities with security safeguards.
5
+ """
6
+ import asyncio
7
+ import os
8
+ import time
9
+ import uuid
10
+ from typing import Any, Dict, List, Optional, Union, Literal
11
+ from datetime import datetime
12
+ import httpx
13
+ import structlog
14
+
15
+ logger = structlog.get_logger()
16
+
17
+
18
+ class AgentCommunicationTools:
19
+ """
20
+ Tools for agent-to-agent and agent-to-team communication.
21
+
22
+ Enables hierarchical agent execution with:
23
+ - Parent-child execution tracking
24
+ - Execution depth control
25
+ - Circular call prevention
26
+ - Real-time status monitoring
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ allowed_operations: List[str] = None,
32
+ allowed_agents: Union[List[str], str] = None,
33
+ allowed_teams: Union[List[str], str] = None,
34
+ max_execution_depth: int = 2,
35
+ timeout: int = 300,
36
+ wait_for_completion: bool = True,
37
+ inherit_context: bool = True,
38
+ max_concurrent_calls: int = 3,
39
+ allow_session_continuation: bool = True,
40
+ streaming_enabled: bool = True,
41
+ parent_execution_id: Optional[str] = None,
42
+ execution_depth: int = 0,
43
+ control_plane_base_url: str = None,
44
+ kubiya_api_key: str = None,
45
+ organization_id: Optional[str] = None,
46
+ user_id: Optional[str] = None,
47
+ user_email: Optional[str] = None,
48
+ **kwargs
49
+ ):
50
+ """
51
+ Initialize Agent Communication Tools.
52
+
53
+ Args:
54
+ allowed_operations: List of allowed tool operations
55
+ allowed_agents: List of agent IDs that can be called, or '*' for all
56
+ allowed_teams: List of team IDs that can be called, or '*' for all
57
+ max_execution_depth: Maximum nesting depth for child executions
58
+ timeout: Maximum wait time for child execution in seconds
59
+ wait_for_completion: Whether to wait for child execution to complete
60
+ inherit_context: Whether to pass parent execution context to child
61
+ max_concurrent_calls: Maximum number of concurrent child executions
62
+ allow_session_continuation: Allow following up on existing sessions
63
+ streaming_enabled: Stream child execution events to parent
64
+ parent_execution_id: ID of parent execution (for tracking)
65
+ execution_depth: Current depth in execution tree
66
+ control_plane_base_url: Control Plane API base URL
67
+ kubiya_api_key: Kubiya API key for authentication
68
+ organization_id: Organization ID for context
69
+ user_id: User ID for context
70
+ user_email: User email for context
71
+ **kwargs: Additional configuration
72
+ """
73
+ self.allowed_operations = allowed_operations or ["get_execution_status"]
74
+ self.allowed_agents = allowed_agents or []
75
+ self.allowed_teams = allowed_teams or []
76
+ self.max_execution_depth = max_execution_depth
77
+ self.timeout = timeout
78
+ self.wait_for_completion = wait_for_completion
79
+ self.inherit_context = inherit_context
80
+ self.max_concurrent_calls = max_concurrent_calls
81
+ self.allow_session_continuation = allow_session_continuation
82
+ self.streaming_enabled = streaming_enabled
83
+ self.parent_execution_id = parent_execution_id
84
+ self.execution_depth = execution_depth
85
+ self.organization_id = organization_id
86
+ self.user_id = user_id
87
+ self.user_email = user_email
88
+
89
+ # Get control plane URL from environment or parameter
90
+ self.control_plane_base_url = (
91
+ control_plane_base_url or
92
+ os.environ.get("CONTROL_PLANE_BASE_URL") or
93
+ os.environ.get("CONTROL_PLANE_URL", "http://localhost:8000")
94
+ ).rstrip("/")
95
+
96
+ self.kubiya_api_key = kubiya_api_key or os.environ.get("KUBIYA_API_KEY")
97
+ if not self.kubiya_api_key:
98
+ raise ValueError("KUBIYA_API_KEY is required for Agent Communication tools")
99
+
100
+ # Semaphore for concurrent execution limit
101
+ self._semaphore = asyncio.Semaphore(max_concurrent_calls)
102
+
103
+ # HTTP client for API calls
104
+ self._client = httpx.AsyncClient(timeout=httpx.Timeout(timeout, connect=5.0))
105
+
106
+ logger.info(
107
+ "agent_communication_tools_initialized",
108
+ allowed_operations=self.allowed_operations,
109
+ allowed_agents=self.allowed_agents if self.allowed_agents != "*" else "all",
110
+ allowed_teams=self.allowed_teams if self.allowed_teams != "*" else "all",
111
+ max_execution_depth=max_execution_depth,
112
+ execution_depth=execution_depth,
113
+ parent_execution_id=parent_execution_id,
114
+ )
115
+
116
+ def _get_headers(self) -> Dict[str, str]:
117
+ """Get headers for API requests."""
118
+ return {
119
+ "Authorization": f"UserKey {self.kubiya_api_key}",
120
+ "Content-Type": "application/json",
121
+ "Accept": "application/json",
122
+ }
123
+
124
+ def _check_operation_allowed(self, operation: str) -> bool:
125
+ """Check if an operation is allowed."""
126
+ return operation in self.allowed_operations
127
+
128
+ def _check_allowed_entity(self, entity_type: str, entity_id: str) -> bool:
129
+ """
130
+ Check if entity is allowed to be called.
131
+
132
+ Args:
133
+ entity_type: "agent" or "team"
134
+ entity_id: Agent or team ID
135
+
136
+ Returns:
137
+ True if allowed, False otherwise
138
+ """
139
+ allowed_list = self.allowed_agents if entity_type == "agent" else self.allowed_teams
140
+
141
+ # Wildcard = all allowed
142
+ if allowed_list == "*":
143
+ return True
144
+
145
+ # Check explicit whitelist
146
+ if isinstance(allowed_list, list):
147
+ return entity_id in allowed_list
148
+
149
+ return False
150
+
151
+ def _check_execution_depth(self, child_depth: int) -> None:
152
+ """
153
+ Check if execution depth is within limits.
154
+
155
+ Args:
156
+ child_depth: Depth of child execution
157
+
158
+ Raises:
159
+ RuntimeError: If depth exceeds max_execution_depth
160
+ """
161
+ if child_depth > self.max_execution_depth:
162
+ raise RuntimeError(
163
+ f"Execution depth limit exceeded: {child_depth} > {self.max_execution_depth}. "
164
+ f"Cannot create deeper child executions."
165
+ )
166
+
167
+ async def _get_execution_chain(self, execution_id: str) -> List[Dict[str, Any]]:
168
+ """
169
+ Build execution chain from root to current execution.
170
+
171
+ Args:
172
+ execution_id: Execution ID to start from
173
+
174
+ Returns:
175
+ List of execution info dicts with execution_id, entity_type, entity_id
176
+ """
177
+ chain = []
178
+ current_id = execution_id
179
+ max_depth = 20 # Prevent infinite loops
180
+
181
+ while current_id and len(chain) < max_depth:
182
+ try:
183
+ url = f"{self.control_plane_base_url}/api/v1/executions/{current_id}"
184
+ response = await self._client.get(url, headers=self._get_headers())
185
+
186
+ if response.status_code != 200:
187
+ logger.warning(
188
+ "failed_to_fetch_execution_in_chain",
189
+ execution_id=current_id,
190
+ status_code=response.status_code
191
+ )
192
+ break
193
+
194
+ execution = response.json()
195
+ chain.append({
196
+ "execution_id": current_id,
197
+ "entity_type": execution.get("entity_type"),
198
+ "entity_id": execution.get("entity_id"),
199
+ })
200
+
201
+ # Get parent execution ID
202
+ current_id = execution.get("parent_execution_id")
203
+
204
+ except Exception as e:
205
+ logger.error(
206
+ "error_building_execution_chain",
207
+ execution_id=current_id,
208
+ error=str(e)
209
+ )
210
+ break
211
+
212
+ return chain
213
+
214
+ def _is_circular_call(self, chain: List[Dict[str, Any]], target_entity_id: str) -> bool:
215
+ """
216
+ Check if target_entity_id appears in execution chain (circular call).
217
+
218
+ Args:
219
+ chain: Execution chain from _get_execution_chain
220
+ target_entity_id: Agent or team ID to check
221
+
222
+ Returns:
223
+ True if circular call detected, False otherwise
224
+ """
225
+ return any(e.get("entity_id") == target_entity_id for e in chain)
226
+
227
+ async def _check_circular_execution(self, target_entity_id: str) -> None:
228
+ """
229
+ Check if calling target_entity_id would create a circular execution.
230
+
231
+ Args:
232
+ target_entity_id: Agent or team ID to check
233
+
234
+ Raises:
235
+ RuntimeError: If circular call detected
236
+ """
237
+ if not self.parent_execution_id:
238
+ # No parent, so no cycle possible
239
+ return
240
+
241
+ # Build execution chain
242
+ chain = await self._get_execution_chain(self.parent_execution_id)
243
+
244
+ # Check for circular call
245
+ if self._is_circular_call(chain, target_entity_id):
246
+ chain_str = " -> ".join([e["entity_id"] for e in chain])
247
+ raise RuntimeError(
248
+ f"Circular execution detected: {target_entity_id} already appears in execution chain. "
249
+ f"Chain: {chain_str} -> {target_entity_id}"
250
+ )
251
+
252
+ def _build_child_context(self) -> Dict[str, Any]:
253
+ """
254
+ Build context to pass to child execution.
255
+
256
+ Returns:
257
+ Dict with parent context information
258
+ """
259
+ context = {
260
+ "parent_execution_id": self.parent_execution_id,
261
+ "execution_depth": self.execution_depth + 1,
262
+ }
263
+
264
+ if self.inherit_context:
265
+ # Add user context
266
+ if self.user_id:
267
+ context["user_id"] = self.user_id
268
+ if self.user_email:
269
+ context["user_email"] = self.user_email
270
+ if self.organization_id:
271
+ context["organization_id"] = self.organization_id
272
+
273
+ return context
274
+
275
+ async def _wait_for_execution(
276
+ self,
277
+ execution_id: str,
278
+ timeout: Optional[int] = None
279
+ ) -> Dict[str, Any]:
280
+ """
281
+ Poll execution status until complete or timeout.
282
+
283
+ Args:
284
+ execution_id: Execution ID to wait for
285
+ timeout: Timeout in seconds (defaults to self.timeout)
286
+
287
+ Returns:
288
+ Dict with execution result
289
+ """
290
+ timeout = timeout or self.timeout
291
+ start_time = time.time()
292
+ poll_interval = 2 # seconds
293
+
294
+ while True:
295
+ elapsed = time.time() - start_time
296
+
297
+ if elapsed >= timeout:
298
+ logger.warning(
299
+ "execution_timeout",
300
+ execution_id=execution_id,
301
+ timeout=timeout
302
+ )
303
+ return {
304
+ "execution_id": execution_id,
305
+ "status": "timeout",
306
+ "error": f"Execution exceeded timeout of {timeout}s",
307
+ "duration_ms": int(elapsed * 1000),
308
+ }
309
+
310
+ # Fetch execution status
311
+ try:
312
+ status_result = await self.get_execution_status(
313
+ execution_id=execution_id,
314
+ include_events=False,
315
+ include_session=False
316
+ )
317
+
318
+ status = status_result.get("status")
319
+
320
+ # Check if completed
321
+ if status in ["completed", "failed", "cancelled"]:
322
+ return status_result
323
+
324
+ # Still running or waiting for input, continue polling
325
+ await asyncio.sleep(poll_interval)
326
+
327
+ except Exception as e:
328
+ logger.error(
329
+ "error_polling_execution_status",
330
+ execution_id=execution_id,
331
+ error=str(e)
332
+ )
333
+ return {
334
+ "execution_id": execution_id,
335
+ "status": "error",
336
+ "error": f"Failed to poll execution status: {str(e)}",
337
+ }
338
+
339
+ async def _stream_child_events(
340
+ self,
341
+ child_execution_id: str,
342
+ parent_execution_id: str
343
+ ) -> None:
344
+ """
345
+ Stream child execution events to parent execution (background task).
346
+
347
+ Args:
348
+ child_execution_id: Child execution ID
349
+ parent_execution_id: Parent execution ID to stream events to
350
+ """
351
+ # TODO: Implement event streaming from child to parent
352
+ # This would require subscribing to child execution events via WebSocket or SSE
353
+ # and republishing them to the parent execution
354
+ logger.info(
355
+ "event_streaming_placeholder",
356
+ child_execution_id=child_execution_id,
357
+ parent_execution_id=parent_execution_id,
358
+ message="Event streaming not yet implemented"
359
+ )
360
+
361
+ async def execute_agent(
362
+ self,
363
+ agent_id: str,
364
+ prompt: str,
365
+ wait_for_completion: Optional[bool] = None,
366
+ timeout: Optional[int] = None,
367
+ inherit_context: Optional[bool] = None,
368
+ ) -> Dict[str, Any]:
369
+ """
370
+ Execute another agent with a specific prompt.
371
+
372
+ Creates a new child execution and optionally waits for completion.
373
+
374
+ Args:
375
+ agent_id: The ID of the agent to execute
376
+ prompt: The prompt/instruction to send to the agent
377
+ wait_for_completion: Whether to wait for the agent to finish (defaults to config)
378
+ timeout: Maximum wait time in seconds (defaults to config)
379
+ inherit_context: Whether to pass parent execution context (defaults to config)
380
+
381
+ Returns:
382
+ Dict containing:
383
+ - execution_id: ID of the child execution
384
+ - status: Current execution status
385
+ - result: Final result if wait_for_completion=True
386
+ - events: List of execution events
387
+
388
+ Raises:
389
+ ValueError: If agent_id not in allowed_agents or operation not allowed
390
+ RuntimeError: If max_execution_depth exceeded or circular execution detected
391
+ """
392
+ # Check operation allowed
393
+ if not self._check_operation_allowed("execute_agent"):
394
+ raise ValueError(
395
+ "Operation 'execute_agent' not allowed. "
396
+ f"Allowed operations: {self.allowed_operations}"
397
+ )
398
+
399
+ # Check agent allowed
400
+ if not self._check_allowed_entity("agent", agent_id):
401
+ raise ValueError(
402
+ f"Agent '{agent_id}' not in allowed agents list. "
403
+ f"Allowed agents: {self.allowed_agents}"
404
+ )
405
+
406
+ # Use defaults if not specified
407
+ wait = wait_for_completion if wait_for_completion is not None else self.wait_for_completion
408
+ timeout_val = timeout if timeout is not None else self.timeout
409
+ inherit = inherit_context if inherit_context is not None else self.inherit_context
410
+
411
+ # Check execution depth
412
+ child_depth = self.execution_depth + 1
413
+ self._check_execution_depth(child_depth)
414
+
415
+ # Check circular execution
416
+ await self._check_circular_execution(agent_id)
417
+
418
+ # Acquire semaphore for concurrent execution limit
419
+ async with self._semaphore:
420
+ # Create child execution
421
+ execution_id = str(uuid.uuid4())
422
+
423
+ logger.info(
424
+ "creating_child_agent_execution",
425
+ parent_execution_id=self.parent_execution_id,
426
+ child_execution_id=execution_id,
427
+ agent_id=agent_id,
428
+ execution_depth=child_depth,
429
+ wait_for_completion=wait
430
+ )
431
+
432
+ try:
433
+ # Build child context
434
+ child_context = self._build_child_context() if inherit else {}
435
+
436
+ # Create execution via Control Plane API
437
+ url = f"{self.control_plane_base_url}/api/v1/executions"
438
+ payload = {
439
+ "execution_id": execution_id,
440
+ "entity_type": "agent",
441
+ "entity_id": agent_id,
442
+ "prompt": prompt,
443
+ "parent_execution_id": self.parent_execution_id,
444
+ "execution_depth": child_depth,
445
+ "context": child_context,
446
+ }
447
+
448
+ if self.organization_id:
449
+ payload["organization_id"] = self.organization_id
450
+
451
+ response = await self._client.post(
452
+ url,
453
+ headers=self._get_headers(),
454
+ json=payload
455
+ )
456
+
457
+ if response.status_code not in [200, 201, 202]:
458
+ error_msg = f"Failed to create execution: {response.status_code} {response.text}"
459
+ logger.error(
460
+ "create_execution_failed",
461
+ execution_id=execution_id,
462
+ agent_id=agent_id,
463
+ status_code=response.status_code,
464
+ error=response.text[:500]
465
+ )
466
+ return {
467
+ "execution_id": execution_id,
468
+ "status": "failed",
469
+ "error": error_msg,
470
+ }
471
+
472
+ # Start event streaming if enabled (background task)
473
+ if self.streaming_enabled and self.parent_execution_id:
474
+ asyncio.create_task(
475
+ self._stream_child_events(execution_id, self.parent_execution_id)
476
+ )
477
+
478
+ # Wait for completion if requested
479
+ if wait:
480
+ result = await self._wait_for_execution(execution_id, timeout_val)
481
+ logger.info(
482
+ "child_agent_execution_completed",
483
+ execution_id=execution_id,
484
+ agent_id=agent_id,
485
+ status=result.get("status")
486
+ )
487
+ return result
488
+ else:
489
+ # Return immediately with execution ID
490
+ return {
491
+ "execution_id": execution_id,
492
+ "status": "running",
493
+ "entity_type": "agent",
494
+ "entity_id": agent_id,
495
+ "parent_execution_id": self.parent_execution_id,
496
+ "execution_depth": child_depth,
497
+ "message": "Execution started asynchronously. Use get_execution_status() to check progress.",
498
+ }
499
+
500
+ except Exception as e:
501
+ logger.error(
502
+ "execute_agent_error",
503
+ execution_id=execution_id,
504
+ agent_id=agent_id,
505
+ error=str(e)
506
+ )
507
+ return {
508
+ "execution_id": execution_id,
509
+ "status": "failed",
510
+ "error": f"Failed to execute agent: {str(e)}",
511
+ }
512
+
513
+ async def execute_team(
514
+ self,
515
+ team_id: str,
516
+ prompt: str,
517
+ wait_for_completion: Optional[bool] = None,
518
+ timeout: Optional[int] = None,
519
+ inherit_context: Optional[bool] = None,
520
+ ) -> Dict[str, Any]:
521
+ """
522
+ Execute a team with a specific prompt.
523
+
524
+ Creates a new child team execution with multi-agent collaboration.
525
+
526
+ Args:
527
+ team_id: The ID of the team to execute
528
+ prompt: The prompt/instruction to send to the team
529
+ wait_for_completion: Whether to wait for team to finish
530
+ timeout: Maximum wait time in seconds
531
+ inherit_context: Whether to pass parent execution context
532
+
533
+ Returns:
534
+ Dict containing:
535
+ - execution_id: ID of the child execution
536
+ - status: Current execution status
537
+ - result: Final result if wait_for_completion=True
538
+ - team_members: List of agents in the team
539
+ - events: List of execution events
540
+
541
+ Raises:
542
+ ValueError: If team_id not in allowed_teams or operation not allowed
543
+ RuntimeError: If max_execution_depth exceeded or circular execution detected
544
+ """
545
+ # Check operation allowed
546
+ if not self._check_operation_allowed("execute_team"):
547
+ raise ValueError(
548
+ "Operation 'execute_team' not allowed. "
549
+ f"Allowed operations: {self.allowed_operations}"
550
+ )
551
+
552
+ # Check team allowed
553
+ if not self._check_allowed_entity("team", team_id):
554
+ raise ValueError(
555
+ f"Team '{team_id}' not in allowed teams list. "
556
+ f"Allowed teams: {self.allowed_teams}"
557
+ )
558
+
559
+ # Use defaults if not specified
560
+ wait = wait_for_completion if wait_for_completion is not None else self.wait_for_completion
561
+ timeout_val = timeout if timeout is not None else self.timeout
562
+ inherit = inherit_context if inherit_context is not None else self.inherit_context
563
+
564
+ # Check execution depth
565
+ child_depth = self.execution_depth + 1
566
+ self._check_execution_depth(child_depth)
567
+
568
+ # Check circular execution
569
+ await self._check_circular_execution(team_id)
570
+
571
+ # Acquire semaphore for concurrent execution limit
572
+ async with self._semaphore:
573
+ # Create child execution
574
+ execution_id = str(uuid.uuid4())
575
+
576
+ logger.info(
577
+ "creating_child_team_execution",
578
+ parent_execution_id=self.parent_execution_id,
579
+ child_execution_id=execution_id,
580
+ team_id=team_id,
581
+ execution_depth=child_depth,
582
+ wait_for_completion=wait
583
+ )
584
+
585
+ try:
586
+ # Build child context
587
+ child_context = self._build_child_context() if inherit else {}
588
+
589
+ # Create execution via Control Plane API
590
+ url = f"{self.control_plane_base_url}/api/v1/executions"
591
+ payload = {
592
+ "execution_id": execution_id,
593
+ "entity_type": "team",
594
+ "entity_id": team_id,
595
+ "prompt": prompt,
596
+ "parent_execution_id": self.parent_execution_id,
597
+ "execution_depth": child_depth,
598
+ "context": child_context,
599
+ }
600
+
601
+ if self.organization_id:
602
+ payload["organization_id"] = self.organization_id
603
+
604
+ response = await self._client.post(
605
+ url,
606
+ headers=self._get_headers(),
607
+ json=payload
608
+ )
609
+
610
+ if response.status_code not in [200, 201, 202]:
611
+ error_msg = f"Failed to create execution: {response.status_code} {response.text}"
612
+ logger.error(
613
+ "create_execution_failed",
614
+ execution_id=execution_id,
615
+ team_id=team_id,
616
+ status_code=response.status_code,
617
+ error=response.text[:500]
618
+ )
619
+ return {
620
+ "execution_id": execution_id,
621
+ "status": "failed",
622
+ "error": error_msg,
623
+ }
624
+
625
+ # Start event streaming if enabled (background task)
626
+ if self.streaming_enabled and self.parent_execution_id:
627
+ asyncio.create_task(
628
+ self._stream_child_events(execution_id, self.parent_execution_id)
629
+ )
630
+
631
+ # Wait for completion if requested
632
+ if wait:
633
+ result = await self._wait_for_execution(execution_id, timeout_val)
634
+ logger.info(
635
+ "child_team_execution_completed",
636
+ execution_id=execution_id,
637
+ team_id=team_id,
638
+ status=result.get("status")
639
+ )
640
+ return result
641
+ else:
642
+ # Return immediately with execution ID
643
+ return {
644
+ "execution_id": execution_id,
645
+ "status": "running",
646
+ "entity_type": "team",
647
+ "entity_id": team_id,
648
+ "parent_execution_id": self.parent_execution_id,
649
+ "execution_depth": child_depth,
650
+ "message": "Execution started asynchronously. Use get_execution_status() to check progress.",
651
+ }
652
+
653
+ except Exception as e:
654
+ logger.error(
655
+ "execute_team_error",
656
+ execution_id=execution_id,
657
+ team_id=team_id,
658
+ error=str(e)
659
+ )
660
+ return {
661
+ "execution_id": execution_id,
662
+ "status": "failed",
663
+ "error": f"Failed to execute team: {str(e)}",
664
+ }
665
+
666
+ async def followup_execution(
667
+ self,
668
+ execution_id: str,
669
+ followup_prompt: str,
670
+ wait_for_completion: Optional[bool] = None,
671
+ timeout: Optional[int] = None,
672
+ ) -> Dict[str, Any]:
673
+ """
674
+ Send a followup prompt to an existing execution (continue conversation).
675
+
676
+ Only available if allow_session_continuation=True.
677
+
678
+ Args:
679
+ execution_id: The ID of the execution to follow up on
680
+ followup_prompt: Additional prompt/instruction to add to conversation
681
+ wait_for_completion: Whether to wait for response
682
+ timeout: Maximum wait time in seconds
683
+
684
+ Returns:
685
+ Dict containing:
686
+ - execution_id: Same execution ID
687
+ - status: Current execution status
688
+ - result: Response to followup
689
+ - message_count: Total messages in session
690
+
691
+ Raises:
692
+ RuntimeError: If allow_session_continuation=False
693
+ ValueError: If execution_id not found or not accessible or operation not allowed
694
+ """
695
+ # Check operation allowed
696
+ if not self._check_operation_allowed("followup_execution"):
697
+ raise ValueError(
698
+ "Operation 'followup_execution' not allowed. "
699
+ f"Allowed operations: {self.allowed_operations}"
700
+ )
701
+
702
+ # Check if session continuation is allowed
703
+ if not self.allow_session_continuation:
704
+ raise RuntimeError(
705
+ "Session continuation is not allowed. "
706
+ "Set allow_session_continuation=True to enable this operation."
707
+ )
708
+
709
+ # Use defaults if not specified
710
+ wait = wait_for_completion if wait_for_completion is not None else self.wait_for_completion
711
+ timeout_val = timeout if timeout is not None else self.timeout
712
+
713
+ logger.info(
714
+ "following_up_execution",
715
+ execution_id=execution_id,
716
+ wait_for_completion=wait
717
+ )
718
+
719
+ try:
720
+ # Send followup via Control Plane API
721
+ url = f"{self.control_plane_base_url}/api/v1/executions/{execution_id}/followup"
722
+ payload = {
723
+ "prompt": followup_prompt,
724
+ }
725
+
726
+ response = await self._client.post(
727
+ url,
728
+ headers=self._get_headers(),
729
+ json=payload
730
+ )
731
+
732
+ if response.status_code not in [200, 201, 202]:
733
+ error_msg = f"Failed to send followup: {response.status_code} {response.text}"
734
+ logger.error(
735
+ "followup_execution_failed",
736
+ execution_id=execution_id,
737
+ status_code=response.status_code,
738
+ error=response.text[:500]
739
+ )
740
+ return {
741
+ "execution_id": execution_id,
742
+ "status": "failed",
743
+ "error": error_msg,
744
+ }
745
+
746
+ # Wait for completion if requested
747
+ if wait:
748
+ result = await self._wait_for_execution(execution_id, timeout_val)
749
+ logger.info(
750
+ "followup_execution_completed",
751
+ execution_id=execution_id,
752
+ status=result.get("status")
753
+ )
754
+ return result
755
+ else:
756
+ return {
757
+ "execution_id": execution_id,
758
+ "status": "running",
759
+ "message": "Followup sent successfully. Use get_execution_status() to check progress.",
760
+ }
761
+
762
+ except Exception as e:
763
+ logger.error(
764
+ "followup_execution_error",
765
+ execution_id=execution_id,
766
+ error=str(e)
767
+ )
768
+ return {
769
+ "execution_id": execution_id,
770
+ "status": "failed",
771
+ "error": f"Failed to send followup: {str(e)}",
772
+ }
773
+
774
+ async def get_execution_status(
775
+ self,
776
+ execution_id: str,
777
+ include_events: bool = False,
778
+ include_session: bool = False,
779
+ ) -> Dict[str, Any]:
780
+ """
781
+ Get the current status of an execution.
782
+
783
+ Available for all variants (monitoring-only to full orchestration).
784
+
785
+ Args:
786
+ execution_id: The ID of the execution to check
787
+ include_events: Whether to include full event history
788
+ include_session: Whether to include conversation history
789
+
790
+ Returns:
791
+ Dict containing:
792
+ - execution_id: Execution ID
793
+ - status: Current status (pending, running, completed, failed)
794
+ - entity_type: "agent" or "team"
795
+ - entity_id: Agent or team ID
796
+ - created_at: Start timestamp
797
+ - completed_at: End timestamp (if completed)
798
+ - duration_ms: Execution duration
799
+ - result: Final result (if completed)
800
+ - error: Error message (if failed)
801
+ - events: Event history (if include_events=True)
802
+ - session: Conversation history (if include_session=True)
803
+
804
+ Raises:
805
+ ValueError: If operation not allowed
806
+ """
807
+ # Check operation allowed
808
+ if not self._check_operation_allowed("get_execution_status"):
809
+ raise ValueError(
810
+ "Operation 'get_execution_status' not allowed. "
811
+ f"Allowed operations: {self.allowed_operations}"
812
+ )
813
+
814
+ logger.debug(
815
+ "fetching_execution_status",
816
+ execution_id=execution_id,
817
+ include_events=include_events,
818
+ include_session=include_session
819
+ )
820
+
821
+ try:
822
+ # Fetch execution from Control Plane API
823
+ url = f"{self.control_plane_base_url}/api/v1/executions/{execution_id}"
824
+ params = {}
825
+ if include_events:
826
+ params["include_events"] = "true"
827
+ if include_session:
828
+ params["include_session"] = "true"
829
+
830
+ response = await self._client.get(
831
+ url,
832
+ headers=self._get_headers(),
833
+ params=params
834
+ )
835
+
836
+ if response.status_code == 404:
837
+ return {
838
+ "execution_id": execution_id,
839
+ "status": "not_found",
840
+ "error": f"Execution '{execution_id}' not found",
841
+ }
842
+
843
+ if response.status_code != 200:
844
+ error_msg = f"Failed to fetch execution: {response.status_code} {response.text}"
845
+ logger.error(
846
+ "fetch_execution_failed",
847
+ execution_id=execution_id,
848
+ status_code=response.status_code,
849
+ error=response.text[:500]
850
+ )
851
+ return {
852
+ "execution_id": execution_id,
853
+ "status": "error",
854
+ "error": error_msg,
855
+ }
856
+
857
+ execution_data = response.json()
858
+
859
+ # Format response
860
+ result = {
861
+ "execution_id": execution_id,
862
+ "status": execution_data.get("status", "unknown"),
863
+ "entity_type": execution_data.get("entity_type"),
864
+ "entity_id": execution_data.get("entity_id"),
865
+ "created_at": execution_data.get("created_at"),
866
+ "completed_at": execution_data.get("completed_at"),
867
+ "duration_ms": execution_data.get("duration_ms"),
868
+ "parent_execution_id": execution_data.get("parent_execution_id"),
869
+ "execution_depth": execution_data.get("execution_depth", 0),
870
+ }
871
+
872
+ # Add result if completed
873
+ if execution_data.get("result"):
874
+ result["result"] = execution_data["result"]
875
+
876
+ # Add error if failed
877
+ if execution_data.get("error"):
878
+ result["error"] = execution_data["error"]
879
+
880
+ # Add events if requested
881
+ if include_events and execution_data.get("events"):
882
+ result["events"] = execution_data["events"]
883
+
884
+ # Add session if requested
885
+ if include_session and execution_data.get("session"):
886
+ result["session"] = execution_data["session"]
887
+
888
+ return result
889
+
890
+ except Exception as e:
891
+ logger.error(
892
+ "get_execution_status_error",
893
+ execution_id=execution_id,
894
+ error=str(e)
895
+ )
896
+ return {
897
+ "execution_id": execution_id,
898
+ "status": "error",
899
+ "error": f"Failed to get execution status: {str(e)}",
900
+ }
901
+
902
+ async def __aenter__(self):
903
+ """Async context manager entry."""
904
+ return self
905
+
906
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
907
+ """Async context manager exit - cleanup."""
908
+ await self._client.aclose()