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,650 @@
1
+ """
2
+ Task Queues router - Worker queue management for routing work to specific workers.
3
+
4
+ This router handles task queue CRUD operations and tracks worker availability.
5
+ """
6
+
7
+ from fastapi import APIRouter, Depends, HTTPException, status, Request
8
+ from sqlalchemy.orm import Session
9
+ from sqlalchemy.exc import IntegrityError
10
+ from typing import List, Optional
11
+ from datetime import datetime
12
+ from pydantic import BaseModel, Field
13
+ import structlog
14
+ import uuid
15
+ import os
16
+
17
+ from control_plane_api.app.middleware.auth import get_current_organization
18
+ from control_plane_api.app.database import get_db
19
+ from control_plane_api.app.models.environment import Environment
20
+ from control_plane_api.app.models.orchestration import TemporalNamespace
21
+ from control_plane_api.app.lib.temporal_client import get_temporal_client
22
+
23
+ logger = structlog.get_logger()
24
+
25
+ router = APIRouter()
26
+
27
+
28
+ # Pydantic schemas
29
+ class TaskQueueCreate(BaseModel):
30
+ name: str = Field(..., description="Queue name (e.g., default, high-priority)", min_length=2, max_length=100)
31
+ display_name: str | None = Field(None, description="User-friendly display name")
32
+ description: str | None = Field(None, description="Queue description")
33
+ tags: List[str] = Field(default_factory=list, description="Tags for categorization")
34
+ settings: dict = Field(default_factory=dict, description="Queue settings")
35
+ priority: int | None = Field(None, ge=1, le=10, description="Priority level (1-10)")
36
+ policy_ids: List[str] = Field(default_factory=list, description="OPA policy IDs")
37
+
38
+
39
+ class TaskQueueUpdate(BaseModel):
40
+ name: str | None = None
41
+ display_name: str | None = None
42
+ description: str | None = None
43
+ tags: List[str] | None = None
44
+ settings: dict | None = None
45
+ status: str | None = None
46
+ priority: int | None = Field(None, ge=1, le=10)
47
+ policy_ids: List[str] | None = None
48
+
49
+
50
+ class TaskQueueResponse(BaseModel):
51
+ id: str
52
+ organization_id: str
53
+ name: str
54
+ display_name: str | None
55
+ description: str | None
56
+ tags: List[str]
57
+ settings: dict
58
+ status: str
59
+ priority: int | None = None
60
+ policy_ids: List[str] = []
61
+ created_at: str
62
+ updated_at: str
63
+ created_by: str | None
64
+
65
+ # Temporal Cloud provisioning fields
66
+ worker_token: str | None = None # UUID token for worker registration
67
+ provisioning_workflow_id: str | None = None # Temporal workflow ID
68
+ provisioned_at: str | None = None
69
+ error_message: str | None = None
70
+ temporal_namespace_id: str | None = None
71
+
72
+ # Worker metrics
73
+ active_workers: int = 0
74
+ idle_workers: int = 0
75
+ busy_workers: int = 0
76
+
77
+
78
+ class WorkerHeartbeatResponse(BaseModel):
79
+ id: str
80
+ organization_id: str
81
+ task_queue_name: str
82
+ worker_id: str
83
+ hostname: str | None
84
+ worker_metadata: dict
85
+ last_heartbeat: str
86
+ status: str
87
+ tasks_processed: int
88
+ current_task_id: str | None
89
+ registered_at: str
90
+ updated_at: str
91
+
92
+
93
+ def ensure_default_queue(db: Session, organization: dict) -> Optional[Environment]:
94
+ """
95
+ Ensure the organization has a default task queue.
96
+ Creates one if it doesn't exist.
97
+
98
+ Returns the default queue or None if creation failed.
99
+ """
100
+ try:
101
+ # Check if default queue exists
102
+ existing = db.query(Environment).filter(
103
+ Environment.organization_id == organization["id"],
104
+ Environment.name == "default"
105
+ ).first()
106
+
107
+ if existing:
108
+ return existing
109
+
110
+ # Create default queue
111
+ default_queue = Environment(
112
+ id=uuid.uuid4(),
113
+ organization_id=organization["id"],
114
+ name="default",
115
+ display_name="Default Queue",
116
+ description="Default task queue for all workers",
117
+ tags=[],
118
+ settings={},
119
+ status="active",
120
+ created_by=organization.get("user_id"),
121
+ )
122
+
123
+ db.add(default_queue)
124
+ db.commit()
125
+ db.refresh(default_queue)
126
+
127
+ logger.info(
128
+ "default_queue_created",
129
+ queue_id=str(default_queue.id),
130
+ org_id=organization["id"],
131
+ )
132
+ return default_queue
133
+
134
+ except Exception as e:
135
+ db.rollback()
136
+ logger.error("ensure_default_queue_failed", error=str(e), org_id=organization.get("id"))
137
+ return None
138
+
139
+
140
+ @router.post("", response_model=TaskQueueResponse, status_code=status.HTTP_201_CREATED)
141
+ async def create_task_queue(
142
+ queue_data: TaskQueueCreate,
143
+ request: Request,
144
+ organization: dict = Depends(get_current_organization),
145
+ db: Session = Depends(get_db),
146
+ ):
147
+ """
148
+ Create a new task queue.
149
+
150
+ If this is the first task queue for the organization, it will trigger
151
+ Temporal Cloud namespace provisioning workflow.
152
+ """
153
+ try:
154
+ # Check if queue name already exists for this organization
155
+ existing = db.query(Environment).filter(
156
+ Environment.organization_id == organization["id"],
157
+ Environment.name == queue_data.name
158
+ ).first()
159
+
160
+ if existing:
161
+ raise HTTPException(
162
+ status_code=status.HTTP_409_CONFLICT,
163
+ detail=f"Task queue with name '{queue_data.name}' already exists"
164
+ )
165
+
166
+ # Check if this is the first task queue for this org
167
+ queue_count = db.query(Environment).filter(
168
+ Environment.organization_id == organization["id"]
169
+ ).count()
170
+ is_first_queue = queue_count == 0
171
+
172
+ # Check if namespace already exists
173
+ has_namespace = db.query(TemporalNamespace).filter(
174
+ TemporalNamespace.organization_id == organization["id"]
175
+ ).first() is not None
176
+ needs_provisioning = is_first_queue and not has_namespace
177
+
178
+ # Set initial status
179
+ initial_status = "provisioning" if needs_provisioning else "ready"
180
+
181
+ queue = Environment(
182
+ id=uuid.uuid4(),
183
+ organization_id=organization["id"],
184
+ name=queue_data.name,
185
+ display_name=queue_data.display_name or queue_data.name,
186
+ description=queue_data.description,
187
+ tags=queue_data.tags,
188
+ settings=queue_data.settings,
189
+ status=initial_status,
190
+ created_by=organization.get("user_id"),
191
+ worker_token=uuid.uuid4(), # Generate worker token
192
+ policy_ids=queue_data.policy_ids,
193
+ )
194
+
195
+ db.add(queue)
196
+ db.commit()
197
+ db.refresh(queue)
198
+
199
+ queue_id = str(queue.id)
200
+
201
+ # Trigger namespace provisioning workflow if needed
202
+ if needs_provisioning:
203
+ try:
204
+ from control_plane_api.app.workflows.namespace_provisioning import (
205
+ ProvisionTemporalNamespaceWorkflow,
206
+ ProvisionNamespaceInput,
207
+ )
208
+
209
+ temporal_client = await get_temporal_client()
210
+ account_id = os.environ.get("TEMPORAL_CLOUD_ACCOUNT_ID", "default-account")
211
+
212
+ workflow_input = ProvisionNamespaceInput(
213
+ organization_id=organization["id"],
214
+ organization_name=organization.get("name", organization["id"]),
215
+ task_queue_id=queue_id,
216
+ account_id=account_id,
217
+ region=os.environ.get("TEMPORAL_CLOUD_REGION", "aws-us-east-1"),
218
+ )
219
+
220
+ # Start provisioning workflow on control plane's task queue
221
+ workflow_handle = await temporal_client.start_workflow(
222
+ ProvisionTemporalNamespaceWorkflow.run,
223
+ workflow_input,
224
+ id=f"provision-namespace-{organization['id']}",
225
+ task_queue="agent-control-plane", # Control plane's task queue
226
+ )
227
+
228
+ # Update queue with workflow ID
229
+ queue.provisioning_workflow_id = workflow_handle.id
230
+ db.commit()
231
+
232
+ logger.info(
233
+ "namespace_provisioning_workflow_started",
234
+ workflow_id=workflow_handle.id,
235
+ queue_id=queue_id,
236
+ org_id=organization["id"],
237
+ )
238
+ except Exception as e:
239
+ logger.error(
240
+ "failed_to_start_provisioning_workflow",
241
+ error=str(e),
242
+ queue_id=queue_id,
243
+ org_id=organization["id"],
244
+ )
245
+ # Update queue status to error
246
+ queue.status = "error"
247
+ queue.error_message = f"Failed to start provisioning: {str(e)}"
248
+ db.commit()
249
+
250
+ logger.info(
251
+ "task_queue_created",
252
+ queue_id=queue_id,
253
+ queue_name=queue.name,
254
+ org_id=organization["id"],
255
+ needs_provisioning=needs_provisioning,
256
+ )
257
+
258
+ return TaskQueueResponse(
259
+ id=str(queue.id),
260
+ organization_id=queue.organization_id,
261
+ name=queue.name,
262
+ display_name=queue.display_name,
263
+ description=queue.description,
264
+ tags=queue.tags or [],
265
+ settings=queue.settings or {},
266
+ status=queue.status,
267
+ priority=None,
268
+ policy_ids=queue.policy_ids or [],
269
+ created_at=queue.created_at.isoformat() if queue.created_at else datetime.utcnow().isoformat(),
270
+ updated_at=queue.updated_at.isoformat() if queue.updated_at else datetime.utcnow().isoformat(),
271
+ created_by=queue.created_by,
272
+ worker_token=str(queue.worker_token) if queue.worker_token else None,
273
+ provisioning_workflow_id=queue.provisioning_workflow_id,
274
+ provisioned_at=queue.provisioned_at.isoformat() if queue.provisioned_at else None,
275
+ error_message=queue.error_message,
276
+ temporal_namespace_id=str(queue.temporal_namespace_id) if queue.temporal_namespace_id else None,
277
+ active_workers=0,
278
+ idle_workers=0,
279
+ busy_workers=0,
280
+ )
281
+
282
+ except HTTPException:
283
+ raise
284
+ except Exception as e:
285
+ db.rollback()
286
+ logger.error("task_queue_creation_failed", error=str(e), org_id=organization["id"])
287
+ raise HTTPException(
288
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
289
+ detail=f"Failed to create task queue: {str(e)}"
290
+ )
291
+
292
+
293
+ @router.get("", response_model=List[TaskQueueResponse])
294
+ async def list_task_queues(
295
+ request: Request,
296
+ status_filter: str | None = None,
297
+ organization: dict = Depends(get_current_organization),
298
+ db: Session = Depends(get_db),
299
+ ):
300
+ """List all task queues in the organization"""
301
+ try:
302
+ # Ensure default queue exists
303
+ ensure_default_queue(db, organization)
304
+
305
+ # Query queues
306
+ query = db.query(Environment).filter(
307
+ Environment.organization_id == organization["id"]
308
+ )
309
+
310
+ if status_filter:
311
+ query = query.filter(Environment.status == status_filter)
312
+
313
+ queues_data = query.order_by(Environment.created_at.asc()).all()
314
+
315
+ # Note: Worker stats are now tracked at worker_queue level, not environment level
316
+ # For backward compatibility, we return 0 for environment-level worker counts
317
+ # Use worker_queues endpoints for detailed worker information
318
+ queues = []
319
+ for queue in queues_data:
320
+ queues.append(
321
+ TaskQueueResponse(
322
+ id=str(queue.id),
323
+ organization_id=queue.organization_id,
324
+ name=queue.name,
325
+ display_name=queue.display_name,
326
+ description=queue.description,
327
+ tags=queue.tags or [],
328
+ settings=queue.settings or {},
329
+ status=queue.status,
330
+ priority=None,
331
+ policy_ids=queue.policy_ids or [],
332
+ created_at=queue.created_at.isoformat() if queue.created_at else datetime.utcnow().isoformat(),
333
+ updated_at=queue.updated_at.isoformat() if queue.updated_at else datetime.utcnow().isoformat(),
334
+ created_by=queue.created_by,
335
+ worker_token=str(queue.worker_token) if queue.worker_token else None,
336
+ provisioning_workflow_id=queue.provisioning_workflow_id,
337
+ provisioned_at=queue.provisioned_at.isoformat() if queue.provisioned_at else None,
338
+ error_message=queue.error_message,
339
+ temporal_namespace_id=str(queue.temporal_namespace_id) if queue.temporal_namespace_id else None,
340
+ active_workers=0,
341
+ idle_workers=0,
342
+ busy_workers=0,
343
+ )
344
+ )
345
+
346
+ logger.info(
347
+ "task_queues_listed",
348
+ count=len(queues),
349
+ org_id=organization["id"],
350
+ )
351
+
352
+ return queues
353
+
354
+ except Exception as e:
355
+ logger.error("task_queues_list_failed", error=str(e), org_id=organization["id"])
356
+ raise HTTPException(
357
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
358
+ detail=f"Failed to list task queues: {str(e)}"
359
+ )
360
+
361
+
362
+ @router.get("/{queue_id}", response_model=TaskQueueResponse)
363
+ async def get_task_queue(
364
+ queue_id: str,
365
+ request: Request,
366
+ organization: dict = Depends(get_current_organization),
367
+ db: Session = Depends(get_db),
368
+ ):
369
+ """Get a specific task queue by ID"""
370
+ try:
371
+ queue = db.query(Environment).filter(
372
+ Environment.id == queue_id,
373
+ Environment.organization_id == organization["id"]
374
+ ).first()
375
+
376
+ if not queue:
377
+ raise HTTPException(status_code=404, detail="Task queue not found")
378
+
379
+ # Note: Worker stats are now tracked at worker_queue level
380
+ # Return 0 for environment-level worker counts
381
+ return TaskQueueResponse(
382
+ id=str(queue.id),
383
+ organization_id=queue.organization_id,
384
+ name=queue.name,
385
+ display_name=queue.display_name,
386
+ description=queue.description,
387
+ tags=queue.tags or [],
388
+ settings=queue.settings or {},
389
+ status=queue.status,
390
+ priority=None,
391
+ policy_ids=queue.policy_ids or [],
392
+ created_at=queue.created_at.isoformat() if queue.created_at else datetime.utcnow().isoformat(),
393
+ updated_at=queue.updated_at.isoformat() if queue.updated_at else datetime.utcnow().isoformat(),
394
+ created_by=queue.created_by,
395
+ worker_token=str(queue.worker_token) if queue.worker_token else None,
396
+ provisioning_workflow_id=queue.provisioning_workflow_id,
397
+ provisioned_at=queue.provisioned_at.isoformat() if queue.provisioned_at else None,
398
+ error_message=queue.error_message,
399
+ temporal_namespace_id=str(queue.temporal_namespace_id) if queue.temporal_namespace_id else None,
400
+ active_workers=0,
401
+ idle_workers=0,
402
+ busy_workers=0,
403
+ )
404
+
405
+ except HTTPException:
406
+ raise
407
+ except Exception as e:
408
+ logger.error("task_queue_get_failed", error=str(e), queue_id=queue_id)
409
+ raise HTTPException(
410
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
411
+ detail=f"Failed to get task queue: {str(e)}"
412
+ )
413
+
414
+
415
+ @router.patch("/{queue_id}", response_model=TaskQueueResponse)
416
+ async def update_task_queue(
417
+ queue_id: str,
418
+ queue_data: TaskQueueUpdate,
419
+ request: Request,
420
+ organization: dict = Depends(get_current_organization),
421
+ db: Session = Depends(get_db),
422
+ ):
423
+ """Update a task queue"""
424
+ try:
425
+ # Check if queue exists
426
+ queue = db.query(Environment).filter(
427
+ Environment.id == queue_id,
428
+ Environment.organization_id == organization["id"]
429
+ ).first()
430
+
431
+ if not queue:
432
+ raise HTTPException(status_code=404, detail="Task queue not found")
433
+
434
+ # Build update dict
435
+ update_data = queue_data.model_dump(exclude_unset=True)
436
+
437
+ # Apply updates
438
+ for key, value in update_data.items():
439
+ if hasattr(queue, key):
440
+ setattr(queue, key, value)
441
+
442
+ db.commit()
443
+ db.refresh(queue)
444
+
445
+ logger.info(
446
+ "task_queue_updated",
447
+ queue_id=queue_id,
448
+ org_id=organization["id"],
449
+ )
450
+
451
+ # Note: Worker stats are now tracked at worker_queue level
452
+ return TaskQueueResponse(
453
+ id=str(queue.id),
454
+ organization_id=queue.organization_id,
455
+ name=queue.name,
456
+ display_name=queue.display_name,
457
+ description=queue.description,
458
+ tags=queue.tags or [],
459
+ settings=queue.settings or {},
460
+ status=queue.status,
461
+ priority=None,
462
+ policy_ids=queue.policy_ids or [],
463
+ created_at=queue.created_at.isoformat() if queue.created_at else datetime.utcnow().isoformat(),
464
+ updated_at=queue.updated_at.isoformat() if queue.updated_at else datetime.utcnow().isoformat(),
465
+ created_by=queue.created_by,
466
+ worker_token=str(queue.worker_token) if queue.worker_token else None,
467
+ provisioning_workflow_id=queue.provisioning_workflow_id,
468
+ provisioned_at=queue.provisioned_at.isoformat() if queue.provisioned_at else None,
469
+ error_message=queue.error_message,
470
+ temporal_namespace_id=str(queue.temporal_namespace_id) if queue.temporal_namespace_id else None,
471
+ active_workers=0,
472
+ idle_workers=0,
473
+ busy_workers=0,
474
+ )
475
+
476
+ except HTTPException:
477
+ raise
478
+ except Exception as e:
479
+ db.rollback()
480
+ logger.error("task_queue_update_failed", error=str(e), queue_id=queue_id)
481
+ raise HTTPException(
482
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
483
+ detail=f"Failed to update task queue: {str(e)}"
484
+ )
485
+
486
+
487
+ @router.delete("/{queue_id}", status_code=status.HTTP_204_NO_CONTENT)
488
+ async def delete_task_queue(
489
+ queue_id: str,
490
+ request: Request,
491
+ organization: dict = Depends(get_current_organization),
492
+ db: Session = Depends(get_db),
493
+ ):
494
+ """Delete a task queue"""
495
+ try:
496
+ # Prevent deleting default queue
497
+ queue = db.query(Environment).filter(
498
+ Environment.id == queue_id,
499
+ Environment.organization_id == organization["id"]
500
+ ).first()
501
+
502
+ if not queue:
503
+ raise HTTPException(status_code=404, detail="Task queue not found")
504
+
505
+ if queue.name == "default":
506
+ raise HTTPException(
507
+ status_code=status.HTTP_400_BAD_REQUEST,
508
+ detail="Cannot delete the default queue"
509
+ )
510
+
511
+ db.delete(queue)
512
+ db.commit()
513
+
514
+ logger.info("task_queue_deleted", queue_id=queue_id, org_id=organization["id"])
515
+
516
+ return None
517
+
518
+ except HTTPException:
519
+ raise
520
+ except Exception as e:
521
+ db.rollback()
522
+ logger.error("task_queue_delete_failed", error=str(e), queue_id=queue_id)
523
+ raise HTTPException(
524
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
525
+ detail=f"Failed to delete task queue: {str(e)}"
526
+ )
527
+
528
+
529
+ @router.get("/{queue_name}/workers", response_model=List[WorkerHeartbeatResponse])
530
+ async def list_queue_workers(
531
+ queue_name: str,
532
+ request: Request,
533
+ organization: dict = Depends(get_current_organization),
534
+ ):
535
+ """
536
+ List all workers for a specific queue.
537
+
538
+ NOTE: This endpoint is deprecated. Workers are now organized by worker_queues.
539
+ Use GET /environments/{env_id}/worker-queues and worker_queues endpoints instead.
540
+ """
541
+ logger.warning(
542
+ "deprecated_endpoint_called",
543
+ endpoint="/task-queues/{queue_name}/workers",
544
+ queue_name=queue_name,
545
+ org_id=organization["id"],
546
+ )
547
+
548
+ # Return empty list for backward compatibility
549
+ return []
550
+
551
+
552
+ # Worker Registration
553
+
554
+ class WorkerCommandResponse(BaseModel):
555
+ """Response with worker registration command"""
556
+ worker_token: str
557
+ task_queue_name: str
558
+ command: str
559
+ command_parts: dict # Broken down for UI display
560
+ namespace_status: str # pending, provisioning, ready, error
561
+ can_register: bool
562
+ provisioning_workflow_id: str | None = None
563
+
564
+
565
+ @router.get("/{queue_id}/worker-command", response_model=WorkerCommandResponse)
566
+ async def get_worker_registration_command(
567
+ queue_id: str,
568
+ request: Request,
569
+ organization: dict = Depends(get_current_organization),
570
+ db: Session = Depends(get_db),
571
+ ):
572
+ """
573
+ Get the worker registration command for a task queue.
574
+
575
+ Returns the kubiya worker start command with the worker token that users
576
+ should run to start a worker for this task queue.
577
+
578
+ The UI should display this in a "Register Worker" dialog when the queue
579
+ is ready, and show provisioning status while the namespace is being created.
580
+ """
581
+ try:
582
+ # Get task queue
583
+ queue = db.query(Environment).filter(
584
+ Environment.id == queue_id,
585
+ Environment.organization_id == organization["id"]
586
+ ).first()
587
+
588
+ if not queue:
589
+ raise HTTPException(status_code=404, detail="Task queue not found")
590
+
591
+ worker_token = queue.worker_token
592
+
593
+ # Generate worker_token if it doesn't exist (for existing queues)
594
+ if not worker_token:
595
+ worker_token = uuid.uuid4()
596
+ queue.worker_token = worker_token
597
+ db.commit()
598
+
599
+ logger.info(
600
+ "worker_token_generated",
601
+ queue_id=queue_id,
602
+ org_id=organization["id"],
603
+ )
604
+
605
+ task_queue_name = queue.name
606
+ namespace_status = queue.status or "unknown"
607
+ provisioning_workflow_id = queue.provisioning_workflow_id
608
+
609
+ # Check if namespace is ready
610
+ can_register = namespace_status in ["ready", "active"]
611
+
612
+ # Build command
613
+ worker_token_str = str(worker_token)
614
+ command = f"kubiya worker start --token {worker_token_str} --task-queue {task_queue_name}"
615
+
616
+ command_parts = {
617
+ "binary": "kubiya",
618
+ "subcommand": "worker start",
619
+ "flags": {
620
+ "--token": worker_token_str,
621
+ "--task-queue": task_queue_name,
622
+ },
623
+ }
624
+
625
+ logger.info(
626
+ "worker_command_retrieved",
627
+ queue_id=queue_id,
628
+ can_register=can_register,
629
+ status=namespace_status,
630
+ org_id=organization["id"],
631
+ )
632
+
633
+ return WorkerCommandResponse(
634
+ worker_token=worker_token_str,
635
+ task_queue_name=task_queue_name,
636
+ command=command,
637
+ command_parts=command_parts,
638
+ namespace_status=namespace_status,
639
+ can_register=can_register,
640
+ provisioning_workflow_id=provisioning_workflow_id,
641
+ )
642
+
643
+ except HTTPException:
644
+ raise
645
+ except Exception as e:
646
+ logger.error("worker_command_get_failed", error=str(e), queue_id=queue_id)
647
+ raise HTTPException(
648
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
649
+ detail=f"Failed to get worker command: {str(e)}"
650
+ )