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,646 @@
1
+ """
2
+ Planning Service - Internal service for task planning with direct DB access
3
+
4
+ This service provides access to agents, teams, and context graph data using:
5
+ - Direct SQLAlchemy queries for agents/teams (no HTTP overhead)
6
+ - ContextGraphClient for external context graph API (graph.kubiya.ai)
7
+
8
+ All queries are org-scoped to ensure proper authorization.
9
+ """
10
+
11
+ import structlog
12
+ from typing import List, Dict, Any, Optional
13
+ from sqlalchemy.orm import Session
14
+ from uuid import UUID
15
+
16
+ from control_plane_api.app.models.agent import Agent
17
+ from control_plane_api.app.models.team import Team, TeamStatus
18
+ from control_plane_api.app.services.context_graph_client import ContextGraphClient
19
+
20
+ logger = structlog.get_logger(__name__)
21
+
22
+
23
+ class PlanningService:
24
+ """
25
+ Internal service for accessing planning resources.
26
+
27
+ Uses direct DB queries for agents/teams (fast, no HTTP overhead)
28
+ Uses ContextGraphClient for context graph (external service, HTTP correct)
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ db: Session,
34
+ organization_id: str,
35
+ api_token: str,
36
+ context_graph_base: str = "https://graph.kubiya.ai"
37
+ ):
38
+ """
39
+ Initialize planning service.
40
+
41
+ Args:
42
+ db: SQLAlchemy database session
43
+ organization_id: Organization ID for filtering
44
+ api_token: API token for context graph auth
45
+ context_graph_base: Context graph API base URL
46
+ """
47
+ self.db = db
48
+ self.organization_id = organization_id
49
+ self.api_token = api_token
50
+
51
+ # Initialize context graph client for external queries
52
+ self.context_graph_client = ContextGraphClient(
53
+ api_base=context_graph_base,
54
+ api_token=api_token,
55
+ organization_id=organization_id
56
+ )
57
+
58
+ logger.info(
59
+ "planning_service_initialized",
60
+ organization_id=organization_id[:8]
61
+ )
62
+
63
+ def _agent_to_dict(self, agent: Agent) -> Dict[str, Any]:
64
+ """Convert Agent model to dictionary for planning (status field excluded)."""
65
+ return {
66
+ "id": str(agent.id),
67
+ "name": agent.name,
68
+ "description": agent.description,
69
+ "model_id": agent.model_id or "default",
70
+ "capabilities": agent.capabilities or [],
71
+ "runtime": agent.runtime,
72
+ "team_id": str(agent.team_id) if agent.team_id else None,
73
+ "execution_environment": agent.execution_environment or {},
74
+ }
75
+
76
+ def _team_to_dict(self, team: Team) -> Dict[str, Any]:
77
+ """Convert Team model to dictionary."""
78
+ # Get agent count from configuration
79
+ config = team.configuration or {}
80
+ member_ids = config.get("member_ids", [])
81
+
82
+ # Get agents if they exist
83
+ agents = []
84
+ if member_ids:
85
+ db_agents = self.db.query(Agent).filter(
86
+ Agent.id.in_([UUID(mid) if isinstance(mid, str) else mid for mid in member_ids]),
87
+ Agent.organization_id == self.organization_id
88
+ ).all()
89
+ agents = [
90
+ {
91
+ "id": str(a.id),
92
+ "name": a.name,
93
+ "model_id": a.model_id or "default",
94
+ "status": a.status,
95
+ }
96
+ for a in db_agents
97
+ ]
98
+
99
+ return {
100
+ "id": str(team.id),
101
+ "name": team.name,
102
+ "description": team.description,
103
+ "status": team.status.value if hasattr(team.status, 'value') else str(team.status),
104
+ "agent_count": len(agents),
105
+ "agents": agents,
106
+ "runtime": team.runtime,
107
+ "execution_environment": team.execution_environment or {},
108
+ }
109
+
110
+ def list_agents(
111
+ self,
112
+ limit: int = 20,
113
+ status: str = None # Changed from "active" to None - don't filter by default
114
+ ) -> List[Dict[str, Any]]:
115
+ """
116
+ List agents in the organization.
117
+
118
+ Args:
119
+ limit: Maximum agents to return
120
+ status: Optional status filter (None = return all agents regardless of status)
121
+
122
+ Returns:
123
+ List of agent dictionaries
124
+ """
125
+ try:
126
+ logger.info(
127
+ "list_agents_called",
128
+ organization_id=self.organization_id[:8],
129
+ limit=limit,
130
+ status=status or "all"
131
+ )
132
+
133
+ # DEBUG: Log actual query parameters
134
+ logger.info(
135
+ "list_agents_DEBUG_QUERY",
136
+ full_org_id=self.organization_id,
137
+ org_id_len=len(self.organization_id),
138
+ status_filter=status or "no_filter",
139
+ query_info=f"WHERE organization_id='{self.organization_id}'" + (f" AND status='{status}'" if status else " (no status filter)")
140
+ )
141
+
142
+ # Direct SQLAlchemy query with org filtering
143
+ query = self.db.query(Agent).filter(
144
+ Agent.organization_id == self.organization_id
145
+ )
146
+
147
+ # Only filter by status if explicitly provided
148
+ if status:
149
+ query = query.filter(Agent.status == status)
150
+
151
+ agents = query.limit(limit).all()
152
+
153
+ # DEBUG: Log what we got back
154
+ logger.info(
155
+ "list_agents_DEBUG_RESULTS",
156
+ agents_found=len(agents),
157
+ agent_names=[a.name for a in agents[:5]] if agents else []
158
+ )
159
+
160
+ result = [self._agent_to_dict(a) for a in agents]
161
+
162
+ logger.info(
163
+ "list_agents_completed",
164
+ count=len(result),
165
+ organization_id=self.organization_id[:8]
166
+ )
167
+
168
+ return result
169
+
170
+ except Exception as e:
171
+ logger.error("list_agents_error", error=str(e), exc_info=True)
172
+ return []
173
+
174
+ def search_agents_by_capability(
175
+ self,
176
+ capability: str,
177
+ limit: int = 10
178
+ ) -> List[Dict[str, Any]]:
179
+ """
180
+ Search for agents with a specific capability.
181
+
182
+ Args:
183
+ capability: Capability to search for
184
+ limit: Maximum agents to return
185
+
186
+ Returns:
187
+ List of matching agent dictionaries
188
+ """
189
+ try:
190
+ logger.info(
191
+ "search_agents_by_capability_called",
192
+ capability=capability,
193
+ organization_id=self.organization_id[:8],
194
+ limit=limit
195
+ )
196
+
197
+ # Get all agents in org (no status filter - let LLM decide based on capabilities)
198
+ agents = self.db.query(Agent).filter(
199
+ Agent.organization_id == self.organization_id
200
+ ).all()
201
+
202
+ # Filter by capability (search in capabilities, description, name)
203
+ capability_lower = capability.lower()
204
+ matching = [
205
+ a for a in agents
206
+ if capability_lower in str(a.capabilities).lower()
207
+ or capability_lower in (a.description or "").lower()
208
+ or capability_lower in a.name.lower()
209
+ ]
210
+
211
+ result = [self._agent_to_dict(a) for a in matching[:limit]]
212
+
213
+ logger.info(
214
+ "search_agents_by_capability_completed",
215
+ capability=capability,
216
+ count=len(result),
217
+ organization_id=self.organization_id[:8]
218
+ )
219
+
220
+ return result
221
+
222
+ except Exception as e:
223
+ logger.error("search_agents_by_capability_error", error=str(e), exc_info=True)
224
+ return []
225
+
226
+ def list_teams(
227
+ self,
228
+ limit: int = 20,
229
+ status: str = None # Don't filter by status by default
230
+ ) -> List[Dict[str, Any]]:
231
+ """
232
+ List teams in the organization.
233
+
234
+ Args:
235
+ limit: Maximum teams to return
236
+ status: Optional status filter (None = return all teams regardless of status)
237
+
238
+ Returns:
239
+ List of team dictionaries with agent details
240
+ """
241
+ try:
242
+ logger.info(
243
+ "list_teams_called",
244
+ organization_id=self.organization_id[:8],
245
+ limit=limit,
246
+ status=status or "all"
247
+ )
248
+
249
+ # Direct SQLAlchemy query with org filtering
250
+ query = self.db.query(Team).filter(
251
+ Team.organization_id == self.organization_id
252
+ )
253
+
254
+ # Only filter by status if explicitly provided
255
+ if status:
256
+ query = query.filter(Team.status == status)
257
+
258
+ teams = query.limit(limit).all()
259
+
260
+ result = [self._team_to_dict(t) for t in teams]
261
+
262
+ logger.info(
263
+ "list_teams_completed",
264
+ count=len(result),
265
+ organization_id=self.organization_id[:8]
266
+ )
267
+
268
+ return result
269
+
270
+ except Exception as e:
271
+ logger.error("list_teams_error", error=str(e), exc_info=True)
272
+ return []
273
+
274
+ def search_teams_by_capability(
275
+ self,
276
+ capability: str,
277
+ limit: int = 5
278
+ ) -> List[Dict[str, Any]]:
279
+ """
280
+ Search for teams with agents that have a specific capability.
281
+
282
+ Args:
283
+ capability: Capability to search for
284
+ limit: Maximum teams to return
285
+
286
+ Returns:
287
+ List of matching team dictionaries
288
+ """
289
+ try:
290
+ logger.info(
291
+ "search_teams_by_capability_called",
292
+ capability=capability,
293
+ organization_id=self.organization_id[:8],
294
+ limit=limit
295
+ )
296
+
297
+ # Get all teams (no status filter - let LLM decide based on capabilities)
298
+ teams = self.db.query(Team).filter(
299
+ Team.organization_id == self.organization_id
300
+ ).all()
301
+
302
+ # Filter teams that have the capability in description or members
303
+ capability_lower = capability.lower()
304
+ matching = []
305
+
306
+ for team in teams:
307
+ # Check team description
308
+ if capability_lower in (team.description or "").lower() or capability_lower in team.name.lower():
309
+ matching.append(team)
310
+ continue
311
+
312
+ # Check team member capabilities
313
+ config = team.configuration or {}
314
+ member_ids = config.get("member_ids", [])
315
+ if member_ids:
316
+ agents = self.db.query(Agent).filter(
317
+ Agent.id.in_([UUID(mid) if isinstance(mid, str) else mid for mid in member_ids]),
318
+ Agent.organization_id == self.organization_id
319
+ ).all()
320
+
321
+ for agent in agents:
322
+ if capability_lower in str(agent.capabilities).lower():
323
+ matching.append(team)
324
+ break
325
+
326
+ result = [self._team_to_dict(t) for t in matching[:limit]]
327
+
328
+ logger.info(
329
+ "search_teams_by_capability_completed",
330
+ capability=capability,
331
+ count=len(result),
332
+ organization_id=self.organization_id[:8]
333
+ )
334
+
335
+ return result
336
+
337
+ except Exception as e:
338
+ logger.error("search_teams_by_capability_error", error=str(e), exc_info=True)
339
+ return []
340
+
341
+ def get_agent_details(
342
+ self,
343
+ agent_id: str
344
+ ) -> Optional[Dict[str, Any]]:
345
+ """
346
+ Get complete details for a specific agent.
347
+
348
+ Args:
349
+ agent_id: Agent ID
350
+
351
+ Returns:
352
+ Agent dictionary with full details or None
353
+ """
354
+ try:
355
+ logger.info(
356
+ "get_agent_details_called",
357
+ agent_id=agent_id,
358
+ organization_id=self.organization_id[:8]
359
+ )
360
+
361
+ # Get agent with org filtering
362
+ agent = self.db.query(Agent).filter(
363
+ Agent.id == UUID(agent_id) if isinstance(agent_id, str) else agent_id,
364
+ Agent.organization_id == self.organization_id
365
+ ).first()
366
+
367
+ if not agent:
368
+ logger.warning(
369
+ "agent_not_found",
370
+ agent_id=agent_id,
371
+ organization_id=self.organization_id[:8]
372
+ )
373
+ return None
374
+
375
+ # Import router helpers
376
+ from control_plane_api.app.routers.agents_v2 import (
377
+ get_agent_projects,
378
+ get_agent_environments,
379
+ get_agent_skills_with_inheritance
380
+ )
381
+
382
+ # Get related data
383
+ result = {
384
+ **self._agent_to_dict(agent),
385
+ "projects": get_agent_projects(self.db, str(agent.id)),
386
+ "environments": get_agent_environments(self.db, str(agent.id)),
387
+ "skills": get_agent_skills_with_inheritance(
388
+ self.db,
389
+ self.organization_id,
390
+ str(agent.id),
391
+ str(agent.team_id) if agent.team_id else None
392
+ )
393
+ }
394
+
395
+ logger.info(
396
+ "get_agent_details_completed",
397
+ agent_id=agent_id,
398
+ agent_name=agent.name
399
+ )
400
+
401
+ return result
402
+
403
+ except Exception as e:
404
+ logger.error("get_agent_details_error", error=str(e), exc_info=True)
405
+ return None
406
+
407
+ def get_team_details(
408
+ self,
409
+ team_id: str
410
+ ) -> Optional[Dict[str, Any]]:
411
+ """
412
+ Get complete details for a specific team.
413
+
414
+ Args:
415
+ team_id: Team ID
416
+
417
+ Returns:
418
+ Team dictionary with full details or None
419
+ """
420
+ try:
421
+ logger.info(
422
+ "get_team_details_called",
423
+ team_id=team_id,
424
+ organization_id=self.organization_id[:8]
425
+ )
426
+
427
+ # Get team with org filtering
428
+ team = self.db.query(Team).filter(
429
+ Team.id == UUID(team_id) if isinstance(team_id, str) else team_id,
430
+ Team.organization_id == self.organization_id
431
+ ).first()
432
+
433
+ if not team:
434
+ logger.warning(
435
+ "team_not_found",
436
+ team_id=team_id,
437
+ organization_id=self.organization_id[:8]
438
+ )
439
+ return None
440
+
441
+ result = self._team_to_dict(team)
442
+
443
+ logger.info(
444
+ "get_team_details_completed",
445
+ team_id=team_id,
446
+ team_name=team.name
447
+ )
448
+
449
+ return result
450
+
451
+ except Exception as e:
452
+ logger.error("get_team_details_error", error=str(e), exc_info=True)
453
+ return None
454
+
455
+ def list_environments(
456
+ self,
457
+ status: str = "active",
458
+ limit: int = 20
459
+ ) -> List[Dict[str, Any]]:
460
+ """
461
+ List available environments in the organization.
462
+
463
+ Args:
464
+ status: Filter by status (default: "active")
465
+ limit: Maximum number of environments to return
466
+
467
+ Returns:
468
+ List of environment dictionaries
469
+ """
470
+ try:
471
+ from control_plane_api.app.models.environment import Environment
472
+
473
+ logger.info(
474
+ "list_environments_called",
475
+ organization_id=self.organization_id[:8],
476
+ status=status,
477
+ limit=limit
478
+ )
479
+
480
+ query = self.db.query(Environment).filter(
481
+ Environment.organization_id == self.organization_id
482
+ )
483
+
484
+ if status:
485
+ query = query.filter(Environment.status == status)
486
+
487
+ environments = query.order_by(Environment.created_at.desc()).limit(limit).all()
488
+
489
+ result = [
490
+ {
491
+ "id": str(env.id),
492
+ "name": env.name,
493
+ "display_name": env.display_name or env.name,
494
+ "description": env.description,
495
+ "status": env.status,
496
+ "tags": env.tags or [],
497
+ "created_at": env.created_at.isoformat() if env.created_at else None,
498
+ }
499
+ for env in environments
500
+ ]
501
+
502
+ logger.info(
503
+ "list_environments_completed",
504
+ count=len(result),
505
+ organization_id=self.organization_id[:8]
506
+ )
507
+
508
+ return result
509
+
510
+ except Exception as e:
511
+ logger.error("list_environments_error", error=str(e), exc_info=True)
512
+ return []
513
+
514
+ def list_worker_queues(
515
+ self,
516
+ environment_id: Optional[str] = None,
517
+ status: str = "active",
518
+ limit: int = 20
519
+ ) -> List[Dict[str, Any]]:
520
+ """
521
+ List available worker queues in the organization.
522
+
523
+ Args:
524
+ environment_id: Optional filter by environment
525
+ status: Filter by status (default: "active")
526
+ limit: Maximum number of queues to return
527
+
528
+ Returns:
529
+ List of worker queue dictionaries with active worker counts
530
+ """
531
+ try:
532
+ from control_plane_api.app.models.worker import WorkerQueue, WorkerHeartbeat
533
+ from sqlalchemy import func
534
+ from datetime import datetime, timedelta
535
+
536
+ logger.info(
537
+ "list_worker_queues_called",
538
+ organization_id=self.organization_id[:8],
539
+ environment_id=environment_id,
540
+ status=status,
541
+ limit=limit
542
+ )
543
+
544
+ # Build base query with active worker count
545
+ # Count workers that heartbeated in last 5 minutes
546
+ cutoff_time = datetime.utcnow() - timedelta(minutes=5)
547
+
548
+ query = self.db.query(
549
+ WorkerQueue,
550
+ func.count(WorkerHeartbeat.id).label('active_workers')
551
+ ).outerjoin(
552
+ WorkerHeartbeat,
553
+ (WorkerHeartbeat.worker_queue_id == WorkerQueue.id) &
554
+ (WorkerHeartbeat.status == 'active') &
555
+ (WorkerHeartbeat.last_heartbeat >= cutoff_time)
556
+ ).filter(
557
+ WorkerQueue.organization_id == self.organization_id,
558
+ WorkerQueue.ephemeral == False # Exclude ephemeral queues from planning
559
+ )
560
+
561
+ if environment_id:
562
+ query = query.filter(WorkerQueue.environment_id == environment_id)
563
+
564
+ if status:
565
+ query = query.filter(WorkerQueue.status == status)
566
+
567
+ query = query.group_by(WorkerQueue.id).order_by(WorkerQueue.created_at.desc()).limit(limit)
568
+
569
+ results = query.all()
570
+
571
+ worker_queues = [
572
+ {
573
+ "id": str(wq.id),
574
+ "name": wq.name,
575
+ "display_name": wq.display_name or wq.name,
576
+ "description": wq.description,
577
+ "environment_id": str(wq.environment_id),
578
+ "status": wq.status,
579
+ "active_workers": active_count,
580
+ "created_at": wq.created_at.isoformat() if wq.created_at else None,
581
+ }
582
+ for wq, active_count in results
583
+ ]
584
+
585
+ logger.info(
586
+ "list_worker_queues_completed",
587
+ count=len(worker_queues),
588
+ organization_id=self.organization_id[:8]
589
+ )
590
+
591
+ return worker_queues
592
+
593
+ except Exception as e:
594
+ logger.error("list_worker_queues_error", error=str(e), exc_info=True)
595
+ return []
596
+
597
+ async def search_context_graph(
598
+ self,
599
+ query: str,
600
+ label: Optional[str] = None,
601
+ limit: int = 20
602
+ ) -> Dict[str, Any]:
603
+ """
604
+ Search the context graph for resources.
605
+
606
+ This uses ContextGraphClient which makes HTTP calls to external
607
+ graph.kubiya.ai service - this is correct since it's external.
608
+
609
+ Args:
610
+ query: Search query text
611
+ label: Optional node label filter
612
+ limit: Maximum results
613
+
614
+ Returns:
615
+ Search results dictionary with nodes
616
+ """
617
+ try:
618
+ logger.info(
619
+ "search_context_graph_called",
620
+ query=query[:100],
621
+ label=label,
622
+ organization_id=self.organization_id[:8],
623
+ limit=limit
624
+ )
625
+
626
+ # Use ContextGraphClient (external service - HTTP is correct)
627
+ result = await self.context_graph_client.search_by_text(
628
+ search_text=query,
629
+ label=label,
630
+ limit=limit
631
+ )
632
+
633
+ node_count = len(result.get("nodes", [])) if isinstance(result, dict) else 0
634
+
635
+ logger.info(
636
+ "search_context_graph_completed",
637
+ query=query[:100],
638
+ node_count=node_count,
639
+ organization_id=self.organization_id[:8]
640
+ )
641
+
642
+ return result if isinstance(result, dict) else {"nodes": [], "count": 0}
643
+
644
+ except Exception as e:
645
+ logger.error("search_context_graph_error", error=str(e), exc_info=True)
646
+ return {"nodes": [], "count": 0}