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,520 @@
1
+ """Agent 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.agent_activities import (
11
+ execute_agent_llm,
12
+ update_execution_status,
13
+ update_agent_status,
14
+ ActivityExecuteAgentInput,
15
+ ActivityUpdateExecutionInput,
16
+ ActivityUpdateAgentInput,
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class AgentExecutionInput:
22
+ """Input for agent execution workflow"""
23
+ execution_id: str
24
+ agent_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
+ agent_config: dict = None
31
+ mcp_servers: dict = None # MCP servers configuration
32
+ user_metadata: dict = None
33
+ runtime_type: str = "default" # "default" (Agno) or "claude_code"
34
+ control_plane_url: Optional[str] = None # Control Plane URL for fetching credentials/secrets
35
+ api_key: Optional[str] = None # API key for authentication
36
+ initial_message_timestamp: Optional[str] = None # Real-time timestamp for initial message
37
+ graph_api_url: Optional[str] = None # Context graph API URL for memory tools
38
+ dataset_name: Optional[str] = None # Dataset name for memory scoping (environment name)
39
+
40
+ def __post_init__(self):
41
+ if self.model_config is None:
42
+ self.model_config = {}
43
+ if self.agent_config is None:
44
+ self.agent_config = {}
45
+ if self.mcp_servers is None:
46
+ self.mcp_servers = {}
47
+ if self.user_metadata is None:
48
+ self.user_metadata = {}
49
+
50
+
51
+ @dataclass
52
+ class TeamExecutionInput:
53
+ """Input for team execution workflow (uses same workflow as agent)"""
54
+ execution_id: str
55
+ team_id: str
56
+ organization_id: str
57
+ prompt: str
58
+ system_prompt: Optional[str] = None
59
+ model_id: Optional[str] = None
60
+ model_config: dict = None
61
+ team_config: dict = None
62
+ mcp_servers: dict = None # MCP servers configuration
63
+ user_metadata: dict = None
64
+ runtime_type: str = "default" # "default" (Agno) or "claude_code"
65
+ control_plane_url: Optional[str] = None # Control Plane URL for fetching credentials/secrets
66
+ api_key: Optional[str] = None # API key for authentication
67
+ initial_message_timestamp: Optional[str] = None # Real-time timestamp for initial message
68
+ graph_api_url: Optional[str] = None # Context graph API URL for memory tools
69
+ dataset_name: Optional[str] = None # Dataset name for memory scoping (environment name)
70
+
71
+ def __post_init__(self):
72
+ if self.model_config is None:
73
+ self.model_config = {}
74
+ if self.team_config is None:
75
+ self.team_config = {}
76
+ if self.mcp_servers is None:
77
+ self.mcp_servers = {}
78
+ if self.user_metadata is None:
79
+ self.user_metadata = {}
80
+
81
+ def to_agent_input(self) -> AgentExecutionInput:
82
+ """Convert TeamExecutionInput to AgentExecutionInput for workflow reuse"""
83
+ return AgentExecutionInput(
84
+ execution_id=self.execution_id,
85
+ agent_id=self.team_id, # Use team_id as agent_id
86
+ organization_id=self.organization_id,
87
+ prompt=self.prompt,
88
+ system_prompt=self.system_prompt,
89
+ model_id=self.model_id,
90
+ model_config=self.model_config,
91
+ agent_config=self.team_config,
92
+ mcp_servers=self.mcp_servers,
93
+ user_metadata=self.user_metadata,
94
+ runtime_type=self.runtime_type,
95
+ control_plane_url=self.control_plane_url,
96
+ api_key=self.api_key,
97
+ initial_message_timestamp=self.initial_message_timestamp,
98
+ graph_api_url=self.graph_api_url,
99
+ dataset_name=self.dataset_name,
100
+ )
101
+
102
+
103
+ @dataclass
104
+ class ChatMessage:
105
+ """Represents a message in the conversation"""
106
+ role: str # "user", "assistant", "system", "tool"
107
+ content: str
108
+ timestamp: str
109
+ tool_name: Optional[str] = None
110
+ tool_input: Optional[Dict[str, Any]] = None
111
+ tool_output: Optional[Dict[str, Any]] = None
112
+ message_id: Optional[str] = None # Unique identifier for deduplication
113
+ # User attribution for messages
114
+ user_id: Optional[str] = None
115
+ user_name: Optional[str] = None
116
+ user_email: Optional[str] = None
117
+ user_avatar: Optional[str] = None
118
+
119
+
120
+ @dataclass
121
+ class ExecutionState:
122
+ """Current state of the execution for queries"""
123
+ status: str # "pending", "running", "waiting_for_input", "paused", "completed", "failed"
124
+ messages: List[ChatMessage] = field(default_factory=list)
125
+ current_response: str = ""
126
+ error_message: Optional[str] = None
127
+ usage: Dict[str, Any] = field(default_factory=dict)
128
+ metadata: Dict[str, Any] = field(default_factory=dict)
129
+ is_waiting_for_input: bool = False
130
+ should_complete: bool = False
131
+ is_paused: bool = False
132
+
133
+
134
+ @workflow.defn
135
+ class AgentExecutionWorkflow:
136
+ """
137
+ Workflow for executing an agent with LLM with Temporal message passing support.
138
+
139
+ This workflow:
140
+ 1. Updates execution status to running
141
+ 2. Executes the agent's LLM call
142
+ 3. Updates execution with results
143
+ 4. Updates agent status
144
+ 5. Supports queries for real-time state access
145
+ 6. Supports signals for adding followup messages
146
+ """
147
+
148
+ def __init__(self) -> None:
149
+ """Initialize workflow state"""
150
+ self._state = ExecutionState(status="pending")
151
+ self._lock = asyncio.Lock()
152
+ self._new_message_count = 0
153
+ self._processed_message_count = 0
154
+
155
+ @workflow.query
156
+ def get_state(self) -> ExecutionState:
157
+ """Query handler: Get current execution state including messages and status"""
158
+ return self._state
159
+
160
+ @workflow.signal
161
+ async def add_message(self, message: ChatMessage) -> None:
162
+ """
163
+ Signal handler: Add a message to the conversation.
164
+ This allows clients to send followup messages while the workflow is running.
165
+ The workflow will wake up and process this message.
166
+ """
167
+ async with self._lock:
168
+ self._state.messages.append(message)
169
+ self._new_message_count += 1
170
+ self._state.is_waiting_for_input = False
171
+ workflow.logger.info(
172
+ f"Message added to conversation",
173
+ extra={
174
+ "role": message.role,
175
+ "content_preview": message.content[:100] if message.content else "",
176
+ "total_messages": len(self._state.messages)
177
+ }
178
+ )
179
+
180
+ @workflow.signal
181
+ async def mark_as_done(self) -> None:
182
+ """
183
+ Signal handler: Mark the workflow as complete.
184
+ This signals that the user is done with the conversation and the workflow should complete.
185
+ """
186
+ async with self._lock:
187
+ self._state.should_complete = True
188
+ self._state.is_waiting_for_input = False
189
+ workflow.logger.info("Workflow marked as done by user")
190
+
191
+ @workflow.signal
192
+ async def pause_execution(self) -> None:
193
+ """
194
+ Signal handler: Pause the workflow execution.
195
+ This pauses the workflow - it will stop processing but remain active.
196
+ Resume can be called to continue execution.
197
+ """
198
+ async with self._lock:
199
+ if not self._state.is_paused:
200
+ self._state.is_paused = True
201
+ self._state.status = "paused"
202
+ workflow.logger.info("Workflow paused by user")
203
+
204
+ @workflow.signal
205
+ async def resume_execution(self) -> None:
206
+ """
207
+ Signal handler: Resume a paused workflow execution.
208
+ This resumes the workflow from where it was paused.
209
+ """
210
+ async with self._lock:
211
+ if self._state.is_paused:
212
+ self._state.is_paused = False
213
+ # Restore previous status (either running or waiting_for_input)
214
+ self._state.status = "waiting_for_input" if self._state.is_waiting_for_input else "running"
215
+ workflow.logger.info("Workflow resumed by user")
216
+
217
+ @workflow.run
218
+ async def run(self, input: AgentExecutionInput) -> dict:
219
+ """
220
+ Run the agent execution workflow with Human-in-the-Loop (HITL) pattern.
221
+
222
+ This workflow implements a continuous conversation loop:
223
+ 1. Process the initial user message
224
+ 2. Execute LLM and return response
225
+ 3. Wait for user input (signals)
226
+ 4. Process followup messages in a loop
227
+ 5. Only complete when user explicitly marks as done
228
+
229
+ Args:
230
+ input: Workflow input with execution details
231
+
232
+ Returns:
233
+ Execution result dict with response, usage, etc.
234
+ """
235
+ workflow.logger.info(
236
+ f"Starting agent execution workflow with HITL pattern",
237
+ extra={
238
+ "execution_id": input.execution_id,
239
+ "agent_id": input.agent_id,
240
+ "organization_id": input.organization_id,
241
+ }
242
+ )
243
+
244
+ # Initialize state with user's initial message
245
+ self._state.messages.append(ChatMessage(
246
+ role="user",
247
+ content=input.prompt,
248
+ timestamp=workflow.now().isoformat(),
249
+ ))
250
+ self._state.status = "running"
251
+ self._new_message_count = 1 # Initial message counts as a new message
252
+ self._processed_message_count = 0 # No messages processed yet (no response)
253
+
254
+ try:
255
+ # Step 1: Update execution status to running
256
+ await workflow.execute_activity(
257
+ update_execution_status,
258
+ ActivityUpdateExecutionInput(
259
+ execution_id=input.execution_id,
260
+ status="running",
261
+ started_at=workflow.now().isoformat(),
262
+ execution_metadata={
263
+ "workflow_started": True,
264
+ "has_mcp_servers": bool(input.mcp_servers),
265
+ "mcp_server_count": len(input.mcp_servers) if input.mcp_servers else 0,
266
+ "hitl_enabled": True,
267
+ },
268
+ ),
269
+ start_to_close_timeout=timedelta(seconds=30),
270
+ )
271
+
272
+ # Step 2: Update agent status to running
273
+ await workflow.execute_activity(
274
+ update_agent_status,
275
+ ActivityUpdateAgentInput(
276
+ agent_id=input.agent_id,
277
+ organization_id=input.organization_id,
278
+ status="running",
279
+ last_active_at=workflow.now().isoformat(),
280
+ ),
281
+ start_to_close_timeout=timedelta(seconds=30),
282
+ )
283
+
284
+ # HITL Conversation Loop - Continue until user marks as done
285
+ conversation_turn = 0
286
+ while not self._state.should_complete:
287
+ # Check if workflow is paused - wait until resumed
288
+ if self._state.is_paused:
289
+ workflow.logger.info("Workflow is paused, waiting for resume signal")
290
+ await workflow.wait_condition(
291
+ lambda: not self._state.is_paused or self._state.should_complete,
292
+ timeout=timedelta(hours=24)
293
+ )
294
+ if self._state.should_complete:
295
+ break
296
+ workflow.logger.info("Workflow resumed, continuing execution")
297
+
298
+ conversation_turn += 1
299
+
300
+ workflow.logger.info(
301
+ f"Starting conversation turn {conversation_turn}",
302
+ extra={"turn": conversation_turn, "message_count": len(self._state.messages)}
303
+ )
304
+
305
+ # Get the latest user message (last message added)
306
+ latest_message = self._state.messages[-1] if self._state.messages else None
307
+ latest_prompt = latest_message.content if latest_message and latest_message.role == "user" else input.prompt
308
+
309
+ # Execute agent LLM call with session_id - Agno handles conversation history automatically
310
+ llm_result = await workflow.execute_activity(
311
+ execute_agent_llm,
312
+ ActivityExecuteAgentInput(
313
+ execution_id=input.execution_id,
314
+ agent_id=input.agent_id,
315
+ organization_id=input.organization_id,
316
+ prompt=latest_prompt, # Current turn's prompt
317
+ system_prompt=input.system_prompt,
318
+ model_id=input.model_id,
319
+ model_config=input.model_config,
320
+ mcp_servers=input.mcp_servers,
321
+ session_id=input.execution_id, # Use execution_id as session_id for 1:1 mapping
322
+ user_id=input.user_metadata.get("user_id") if input.user_metadata else None,
323
+ control_plane_url=input.control_plane_url, # Pass Control Plane URL from workflow input
324
+ api_key=input.api_key, # Pass API key from workflow input
325
+ graph_api_url=input.graph_api_url, # Pass graph API URL for memory tools
326
+ dataset_name=input.dataset_name, # Pass dataset name for memory scoping
327
+ ),
328
+ start_to_close_timeout=timedelta(minutes=10),
329
+ )
330
+
331
+ # Update state with assistant response
332
+ if llm_result.get("response"):
333
+ async with self._lock:
334
+ self._state.messages.append(ChatMessage(
335
+ role="assistant",
336
+ content=llm_result["response"],
337
+ timestamp=workflow.now().isoformat(),
338
+ ))
339
+ self._state.current_response = llm_result["response"]
340
+ self._processed_message_count += 1
341
+
342
+ # Update usage and metadata (accumulate across turns)
343
+ if llm_result.get("usage"):
344
+ # Accumulate token usage across conversation turns
345
+ current_usage = self._state.usage
346
+ new_usage = llm_result.get("usage", {})
347
+ self._state.usage = {
348
+ "prompt_tokens": current_usage.get("prompt_tokens", 0) + new_usage.get("prompt_tokens", 0),
349
+ "completion_tokens": current_usage.get("completion_tokens", 0) + new_usage.get("completion_tokens", 0),
350
+ "total_tokens": current_usage.get("total_tokens", 0) + new_usage.get("total_tokens", 0),
351
+ }
352
+
353
+ # Update metadata with latest turn info
354
+ self._state.metadata.update({
355
+ "model": llm_result.get("model"),
356
+ "latest_finish_reason": llm_result.get("finish_reason"),
357
+ "mcp_tools_used": self._state.metadata.get("mcp_tools_used", 0) + llm_result.get("mcp_tools_used", 0),
358
+ "latest_run_id": llm_result.get("run_id"),
359
+ "conversation_turns": conversation_turn,
360
+ })
361
+
362
+ # Check if LLM call failed
363
+ if not llm_result.get("success"):
364
+ self._state.status = "failed"
365
+ self._state.error_message = llm_result.get("error")
366
+ break
367
+
368
+ # Update execution status to waiting_for_input
369
+ self._state.status = "waiting_for_input"
370
+ self._state.is_waiting_for_input = True
371
+
372
+ # Update database to reflect waiting state
373
+ await workflow.execute_activity(
374
+ update_execution_status,
375
+ ActivityUpdateExecutionInput(
376
+ execution_id=input.execution_id,
377
+ status="waiting_for_input",
378
+ response=self._state.current_response,
379
+ usage=self._state.usage,
380
+ execution_metadata={
381
+ **self._state.metadata,
382
+ "conversation_turns": conversation_turn,
383
+ "waiting_for_user": True,
384
+ },
385
+ ),
386
+ start_to_close_timeout=timedelta(seconds=30),
387
+ )
388
+
389
+ workflow.logger.info(
390
+ f"Waiting for user input after turn {conversation_turn}",
391
+ extra={"turn": conversation_turn}
392
+ )
393
+
394
+ # Wait for either:
395
+ # 1. New message from user (add_message signal)
396
+ # 2. User marks as done (mark_as_done signal)
397
+ # 3. User pauses execution (pause_execution signal)
398
+ # 4. Timeout (24 hours for long-running conversations)
399
+ await workflow.wait_condition(
400
+ lambda: self._new_message_count > self._processed_message_count or self._state.should_complete or self._state.is_paused,
401
+ timeout=timedelta(hours=24)
402
+ )
403
+
404
+ # Don't update processed count here - it will be updated after we add the assistant's response
405
+
406
+ if self._state.should_complete:
407
+ workflow.logger.info("User marked workflow as done")
408
+ break
409
+
410
+ # If paused while waiting, loop back to check pause condition at top of while loop
411
+ if self._state.is_paused:
412
+ workflow.logger.info("Workflow paused while waiting for input")
413
+ continue
414
+
415
+ # Continue loop to process new message
416
+ self._state.status = "running"
417
+
418
+ # Conversation complete - finalize workflow
419
+ final_status = "failed" if self._state.status == "failed" else "completed"
420
+ self._state.status = final_status
421
+
422
+ await workflow.execute_activity(
423
+ update_execution_status,
424
+ ActivityUpdateExecutionInput(
425
+ execution_id=input.execution_id,
426
+ status=final_status,
427
+ completed_at=workflow.now().isoformat(),
428
+ response=self._state.current_response,
429
+ error_message=self._state.error_message,
430
+ usage=self._state.usage,
431
+ execution_metadata={
432
+ **self._state.metadata,
433
+ "workflow_completed": True,
434
+ "total_conversation_turns": conversation_turn,
435
+ },
436
+ ),
437
+ start_to_close_timeout=timedelta(seconds=30),
438
+ )
439
+
440
+ # Update agent final status
441
+ agent_final_status = "completed" if final_status == "completed" else "failed"
442
+ await workflow.execute_activity(
443
+ update_agent_status,
444
+ ActivityUpdateAgentInput(
445
+ agent_id=input.agent_id,
446
+ organization_id=input.organization_id,
447
+ status=agent_final_status,
448
+ last_active_at=workflow.now().isoformat(),
449
+ error_message=self._state.error_message if final_status == "failed" else None,
450
+ ),
451
+ start_to_close_timeout=timedelta(seconds=30),
452
+ )
453
+
454
+ workflow.logger.info(
455
+ f"Agent execution workflow completed with HITL",
456
+ extra={
457
+ "execution_id": input.execution_id,
458
+ "status": final_status,
459
+ "conversation_turns": conversation_turn,
460
+ }
461
+ )
462
+
463
+ return {
464
+ "success": final_status == "completed",
465
+ "execution_id": input.execution_id,
466
+ "status": final_status,
467
+ "response": self._state.current_response,
468
+ "usage": self._state.usage,
469
+ "conversation_turns": conversation_turn,
470
+ }
471
+
472
+ except Exception as e:
473
+ # Update state with error
474
+ self._state.status = "failed"
475
+ self._state.error_message = str(e)
476
+ self._state.metadata["error_type"] = type(e).__name__
477
+
478
+ workflow.logger.error(
479
+ f"Agent execution workflow failed",
480
+ extra={
481
+ "execution_id": input.execution_id,
482
+ "error": str(e),
483
+ }
484
+ )
485
+
486
+ # Update execution as failed
487
+ try:
488
+ await workflow.execute_activity(
489
+ update_execution_status,
490
+ ActivityUpdateExecutionInput(
491
+ execution_id=input.execution_id,
492
+ status="failed",
493
+ completed_at=workflow.now().isoformat(),
494
+ error_message=f"Workflow error: {str(e)}",
495
+ execution_metadata={
496
+ "workflow_error": True,
497
+ "error_type": type(e).__name__,
498
+ },
499
+ ),
500
+ start_to_close_timeout=timedelta(seconds=30),
501
+ )
502
+
503
+ await workflow.execute_activity(
504
+ update_agent_status,
505
+ ActivityUpdateAgentInput(
506
+ agent_id=input.agent_id,
507
+ organization_id=input.organization_id,
508
+ status="failed",
509
+ last_active_at=workflow.now().isoformat(),
510
+ error_message=str(e),
511
+ ),
512
+ start_to_close_timeout=timedelta(seconds=30),
513
+ )
514
+ except Exception as update_error:
515
+ workflow.logger.error(
516
+ f"Failed to update status after error",
517
+ extra={"error": str(update_error)}
518
+ )
519
+
520
+ raise