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,793 @@
1
+ """
2
+ Refactored Agent executor service using runtime abstraction.
3
+
4
+ This version delegates execution to pluggable runtime implementations,
5
+ making the code more maintainable and extensible.
6
+ """
7
+
8
+ from typing import Dict, Any, Optional, List
9
+ import structlog
10
+ import time
11
+ import os
12
+ from datetime import datetime, timezone
13
+
14
+ from control_plane_api.worker.control_plane_client import ControlPlaneClient
15
+ from control_plane_api.worker.services.session_service import SessionService
16
+ from control_plane_api.worker.services.cancellation_manager import CancellationManager
17
+ from control_plane_api.worker.services.analytics_service import AnalyticsService
18
+ from control_plane_api.worker.services.runtime_analytics import submit_runtime_analytics
19
+ from control_plane_api.worker.runtimes import (
20
+ RuntimeFactory,
21
+ RuntimeType,
22
+ RuntimeExecutionContext,
23
+ )
24
+ from control_plane_api.worker.utils.streaming_utils import StreamingHelper
25
+ from control_plane_api.app.lib.templating.types import TemplateContext
26
+ from control_plane_api.app.lib.templating.resolver import resolve_templates
27
+ from control_plane_api.worker.utils.logging_config import sanitize_value
28
+
29
+ logger = structlog.get_logger()
30
+
31
+
32
+ class AgentExecutorServiceV2:
33
+ """
34
+ Service for executing agents using runtime abstraction.
35
+
36
+ This service orchestrates agent execution by:
37
+ 1. Loading session history
38
+ 2. Selecting appropriate runtime based on agent config
39
+ 3. Delegating execution to the runtime
40
+ 4. Persisting session after execution
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ control_plane: ControlPlaneClient,
46
+ session_service: SessionService,
47
+ cancellation_manager: CancellationManager,
48
+ ):
49
+ """
50
+ Initialize the agent executor service.
51
+
52
+ Args:
53
+ control_plane: Control Plane API client
54
+ session_service: Session management service
55
+ cancellation_manager: Execution cancellation manager
56
+ """
57
+ self.control_plane = control_plane
58
+ self.session_service = session_service
59
+ self.cancellation_manager = cancellation_manager
60
+ self.runtime_factory = RuntimeFactory()
61
+
62
+ # Initialize analytics service for tracking LLM usage, tool calls, etc.
63
+ control_plane_url = os.getenv("CONTROL_PLANE_URL", "http://localhost:8000")
64
+ api_key = os.getenv("KUBIYA_API_KEY", "")
65
+ self.analytics_service = AnalyticsService(control_plane_url, api_key)
66
+
67
+ async def execute(self, input: Any) -> Dict[str, Any]:
68
+ """
69
+ Execute an agent using the configured runtime.
70
+
71
+ This method:
72
+ 1. Loads session history
73
+ 2. Determines runtime type from agent config
74
+ 3. Creates runtime instance
75
+ 4. Executes agent via runtime
76
+ 5. Persists session
77
+ 6. Returns standardized result
78
+
79
+ Args:
80
+ input: AgentExecutionInput with execution details
81
+
82
+ Returns:
83
+ Dict with response, usage, success flag, runtime_type, etc.
84
+ """
85
+ execution_id = input.execution_id
86
+
87
+ # print("\n" + "=" * 80)
88
+ # print("=" * 80)
89
+ # print(f"Execution ID: {execution_id}")
90
+ # print(f"Agent ID: {input.agent_id}")
91
+ # print(f"Organization: {input.organization_id}")
92
+ # print(f"Model: {input.model_id or 'default'}")
93
+ # print(f"Session ID: {input.session_id}")
94
+ # print(
95
+ # f"Prompt: {input.prompt[:100]}..."
96
+ # if len(input.prompt) > 100
97
+ # else f"Prompt: {input.prompt}"
98
+ # )
99
+ # print("=" * 80 + "\n")
100
+
101
+ logger.info(
102
+ "agent_workflow_start",
103
+ execution_id=execution_id[:8],
104
+ agent_id=input.agent_id,
105
+ organization_id=input.organization_id,
106
+ model=input.model_id or "default",
107
+ session_id=input.session_id,
108
+ )
109
+
110
+ try:
111
+ # Capture timestamp at start of execution for accurate user message timestamp
112
+ from datetime import datetime, timezone
113
+ user_message_timestamp = datetime.now(timezone.utc).isoformat()
114
+
115
+ # STEP 1: Load session history
116
+ logger.info("loading_session_history", session_id=input.session_id)
117
+ session_history = self.session_service.load_session(
118
+ execution_id=execution_id, session_id=input.session_id
119
+ )
120
+
121
+ if session_history:
122
+ # print(f"✅ Loaded {len(session_history)} messages from previous session\n")
123
+ pass
124
+ else:
125
+ logger.info("starting_new_conversation")
126
+
127
+ # STEP 2: Determine runtime type
128
+ agent_config = input.agent_config or {}
129
+ runtime_type_str = agent_config.get("runtime", "default")
130
+ runtime_type = self.runtime_factory.parse_runtime_type(runtime_type_str)
131
+
132
+ logger.info("runtime_type_selected",
133
+ runtime_type=runtime_type.value,
134
+ framework=self._get_framework_name(runtime_type))
135
+
136
+ logger.info(
137
+ "runtime_selected",
138
+ execution_id=execution_id[:8],
139
+ runtime=runtime_type.value,
140
+ )
141
+
142
+ # STEP 3: Create runtime instance
143
+ # print(f"⚙️ Creating runtime instance...")
144
+ runtime = self.runtime_factory.create_runtime(
145
+ runtime_type=runtime_type,
146
+ control_plane_client=self.control_plane,
147
+ cancellation_manager=self.cancellation_manager,
148
+ )
149
+ # print(f"✅ Runtime created: {runtime.get_runtime_info()}\n")
150
+
151
+ # STEP 4: Get skills (if runtime supports tools)
152
+ skills = []
153
+ if runtime.supports_tools():
154
+ logger.info("fetching_skills")
155
+ try:
156
+ skill_configs = self.control_plane.get_skills(input.agent_id)
157
+ if skill_configs:
158
+ logger.info("skills_resolved", count=len(skill_configs))
159
+ # Skill details logged in skills_resolved
160
+ # Skill details logged in skills_resolved
161
+ # Skill details logged in skills_resolved
162
+
163
+ # DEBUG: Show full config for workflow_executor skills
164
+ for cfg in skill_configs:
165
+ if cfg.get('type') in ['workflow_executor', 'workflow']:
166
+ logger.debug("workflow_executor_skill_config")
167
+ # print(f" Name: {cfg.get('name')}")
168
+ # print(f" Type: {cfg.get('type')}")
169
+ # Skill details logged in skills_resolved
170
+ # print(f" Config Keys: {list(cfg.get('configuration', {}).keys())}\n")
171
+
172
+ # Always include built-in context_graph_search skill
173
+ builtin_skill_types = {'context_graph_search'}
174
+ existing_skill_types = {cfg.get('type') for cfg in skill_configs}
175
+
176
+ for builtin_type in builtin_skill_types:
177
+ if builtin_type not in existing_skill_types:
178
+ builtin_config = {
179
+ 'name': builtin_type,
180
+ 'type': builtin_type,
181
+ 'enabled': True,
182
+ 'configuration': {}
183
+ }
184
+ skill_configs.append(builtin_config)
185
+ logger.info("builtin_skill_included", skill_type=builtin_type)
186
+
187
+ # Import here to avoid circular dependency
188
+ from control_plane_api.worker.services.skill_factory import SkillFactory
189
+
190
+ # Create factory instance for the current runtime
191
+ skill_factory = SkillFactory(runtime_type=runtime_type.value)
192
+ skill_factory.initialize()
193
+
194
+ skills = skill_factory.create_skills_from_list(
195
+ skill_configs, execution_id=execution_id
196
+ )
197
+
198
+ if skills:
199
+ logger.info("skills_instantiated", count=len(skills))
200
+ # Show types of instantiated skills
201
+ skill_types = [type(s).__name__ for s in skills]
202
+ # Skill classes logged in skills_instantiated
203
+ else:
204
+ logger.warning("no_skills_instantiated")
205
+ else:
206
+ logger.warning("no_skills_found")
207
+ # Even if no skills configured, add built-in skills
208
+ skill_configs = []
209
+ builtin_skill_types = {'context_graph_search'}
210
+
211
+ for builtin_type in builtin_skill_types:
212
+ builtin_config = {
213
+ 'name': builtin_type,
214
+ 'type': builtin_type,
215
+ 'enabled': True,
216
+ 'configuration': {}
217
+ }
218
+ skill_configs.append(builtin_config)
219
+ logger.info("builtin_skill_included", skill_type=builtin_type)
220
+
221
+ if skill_configs:
222
+ # Import here to avoid circular dependency
223
+ from control_plane_api.worker.services.skill_factory import SkillFactory
224
+
225
+ # Create factory instance for the current runtime
226
+ skill_factory = SkillFactory(runtime_type=runtime_type.value)
227
+ skill_factory.initialize()
228
+
229
+ skills = skill_factory.create_skills_from_list(
230
+ skill_configs, execution_id=execution_id
231
+ )
232
+
233
+ if skills:
234
+ logger.info("skills_instantiated", count=len(skills))
235
+ skill_types = [type(s).__name__ for s in skills]
236
+ # Skill classes logged in skills_instantiated
237
+ except Exception as e:
238
+ logger.warning("skill_fetch_error", error=str(e))
239
+ logger.error("skill_fetch_error", error=str(e), exc_info=True)
240
+
241
+ # STEP 5: Inject environment variables into MCP servers (runtime-agnostic)
242
+ logger.info("fetching_resolved_environment")
243
+ from control_plane_api.worker.activities.runtime_activities import inject_env_vars_into_mcp_servers
244
+ mcp_servers_with_env = inject_env_vars_into_mcp_servers(
245
+ mcp_servers=input.mcp_servers,
246
+ agent_config=agent_config,
247
+ runtime_config=agent_config.get("runtime_config"),
248
+ control_plane_client=self.control_plane,
249
+ agent_id=input.agent_id,
250
+ )
251
+
252
+ # STEP 6: Compile system prompt templates
253
+ logger.info("compiling_system_prompt_templates")
254
+ compiled_system_prompt = input.system_prompt
255
+ if input.system_prompt:
256
+ try:
257
+ # Build template context with available variables
258
+ template_context = TemplateContext(
259
+ variables=input.user_metadata or {},
260
+ secrets=agent_config.get("secrets", {}),
261
+ env_vars=dict(os.environ), # Make all env vars available
262
+ # Context graph API configuration for {{.graph.node-id}} templates
263
+ graph_api_base=os.environ.get("CONTEXT_GRAPH_API_BASE", "https://graph.kubiya.ai"),
264
+ graph_api_key=os.environ.get("KUBIYA_API_KEY"),
265
+ graph_org_id=os.environ.get("KUBIYA_ORG_ID") or input.organization_id
266
+ )
267
+
268
+ # Resolve templates in system prompt
269
+ compiled_system_prompt = resolve_templates(
270
+ input.system_prompt,
271
+ template_context,
272
+ strict=False, # Don't fail on missing variables
273
+ skip_on_error=True # Return original if compilation fails
274
+ )
275
+
276
+ if compiled_system_prompt != input.system_prompt:
277
+ logger.info(
278
+ "system_prompt_templates_compiled",
279
+ original_length=len(input.system_prompt),
280
+ compiled_length=len(compiled_system_prompt)
281
+ )
282
+ logger.info("system_prompt_templates_compiled")
283
+ else:
284
+ logger.info("no_templates_in_system_prompt")
285
+
286
+ except Exception as e:
287
+ logger.warning(
288
+ "system_prompt_template_compilation_failed",
289
+ error=str(e),
290
+ exc_info=True
291
+ )
292
+ logger.warning("system_prompt_compilation_failed", error=str(e))
293
+ # Use original system prompt if compilation fails
294
+ compiled_system_prompt = input.system_prompt
295
+
296
+ # STEP 6.5: Enhance system prompt with complete execution environment context
297
+ execution_context_info = self._build_execution_context_info(
298
+ runtime_config=agent_config.get("runtime_config", {}),
299
+ skills=skills,
300
+ mcp_servers=mcp_servers_with_env,
301
+ agent_config=agent_config
302
+ )
303
+ if execution_context_info:
304
+ if compiled_system_prompt:
305
+ compiled_system_prompt = compiled_system_prompt + "\n\n" + execution_context_info
306
+ else:
307
+ compiled_system_prompt = execution_context_info
308
+ logger.info("system_prompt_enhanced")
309
+
310
+ # STEP 7: Build execution context
311
+ logger.info("building_execution_context")
312
+ context = RuntimeExecutionContext(
313
+ execution_id=execution_id,
314
+ agent_id=input.agent_id,
315
+ organization_id=input.organization_id,
316
+ prompt=input.prompt,
317
+ system_prompt=compiled_system_prompt,
318
+ conversation_history=session_history,
319
+ model_id=input.model_id,
320
+ model_config=input.model_config,
321
+ agent_config=agent_config,
322
+ skills=skills,
323
+ skill_configs=skill_configs, # Original skill configurations for prompt enhancement
324
+ mcp_servers=mcp_servers_with_env, # Use MCP servers with injected env vars
325
+ user_metadata=input.user_metadata,
326
+ runtime_config=agent_config.get("runtime_config"),
327
+ )
328
+ logger.info("execution_context_ready")
329
+
330
+ # STEP 7: Publish user message to stream before execution
331
+ # This ensures chronological ordering in UI
332
+ turn_number = len(session_history) // 2 + 1
333
+ user_message_id = f"{execution_id}_user_{turn_number}"
334
+ self.control_plane.publish_event(
335
+ execution_id=execution_id,
336
+ event_type="message",
337
+ data={
338
+ "role": "user",
339
+ "content": input.prompt,
340
+ "timestamp": user_message_timestamp,
341
+ "message_id": user_message_id,
342
+ "user_id": input.user_id,
343
+ "user_name": getattr(input, "user_name", None),
344
+ "user_email": getattr(input, "user_email", None),
345
+ "user_avatar": getattr(input, "user_avatar", None),
346
+ }
347
+ )
348
+ logger.debug("user_message_published", message_id=user_message_id)
349
+
350
+ # Publish status message chunk: Connecting to worker
351
+ assistant_message_id = f"{execution_id}_assistant_{turn_number}"
352
+ await self.control_plane.publish_event_async(
353
+ execution_id=execution_id,
354
+ event_type="message_chunk",
355
+ data={
356
+ "role": "assistant",
357
+ "content": "Connecting to worker...\n",
358
+ "is_chunk": True,
359
+ "message_id": assistant_message_id,
360
+ }
361
+ )
362
+ # print(f" 📤 Published status: 'Connecting to worker...'\n")
363
+
364
+ # STEP 8: Execute via runtime (with streaming if supported)
365
+ # print("⚡ Executing via runtime...\n")
366
+
367
+ # Track turn start time for analytics
368
+ turn_start_time = time.time()
369
+
370
+ # Create streaming helper for tracking tool messages (used in both streaming and non-streaming)
371
+ streaming_helper = StreamingHelper(
372
+ control_plane_client=self.control_plane, execution_id=execution_id
373
+ )
374
+
375
+ if runtime.supports_streaming():
376
+ result = await self._execute_streaming(runtime, context, input, streaming_helper)
377
+ else:
378
+ result = await runtime.execute(context)
379
+
380
+ # Track turn end time
381
+ turn_end_time = time.time()
382
+
383
+ # print("\n✅ Runtime execution completed!")
384
+ logger.info("response_length", length=len(result["response"]))
385
+ # print(f" Success: {result.success}\n")
386
+
387
+ logger.info(
388
+ "agent_execution_completed",
389
+ execution_id=execution_id[:8],
390
+ success=result.success,
391
+ response_length=len(result.response),
392
+ )
393
+
394
+ # STEP 7.5: Submit analytics (non-blocking, fire-and-forget)
395
+ if result.success and result.usage:
396
+ try:
397
+ # Submit analytics in the background (doesn't block execution)
398
+ await submit_runtime_analytics(
399
+ result=result,
400
+ execution_id=execution_id,
401
+ turn_number=turn_number,
402
+ turn_start_time=turn_start_time,
403
+ turn_end_time=turn_end_time,
404
+ analytics_service=self.analytics_service,
405
+ )
406
+ logger.info(
407
+ "analytics_submitted",
408
+ execution_id=execution_id[:8],
409
+ tokens=result.usage.get("total_tokens", 0),
410
+ )
411
+ except Exception as analytics_error:
412
+ # Analytics failures should not break execution
413
+ logger.warning(
414
+ "analytics_submission_failed",
415
+ execution_id=execution_id[:8],
416
+ error=str(analytics_error),
417
+ )
418
+
419
+ # STEP 7: Persist session
420
+ if result.success and result.response:
421
+ logger.info("persisting_session_history")
422
+
423
+ # Finalize streaming to transition to post-tool phase
424
+ streaming_helper.finalize_streaming()
425
+
426
+ # Build user message
427
+ user_message = {
428
+ "role": "user",
429
+ "content": input.prompt,
430
+ "timestamp": user_message_timestamp, # Use timestamp from start
431
+ "message_id": f"{execution_id}_user_{turn_number}",
432
+ "user_id": input.user_id,
433
+ "user_name": getattr(input, "user_name", None),
434
+ "user_email": getattr(input, "user_email", None),
435
+ }
436
+
437
+ # Build structured messages using StreamingHelper
438
+ # This properly splits assistant messages around tool usage
439
+ new_messages = streaming_helper.build_structured_messages(
440
+ execution_id=execution_id,
441
+ turn_number=turn_number,
442
+ user_message=user_message,
443
+ )
444
+
445
+ logger.debug("structured_messages_built", count=len(new_messages))
446
+ if streaming_helper.has_any_tools:
447
+ logger.debug("assistant_message_split_into_phases")
448
+ assistant_parts = streaming_helper.get_assistant_message_parts()
449
+ for part in assistant_parts:
450
+ logger.debug("message_part", phase=part["phase"], length=len(part["content"]))
451
+
452
+ # Combine with previous history
453
+ complete_session = session_history + new_messages
454
+
455
+ # CRITICAL: Deduplicate messages by message_id AND content to prevent duplicates
456
+ # Use session_service.deduplicate_messages() which has enhanced two-level deduplication
457
+ original_count = len(complete_session)
458
+ complete_session = self.session_service.deduplicate_messages(complete_session)
459
+ deduplicated_count = len(complete_session)
460
+
461
+ # CRITICAL: Sort by timestamp to ensure chronological order
462
+ # Tool messages happen DURING streaming, so they need to be interleaved with user/assistant messages
463
+ complete_session.sort(key=lambda msg: msg.get("timestamp", ""))
464
+ logger.info("messages_deduplicated", before=original_count, after=deduplicated_count, removed=original_count - deduplicated_count)
465
+
466
+ success = self.session_service.persist_session(
467
+ execution_id=execution_id,
468
+ session_id=input.session_id or execution_id,
469
+ user_id=input.user_id,
470
+ messages=complete_session,
471
+ metadata={
472
+ "agent_id": input.agent_id,
473
+ "organization_id": input.organization_id,
474
+ "runtime_type": runtime_type.value,
475
+ "turn_count": len(complete_session),
476
+ },
477
+ )
478
+
479
+ if success:
480
+ # print(f"✅ Session persisted ({len(complete_session)} total messages)\n")
481
+ pass
482
+ else:
483
+ logger.warning("session_persistence_failed")
484
+
485
+ # STEP 8: Print usage metrics
486
+ if result.usage:
487
+ logger.info("token_usage",
488
+ prompt_tokens=result["usage"].get("prompt_tokens", 0),
489
+ completion_tokens=result["usage"].get("completion_tokens", 0),
490
+ total_tokens=result["usage"].get("total_tokens", 0))
491
+
492
+ # print("=" * 80)
493
+ logger.info("agent_execution_end")
494
+ # print("=" * 80 + "\n")
495
+
496
+ # Return standardized result
497
+ return {
498
+ "success": result.success,
499
+ "response": result.response,
500
+ "usage": result.usage,
501
+ "model": result.model or input.model_id,
502
+ "finish_reason": result.finish_reason or "stop",
503
+ "run_id": result.run_id,
504
+ "tool_messages": result.tool_messages or [],
505
+ "runtime_type": runtime_type.value,
506
+ "error": result.error,
507
+ }
508
+
509
+ except Exception as e:
510
+ # print("\n" + "=" * 80)
511
+ logger.error("agent_execution_failed")
512
+ # print("=" * 80)
513
+ logger.error("execution_error", error=str(e))
514
+ # print("=" * 80 + "\n")
515
+
516
+ logger.error(
517
+ "agent_execution_failed", execution_id=execution_id[:8], error=str(e)
518
+ )
519
+
520
+ # Publish critical error as message to the stream
521
+ try:
522
+ error_message = f"❌ Critical Error: {str(e)}"
523
+ turn_number = len(session_history) // 2 + 1 if "session_history" in locals() else 1
524
+ assistant_message_id = f"{execution_id}_assistant_{turn_number}"
525
+
526
+ self.control_plane.publish_event(
527
+ execution_id=execution_id,
528
+ event_type="message",
529
+ data={
530
+ "role": "assistant",
531
+ "content": error_message,
532
+ "timestamp": datetime.now(timezone.utc).isoformat(),
533
+ "message_id": assistant_message_id,
534
+ "is_error": True,
535
+ }
536
+ )
537
+ # print(f" 📤 Published critical error to stream\n")
538
+ except Exception as publish_error:
539
+ # Don't let error publishing break the error handling
540
+ logger.warning(
541
+ "failed_to_publish_error_event",
542
+ execution_id=execution_id[:8],
543
+ error=str(publish_error)
544
+ )
545
+
546
+ return {
547
+ "success": False,
548
+ "error": str(e),
549
+ "model": input.model_id,
550
+ "usage": {},
551
+ "finish_reason": "error",
552
+ "runtime_type": runtime_type.value if "runtime_type" in locals() else "unknown",
553
+ }
554
+
555
+ async def _execute_streaming(
556
+ self, runtime, context: RuntimeExecutionContext, input: Any, streaming_helper: StreamingHelper
557
+ ) -> Any:
558
+ """
559
+ Execute with streaming and publish events to Control Plane.
560
+
561
+ Args:
562
+ runtime: Runtime instance
563
+ context: Execution context
564
+ input: Original input for additional metadata
565
+ streaming_helper: StreamingHelper instance for tracking events
566
+
567
+ Returns:
568
+ Final RuntimeExecutionResult
569
+ """
570
+ # streaming_helper is now passed as parameter instead of created here
571
+
572
+ accumulated_response = ""
573
+ final_result = None
574
+
575
+ # Define event callback for publishing to Control Plane
576
+ def event_callback(event: Dict):
577
+ """Callback to publish events to Control Plane SSE"""
578
+ event_type = event.get("type")
579
+
580
+ if event_type == "content_chunk":
581
+ # Publish content chunk
582
+ streaming_helper.publish_content_chunk(
583
+ content=event.get("content", ""),
584
+ message_id=event.get("message_id", context.execution_id),
585
+ )
586
+ elif event_type == "tool_start":
587
+ # Publish tool start
588
+ streaming_helper.publish_tool_start(
589
+ tool_name=event.get("tool_name"),
590
+ tool_execution_id=event.get("tool_execution_id"),
591
+ tool_args=event.get("tool_args", {}),
592
+ source="agent",
593
+ )
594
+ elif event_type == "tool_complete":
595
+ # Publish tool completion
596
+ streaming_helper.publish_tool_complete(
597
+ tool_name=event.get("tool_name"),
598
+ tool_execution_id=event.get("tool_execution_id"),
599
+ status=event.get("status", "success"),
600
+ output=event.get("output"),
601
+ error=event.get("error"),
602
+ source="agent",
603
+ )
604
+
605
+ # Stream execution
606
+ async for chunk in runtime.stream_execute(context, event_callback):
607
+ if chunk.response:
608
+ accumulated_response += chunk.response
609
+ # print(chunk.response, end="", flush=True)
610
+
611
+ # Keep final result for metadata
612
+ if chunk.usage or chunk.finish_reason:
613
+ final_result = chunk
614
+
615
+ # Empty line removed
616
+
617
+ # Return final result with accumulated response
618
+ if final_result:
619
+ # Update response with accumulated content
620
+ final_result.response = accumulated_response
621
+ return final_result
622
+ else:
623
+ # Create final result if not provided
624
+ from runtimes.base import RuntimeExecutionResult
625
+
626
+ return RuntimeExecutionResult(
627
+ response=accumulated_response,
628
+ usage={},
629
+ success=True,
630
+ finish_reason="stop",
631
+ )
632
+
633
+ def _build_execution_context_info(
634
+ self,
635
+ runtime_config: Dict[str, Any],
636
+ skills: List[Any],
637
+ mcp_servers: Optional[Dict[str, Any]],
638
+ agent_config: Dict[str, Any]
639
+ ) -> str:
640
+ """
641
+ Build comprehensive execution environment context for system prompt.
642
+
643
+ This provides the agent with awareness of:
644
+ - Available environment variables (secrets, integrations, config)
645
+ - Available skills/tools
646
+ - MCP servers
647
+ - Runtime configuration
648
+
649
+ Args:
650
+ runtime_config: Runtime configuration with env vars
651
+ skills: List of available skills
652
+ mcp_servers: Dictionary of MCP server configurations
653
+ agent_config: Agent configuration
654
+
655
+ Returns:
656
+ Formatted execution context string for system prompt
657
+ """
658
+ context_parts = []
659
+ context_parts.append("---")
660
+ context_parts.append("")
661
+ context_parts.append("# 🔧 Execution Environment Context")
662
+ context_parts.append("")
663
+ context_parts.append("You are running in a managed execution environment with the following resources available:")
664
+ context_parts.append("")
665
+
666
+ # 1. Environment Variables
667
+ if runtime_config and "env" in runtime_config:
668
+ available_env_vars = runtime_config["env"]
669
+
670
+ # Categorize environment variables
671
+ secrets = [k for k in available_env_vars.keys() if any(
672
+ keyword in k.lower()
673
+ for keyword in ["secret", "password", "credential", "api_key", "private_key"]
674
+ ) and k not in ["KUBIYA_API_KEY", "KUBIYA_API_BASE", "ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"]]
675
+
676
+ integrations = [k for k in available_env_vars.keys() if any(
677
+ prefix in k
678
+ for prefix in ["GH_TOKEN", "GITHUB_", "JIRA_", "SLACK_", "AWS_", "GCP_", "AZURE_"]
679
+ )]
680
+
681
+ inherited_vars = [k for k in available_env_vars.keys()
682
+ if k not in secrets
683
+ and k not in integrations
684
+ and k not in ["KUBIYA_API_KEY", "KUBIYA_API_BASE", "ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "LITELLM_API_KEY", "LITELLM_API_BASE"]]
685
+
686
+ context_parts.append("## 📦 Environment Variables")
687
+ context_parts.append("")
688
+
689
+ if secrets:
690
+ context_parts.append("**Secrets & API Keys** (use these for authenticated operations):")
691
+ for var in sorted(secrets):
692
+ context_parts.append(f"- `${var}` - Secret/credential available as environment variable")
693
+ context_parts.append("")
694
+
695
+ if integrations:
696
+ context_parts.append("**Integration Tokens** (pre-configured service access):")
697
+ for var in sorted(integrations):
698
+ service = var.split("_")[0].title() if "_" in var else var
699
+ context_parts.append(f"- `${var}` - {service} integration token")
700
+ context_parts.append("")
701
+
702
+ if inherited_vars:
703
+ context_parts.append("**Configuration Variables** (inherited from environment):")
704
+ # Limit to first 10 to avoid clutter
705
+ for var in sorted(inherited_vars)[:10]:
706
+ context_parts.append(f"- `${var}`")
707
+ if len(inherited_vars) > 10:
708
+ context_parts.append(f"- ... and {len(inherited_vars) - 10} more")
709
+ context_parts.append("")
710
+
711
+ if secrets or integrations or inherited_vars:
712
+ context_parts.append("**Usage Examples:**")
713
+ context_parts.append("```bash")
714
+ context_parts.append("# Access in Bash commands")
715
+ context_parts.append("echo $VARIABLE_NAME")
716
+ context_parts.append("")
717
+ if integrations:
718
+ example_token = sorted(integrations)[0]
719
+ if "GH" in example_token or "GITHUB" in example_token:
720
+ context_parts.append("# Use with GitHub API")
721
+ context_parts.append(f"curl -H \"Authorization: token ${example_token}\" https://api.github.com/user")
722
+ elif "JIRA" in example_token:
723
+ context_parts.append("# Use with Jira API")
724
+ context_parts.append(f"curl -H \"Authorization: Bearer ${example_token}\" https://yourinstance.atlassian.net/rest/api/3/myself")
725
+ context_parts.append("```")
726
+ context_parts.append("")
727
+
728
+ # 2. Available Skills/Tools
729
+ if skills:
730
+ context_parts.append("## 🛠️ Available Skills/Tools")
731
+ context_parts.append("")
732
+ skill_names = []
733
+ for skill in skills:
734
+ if isinstance(skill, dict):
735
+ skill_names.append(skill.get("name", skill.get("type", "Unknown")))
736
+ else:
737
+ skill_names.append(type(skill).__name__ if hasattr(skill, '__class__') else str(skill))
738
+
739
+ if skill_names:
740
+ context_parts.append(f"You have access to {len(skill_names)} skill(s):")
741
+ for skill_name in sorted(set(skill_names))[:15]: # Limit to 15 to avoid clutter
742
+ context_parts.append(f"- `{skill_name}`")
743
+ if len(set(skill_names)) > 15:
744
+ context_parts.append(f"- ... and {len(set(skill_names)) - 15} more")
745
+ context_parts.append("")
746
+
747
+ # 3. MCP Servers
748
+ if mcp_servers:
749
+ context_parts.append("## 🔌 MCP Servers")
750
+ context_parts.append("")
751
+ context_parts.append(f"You have access to {len(mcp_servers)} MCP server(s):")
752
+ for server_name in sorted(mcp_servers.keys())[:10]: # Limit to 10
753
+ context_parts.append(f"- `{server_name}`")
754
+ if len(mcp_servers) > 10:
755
+ context_parts.append(f"- ... and {len(mcp_servers) - 10} more")
756
+ context_parts.append("")
757
+ context_parts.append("**Note:** All environment variables listed above are automatically available to these MCP servers.")
758
+ context_parts.append("")
759
+
760
+ # 4. Best Practices
761
+ context_parts.append("## 💡 Best Practices")
762
+ context_parts.append("")
763
+ context_parts.append("- **Environment Variables**: All listed variables are ready to use - no configuration needed")
764
+ context_parts.append("- **Secrets**: Never log or display secret values in responses")
765
+ context_parts.append("- **Integration Tokens**: These provide pre-authorized access to external services")
766
+ context_parts.append("- **MCP Tools**: Prefer using MCP tools over Bash when available for better reliability")
767
+ context_parts.append("")
768
+ context_parts.append("---")
769
+
770
+ logger.info(
771
+ "execution_context_info_built",
772
+ env_vars_count=len(runtime_config.get("env", {})) if runtime_config else 0,
773
+ skills_count=len(skills) if skills else 0,
774
+ mcp_servers_count=len(mcp_servers) if mcp_servers else 0
775
+ )
776
+
777
+ return "\n".join(context_parts)
778
+
779
+ def _get_framework_name(self, runtime_type: RuntimeType) -> str:
780
+ """
781
+ Get friendly framework name for runtime type.
782
+
783
+ Args:
784
+ runtime_type: Runtime type enum
785
+
786
+ Returns:
787
+ Framework name string
788
+ """
789
+ mapping = {
790
+ RuntimeType.DEFAULT: "Agno",
791
+ RuntimeType.CLAUDE_CODE: "Claude Code SDK",
792
+ }
793
+ return mapping.get(runtime_type, "Unknown")