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,389 @@
1
+ """
2
+ Task Planning Agents - Agent factory functions for workflow steps
3
+
4
+ This module contains factory functions to create Agno agents for each
5
+ step in the task planning workflow:
6
+ - Step 1: Analysis & Selection Agent (tool calling + structured output)
7
+ - Step 2: Plan Generation Agent (structured output only)
8
+ """
9
+
10
+ from typing import Optional, Dict, Any, List, TYPE_CHECKING
11
+ import json
12
+ import structlog
13
+
14
+ from agno.agent import Agent
15
+ from agno.models.litellm import LiteLLM
16
+ from agno.tools.function import Function
17
+
18
+ from control_plane_api.app.models.task_planning import (
19
+ TaskPlanResponse,
20
+ AnalysisAndSelectionOutput,
21
+ )
22
+ from .models import (
23
+ TaskAnalysisOutput,
24
+ ResourceDiscoveryOutput,
25
+ CostEstimationOutput,
26
+ FastSelectionOutput,
27
+ )
28
+
29
+ if TYPE_CHECKING:
30
+ from control_plane_api.app.lib.planning_tools.agno_toolkit import PlanningToolkit
31
+
32
+ logger = structlog.get_logger()
33
+
34
+
35
+ # ============================================================================
36
+ # Tool Builders for Pre-fetched Data
37
+ # ============================================================================
38
+
39
+ def build_prefetch_tools(outer_context: Optional[Dict[str, Any]]) -> List[Function]:
40
+ """
41
+ Build synthetic tools for pre-fetched data access.
42
+
43
+ These tools provide instant access to cached data without API calls.
44
+
45
+ Args:
46
+ outer_context: Pre-fetched context with agents, teams, environments, queues
47
+
48
+ Returns:
49
+ List of Function objects for pre-fetched data access
50
+ """
51
+ tools = []
52
+
53
+ if not outer_context:
54
+ return tools
55
+
56
+ if outer_context.get("agents"):
57
+ agents_data = outer_context["agents"]
58
+
59
+ def get_top_agents() -> str:
60
+ """Get top 20 pre-fetched agents (instant, no API call).
61
+ Use this first before calling search tools."""
62
+ return json.dumps({
63
+ "success": True,
64
+ "data": {
65
+ "agents": agents_data,
66
+ "count": len(agents_data),
67
+ "note": "Top 20 agents. Use search_agents_by_capability() if you need more."
68
+ }
69
+ }, indent=2)
70
+
71
+ tools.append(Function.from_callable(get_top_agents))
72
+
73
+ if outer_context.get("teams"):
74
+ teams_data = outer_context["teams"]
75
+
76
+ def get_top_teams() -> str:
77
+ """Get top 20 pre-fetched teams (instant, no API call).
78
+ Use this first before calling search tools."""
79
+ return json.dumps({
80
+ "success": True,
81
+ "data": {
82
+ "teams": teams_data,
83
+ "count": len(teams_data),
84
+ "note": "Top 20 teams. Use search_teams_by_capability() if you need more."
85
+ }
86
+ }, indent=2)
87
+
88
+ tools.append(Function.from_callable(get_top_teams))
89
+
90
+ if outer_context.get("environments"):
91
+ envs_data = outer_context["environments"]
92
+
93
+ def get_top_environments() -> str:
94
+ """Get top 20 pre-fetched environments (instant, no API call)."""
95
+ return json.dumps({
96
+ "success": True,
97
+ "data": {
98
+ "environments": envs_data,
99
+ "count": len(envs_data),
100
+ "note": "Top 20 environments."
101
+ }
102
+ }, indent=2)
103
+
104
+ tools.append(Function.from_callable(get_top_environments))
105
+
106
+ if outer_context.get("worker_queues"):
107
+ queues_data = outer_context["worker_queues"]
108
+
109
+ def get_top_worker_queues() -> str:
110
+ """Get top 20 pre-fetched worker queues (instant, no API call)."""
111
+ return json.dumps({
112
+ "success": True,
113
+ "data": {
114
+ "worker_queues": queues_data,
115
+ "count": len(queues_data),
116
+ "note": "Top 20 worker queues."
117
+ }
118
+ }, indent=2)
119
+
120
+ tools.append(Function.from_callable(get_top_worker_queues))
121
+
122
+ return tools
123
+
124
+
125
+ def build_search_tools(planning_toolkit: Optional['PlanningToolkit']) -> List[Function]:
126
+ """
127
+ Build search tools from planning toolkit.
128
+
129
+ Args:
130
+ planning_toolkit: Planning toolkit with search functions
131
+
132
+ Returns:
133
+ List of Function objects for searching
134
+ """
135
+ tools = []
136
+
137
+ if not planning_toolkit or not hasattr(planning_toolkit, 'functions'):
138
+ return tools
139
+
140
+ search_tool_names = [
141
+ "search_agents_by_capability",
142
+ "search_teams_by_capability",
143
+ "get_agent_details",
144
+ "get_team_details",
145
+ "get_fallback_agent",
146
+ ]
147
+
148
+ for tool_name in search_tool_names:
149
+ if tool_name in planning_toolkit.functions:
150
+ tools.append(planning_toolkit.functions[tool_name])
151
+
152
+ return tools
153
+
154
+
155
+ # ============================================================================
156
+ # Step 1: Analysis & Selection Agent
157
+ # ============================================================================
158
+
159
+ def create_analysis_and_selection_agent(
160
+ model: LiteLLM,
161
+ planning_toolkit: Optional['PlanningToolkit'] = None,
162
+ outer_context: Optional[Dict[str, Any]] = None
163
+ ) -> Agent:
164
+ """
165
+ Create Step 1 agent: Task Analysis & Resource Selection.
166
+
167
+ Combines task analysis and resource selection into a single efficient agent.
168
+ Uses pre-fetched data for instant access + search tools for specific queries.
169
+
170
+ Args:
171
+ model: LiteLLM model instance for the agent
172
+ planning_toolkit: Planning toolkit with search functions
173
+ outer_context: Pre-fetched context (agents, teams, environments, queues)
174
+
175
+ Returns:
176
+ Configured Agent for analysis and selection
177
+ """
178
+ # Build tools: pre-fetched data + search tools
179
+ toolkit_tools = build_prefetch_tools(outer_context)
180
+ toolkit_tools.extend(build_search_tools(planning_toolkit))
181
+
182
+ # Extract preferred_runtime from outer_context if provided
183
+ preferred_runtime = outer_context.get("preferred_runtime") if outer_context else None
184
+
185
+ # Build runtime preference instruction
186
+ if preferred_runtime:
187
+ runtime_instruction = f"MANDATORY: Select agents with runtime='{preferred_runtime}' (user override)"
188
+ else:
189
+ runtime_instruction = (
190
+ "MANDATORY: Select agents with runtime='claude_code' OVER 'default' "
191
+ "when both have the capability. claude_code agents are more capable."
192
+ )
193
+
194
+ return Agent(
195
+ name="Task Analyzer & Resource Selector",
196
+ role="Fast agent and environment selection",
197
+ model=model,
198
+ output_schema=AnalysisAndSelectionOutput,
199
+ tools=toolkit_tools,
200
+ instructions=[
201
+ "Select best agent AND environment for task. BE EXTREMELY FAST.",
202
+ "",
203
+ "MANDATORY PROCESS (must call BOTH tools):",
204
+ "1. FIRST: Call get_top_agents() → pick best agent match",
205
+ "2. SECOND: Call get_top_environments() → pick first environment from list",
206
+ "3. Return JSON with BOTH agent AND environment selections",
207
+ "",
208
+ f"CRITICAL RUNTIME RULE: {runtime_instruction}",
209
+ "If multiple agents have the needed capability, ALWAYS pick the one with runtime='claude_code'.",
210
+ "",
211
+ "CRITICAL ENVIRONMENT RULE (DO NOT SKIP):",
212
+ "You MUST call get_top_environments() and select the FIRST environment.",
213
+ "Set selected_environment_id = first environment's 'id' field",
214
+ "Set selected_environment_name = first environment's 'name' field",
215
+ "NEVER leave environment fields null if environments exist!",
216
+ "",
217
+ "UUID: Use EXACT id from tool results, never invent",
218
+ "FALLBACK: Use get_fallback_agent() if no agent match",
219
+ "",
220
+ "OUTPUT: Pure JSON only, start with {",
221
+ ],
222
+ markdown=False,
223
+ )
224
+
225
+
226
+ # ============================================================================
227
+ # Step 2: Plan Generation Agent
228
+ # ============================================================================
229
+
230
+ def create_plan_generation_agent(model: LiteLLM) -> Agent:
231
+ """
232
+ Create Step 2 agent: Structured Plan Generation.
233
+
234
+ Generates TaskPlanResponse from Step 1 output using Agno's output_schema.
235
+
236
+ Args:
237
+ model: LiteLLM model instance for the agent
238
+
239
+ Returns:
240
+ Configured Agent for plan generation
241
+ """
242
+ return Agent(
243
+ name="Plan Generator",
244
+ role="Generate structured execution plan",
245
+ model=model,
246
+ output_schema=TaskPlanResponse,
247
+ instructions=[
248
+ "Generate a complete TaskPlanResponse based on Step 1 analysis.",
249
+ "",
250
+ "COPY FROM STEP 1 INPUT:",
251
+ "- entity_id → recommended_execution.entity_id",
252
+ "- entity_name → recommended_execution.entity_name",
253
+ "- entity_type → recommended_execution.entity_type",
254
+ "- runtime, model_id → recommended_execution",
255
+ "- selected_environment_id → recommended_execution.recommended_environment_id AND selected_environment_id",
256
+ "- selected_environment_name → recommended_execution.recommended_environment_name AND selected_environment_name",
257
+ "",
258
+ "CRITICAL: Copy environment fields from Step 1!",
259
+ "If Step 1 has selected_environment_id, you MUST set:",
260
+ " - recommended_execution.recommended_environment_id = <same value>",
261
+ " - recommended_execution.recommended_environment_name = <same value>",
262
+ " - selected_environment_id = <same value>",
263
+ " - selected_environment_name = <same value>",
264
+ "",
265
+ "IMPORTANT: team_breakdown[].tasks MUST be an empty array: tasks: []",
266
+ "Fill ALL required fields including: estimated_time_hours in team_breakdown, without_kubiya_resources array in realized_savings.",
267
+ ],
268
+ markdown=False,
269
+ )
270
+
271
+
272
+ # ============================================================================
273
+ # Legacy Agent Factories (for backward compatibility)
274
+ # ============================================================================
275
+
276
+ def create_task_analysis_agent(model: LiteLLM) -> Agent:
277
+ """
278
+ Legacy Step 1: Task Analysis Agent (deprecated).
279
+
280
+ Use create_analysis_and_selection_agent() instead.
281
+ """
282
+ return Agent(
283
+ name="Task Analyzer",
284
+ role="Expert at understanding task requirements and complexity",
285
+ model=model,
286
+ output_schema=TaskAnalysisOutput,
287
+ instructions=[
288
+ "You analyze task descriptions to understand what's needed.",
289
+ "",
290
+ "**Your Responsibilities:**",
291
+ "1. Read the task description carefully",
292
+ "2. Identify what capabilities/skills are required (AWS, Kubernetes, Python, etc.)",
293
+ "3. Determine the task type (deployment, analysis, automation, etc.)",
294
+ "4. Assess complexity on the Fibonacci scale (1, 2, 3, 5, 8, 13, 21)",
295
+ "5. Decide if this needs a single agent or multiple agents (team)",
296
+ "",
297
+ "**Complexity Guidelines:**",
298
+ "- 1-3 points: Simple tasks (list files, basic queries, single API calls)",
299
+ "- 5-8 points: Medium tasks (deployments, multi-step operations)",
300
+ "- 13-21 points: Complex tasks (multi-system integrations, migrations)",
301
+ "",
302
+ "**Output:**",
303
+ "Provide clear analysis with reasoning.",
304
+ ],
305
+ markdown=False,
306
+ )
307
+
308
+
309
+ def create_cost_estimation_agent(model: LiteLLM) -> Agent:
310
+ """
311
+ Legacy Step 3: Cost Estimation Agent (deprecated).
312
+
313
+ Cost estimation is now included in Step 2 (plan generation).
314
+ """
315
+ return Agent(
316
+ name="Cost Estimator",
317
+ role="Expert at estimating time and cost for AI agent tasks",
318
+ model=model,
319
+ output_schema=CostEstimationOutput,
320
+ instructions=[
321
+ "You calculate realistic time and cost estimates for AI agent execution.",
322
+ "",
323
+ "**Pricing Reference:**",
324
+ "- Claude Sonnet 4: $0.003/1K input, $0.015/1K output tokens",
325
+ "- Claude Haiku: $0.00025/1K input, $0.00125/1K output tokens",
326
+ "- GPT-4o: $0.0025/1K input, $0.01/1K output tokens",
327
+ "- Tool calls: $0.0001 - $0.001 per call",
328
+ "- Worker runtime: $0.10/hour",
329
+ "",
330
+ "**Token Estimation:**",
331
+ "- Simple tasks (1-3 points): 2-5K input, 1-2K output",
332
+ "- Medium tasks (5-8 points): 5-10K input, 2-5K output",
333
+ "- Complex tasks (13-21 points): 10-20K input, 5-10K output",
334
+ "",
335
+ "**Output:**",
336
+ "Provide detailed cost breakdown with reasoning.",
337
+ ],
338
+ markdown=False,
339
+ )
340
+
341
+
342
+ # ============================================================================
343
+ # Fast Selection Agent (--local mode)
344
+ # ============================================================================
345
+
346
+ def create_fast_selection_agent(
347
+ model: LiteLLM,
348
+ outer_context: Dict[str, Any]
349
+ ) -> Agent:
350
+ """
351
+ Create fast selection agent for --local mode.
352
+
353
+ Optimized for speed with minimal output schema and pre-fetched data only.
354
+
355
+ Args:
356
+ model: LiteLLM model instance
357
+ outer_context: Pre-fetched context (required for fast mode)
358
+
359
+ Returns:
360
+ Configured Agent for fast selection
361
+ """
362
+ # Build only pre-fetched data tools (no API calls)
363
+ toolkit_tools = build_prefetch_tools(outer_context)
364
+
365
+ preferred_runtime = outer_context.get("preferred_runtime")
366
+ if preferred_runtime:
367
+ runtime_instruction = f"SELECT agents with runtime='{preferred_runtime}'"
368
+ else:
369
+ runtime_instruction = "PREFER runtime='claude_code' over 'default'"
370
+
371
+ return Agent(
372
+ name="Fast Selector",
373
+ role="Quick agent and environment selection",
374
+ model=model,
375
+ output_schema=FastSelectionOutput,
376
+ tools=toolkit_tools,
377
+ instructions=[
378
+ "Select best agent AND environment FAST. Use pre-fetched data only.",
379
+ "",
380
+ f"RUNTIME: {runtime_instruction}",
381
+ "",
382
+ "ENVIRONMENT: Call get_top_environments(), use first environment's id and name",
383
+ "Set recommended_environment_id and recommended_environment_name",
384
+ "",
385
+ "UUID: Use EXACT id from tool results, never invent",
386
+ "OUTPUT: Pure JSON only",
387
+ ],
388
+ markdown=False,
389
+ )
@@ -0,0 +1,218 @@
1
+ """
2
+ Task Planning Cache - In-memory caching for pre-fetched resources
3
+
4
+ This module provides caching functionality to avoid repeated database calls
5
+ for frequently accessed resources (agents, teams, environments, queues).
6
+
7
+ Features:
8
+ - TTL-based cache expiration (default 5 minutes)
9
+ - Per-organization caching
10
+ - Thread-safe operations
11
+ """
12
+
13
+ from typing import Optional, Dict, Any
14
+ import time
15
+ import threading
16
+ import structlog
17
+
18
+ logger = structlog.get_logger()
19
+
20
+
21
+ # ============================================================================
22
+ # Cache Configuration
23
+ # ============================================================================
24
+
25
+ DEFAULT_CACHE_TTL = 300 # 5 minutes
26
+
27
+
28
+ # ============================================================================
29
+ # Thread-Safe Cache Implementation
30
+ # ============================================================================
31
+
32
+ class PrefetchCache:
33
+ """
34
+ Thread-safe in-memory cache for pre-fetched organization resources.
35
+
36
+ Features:
37
+ - TTL-based expiration
38
+ - Per-organization isolation
39
+ - Automatic cleanup of expired entries
40
+ """
41
+
42
+ def __init__(self, ttl_seconds: int = DEFAULT_CACHE_TTL):
43
+ self._cache: Dict[str, Dict[str, Any]] = {}
44
+ self._lock = threading.RLock()
45
+ self._ttl = ttl_seconds
46
+
47
+ def get(self, organization_id: str) -> Optional[Dict[str, Any]]:
48
+ """
49
+ Get cached pre-fetched data for an organization if still valid.
50
+
51
+ Args:
52
+ organization_id: Organization identifier
53
+
54
+ Returns:
55
+ Cached data dict or None if not found/expired
56
+ """
57
+ cache_key = f"prefetch_{organization_id}"
58
+
59
+ with self._lock:
60
+ cached = self._cache.get(cache_key)
61
+ if cached and time.time() - cached.get("timestamp", 0) < self._ttl:
62
+ logger.info("prefetch_cache_hit", organization_id=organization_id[:8])
63
+ return cached.get("data")
64
+
65
+ # Clean up expired entry if exists
66
+ if cached:
67
+ del self._cache[cache_key]
68
+ logger.debug("prefetch_cache_expired", organization_id=organization_id[:8])
69
+
70
+ return None
71
+
72
+ def set(self, organization_id: str, data: Dict[str, Any]) -> None:
73
+ """
74
+ Cache pre-fetched data for an organization.
75
+
76
+ Args:
77
+ organization_id: Organization identifier
78
+ data: Data to cache (agents, teams, environments, queues)
79
+ """
80
+ cache_key = f"prefetch_{organization_id}"
81
+
82
+ with self._lock:
83
+ self._cache[cache_key] = {
84
+ "timestamp": time.time(),
85
+ "data": data
86
+ }
87
+ logger.info("prefetch_cache_set", organization_id=organization_id[:8])
88
+
89
+ def invalidate(self, organization_id: str) -> bool:
90
+ """
91
+ Invalidate (remove) cached data for an organization.
92
+
93
+ Args:
94
+ organization_id: Organization identifier
95
+
96
+ Returns:
97
+ True if entry was removed, False if not found
98
+ """
99
+ cache_key = f"prefetch_{organization_id}"
100
+
101
+ with self._lock:
102
+ if cache_key in self._cache:
103
+ del self._cache[cache_key]
104
+ logger.info("prefetch_cache_invalidated", organization_id=organization_id[:8])
105
+ return True
106
+ return False
107
+
108
+ def clear(self) -> int:
109
+ """
110
+ Clear all cached data.
111
+
112
+ Returns:
113
+ Number of entries cleared
114
+ """
115
+ with self._lock:
116
+ count = len(self._cache)
117
+ self._cache.clear()
118
+ logger.info("prefetch_cache_cleared", entries_cleared=count)
119
+ return count
120
+
121
+ def cleanup_expired(self) -> int:
122
+ """
123
+ Remove all expired entries from cache.
124
+
125
+ Returns:
126
+ Number of entries removed
127
+ """
128
+ current_time = time.time()
129
+ removed = 0
130
+
131
+ with self._lock:
132
+ expired_keys = [
133
+ key for key, value in self._cache.items()
134
+ if current_time - value.get("timestamp", 0) >= self._ttl
135
+ ]
136
+
137
+ for key in expired_keys:
138
+ del self._cache[key]
139
+ removed += 1
140
+
141
+ if removed > 0:
142
+ logger.info("prefetch_cache_cleanup", entries_removed=removed)
143
+
144
+ return removed
145
+
146
+ def stats(self) -> Dict[str, Any]:
147
+ """
148
+ Get cache statistics.
149
+
150
+ Returns:
151
+ Dict with cache stats (size, oldest entry age, etc.)
152
+ """
153
+ current_time = time.time()
154
+
155
+ with self._lock:
156
+ if not self._cache:
157
+ return {"size": 0, "oldest_age_seconds": 0, "ttl_seconds": self._ttl}
158
+
159
+ ages = [
160
+ current_time - v.get("timestamp", current_time)
161
+ for v in self._cache.values()
162
+ ]
163
+
164
+ return {
165
+ "size": len(self._cache),
166
+ "oldest_age_seconds": max(ages) if ages else 0,
167
+ "newest_age_seconds": min(ages) if ages else 0,
168
+ "ttl_seconds": self._ttl
169
+ }
170
+
171
+
172
+ # ============================================================================
173
+ # Global Cache Instance (Singleton)
174
+ # ============================================================================
175
+
176
+ _prefetch_cache: Optional[PrefetchCache] = None
177
+ _cache_lock = threading.Lock()
178
+
179
+
180
+ def get_prefetch_cache() -> PrefetchCache:
181
+ """
182
+ Get the global prefetch cache instance (singleton).
183
+
184
+ Returns:
185
+ PrefetchCache instance
186
+ """
187
+ global _prefetch_cache
188
+
189
+ if _prefetch_cache is None:
190
+ with _cache_lock:
191
+ if _prefetch_cache is None:
192
+ _prefetch_cache = PrefetchCache()
193
+
194
+ return _prefetch_cache
195
+
196
+
197
+ # ============================================================================
198
+ # Convenience Functions (backward compatible)
199
+ # ============================================================================
200
+
201
+ def get_cached_prefetch(organization_id: str) -> Optional[Dict[str, Any]]:
202
+ """Get cached pre-fetched data for an organization if still valid."""
203
+ return get_prefetch_cache().get(organization_id)
204
+
205
+
206
+ def set_cached_prefetch(organization_id: str, data: Dict[str, Any]) -> None:
207
+ """Cache pre-fetched data for an organization."""
208
+ get_prefetch_cache().set(organization_id, data)
209
+
210
+
211
+ def invalidate_prefetch_cache(organization_id: str) -> bool:
212
+ """Invalidate cached data for an organization."""
213
+ return get_prefetch_cache().invalidate(organization_id)
214
+
215
+
216
+ def clear_prefetch_cache() -> int:
217
+ """Clear all cached data."""
218
+ return get_prefetch_cache().clear()