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,656 @@
1
+ """
2
+ Task Planning Runner - Workflow execution with streaming and validation
3
+
4
+ This module handles workflow execution with:
5
+ - Step-by-step streaming events
6
+ - Tool call/result tracking
7
+ - Post-hook validation
8
+ - Retry logic with error feedback
9
+ """
10
+
11
+ from typing import Optional, Dict, Any, Callable, TYPE_CHECKING
12
+ import time
13
+ import uuid
14
+ import json
15
+ import re
16
+ import copy
17
+ from datetime import datetime
18
+ import structlog
19
+
20
+ from agno.agent import Agent
21
+ from agno.workflow import Workflow
22
+ from pydantic import ValidationError
23
+
24
+ from control_plane_api.app.models.task_planning import (
25
+ TaskPlanRequest,
26
+ TaskPlanResponse,
27
+ AnalysisAndSelectionOutput,
28
+ )
29
+ from .hooks import (
30
+ validate_step1_output,
31
+ validate_step2_output,
32
+ HallucinatedIdError,
33
+ OutputValidationError,
34
+ )
35
+
36
+ if TYPE_CHECKING:
37
+ from control_plane_api.app.lib.planning_tools.agno_toolkit import PlanningToolkit
38
+
39
+ logger = structlog.get_logger()
40
+
41
+
42
+ # ============================================================================
43
+ # Step Configuration
44
+ # ============================================================================
45
+
46
+ STEP_DESCRIPTIONS = {
47
+ 1: "Discovering available agents and teams in your organization and selecting the best match for your task",
48
+ 2: "Creating detailed execution plan with cost estimates, risks, and success criteria"
49
+ }
50
+
51
+ STEP_STAGE_NAMES = {
52
+ 1: "analyzing",
53
+ 2: "generating"
54
+ }
55
+
56
+ STEP_PROGRESS_MAP = {
57
+ 1: 50,
58
+ 2: 95
59
+ }
60
+
61
+
62
+ # ============================================================================
63
+ # Tool Wrapper for Event Streaming
64
+ # ============================================================================
65
+
66
+ def create_tool_wrapper(
67
+ tool: Any,
68
+ publish_event: Callable,
69
+ step_number: int
70
+ ) -> Any:
71
+ """
72
+ Wrap a tool to emit events before/after execution.
73
+
74
+ Args:
75
+ tool: The Agno tool to wrap
76
+ publish_event: Callback to emit streaming events
77
+ step_number: Current workflow step number
78
+
79
+ Returns:
80
+ Wrapped tool that emits events
81
+ """
82
+ # Get the original function
83
+ if hasattr(tool, 'entrypoint'):
84
+ original_func = tool.entrypoint
85
+ elif hasattr(tool, 'function'):
86
+ original_func = tool.function
87
+ elif callable(tool):
88
+ original_func = tool
89
+ else:
90
+ return tool
91
+
92
+ def wrapped_function(*args, **kwargs):
93
+ tool_id = str(uuid.uuid4())
94
+ tool_name = getattr(tool, 'name', getattr(original_func, '__name__', 'unknown'))
95
+
96
+ logger.info(
97
+ "tool_call_started",
98
+ tool_name=tool_name,
99
+ step=step_number
100
+ )
101
+
102
+ # Handle Agno's nested args/kwargs
103
+ if 'args' in kwargs:
104
+ nested_args = kwargs.pop('args')
105
+ if isinstance(nested_args, list) and not args:
106
+ args = tuple(nested_args)
107
+
108
+ if 'kwargs' in kwargs:
109
+ nested_kwargs = kwargs.pop('kwargs')
110
+ if isinstance(nested_kwargs, dict):
111
+ kwargs.update(nested_kwargs)
112
+
113
+ # Emit tool_call event
114
+ try:
115
+ publish_event({
116
+ "event": "tool_call",
117
+ "data": {
118
+ "tool_id": tool_id,
119
+ "tool_name": tool_name,
120
+ "arguments": {"args": list(args), **kwargs},
121
+ "step": step_number,
122
+ "timestamp": datetime.now().isoformat()
123
+ }
124
+ })
125
+ except Exception as e:
126
+ logger.warning("failed_to_emit_tool_call", error=str(e))
127
+
128
+ start_time = time.time()
129
+
130
+ try:
131
+ result = original_func(*args, **kwargs)
132
+ duration = time.time() - start_time
133
+
134
+ # Emit success event
135
+ try:
136
+ publish_event({
137
+ "event": "tool_result",
138
+ "data": {
139
+ "tool_id": tool_id,
140
+ "tool_name": tool_name,
141
+ "status": "success",
142
+ "result": str(result)[:1000],
143
+ "duration": duration,
144
+ "step": step_number,
145
+ "timestamp": datetime.now().isoformat()
146
+ }
147
+ })
148
+ except Exception as e:
149
+ logger.warning("failed_to_emit_tool_result", error=str(e))
150
+
151
+ return result
152
+
153
+ except Exception as e:
154
+ duration = time.time() - start_time
155
+
156
+ try:
157
+ publish_event({
158
+ "event": "tool_result",
159
+ "data": {
160
+ "tool_id": tool_id,
161
+ "tool_name": tool_name,
162
+ "status": "failed",
163
+ "error": str(e),
164
+ "duration": duration,
165
+ "step": step_number,
166
+ "timestamp": datetime.now().isoformat()
167
+ }
168
+ })
169
+ except Exception:
170
+ pass
171
+
172
+ raise
173
+
174
+ # Create wrapped copy
175
+ try:
176
+ wrapped_tool = copy.copy(tool)
177
+ if hasattr(tool, 'entrypoint'):
178
+ wrapped_tool.entrypoint = wrapped_function
179
+ elif hasattr(tool, 'function'):
180
+ wrapped_tool.function = wrapped_function
181
+ else:
182
+ return wrapped_function
183
+ return wrapped_tool
184
+ except Exception:
185
+ return wrapped_function
186
+
187
+
188
+ # ============================================================================
189
+ # JSON Extraction from Mixed Content
190
+ # ============================================================================
191
+
192
+ def extract_json_from_content(content: str) -> dict:
193
+ """
194
+ Extract JSON object from mixed text content.
195
+
196
+ Handles various LLM output patterns:
197
+ 1. Pure JSON
198
+ 2. Markdown code blocks
199
+ 3. Text with inline JSON
200
+
201
+ Args:
202
+ content: Raw string content from LLM
203
+
204
+ Returns:
205
+ Parsed JSON as dict
206
+
207
+ Raises:
208
+ ValueError: If no valid JSON found
209
+ """
210
+ cleaned = content.strip()
211
+
212
+ # Strategy 1: Direct parse
213
+ try:
214
+ return json.loads(cleaned)
215
+ except json.JSONDecodeError:
216
+ pass
217
+
218
+ # Strategy 2: Markdown code block
219
+ if '```' in cleaned:
220
+ match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', cleaned, re.DOTALL)
221
+ if match:
222
+ try:
223
+ return json.loads(match.group(1))
224
+ except json.JSONDecodeError:
225
+ pass
226
+
227
+ # Strategy 3: First { to last }
228
+ first_brace = cleaned.find('{')
229
+ last_brace = cleaned.rfind('}')
230
+ if first_brace != -1 and last_brace > first_brace:
231
+ try:
232
+ return json.loads(cleaned[first_brace:last_brace + 1])
233
+ except json.JSONDecodeError:
234
+ pass
235
+
236
+ # Strategy 4: Find complete JSON objects
237
+ for match in re.finditer(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', cleaned, re.DOTALL):
238
+ try:
239
+ return json.loads(match.group(0))
240
+ except json.JSONDecodeError:
241
+ continue
242
+
243
+ raise ValueError(f"Could not extract JSON from: {cleaned[:300]}")
244
+
245
+
246
+ # ============================================================================
247
+ # Step Execution with Validation
248
+ # ============================================================================
249
+
250
+ def execute_step(
251
+ step: Agent,
252
+ input_data: str,
253
+ publish_event: Callable,
254
+ step_number: int,
255
+ outer_context: Optional[Dict[str, Any]] = None,
256
+ max_retries: int = 2
257
+ ) -> Any:
258
+ """
259
+ Execute a workflow step with validation and retry logic.
260
+
261
+ Args:
262
+ step: The Agno Agent to execute
263
+ input_data: Input string for the agent
264
+ publish_event: Callback for streaming events
265
+ step_number: Current step number
266
+ outer_context: Context for validation
267
+ max_retries: Maximum retry attempts
268
+
269
+ Returns:
270
+ Step output (validated)
271
+
272
+ Raises:
273
+ ValueError: If validation fails after all retries
274
+ """
275
+ original_tools = None
276
+
277
+ for attempt in range(max_retries):
278
+ try:
279
+ # Wrap tools for event tracking
280
+ if hasattr(step, 'tools') and step.tools and original_tools is None:
281
+ original_tools = step.tools
282
+ step.tools = [
283
+ create_tool_wrapper(tool, publish_event, step_number)
284
+ for tool in original_tools
285
+ ]
286
+
287
+ logger.info(
288
+ "executing_step",
289
+ step=step_number,
290
+ step_name=step.name,
291
+ attempt=attempt + 1
292
+ )
293
+
294
+ # Execute step
295
+ result = step.run(input_data)
296
+ content = result.content if hasattr(result, 'content') else result
297
+
298
+ # Handle None output
299
+ if content is None:
300
+ raise ValueError(f"Step {step_number} returned None")
301
+
302
+ # Parse string output if needed
303
+ if isinstance(content, str) and hasattr(step, 'output_schema'):
304
+ logger.warning("parsing_string_output", step=step_number)
305
+ content_dict = extract_json_from_content(content)
306
+ content = step.output_schema.model_validate(content_dict)
307
+
308
+ # Post-hook validation
309
+ if step_number == 1 and hasattr(content, 'model_dump'):
310
+ content_dict = content.model_dump() if hasattr(content, 'model_dump') else vars(content)
311
+ validated = validate_step1_output(content_dict, outer_context)
312
+ # Update content with validated values
313
+ for key, value in validated.items():
314
+ if hasattr(content, key):
315
+ setattr(content, key, value)
316
+
317
+ elif step_number == 2 and hasattr(content, 'model_dump'):
318
+ content_dict = content.model_dump() if hasattr(content, 'model_dump') else vars(content)
319
+ validate_step2_output(content_dict)
320
+
321
+ # Emit reasoning if available
322
+ if hasattr(result, 'messages') and result.messages:
323
+ for message in result.messages:
324
+ if hasattr(message, 'role') and message.role == 'assistant':
325
+ if hasattr(message, 'content') and message.content:
326
+ text = str(message.content)
327
+ if len(text) > 20:
328
+ publish_event({
329
+ "event": "thinking",
330
+ "data": {
331
+ "content": text,
332
+ "step": step_number,
333
+ "step_name": step.name,
334
+ "timestamp": datetime.now().isoformat()
335
+ }
336
+ })
337
+
338
+ logger.info("step_validation_passed", step=step_number, attempt=attempt + 1)
339
+ return content
340
+
341
+ except (ValueError, ValidationError, HallucinatedIdError, OutputValidationError) as e:
342
+ logger.warning(
343
+ "step_validation_failed",
344
+ step=step_number,
345
+ attempt=attempt + 1,
346
+ error=str(e)
347
+ )
348
+
349
+ publish_event({
350
+ "event": "validation_error",
351
+ "data": {
352
+ "step": step_number,
353
+ "attempt": attempt + 1,
354
+ "error": str(e),
355
+ "retrying": attempt < max_retries - 1
356
+ }
357
+ })
358
+
359
+ if attempt < max_retries - 1:
360
+ input_data = f"""
361
+ VALIDATION ERROR - Previous output was REJECTED:
362
+ {str(e)}
363
+
364
+ CRITICAL: Output ONLY valid JSON starting with {{ and ending with }}
365
+ Do NOT add any text before or after the JSON.
366
+ Use ONLY IDs from actual tool results.
367
+
368
+ Original Task:
369
+ {input_data}
370
+
371
+ Try again (attempt {attempt + 2} of {max_retries}).
372
+ """
373
+ continue
374
+
375
+ raise ValueError(
376
+ f"Step {step_number} failed after {max_retries} attempts: {e}"
377
+ )
378
+
379
+ except Exception as e:
380
+ logger.error("step_execution_error", step=step_number, error=str(e))
381
+ raise
382
+
383
+ finally:
384
+ if original_tools is not None:
385
+ step.tools = original_tools
386
+
387
+
388
+ # ============================================================================
389
+ # Main Workflow Runner
390
+ # ============================================================================
391
+
392
+ def run_workflow_stream(
393
+ workflow: Workflow,
394
+ task_request: TaskPlanRequest,
395
+ publish_event: Callable,
396
+ quick_mode: bool = False
397
+ ) -> TaskPlanResponse:
398
+ """
399
+ Run the planning workflow with streaming events.
400
+
401
+ Executes each step sequentially with:
402
+ - Progress events
403
+ - Tool call/result tracking
404
+ - Post-hook validation
405
+ - Runtime/model_id auto-population
406
+
407
+ Args:
408
+ workflow: Planning workflow instance
409
+ task_request: Task plan request
410
+ publish_event: Event streaming callback
411
+ quick_mode: Skip verbose reasoning (for --local mode)
412
+
413
+ Returns:
414
+ TaskPlanResponse from final step
415
+ """
416
+ # Build input
417
+ workflow_input = f"""
418
+ Task: {task_request.description}
419
+ Priority: {task_request.priority}
420
+ Context: {task_request.conversation_context or 'New task'}
421
+
422
+ Analyze this task and select the best agent/team to execute it.
423
+ """
424
+
425
+ logger.info(
426
+ "workflow_runner_starting",
427
+ input_length=len(workflow_input),
428
+ steps=len(workflow.steps)
429
+ )
430
+
431
+ # Initial progress
432
+ publish_event({
433
+ "event": "progress",
434
+ "data": {
435
+ "stage": "initializing",
436
+ "message": "Initializing AI Task Planner - analyzing your request...",
437
+ "progress": 10
438
+ }
439
+ })
440
+
441
+ # Get outer context for validation
442
+ outer_context = getattr(workflow, '_outer_context', None)
443
+
444
+ step_outputs = {}
445
+ current_input = workflow_input
446
+
447
+ # Execute each step
448
+ for i, step in enumerate(workflow.steps, 1):
449
+ step_start = time.time()
450
+
451
+ logger.info("starting_workflow_step", step=i, step_name=step.name)
452
+
453
+ # Emit step progress
454
+ progress = STEP_PROGRESS_MAP.get(i, 50)
455
+ stage = STEP_STAGE_NAMES.get(i, "processing")
456
+ description = STEP_DESCRIPTIONS.get(i, f"Executing {step.name}")
457
+
458
+ publish_event({
459
+ "event": "progress",
460
+ "data": {
461
+ "message": description,
462
+ "progress": progress
463
+ }
464
+ })
465
+
466
+ publish_event({
467
+ "event": "progress",
468
+ "data": {
469
+ "stage": stage,
470
+ "message": description,
471
+ "progress": progress
472
+ }
473
+ })
474
+
475
+ # Execute step with validation
476
+ output = execute_step(
477
+ step=step,
478
+ input_data=current_input,
479
+ publish_event=publish_event,
480
+ step_number=i,
481
+ outer_context=outer_context,
482
+ max_retries=2 if i == 1 else 1 # More retries for Step 1
483
+ )
484
+
485
+ step_duration = time.time() - step_start
486
+ step_outputs[i] = output
487
+
488
+ # Step 1 post-processing: Extract runtime/model_id
489
+ if i == 1 and hasattr(output, 'selected_entity_id'):
490
+ entity_id = output.selected_entity_id
491
+ entity_type = getattr(output, 'selected_entity_type', 'agent')
492
+
493
+ # Try to get runtime/model_id from discovered agents
494
+ runtime = getattr(output, 'selected_agent_runtime', None)
495
+ model_id = getattr(output, 'selected_agent_model_id', None)
496
+
497
+ if not runtime and entity_type == 'agent':
498
+ discovered = getattr(output, 'discovered_agents', [])
499
+ for agent in discovered:
500
+ if str(agent.get('id')) == entity_id:
501
+ runtime = agent.get('runtime', 'default')
502
+ model_id = agent.get('model_id', 'claude-sonnet-4')
503
+ break
504
+
505
+ logger.info(
506
+ "step1_entity_selected",
507
+ entity_id=entity_id[:12] if entity_id else None,
508
+ runtime=runtime,
509
+ model_id=model_id
510
+ )
511
+
512
+ publish_event({
513
+ "event": "progress",
514
+ "data": {
515
+ "message": f"{step.name} completed",
516
+ "progress": progress
517
+ }
518
+ })
519
+
520
+ logger.info(
521
+ "workflow_step_completed",
522
+ step=i,
523
+ step_name=step.name,
524
+ duration_seconds=round(step_duration, 2)
525
+ )
526
+
527
+ # Prepare input for next step
528
+ if i < len(workflow.steps):
529
+ if hasattr(output, 'model_dump'):
530
+ current_input = json.dumps(output.model_dump(), indent=2)
531
+ elif hasattr(output, '__dict__'):
532
+ current_input = json.dumps(output.__dict__, indent=2)
533
+ else:
534
+ current_input = str(output)
535
+
536
+ # Final output processing
537
+ final_output = step_outputs.get(len(workflow.steps))
538
+
539
+ # Auto-populate runtime/model_id/environment from Step 1 if missing
540
+ if len(workflow.steps) >= 2:
541
+ step1_output = step_outputs.get(1)
542
+ if step1_output and final_output:
543
+ # Propagate runtime
544
+ if hasattr(step1_output, 'selected_agent_runtime'):
545
+ runtime = step1_output.selected_agent_runtime
546
+ if runtime and hasattr(final_output, 'selected_agent_runtime'):
547
+ if not final_output.selected_agent_runtime:
548
+ final_output.selected_agent_runtime = runtime
549
+
550
+ # Propagate model_id
551
+ if hasattr(step1_output, 'selected_agent_model_id'):
552
+ model_id = step1_output.selected_agent_model_id
553
+ if model_id and hasattr(final_output, 'selected_agent_model_id'):
554
+ if not final_output.selected_agent_model_id:
555
+ final_output.selected_agent_model_id = model_id
556
+
557
+ # Propagate environment_id to recommended_execution and top-level fields
558
+ env_id = getattr(step1_output, 'selected_environment_id', None)
559
+ env_name = getattr(step1_output, 'selected_environment_name', None)
560
+
561
+ if env_id:
562
+ # Set top-level fields
563
+ if hasattr(final_output, 'selected_environment_id') and not final_output.selected_environment_id:
564
+ final_output.selected_environment_id = env_id
565
+ if hasattr(final_output, 'selected_environment_name') and not final_output.selected_environment_name:
566
+ final_output.selected_environment_name = env_name
567
+
568
+ # Set recommended_execution fields (this is what CLI checks!)
569
+ if hasattr(final_output, 'recommended_execution'):
570
+ rec_exec = final_output.recommended_execution
571
+ if hasattr(rec_exec, 'recommended_environment_id') and not rec_exec.recommended_environment_id:
572
+ rec_exec.recommended_environment_id = env_id
573
+ if hasattr(rec_exec, 'recommended_environment_name') and not rec_exec.recommended_environment_name:
574
+ rec_exec.recommended_environment_name = env_name
575
+
576
+ logger.info(
577
+ "propagated_environment_from_step1",
578
+ env_id=env_id[:12] if env_id else None,
579
+ env_name=env_name
580
+ )
581
+
582
+ # Emit completion
583
+ title = getattr(final_output, 'title', 'Task Plan')
584
+ publish_event({
585
+ "event": "progress",
586
+ "data": {
587
+ "stage": "completed",
588
+ "message": f"Execution plan '{title}' generated successfully!",
589
+ "progress": 100
590
+ }
591
+ })
592
+
593
+ logger.info(
594
+ "workflow_completed_successfully",
595
+ title=title,
596
+ steps_executed=len(workflow.steps)
597
+ )
598
+
599
+ return final_output
600
+
601
+
602
+ # ============================================================================
603
+ # Fast Workflow Runner (--local mode)
604
+ # ============================================================================
605
+
606
+ def run_fast_workflow_stream(
607
+ workflow: Workflow,
608
+ task_request: TaskPlanRequest,
609
+ publish_event: Callable
610
+ ) -> 'FastSelectionOutput':
611
+ """
612
+ Run fast single-step workflow for --local mode.
613
+
614
+ Args:
615
+ workflow: Fast workflow (single step)
616
+ task_request: Task request
617
+ publish_event: Event callback
618
+
619
+ Returns:
620
+ FastSelectionOutput
621
+ """
622
+ from .models import FastSelectionOutput
623
+
624
+ workflow_input = f"Select best agent for: {task_request.description}"
625
+
626
+ publish_event({
627
+ "event": "progress",
628
+ "data": {
629
+ "stage": "selecting",
630
+ "message": "Quick agent selection...",
631
+ "progress": 50
632
+ }
633
+ })
634
+
635
+ step = workflow.steps[0]
636
+ outer_context = getattr(workflow, '_outer_context', None)
637
+
638
+ output = execute_step(
639
+ step=step,
640
+ input_data=workflow_input,
641
+ publish_event=publish_event,
642
+ step_number=1,
643
+ outer_context=outer_context,
644
+ max_retries=1
645
+ )
646
+
647
+ publish_event({
648
+ "event": "progress",
649
+ "data": {
650
+ "stage": "completed",
651
+ "message": "Agent selected!",
652
+ "progress": 100
653
+ }
654
+ })
655
+
656
+ return output