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,254 @@
1
+ """Plan generation workflow for Temporal"""
2
+
3
+ from dataclasses import dataclass, field
4
+ from datetime import timedelta
5
+ from typing import Optional, Dict, Any, List
6
+ from temporalio import workflow
7
+ from temporalio.common import RetryPolicy
8
+ import asyncio
9
+
10
+ with workflow.unsafe.imports_passed_through():
11
+ from control_plane_api.app.activities.plan_generation_activities import (
12
+ generate_plan_activity,
13
+ store_plan_activity,
14
+ update_plan_generation_status,
15
+ ActivityGeneratePlanInput,
16
+ ActivityStorePlanInput,
17
+ ActivityUpdatePlanGenerationInput,
18
+ )
19
+
20
+
21
+ @dataclass
22
+ class PlanGenerationInput:
23
+ """Input for plan generation workflow"""
24
+ execution_id: str
25
+ organization_id: str
26
+ task_request: Dict[str, Any] # TaskPlanRequest as dict (for serialization)
27
+ user_metadata: Optional[Dict[str, Any]] = None
28
+ api_token: Optional[str] = None # API token for accessing resources
29
+
30
+ def __post_init__(self):
31
+ if self.user_metadata is None:
32
+ self.user_metadata = {}
33
+
34
+
35
+ @dataclass
36
+ class PlanGenerationState:
37
+ """Current state of plan generation for queries"""
38
+ status: str # "pending", "analyzing", "generating", "completed", "failed"
39
+ current_step: str = ""
40
+ error_message: Optional[str] = None
41
+ plan_json: Optional[Dict[str, Any]] = None
42
+ progress_percentage: int = 0
43
+
44
+ def to_dict(self) -> Dict[str, Any]:
45
+ """Convert state to dict for serialization"""
46
+ return {
47
+ "status": self.status,
48
+ "current_step": self.current_step,
49
+ "error_message": self.error_message,
50
+ "plan_json": self.plan_json,
51
+ "progress_percentage": self.progress_percentage,
52
+ }
53
+
54
+
55
+ @workflow.defn
56
+ class PlanGenerationWorkflow:
57
+ """
58
+ Workflow for generating a task plan asynchronously.
59
+
60
+ This workflow:
61
+ 1. Updates execution status to running
62
+ 2. Generates the plan using the planning strategy
63
+ 3. Stores the generated plan in the execution record
64
+ 4. Updates execution with results
65
+ 5. Supports queries for real-time state access
66
+ """
67
+
68
+ def __init__(self) -> None:
69
+ """Initialize workflow state"""
70
+ self._state = PlanGenerationState(status="pending")
71
+ self._lock = asyncio.Lock()
72
+
73
+ @workflow.query
74
+ def get_state(self) -> PlanGenerationState:
75
+ """Query handler: Get current plan generation state"""
76
+ return self._state
77
+
78
+ @workflow.run
79
+ async def run(self, input: PlanGenerationInput) -> Dict[str, Any]:
80
+ """
81
+ Run the plan generation workflow.
82
+
83
+ Args:
84
+ input: Workflow input with plan generation details
85
+
86
+ Returns:
87
+ Result dict with generated plan and metadata
88
+ """
89
+ workflow.logger.info(
90
+ f"Starting plan generation workflow",
91
+ extra={
92
+ "execution_id": input.execution_id,
93
+ "organization_id": input.organization_id,
94
+ "quick_mode": input.task_request.get("quick_mode", False),
95
+ }
96
+ )
97
+
98
+ try:
99
+ # Step 1: Update execution status to running
100
+ async with self._lock:
101
+ self._state.status = "running"
102
+ self._state.current_step = "Initializing plan generation"
103
+ self._state.progress_percentage = 10
104
+
105
+ await workflow.execute_activity(
106
+ update_plan_generation_status,
107
+ ActivityUpdatePlanGenerationInput(
108
+ execution_id=input.execution_id,
109
+ status="running",
110
+ current_step="Initializing plan generation",
111
+ ),
112
+ start_to_close_timeout=timedelta(seconds=30),
113
+ retry_policy=RetryPolicy(
114
+ maximum_attempts=3,
115
+ initial_interval=timedelta(seconds=1),
116
+ maximum_interval=timedelta(seconds=10),
117
+ ),
118
+ )
119
+
120
+ # Step 2: Analyze and generate plan
121
+ async with self._lock:
122
+ self._state.status = "analyzing"
123
+ self._state.current_step = "Analyzing task requirements"
124
+ self._state.progress_percentage = 30
125
+
126
+ await workflow.execute_activity(
127
+ update_plan_generation_status,
128
+ ActivityUpdatePlanGenerationInput(
129
+ execution_id=input.execution_id,
130
+ status="analyzing",
131
+ current_step="Analyzing task requirements",
132
+ ),
133
+ start_to_close_timeout=timedelta(seconds=30),
134
+ )
135
+
136
+ # Generate the plan (this is the long-running step: 1-3 minutes)
137
+ async with self._lock:
138
+ self._state.status = "generating"
139
+ self._state.current_step = "Generating execution plan"
140
+ self._state.progress_percentage = 50
141
+
142
+ await workflow.execute_activity(
143
+ update_plan_generation_status,
144
+ ActivityUpdatePlanGenerationInput(
145
+ execution_id=input.execution_id,
146
+ status="generating",
147
+ current_step="Generating execution plan",
148
+ ),
149
+ start_to_close_timeout=timedelta(seconds=30),
150
+ )
151
+
152
+ workflow.logger.info("Executing plan generation activity")
153
+ plan_result = await workflow.execute_activity(
154
+ generate_plan_activity,
155
+ ActivityGeneratePlanInput(
156
+ execution_id=input.execution_id,
157
+ organization_id=input.organization_id,
158
+ task_request=input.task_request,
159
+ api_token=input.api_token,
160
+ ),
161
+ start_to_close_timeout=timedelta(minutes=10), # Plan generation can take 1-3 minutes, allow extra buffer
162
+ # No heartbeat timeout - plan generation is a long-running operation
163
+ retry_policy=RetryPolicy(
164
+ maximum_attempts=2,
165
+ initial_interval=timedelta(seconds=5),
166
+ maximum_interval=timedelta(seconds=30),
167
+ ),
168
+ )
169
+
170
+ async with self._lock:
171
+ self._state.plan_json = plan_result.get("plan_json")
172
+ self._state.progress_percentage = 80
173
+
174
+ workflow.logger.info("Plan generated successfully")
175
+
176
+ # Step 3: Store the plan
177
+ async with self._lock:
178
+ self._state.current_step = "Storing generated plan"
179
+ self._state.progress_percentage = 90
180
+
181
+ await workflow.execute_activity(
182
+ store_plan_activity,
183
+ ActivityStorePlanInput(
184
+ execution_id=input.execution_id,
185
+ plan_json=plan_result["plan_json"],
186
+ metadata=plan_result.get("metadata", {}),
187
+ ),
188
+ start_to_close_timeout=timedelta(seconds=30),
189
+ retry_policy=RetryPolicy(
190
+ maximum_attempts=3,
191
+ initial_interval=timedelta(seconds=1),
192
+ maximum_interval=timedelta(seconds=10),
193
+ ),
194
+ )
195
+
196
+ workflow.logger.info("Plan stored successfully")
197
+
198
+ # Step 4: Mark as completed
199
+ async with self._lock:
200
+ self._state.status = "completed"
201
+ self._state.current_step = "Plan generation complete"
202
+ self._state.progress_percentage = 100
203
+
204
+ await workflow.execute_activity(
205
+ update_plan_generation_status,
206
+ ActivityUpdatePlanGenerationInput(
207
+ execution_id=input.execution_id,
208
+ status="completed",
209
+ current_step="Plan generation complete",
210
+ plan_json=plan_result["plan_json"],
211
+ ),
212
+ start_to_close_timeout=timedelta(seconds=30),
213
+ )
214
+
215
+ workflow.logger.info(
216
+ "Plan generation workflow completed successfully",
217
+ extra={"execution_id": input.execution_id}
218
+ )
219
+
220
+ return {
221
+ "status": "completed",
222
+ "plan_json": plan_result["plan_json"],
223
+ "metadata": plan_result.get("metadata", {}),
224
+ }
225
+
226
+ except Exception as e:
227
+ workflow.logger.error(
228
+ f"Plan generation workflow failed: {str(e)}",
229
+ extra={"execution_id": input.execution_id},
230
+ exc_info=True,
231
+ )
232
+
233
+ async with self._lock:
234
+ self._state.status = "failed"
235
+ self._state.error_message = str(e)
236
+
237
+ # Update execution status to failed
238
+ try:
239
+ await workflow.execute_activity(
240
+ update_plan_generation_status,
241
+ ActivityUpdatePlanGenerationInput(
242
+ execution_id=input.execution_id,
243
+ status="failed",
244
+ error_message=str(e),
245
+ ),
246
+ start_to_close_timeout=timedelta(seconds=30),
247
+ )
248
+ except Exception as update_error:
249
+ workflow.logger.error(f"Failed to update error status: {str(update_error)}")
250
+
251
+ return {
252
+ "status": "failed",
253
+ "error": str(e),
254
+ }
@@ -0,0 +1,442 @@
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
+ import asyncio
8
+
9
+ with workflow.unsafe.imports_passed_through():
10
+ from control_plane_api.app.activities.team_activities import (
11
+ get_team_agents,
12
+ execute_team_coordination,
13
+ update_execution_status,
14
+ ActivityGetTeamAgentsInput,
15
+ ActivityExecuteTeamInput,
16
+ ActivityUpdateExecutionInput,
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class TeamExecutionInput:
22
+ """Input for team execution workflow"""
23
+ execution_id: str
24
+ team_id: str
25
+ organization_id: str
26
+ prompt: str
27
+ system_prompt: Optional[str] = None
28
+ model_id: Optional[str] = None
29
+ model_config: dict = None
30
+ team_config: dict = None
31
+ mcp_servers: dict = None
32
+ user_metadata: dict = None
33
+
34
+ def __post_init__(self):
35
+ if self.model_config is None:
36
+ self.model_config = {}
37
+ if self.team_config is None:
38
+ self.team_config = {}
39
+ if self.mcp_servers is None:
40
+ self.mcp_servers = {}
41
+ if self.user_metadata is None:
42
+ self.user_metadata = {}
43
+
44
+
45
+ @dataclass
46
+ class ChatMessage:
47
+ """Represents a message in the conversation"""
48
+ role: str # "user", "assistant", "system", "tool"
49
+ content: str
50
+ timestamp: str
51
+ tool_name: Optional[str] = None
52
+ tool_input: Optional[Dict[str, Any]] = None
53
+ tool_output: Optional[Dict[str, Any]] = None
54
+ # User attribution for messages
55
+ user_id: Optional[str] = None
56
+ user_name: Optional[str] = None
57
+ user_email: Optional[str] = None
58
+ user_avatar: Optional[str] = None
59
+
60
+
61
+ @dataclass
62
+ class ExecutionState:
63
+ """Current state of the execution for queries"""
64
+ status: str # "pending", "running", "waiting_for_input", "paused", "completed", "failed"
65
+ messages: List[ChatMessage] = field(default_factory=list)
66
+ current_response: str = ""
67
+ error_message: Optional[str] = None
68
+ usage: Dict[str, Any] = field(default_factory=dict)
69
+ metadata: Dict[str, Any] = field(default_factory=dict)
70
+ is_waiting_for_input: bool = False
71
+ should_complete: bool = False
72
+ is_paused: bool = False
73
+
74
+
75
+ @workflow.defn
76
+ class TeamExecutionWorkflow:
77
+ """
78
+ Workflow for executing a team of agents with HITL support.
79
+
80
+ This workflow:
81
+ 1. Gets team agents
82
+ 2. Coordinates execution across agents
83
+ 3. Aggregates results
84
+ 4. Updates execution status
85
+ 5. Supports queries for real-time state access
86
+ 6. Supports signals for adding followup messages
87
+ """
88
+
89
+ def __init__(self) -> None:
90
+ """Initialize workflow state"""
91
+ self._state = ExecutionState(status="pending")
92
+ self._lock = asyncio.Lock()
93
+ self._new_message_count = 0
94
+ self._processed_message_count = 0
95
+
96
+ @workflow.query
97
+ def get_state(self) -> ExecutionState:
98
+ """Query handler: Get current execution state including messages and status"""
99
+ return self._state
100
+
101
+ @workflow.signal
102
+ async def add_message(self, message: ChatMessage) -> None:
103
+ """
104
+ Signal handler: Add a message to the conversation.
105
+ This allows clients to send followup messages while the workflow is running.
106
+ """
107
+ async with self._lock:
108
+ self._state.messages.append(message)
109
+ self._new_message_count += 1
110
+ self._state.is_waiting_for_input = False
111
+ workflow.logger.info(
112
+ f"Message added to team conversation",
113
+ extra={
114
+ "role": message.role,
115
+ "content_preview": message.content[:100] if message.content else "",
116
+ "total_messages": len(self._state.messages)
117
+ }
118
+ )
119
+
120
+ @workflow.signal
121
+ async def mark_as_done(self) -> None:
122
+ """
123
+ Signal handler: Mark the workflow as complete.
124
+ """
125
+ async with self._lock:
126
+ self._state.should_complete = True
127
+ self._state.is_waiting_for_input = False
128
+ workflow.logger.info("Team workflow marked as done by user")
129
+
130
+ @workflow.signal
131
+ async def pause_execution(self) -> None:
132
+ """
133
+ Signal handler: Pause the workflow execution.
134
+ This pauses the workflow - it will stop processing but remain active.
135
+ Resume can be called to continue execution.
136
+ """
137
+ async with self._lock:
138
+ if not self._state.is_paused:
139
+ self._state.is_paused = True
140
+ self._state.status = "paused"
141
+ workflow.logger.info("Team workflow paused by user")
142
+
143
+ @workflow.signal
144
+ async def resume_execution(self) -> None:
145
+ """
146
+ Signal handler: Resume a paused workflow execution.
147
+ This resumes the workflow from where it was paused.
148
+ """
149
+ async with self._lock:
150
+ if self._state.is_paused:
151
+ self._state.is_paused = False
152
+ # Restore previous status (either running or waiting_for_input)
153
+ self._state.status = "waiting_for_input" if self._state.is_waiting_for_input else "running"
154
+ workflow.logger.info("Team workflow resumed by user")
155
+
156
+ @workflow.run
157
+ async def run(self, input: TeamExecutionInput) -> dict:
158
+ """
159
+ Run the team execution workflow with HITL pattern.
160
+
161
+ This workflow implements a continuous conversation loop:
162
+ 1. Process the initial user message
163
+ 2. Execute team coordination and return response
164
+ 3. Wait for user input (signals)
165
+ 4. Process followup messages in a loop
166
+ 5. Only complete when user explicitly marks as done
167
+
168
+ Args:
169
+ input: Workflow input with team execution details
170
+
171
+ Returns:
172
+ Team execution result dict
173
+ """
174
+ workflow.logger.info(
175
+ f"Starting team execution workflow with HITL pattern",
176
+ extra={
177
+ "execution_id": input.execution_id,
178
+ "team_id": input.team_id,
179
+ "organization_id": input.organization_id,
180
+ }
181
+ )
182
+
183
+ # Initialize state with user's initial message
184
+ self._state.messages.append(ChatMessage(
185
+ role="user",
186
+ content=input.prompt,
187
+ timestamp=workflow.now().isoformat(),
188
+ ))
189
+ self._state.status = "running"
190
+ self._new_message_count = 1
191
+ self._processed_message_count = 0
192
+
193
+ try:
194
+ # Step 1: Update execution status to running
195
+ await workflow.execute_activity(
196
+ update_execution_status,
197
+ ActivityUpdateExecutionInput(
198
+ execution_id=input.execution_id,
199
+ status="running",
200
+ started_at=workflow.now().isoformat(),
201
+ execution_metadata={
202
+ "workflow_started": True,
203
+ "hitl_enabled": True,
204
+ },
205
+ ),
206
+ start_to_close_timeout=timedelta(seconds=30),
207
+ )
208
+
209
+ # Step 2: Get team agents once at the beginning
210
+ workflow.logger.info(
211
+ f"[WORKFLOW] About to call get_team_agents",
212
+ extra={
213
+ "team_id": input.team_id,
214
+ "organization_id": input.organization_id,
215
+ }
216
+ )
217
+
218
+ team_agents = await workflow.execute_activity(
219
+ get_team_agents,
220
+ ActivityGetTeamAgentsInput(
221
+ team_id=input.team_id,
222
+ organization_id=input.organization_id,
223
+ ),
224
+ start_to_close_timeout=timedelta(seconds=30),
225
+ )
226
+
227
+ workflow.logger.info(
228
+ f"[WORKFLOW] get_team_agents returned",
229
+ extra={
230
+ "result": team_agents,
231
+ "agents_count": len(team_agents.get("agents", [])) if team_agents else 0,
232
+ }
233
+ )
234
+
235
+ if not team_agents.get("agents"):
236
+ workflow.logger.error(
237
+ f"[WORKFLOW] NO AGENTS RETURNED!",
238
+ extra={
239
+ "team_agents": team_agents,
240
+ "team_id": input.team_id,
241
+ "organization_id": input.organization_id,
242
+ }
243
+ )
244
+ raise ValueError("No agents found in team")
245
+
246
+ # HITL Conversation Loop - Continue until user marks as done
247
+ conversation_turn = 0
248
+ while not self._state.should_complete:
249
+ # Check if workflow is paused - wait until resumed
250
+ if self._state.is_paused:
251
+ workflow.logger.info("Team workflow is paused, waiting for resume signal")
252
+ await workflow.wait_condition(
253
+ lambda: not self._state.is_paused or self._state.should_complete,
254
+ timeout=timedelta(hours=24)
255
+ )
256
+ if self._state.should_complete:
257
+ break
258
+ workflow.logger.info("Team workflow resumed, continuing execution")
259
+
260
+ conversation_turn += 1
261
+ workflow.logger.info(
262
+ f"Starting team conversation turn {conversation_turn}",
263
+ extra={"turn": conversation_turn, "message_count": len(self._state.messages)}
264
+ )
265
+
266
+ # Get the latest user message
267
+ latest_message = self._state.messages[-1] if self._state.messages else None
268
+ latest_prompt = latest_message.content if latest_message and latest_message.role == "user" else input.prompt
269
+
270
+ # Step 3: Execute team coordination
271
+ team_result = await workflow.execute_activity(
272
+ execute_team_coordination,
273
+ ActivityExecuteTeamInput(
274
+ execution_id=input.execution_id,
275
+ team_id=input.team_id,
276
+ organization_id=input.organization_id,
277
+ prompt=latest_prompt,
278
+ system_prompt=input.system_prompt,
279
+ agents=team_agents["agents"],
280
+ team_config=input.team_config,
281
+ mcp_servers=input.mcp_servers, # Pass MCP servers
282
+ session_id=input.execution_id, # Use execution_id as session_id for 1:1 mapping
283
+ user_id=input.user_metadata.get("user_id") if input.user_metadata else None, # Extract user_id from JWT
284
+ ),
285
+ start_to_close_timeout=timedelta(minutes=30), # Teams can take longer
286
+ )
287
+
288
+ # Update state with team response
289
+ if team_result.get("response"):
290
+ async with self._lock:
291
+ self._state.messages.append(ChatMessage(
292
+ role="assistant",
293
+ content=team_result["response"],
294
+ timestamp=workflow.now().isoformat(),
295
+ ))
296
+ self._state.current_response = team_result["response"]
297
+ self._processed_message_count += 1
298
+
299
+ # Update usage and metadata (accumulate across turns)
300
+ if team_result.get("usage"):
301
+ current_usage = self._state.usage
302
+ new_usage = team_result.get("usage", {})
303
+ self._state.usage = {
304
+ "input_tokens": current_usage.get("input_tokens", 0) + new_usage.get("input_tokens", 0),
305
+ "output_tokens": current_usage.get("output_tokens", 0) + new_usage.get("output_tokens", 0),
306
+ "total_tokens": current_usage.get("total_tokens", 0) + new_usage.get("total_tokens", 0),
307
+ }
308
+
309
+ # Update metadata
310
+ self._state.metadata.update({
311
+ "agent_count": len(team_agents["agents"]),
312
+ "coordination_type": team_result.get("coordination_type"),
313
+ "conversation_turns": conversation_turn,
314
+ })
315
+
316
+ # Check if team execution failed
317
+ if not team_result.get("success"):
318
+ self._state.status = "failed"
319
+ self._state.error_message = team_result.get("error")
320
+ break
321
+
322
+ # Update execution status to waiting_for_input
323
+ self._state.status = "waiting_for_input"
324
+ self._state.is_waiting_for_input = True
325
+
326
+ # Update database to reflect waiting state
327
+ await workflow.execute_activity(
328
+ update_execution_status,
329
+ ActivityUpdateExecutionInput(
330
+ execution_id=input.execution_id,
331
+ status="waiting_for_input",
332
+ response=self._state.current_response,
333
+ usage=self._state.usage,
334
+ execution_metadata={
335
+ **self._state.metadata,
336
+ "conversation_turns": conversation_turn,
337
+ "waiting_for_user": True,
338
+ },
339
+ ),
340
+ start_to_close_timeout=timedelta(seconds=30),
341
+ )
342
+
343
+ workflow.logger.info(
344
+ f"Waiting for user input after team turn {conversation_turn}",
345
+ extra={"turn": conversation_turn}
346
+ )
347
+
348
+ # Wait for either new message, mark as done, or pause signal
349
+ await workflow.wait_condition(
350
+ lambda: self._new_message_count > self._processed_message_count or self._state.should_complete or self._state.is_paused,
351
+ timeout=timedelta(hours=24)
352
+ )
353
+
354
+ if self._state.should_complete:
355
+ workflow.logger.info("User marked team workflow as done")
356
+ break
357
+
358
+ # If paused while waiting, loop back to check pause condition at top of while loop
359
+ if self._state.is_paused:
360
+ workflow.logger.info("Team workflow paused while waiting for input")
361
+ continue
362
+
363
+ # Continue loop to process new message
364
+ self._state.status = "running"
365
+
366
+ # Conversation complete - finalize workflow
367
+ final_status = "failed" if self._state.status == "failed" else "completed"
368
+ self._state.status = final_status
369
+
370
+ await workflow.execute_activity(
371
+ update_execution_status,
372
+ ActivityUpdateExecutionInput(
373
+ execution_id=input.execution_id,
374
+ status=final_status,
375
+ completed_at=workflow.now().isoformat(),
376
+ response=self._state.current_response,
377
+ error_message=self._state.error_message,
378
+ usage=self._state.usage,
379
+ execution_metadata={
380
+ **self._state.metadata,
381
+ "workflow_completed": True,
382
+ "total_conversation_turns": conversation_turn,
383
+ },
384
+ ),
385
+ start_to_close_timeout=timedelta(seconds=30),
386
+ )
387
+
388
+ workflow.logger.info(
389
+ f"Team execution workflow completed with HITL",
390
+ extra={
391
+ "execution_id": input.execution_id,
392
+ "status": final_status,
393
+ "conversation_turns": conversation_turn,
394
+ }
395
+ )
396
+
397
+ return {
398
+ "success": final_status == "completed",
399
+ "execution_id": input.execution_id,
400
+ "status": final_status,
401
+ "response": self._state.current_response,
402
+ "usage": self._state.usage,
403
+ "conversation_turns": conversation_turn,
404
+ }
405
+
406
+ except Exception as e:
407
+ # Update state with error
408
+ self._state.status = "failed"
409
+ self._state.error_message = str(e)
410
+ self._state.metadata["error_type"] = type(e).__name__
411
+
412
+ workflow.logger.error(
413
+ f"Team execution workflow failed",
414
+ extra={
415
+ "execution_id": input.execution_id,
416
+ "error": str(e),
417
+ }
418
+ )
419
+
420
+ # Update execution as failed
421
+ try:
422
+ await workflow.execute_activity(
423
+ update_execution_status,
424
+ ActivityUpdateExecutionInput(
425
+ execution_id=input.execution_id,
426
+ status="failed",
427
+ completed_at=workflow.now().isoformat(),
428
+ error_message=f"Workflow error: {str(e)}",
429
+ execution_metadata={
430
+ "workflow_error": True,
431
+ "error_type": type(e).__name__,
432
+ },
433
+ ),
434
+ start_to_close_timeout=timedelta(seconds=30),
435
+ )
436
+ except Exception as update_error:
437
+ workflow.logger.error(
438
+ f"Failed to update status after error",
439
+ extra={"error": str(update_error)}
440
+ )
441
+
442
+ raise