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,611 @@
1
+ """Team execution workflow for Temporal"""
2
+
3
+ from dataclasses import dataclass, field
4
+ from datetime import timedelta
5
+ from typing import Optional, List, Dict, Any
6
+ from temporalio import workflow
7
+ from temporalio.common import RetryPolicy
8
+ import asyncio
9
+ import os
10
+
11
+ with workflow.unsafe.imports_passed_through():
12
+ from control_plane_api.worker.activities.team_activities import (
13
+ get_team_agents,
14
+ execute_team_coordination,
15
+ ActivityGetTeamAgentsInput,
16
+ ActivityExecuteTeamInput,
17
+ )
18
+ from control_plane_api.worker.activities.agent_activities import (
19
+ update_execution_status,
20
+ get_execution_details,
21
+ submit_runtime_analytics_activity,
22
+ ActivityUpdateExecutionInput,
23
+ ActivityGetExecutionInput,
24
+ AnalyticsActivityInput,
25
+ )
26
+ from control_plane_api.worker.activities.runtime_activities import (
27
+ publish_user_message,
28
+ PublishUserMessageInput,
29
+ )
30
+
31
+
32
+ # Heartbeat timeout: Prove activity is alive (default 30 minutes)
33
+ # This should be reasonable - heartbeats confirm the activity hasn't crashed
34
+ HEARTBEAT_TIMEOUT_SECONDS = int(os.environ.get("TEAM_ACTIVITY_HEARTBEAT_TIMEOUT_SECONDS", "1800"))
35
+
36
+ # Activity execution timeout: Total time for activity to complete (default 24 hours)
37
+ # This is the maximum time an activity can run. For streaming workflows, this should be VERY long
38
+ # since the activity may stream for hours while the user interacts with the team
39
+ ACTIVITY_EXECUTION_TIMEOUT_SECONDS = int(os.environ.get("TEAM_ACTIVITY_EXECUTION_TIMEOUT_SECONDS", "86400"))
40
+
41
+
42
+ @dataclass
43
+ class TeamExecutionInput:
44
+ """Input for team execution workflow"""
45
+ execution_id: str
46
+ team_id: str
47
+ organization_id: str
48
+ prompt: str
49
+ system_prompt: Optional[str] = None
50
+ team_config: dict = None
51
+ user_metadata: dict = None
52
+ mcp_servers: dict = None # MCP servers configuration
53
+ initial_message_timestamp: Optional[str] = None # Timestamp for initial user message
54
+
55
+ def __post_init__(self):
56
+ if self.team_config is None:
57
+ self.team_config = {}
58
+ if self.user_metadata is None:
59
+ self.user_metadata = {}
60
+ if self.mcp_servers is None:
61
+ self.mcp_servers = {}
62
+
63
+
64
+ @dataclass
65
+ class ChatMessage:
66
+ """Represents a message in the conversation"""
67
+ role: str # "user", "assistant", "system", "tool"
68
+ content: str
69
+ timestamp: str
70
+ tool_name: Optional[str] = None
71
+ tool_input: Optional[Dict[str, Any]] = None
72
+ tool_output: Optional[Dict[str, Any]] = None
73
+ message_id: Optional[str] = None # Unique identifier for deduplication
74
+ user_id: Optional[str] = None # User who sent the message
75
+ user_name: Optional[str] = None
76
+ user_email: Optional[str] = None
77
+ user_avatar: Optional[str] = None
78
+
79
+
80
+ @dataclass
81
+ class ExecutionState:
82
+ """Current state of the execution for queries"""
83
+ status: str # "pending", "running", "waiting_for_input", "paused", "completed", "failed"
84
+ messages: List[ChatMessage] = field(default_factory=list)
85
+ current_response: str = ""
86
+ error_message: Optional[str] = None
87
+ usage: Dict[str, Any] = field(default_factory=dict)
88
+ metadata: Dict[str, Any] = field(default_factory=dict)
89
+ is_waiting_for_input: bool = False
90
+ should_complete: bool = False
91
+ is_paused: bool = False
92
+
93
+
94
+ @workflow.defn
95
+ class TeamExecutionWorkflow:
96
+ """
97
+ Workflow for executing a team of agents with HITL support.
98
+
99
+ This workflow:
100
+ 1. Gets team agents
101
+ 2. Coordinates execution across agents
102
+ 3. Aggregates results
103
+ 4. Updates execution status
104
+ 5. Supports queries for real-time state access
105
+ 6. Supports signals for adding followup messages
106
+ """
107
+
108
+ def __init__(self) -> None:
109
+ """Initialize workflow state"""
110
+ self._state = ExecutionState(status="pending")
111
+ self._lock = asyncio.Lock()
112
+ self._new_message_count = 0
113
+ self._processed_message_count = 0
114
+
115
+ @workflow.query
116
+ def get_state(self) -> ExecutionState:
117
+ """Query handler: Get current execution state including messages and status"""
118
+ return self._state
119
+
120
+ @workflow.signal
121
+ async def add_message(self, message: ChatMessage) -> None:
122
+ """
123
+ Signal handler: Add a message to the conversation.
124
+ This allows clients to send followup messages while the workflow is running.
125
+ """
126
+ async with self._lock:
127
+ self._state.messages.append(message)
128
+ self._new_message_count += 1
129
+ self._state.is_waiting_for_input = False
130
+ workflow.logger.info(
131
+ f"Message added to team conversation",
132
+ extra={
133
+ "role": message.role,
134
+ "content_preview": message.content[:100] if message.content else "",
135
+ "total_messages": len(self._state.messages)
136
+ }
137
+ )
138
+
139
+ @workflow.signal
140
+ async def mark_as_done(self) -> None:
141
+ """
142
+ Signal handler: Mark the workflow as complete.
143
+ """
144
+ async with self._lock:
145
+ self._state.should_complete = True
146
+ self._state.is_waiting_for_input = False
147
+ workflow.logger.info("Team workflow marked as done by user")
148
+
149
+ @workflow.signal
150
+ async def update_streaming_response(self, current_response: str) -> None:
151
+ """
152
+ Signal handler: Update current streaming response.
153
+ Activity sends this periodically during execution for state tracking.
154
+ """
155
+ async with self._lock:
156
+ self._state.current_response = current_response
157
+ workflow.logger.info(
158
+ f"Streaming response updated",
159
+ extra={"response_length": len(current_response)}
160
+ )
161
+
162
+ @workflow.signal
163
+ async def pause_execution(self) -> None:
164
+ """
165
+ Signal handler: Pause the workflow execution.
166
+ This pauses the workflow - it will stop processing but remain active.
167
+ Resume can be called to continue execution.
168
+ """
169
+ async with self._lock:
170
+ if not self._state.is_paused:
171
+ self._state.is_paused = True
172
+ self._state.status = "paused"
173
+ workflow.logger.info("Team workflow paused by user")
174
+
175
+ @workflow.signal
176
+ async def resume_execution(self) -> None:
177
+ """
178
+ Signal handler: Resume a paused workflow execution.
179
+ This resumes the workflow from where it was paused.
180
+ """
181
+ async with self._lock:
182
+ if self._state.is_paused:
183
+ self._state.is_paused = False
184
+ # Restore previous status (either running or waiting_for_input)
185
+ self._state.status = "waiting_for_input" if self._state.is_waiting_for_input else "running"
186
+ workflow.logger.info("Team workflow resumed by user")
187
+
188
+ @workflow.run
189
+ async def run(self, input: TeamExecutionInput) -> dict:
190
+ """
191
+ Run the team execution workflow with HITL pattern.
192
+
193
+ This workflow implements a continuous conversation loop:
194
+ 1. Process the initial user message
195
+ 2. Execute team coordination and return response
196
+ 3. Wait for user input (signals)
197
+ 4. Process followup messages in a loop
198
+ 5. Only complete when user explicitly marks as done
199
+
200
+ Args:
201
+ input: Workflow input with team execution details
202
+
203
+ Returns:
204
+ Team execution result dict
205
+ """
206
+ workflow.logger.info(
207
+ f"Starting team execution workflow with HITL pattern",
208
+ extra={
209
+ "execution_id": input.execution_id,
210
+ "team_id": input.team_id,
211
+ "organization_id": input.organization_id,
212
+ }
213
+ )
214
+
215
+ # Initialize state with user's initial message
216
+ # CRITICAL: Use real-time timestamp (not workflow.now()) to ensure chronological ordering
217
+ # This prevents timestamp mismatches between initial and follow-up messages
218
+ message_timestamp = input.initial_message_timestamp or workflow.now().isoformat()
219
+
220
+ initial_user_message = ChatMessage(
221
+ role="user",
222
+ content=input.prompt,
223
+ timestamp=message_timestamp,
224
+ message_id=f"{input.execution_id}_user_1", # Generate deterministic ID
225
+ )
226
+ self._state.messages.append(initial_user_message)
227
+ self._state.status = "running"
228
+ self._new_message_count = 1
229
+ self._processed_message_count = 0
230
+
231
+ try:
232
+ # Step 1: Update execution status to running
233
+ await workflow.execute_activity(
234
+ update_execution_status,
235
+ ActivityUpdateExecutionInput(
236
+ execution_id=input.execution_id,
237
+ status="running",
238
+ started_at=workflow.now().isoformat(),
239
+ execution_metadata={
240
+ "workflow_started": True,
241
+ "hitl_enabled": True,
242
+ },
243
+ ),
244
+ start_to_close_timeout=timedelta(seconds=30),
245
+ )
246
+
247
+ # Step 1.5: Publish initial user message to stream immediately
248
+ # This ensures the user message appears in UI before assistant response
249
+ #
250
+ # IMPORTANT: Use workflow patching to handle existing workflows that don't have this activity
251
+ # Existing workflows will skip this during replay; new workflows will execute it
252
+ if workflow.patched("publish-user-message-v1"):
253
+ workflow.logger.info(
254
+ f"Publishing initial user message to stream",
255
+ extra={
256
+ "execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown",
257
+ "message_id": initial_user_message.message_id,
258
+ }
259
+ )
260
+ await workflow.execute_activity(
261
+ publish_user_message,
262
+ PublishUserMessageInput(
263
+ execution_id=input.execution_id,
264
+ prompt=input.prompt,
265
+ timestamp=initial_user_message.timestamp,
266
+ message_id=initial_user_message.message_id,
267
+ user_id=input.user_metadata.get("user_id") if input.user_metadata else None,
268
+ user_name=input.user_metadata.get("user_name") if input.user_metadata else None,
269
+ user_email=input.user_metadata.get("user_email") if input.user_metadata else None,
270
+ user_avatar=input.user_metadata.get("user_avatar") if input.user_metadata else None,
271
+ ),
272
+ start_to_close_timeout=timedelta(seconds=10),
273
+ )
274
+
275
+ # Step 2: Get team agents once at the beginning
276
+ workflow.logger.info(
277
+ f"[WORKFLOW] About to call get_team_agents",
278
+ extra={
279
+ "team_id": input.team_id,
280
+ "organization_id": input.organization_id,
281
+ }
282
+ )
283
+
284
+ team_agents = await workflow.execute_activity(
285
+ get_team_agents,
286
+ ActivityGetTeamAgentsInput(
287
+ team_id=input.team_id,
288
+ organization_id=input.organization_id,
289
+ ),
290
+ start_to_close_timeout=timedelta(seconds=30),
291
+ )
292
+
293
+ workflow.logger.info(
294
+ f"[WORKFLOW] get_team_agents returned",
295
+ extra={
296
+ "result": team_agents,
297
+ "agents_count": len(team_agents.get("agents", [])) if team_agents else 0,
298
+ }
299
+ )
300
+
301
+ if not team_agents.get("agents"):
302
+ workflow.logger.error(
303
+ f"[WORKFLOW] NO AGENTS RETURNED!",
304
+ extra={
305
+ "team_agents": team_agents,
306
+ "team_id": input.team_id,
307
+ "organization_id": input.organization_id,
308
+ }
309
+ )
310
+ raise ValueError("No agents found in team")
311
+
312
+ # HITL Conversation Loop - Continue until user marks as done
313
+ conversation_turn = 0
314
+ while not self._state.should_complete:
315
+ # Check if workflow is paused - wait until resumed
316
+ if self._state.is_paused:
317
+ workflow.logger.info(
318
+ "Team workflow is paused, waiting for resume signal",
319
+ extra={"execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown"}
320
+ )
321
+ await workflow.wait_condition(
322
+ lambda: not self._state.is_paused or self._state.should_complete,
323
+ timeout=timedelta(hours=24)
324
+ )
325
+ if self._state.should_complete:
326
+ break
327
+ workflow.logger.info(
328
+ "Team workflow resumed, continuing execution",
329
+ extra={"execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown"}
330
+ )
331
+
332
+ conversation_turn += 1
333
+ workflow.logger.info(
334
+ f"Starting team conversation turn {conversation_turn}",
335
+ extra={"turn": conversation_turn, "message_count": len(self._state.messages)}
336
+ )
337
+
338
+ # Get the latest user message
339
+ latest_message = self._state.messages[-1] if self._state.messages else None
340
+ latest_prompt = latest_message.content if latest_message and latest_message.role == "user" else input.prompt
341
+
342
+ # Capture turn start time for analytics
343
+ # workflow.time() already returns a float timestamp, no need for .timestamp()
344
+ turn_start_time = workflow.time()
345
+
346
+ # Step 3: Execute team coordination
347
+ team_result = await workflow.execute_activity(
348
+ execute_team_coordination,
349
+ ActivityExecuteTeamInput(
350
+ execution_id=input.execution_id,
351
+ team_id=input.team_id,
352
+ organization_id=input.organization_id,
353
+ prompt=latest_prompt,
354
+ system_prompt=input.system_prompt,
355
+ agents=team_agents["agents"],
356
+ team_config=input.team_config,
357
+ mcp_servers=input.mcp_servers, # Pass MCP servers
358
+ session_id=input.execution_id, # Use execution_id as session_id
359
+ user_id=input.user_metadata.get("user_id") if input.user_metadata else None,
360
+ model_id=input.team_config.get("llm", {}).get("model") if input.team_config else None,
361
+ model_config=input.team_config.get("llm", {}) if input.team_config else None,
362
+ # Activity reads CONTROL_PLANE_URL and KUBIYA_API_KEY from worker environment
363
+ ),
364
+ start_to_close_timeout=timedelta(seconds=ACTIVITY_EXECUTION_TIMEOUT_SECONDS), # Configurable, default 24 hours for long-running streaming
365
+ heartbeat_timeout=timedelta(seconds=HEARTBEAT_TIMEOUT_SECONDS), # Configurable, default 30 min for long-running tasks
366
+ )
367
+
368
+ # Add tool execution status messages (real-time updates)
369
+ if team_result.get("tool_execution_messages"):
370
+ async with self._lock:
371
+ for tool_msg in team_result["tool_execution_messages"]:
372
+ self._state.messages.append(ChatMessage(
373
+ role="system",
374
+ content=tool_msg.get("content", ""),
375
+ timestamp=tool_msg.get("timestamp", workflow.now().isoformat()),
376
+ tool_name=tool_msg.get("tool_name"),
377
+ ))
378
+
379
+ # Add tool messages to state (detailed tool info)
380
+ if team_result.get("tool_messages"):
381
+ async with self._lock:
382
+ for tool_msg in team_result["tool_messages"]:
383
+ self._state.messages.append(ChatMessage(
384
+ role="tool",
385
+ content=tool_msg.get("content", ""),
386
+ timestamp=tool_msg.get("timestamp", workflow.now().isoformat()),
387
+ tool_name=tool_msg.get("tool_name"),
388
+ tool_input=tool_msg.get("tool_input"),
389
+ ))
390
+
391
+ # Update state with team response
392
+ if team_result.get("response"):
393
+ async with self._lock:
394
+ # CRITICAL: Use real-time timestamp from team_result if available
395
+ # This ensures chronological ordering with streaming events
396
+ # Fallback to workflow.now() (deterministic) if not provided
397
+ response_timestamp = team_result.get("response_timestamp") or workflow.now().isoformat()
398
+
399
+ self._state.messages.append(ChatMessage(
400
+ role="assistant",
401
+ content=team_result["response"],
402
+ timestamp=response_timestamp,
403
+ ))
404
+ self._state.current_response = team_result["response"]
405
+ self._processed_message_count += 1
406
+
407
+ # Update usage and metadata (accumulate across turns)
408
+ if team_result.get("usage"):
409
+ current_usage = self._state.usage
410
+ new_usage = team_result.get("usage", {})
411
+ self._state.usage = {
412
+ "input_tokens": current_usage.get("input_tokens", 0) + new_usage.get("input_tokens", 0),
413
+ "output_tokens": current_usage.get("output_tokens", 0) + new_usage.get("output_tokens", 0),
414
+ "total_tokens": current_usage.get("total_tokens", 0) + new_usage.get("total_tokens", 0),
415
+ }
416
+
417
+ # Update metadata
418
+ self._state.metadata.update({
419
+ "agent_count": len(team_agents["agents"]),
420
+ "coordination_type": team_result.get("coordination_type"),
421
+ "conversation_turns": conversation_turn,
422
+ })
423
+
424
+ # Check if team execution failed
425
+ if not team_result.get("success"):
426
+ self._state.status = "failed"
427
+ self._state.error_message = team_result.get("error")
428
+ break
429
+
430
+ # Submit turn analytics (fire-and-forget)
431
+ # This triggers the control plane's intelligent state transition system
432
+ workflow.start_activity(
433
+ submit_runtime_analytics_activity,
434
+ AnalyticsActivityInput(
435
+ execution_id=input.execution_id,
436
+ turn_number=conversation_turn,
437
+ result=team_result,
438
+ turn_start_time=turn_start_time,
439
+ ),
440
+ start_to_close_timeout=timedelta(seconds=30),
441
+ retry_policy=RetryPolicy(
442
+ maximum_attempts=3,
443
+ initial_interval=timedelta(seconds=2),
444
+ maximum_interval=timedelta(seconds=10),
445
+ backoff_coefficient=2.0,
446
+ non_retryable_error_types=["ValueError", "TypeError"],
447
+ ),
448
+ )
449
+
450
+ # Wait for control plane to make intelligent state decision
451
+ # The control plane AI analyzes the turn and determines the appropriate state
452
+ workflow.logger.info(
453
+ f"⏳ Waiting for control plane state decision for team turn {conversation_turn}",
454
+ extra={"turn": conversation_turn, "execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown"}
455
+ )
456
+
457
+ # Give control plane time to make AI decision (up to 6 seconds with retries)
458
+ max_retries = 3
459
+ retry_delay = 2 # seconds
460
+
461
+ for retry in range(max_retries):
462
+ await asyncio.sleep(retry_delay)
463
+
464
+ # Query execution state from control plane
465
+ try:
466
+ current_execution = await workflow.execute_activity(
467
+ get_execution_details,
468
+ ActivityGetExecutionInput(execution_id=input.execution_id),
469
+ start_to_close_timeout=timedelta(seconds=10),
470
+ )
471
+
472
+ control_plane_status = current_execution.get("status", "unknown")
473
+
474
+ # Check if status has been updated from "running" (indicates AI made a decision)
475
+ if control_plane_status != "running":
476
+ workflow.logger.info(
477
+ f"✅ Control plane decided state: {control_plane_status}",
478
+ extra={
479
+ "execution_id": input.execution_id,
480
+ "turn": conversation_turn,
481
+ "decided_status": control_plane_status,
482
+ "retry": retry + 1
483
+ }
484
+ )
485
+ break
486
+ else:
487
+ if retry < max_retries - 1:
488
+ workflow.logger.info(
489
+ f"⏳ Control plane still processing, retry {retry + 1}/{max_retries}",
490
+ extra={"turn": conversation_turn}
491
+ )
492
+ except Exception as e:
493
+ workflow.logger.warning(
494
+ f"⚠️ Failed to query execution state: {str(e)}",
495
+ extra={"turn": conversation_turn, "retry": retry + 1}
496
+ )
497
+ if retry == max_retries - 1:
498
+ # Final retry failed - default to waiting_for_input (safe fallback)
499
+ control_plane_status = "waiting_for_input"
500
+ workflow.logger.warning(
501
+ "Using safe fallback state: waiting_for_input",
502
+ extra={"turn": conversation_turn}
503
+ )
504
+
505
+ # Update internal state based on control plane decision
506
+ self._state.status = control_plane_status
507
+ self._state.is_waiting_for_input = (control_plane_status == "waiting_for_input")
508
+
509
+ workflow.logger.info(
510
+ f"Team turn {conversation_turn} complete - state: {control_plane_status}",
511
+ extra={"turn": conversation_turn, "status": control_plane_status}
512
+ )
513
+
514
+ # Wait for either new message, mark as done, or pause signal
515
+ await workflow.wait_condition(
516
+ lambda: self._new_message_count > self._processed_message_count or self._state.should_complete or self._state.is_paused,
517
+ timeout=timedelta(hours=24)
518
+ )
519
+
520
+ if self._state.should_complete:
521
+ workflow.logger.info("User marked team workflow as done")
522
+ break
523
+
524
+ # If paused while waiting, loop back to check pause condition at top of while loop
525
+ if self._state.is_paused:
526
+ workflow.logger.info(
527
+ "Team workflow paused while waiting for input",
528
+ extra={"execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown"}
529
+ )
530
+ continue
531
+
532
+ # Continue loop to process new message
533
+ self._state.status = "running"
534
+
535
+ # Conversation complete - finalize workflow
536
+ final_status = "failed" if self._state.status == "failed" else "completed"
537
+ self._state.status = final_status
538
+
539
+ await workflow.execute_activity(
540
+ update_execution_status,
541
+ ActivityUpdateExecutionInput(
542
+ execution_id=input.execution_id,
543
+ status=final_status,
544
+ completed_at=workflow.now().isoformat(),
545
+ response=self._state.current_response,
546
+ error_message=self._state.error_message,
547
+ usage=self._state.usage,
548
+ execution_metadata={
549
+ **self._state.metadata,
550
+ "workflow_completed": True,
551
+ "total_conversation_turns": conversation_turn,
552
+ },
553
+ ),
554
+ start_to_close_timeout=timedelta(seconds=30),
555
+ )
556
+
557
+ workflow.logger.info(
558
+ f"Team execution workflow completed with HITL",
559
+ extra={
560
+ "execution_id": input.execution_id,
561
+ "status": final_status,
562
+ "conversation_turns": conversation_turn,
563
+ }
564
+ )
565
+
566
+ return {
567
+ "success": final_status == "completed",
568
+ "execution_id": input.execution_id,
569
+ "status": final_status,
570
+ "response": self._state.current_response,
571
+ "usage": self._state.usage,
572
+ "conversation_turns": conversation_turn,
573
+ }
574
+
575
+ except Exception as e:
576
+ # Update state with error
577
+ self._state.status = "failed"
578
+ self._state.error_message = str(e)
579
+ self._state.metadata["error_type"] = type(e).__name__
580
+
581
+ workflow.logger.error(
582
+ f"Team execution workflow failed",
583
+ extra={
584
+ "execution_id": input.execution_id,
585
+ "error": str(e),
586
+ }
587
+ )
588
+
589
+ # Update execution as failed
590
+ try:
591
+ await workflow.execute_activity(
592
+ update_execution_status,
593
+ ActivityUpdateExecutionInput(
594
+ execution_id=input.execution_id,
595
+ status="failed",
596
+ completed_at=workflow.now().isoformat(),
597
+ error_message=f"Workflow error: {str(e)}",
598
+ execution_metadata={
599
+ "workflow_error": True,
600
+ "error_type": type(e).__name__,
601
+ },
602
+ ),
603
+ start_to_close_timeout=timedelta(seconds=30),
604
+ )
605
+ except Exception as update_error:
606
+ workflow.logger.error(
607
+ f"Failed to update status after error",
608
+ extra={"error": str(update_error)}
609
+ )
610
+
611
+ raise