kubiya-control-plane-api 0.9.15__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (479) hide show
  1. control_plane_api/LICENSE +676 -0
  2. control_plane_api/README.md +350 -0
  3. control_plane_api/__init__.py +4 -0
  4. control_plane_api/__version__.py +8 -0
  5. control_plane_api/alembic/README +1 -0
  6. control_plane_api/alembic/env.py +121 -0
  7. control_plane_api/alembic/script.py.mako +28 -0
  8. control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
  9. control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
  10. control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
  11. control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
  12. control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
  13. control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
  14. control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
  15. control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
  16. control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
  17. control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
  18. control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
  19. control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
  20. control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
  21. control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
  22. control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
  23. control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
  24. control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
  25. control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
  26. control_plane_api/alembic.ini +148 -0
  27. control_plane_api/api/index.py +12 -0
  28. control_plane_api/app/__init__.py +11 -0
  29. control_plane_api/app/activities/__init__.py +20 -0
  30. control_plane_api/app/activities/agent_activities.py +384 -0
  31. control_plane_api/app/activities/plan_generation_activities.py +499 -0
  32. control_plane_api/app/activities/team_activities.py +424 -0
  33. control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
  34. control_plane_api/app/config/__init__.py +35 -0
  35. control_plane_api/app/config/api_config.py +469 -0
  36. control_plane_api/app/config/config_loader.py +224 -0
  37. control_plane_api/app/config/model_pricing.py +323 -0
  38. control_plane_api/app/config/storage_config.py +159 -0
  39. control_plane_api/app/config.py +115 -0
  40. control_plane_api/app/controllers/__init__.py +0 -0
  41. control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
  42. control_plane_api/app/database.py +135 -0
  43. control_plane_api/app/exceptions.py +408 -0
  44. control_plane_api/app/lib/__init__.py +11 -0
  45. control_plane_api/app/lib/environment.py +65 -0
  46. control_plane_api/app/lib/event_bus/__init__.py +17 -0
  47. control_plane_api/app/lib/event_bus/base.py +136 -0
  48. control_plane_api/app/lib/event_bus/manager.py +335 -0
  49. control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
  50. control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
  51. control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
  52. control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
  53. control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
  54. control_plane_api/app/lib/job_executor.py +330 -0
  55. control_plane_api/app/lib/kubiya_client.py +293 -0
  56. control_plane_api/app/lib/litellm_pricing.py +166 -0
  57. control_plane_api/app/lib/mcp_validation.py +163 -0
  58. control_plane_api/app/lib/nats/__init__.py +13 -0
  59. control_plane_api/app/lib/nats/credentials_manager.py +288 -0
  60. control_plane_api/app/lib/nats/listener.py +374 -0
  61. control_plane_api/app/lib/planning_prompt_builder.py +153 -0
  62. control_plane_api/app/lib/planning_tools/__init__.py +41 -0
  63. control_plane_api/app/lib/planning_tools/agents.py +409 -0
  64. control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
  65. control_plane_api/app/lib/planning_tools/base.py +119 -0
  66. control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
  67. control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
  68. control_plane_api/app/lib/planning_tools/environments.py +218 -0
  69. control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
  70. control_plane_api/app/lib/planning_tools/models.py +93 -0
  71. control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
  72. control_plane_api/app/lib/planning_tools/resources.py +242 -0
  73. control_plane_api/app/lib/planning_tools/teams.py +334 -0
  74. control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
  75. control_plane_api/app/lib/redis_client.py +803 -0
  76. control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
  77. control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
  78. control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
  79. control_plane_api/app/lib/storage/__init__.py +20 -0
  80. control_plane_api/app/lib/storage/base_provider.py +274 -0
  81. control_plane_api/app/lib/storage/provider_factory.py +157 -0
  82. control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
  83. control_plane_api/app/lib/supabase.py +71 -0
  84. control_plane_api/app/lib/supabase_utils.py +138 -0
  85. control_plane_api/app/lib/task_planning/__init__.py +138 -0
  86. control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
  87. control_plane_api/app/lib/task_planning/agents.py +389 -0
  88. control_plane_api/app/lib/task_planning/cache.py +218 -0
  89. control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
  90. control_plane_api/app/lib/task_planning/helpers.py +293 -0
  91. control_plane_api/app/lib/task_planning/hooks.py +474 -0
  92. control_plane_api/app/lib/task_planning/models.py +503 -0
  93. control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
  94. control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
  95. control_plane_api/app/lib/task_planning/runner.py +656 -0
  96. control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
  97. control_plane_api/app/lib/task_planning/workflow.py +424 -0
  98. control_plane_api/app/lib/templating/__init__.py +88 -0
  99. control_plane_api/app/lib/templating/compiler.py +278 -0
  100. control_plane_api/app/lib/templating/engine.py +178 -0
  101. control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
  102. control_plane_api/app/lib/templating/parsers/base.py +96 -0
  103. control_plane_api/app/lib/templating/parsers/env.py +85 -0
  104. control_plane_api/app/lib/templating/parsers/graph.py +112 -0
  105. control_plane_api/app/lib/templating/parsers/secret.py +87 -0
  106. control_plane_api/app/lib/templating/parsers/simple.py +81 -0
  107. control_plane_api/app/lib/templating/resolver.py +366 -0
  108. control_plane_api/app/lib/templating/types.py +214 -0
  109. control_plane_api/app/lib/templating/validator.py +201 -0
  110. control_plane_api/app/lib/temporal_client.py +232 -0
  111. control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
  112. control_plane_api/app/lib/temporal_credentials_service.py +203 -0
  113. control_plane_api/app/lib/validation/__init__.py +24 -0
  114. control_plane_api/app/lib/validation/runtime_validation.py +388 -0
  115. control_plane_api/app/main.py +531 -0
  116. control_plane_api/app/middleware/__init__.py +10 -0
  117. control_plane_api/app/middleware/auth.py +645 -0
  118. control_plane_api/app/middleware/exception_handler.py +267 -0
  119. control_plane_api/app/middleware/prometheus_middleware.py +173 -0
  120. control_plane_api/app/middleware/rate_limiting.py +384 -0
  121. control_plane_api/app/middleware/request_id.py +202 -0
  122. control_plane_api/app/models/__init__.py +40 -0
  123. control_plane_api/app/models/agent.py +90 -0
  124. control_plane_api/app/models/analytics.py +206 -0
  125. control_plane_api/app/models/associations.py +107 -0
  126. control_plane_api/app/models/auth_user.py +73 -0
  127. control_plane_api/app/models/context.py +161 -0
  128. control_plane_api/app/models/custom_integration.py +99 -0
  129. control_plane_api/app/models/environment.py +64 -0
  130. control_plane_api/app/models/execution.py +125 -0
  131. control_plane_api/app/models/execution_transition.py +50 -0
  132. control_plane_api/app/models/job.py +159 -0
  133. control_plane_api/app/models/llm_model.py +78 -0
  134. control_plane_api/app/models/orchestration.py +66 -0
  135. control_plane_api/app/models/plan_execution.py +102 -0
  136. control_plane_api/app/models/presence.py +49 -0
  137. control_plane_api/app/models/project.py +61 -0
  138. control_plane_api/app/models/project_management.py +85 -0
  139. control_plane_api/app/models/session.py +29 -0
  140. control_plane_api/app/models/skill.py +155 -0
  141. control_plane_api/app/models/system_tables.py +43 -0
  142. control_plane_api/app/models/task_planning.py +372 -0
  143. control_plane_api/app/models/team.py +86 -0
  144. control_plane_api/app/models/trace.py +257 -0
  145. control_plane_api/app/models/user_profile.py +54 -0
  146. control_plane_api/app/models/worker.py +221 -0
  147. control_plane_api/app/models/workflow.py +161 -0
  148. control_plane_api/app/models/workspace.py +50 -0
  149. control_plane_api/app/observability/__init__.py +177 -0
  150. control_plane_api/app/observability/context_logging.py +475 -0
  151. control_plane_api/app/observability/decorators.py +337 -0
  152. control_plane_api/app/observability/local_span_processor.py +702 -0
  153. control_plane_api/app/observability/metrics.py +303 -0
  154. control_plane_api/app/observability/middleware.py +246 -0
  155. control_plane_api/app/observability/optional.py +115 -0
  156. control_plane_api/app/observability/tracing.py +382 -0
  157. control_plane_api/app/policies/README.md +149 -0
  158. control_plane_api/app/policies/approved_users.rego +62 -0
  159. control_plane_api/app/policies/business_hours.rego +51 -0
  160. control_plane_api/app/policies/rate_limiting.rego +100 -0
  161. control_plane_api/app/policies/tool_enforcement/README.md +336 -0
  162. control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
  163. control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
  164. control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
  165. control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
  166. control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
  167. control_plane_api/app/policies/tool_restrictions.rego +86 -0
  168. control_plane_api/app/routers/__init__.py +4 -0
  169. control_plane_api/app/routers/agents.py +382 -0
  170. control_plane_api/app/routers/agents_v2.py +1598 -0
  171. control_plane_api/app/routers/analytics.py +1310 -0
  172. control_plane_api/app/routers/auth.py +59 -0
  173. control_plane_api/app/routers/client_config.py +57 -0
  174. control_plane_api/app/routers/context_graph.py +561 -0
  175. control_plane_api/app/routers/context_manager.py +577 -0
  176. control_plane_api/app/routers/custom_integrations.py +490 -0
  177. control_plane_api/app/routers/enforcer.py +132 -0
  178. control_plane_api/app/routers/environment_context.py +252 -0
  179. control_plane_api/app/routers/environments.py +761 -0
  180. control_plane_api/app/routers/execution_environment.py +847 -0
  181. control_plane_api/app/routers/executions/__init__.py +28 -0
  182. control_plane_api/app/routers/executions/router.py +286 -0
  183. control_plane_api/app/routers/executions/services/__init__.py +22 -0
  184. control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
  185. control_plane_api/app/routers/executions/services/status_service.py +420 -0
  186. control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
  187. control_plane_api/app/routers/executions/services/worker_health.py +514 -0
  188. control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
  189. control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
  190. control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
  191. control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
  192. control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
  193. control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
  194. control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
  195. control_plane_api/app/routers/executions.py +4888 -0
  196. control_plane_api/app/routers/health.py +165 -0
  197. control_plane_api/app/routers/health_v2.py +394 -0
  198. control_plane_api/app/routers/integration_templates.py +496 -0
  199. control_plane_api/app/routers/integrations.py +287 -0
  200. control_plane_api/app/routers/jobs.py +1809 -0
  201. control_plane_api/app/routers/metrics.py +517 -0
  202. control_plane_api/app/routers/models.py +82 -0
  203. control_plane_api/app/routers/models_v2.py +628 -0
  204. control_plane_api/app/routers/plan_executions.py +1481 -0
  205. control_plane_api/app/routers/plan_generation_async.py +304 -0
  206. control_plane_api/app/routers/policies.py +669 -0
  207. control_plane_api/app/routers/presence.py +234 -0
  208. control_plane_api/app/routers/projects.py +987 -0
  209. control_plane_api/app/routers/runners.py +379 -0
  210. control_plane_api/app/routers/runtimes.py +172 -0
  211. control_plane_api/app/routers/secrets.py +171 -0
  212. control_plane_api/app/routers/skills.py +1010 -0
  213. control_plane_api/app/routers/skills_definitions.py +140 -0
  214. control_plane_api/app/routers/storage.py +456 -0
  215. control_plane_api/app/routers/task_planning.py +611 -0
  216. control_plane_api/app/routers/task_queues.py +650 -0
  217. control_plane_api/app/routers/team_context.py +274 -0
  218. control_plane_api/app/routers/teams.py +1747 -0
  219. control_plane_api/app/routers/templates.py +248 -0
  220. control_plane_api/app/routers/traces.py +571 -0
  221. control_plane_api/app/routers/websocket_client.py +479 -0
  222. control_plane_api/app/routers/websocket_executions_status.py +437 -0
  223. control_plane_api/app/routers/websocket_gateway.py +323 -0
  224. control_plane_api/app/routers/websocket_traces.py +576 -0
  225. control_plane_api/app/routers/worker_queues.py +2555 -0
  226. control_plane_api/app/routers/worker_websocket.py +419 -0
  227. control_plane_api/app/routers/workers.py +1004 -0
  228. control_plane_api/app/routers/workflows.py +204 -0
  229. control_plane_api/app/runtimes/__init__.py +6 -0
  230. control_plane_api/app/runtimes/validation.py +344 -0
  231. control_plane_api/app/schemas/__init__.py +1 -0
  232. control_plane_api/app/schemas/job_schemas.py +302 -0
  233. control_plane_api/app/schemas/mcp_schemas.py +311 -0
  234. control_plane_api/app/schemas/template_schemas.py +133 -0
  235. control_plane_api/app/schemas/trace_schemas.py +168 -0
  236. control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
  237. control_plane_api/app/services/__init__.py +1 -0
  238. control_plane_api/app/services/agno_planning_strategy.py +233 -0
  239. control_plane_api/app/services/agno_service.py +838 -0
  240. control_plane_api/app/services/claude_code_planning_service.py +203 -0
  241. control_plane_api/app/services/context_graph_client.py +224 -0
  242. control_plane_api/app/services/custom_integration_service.py +415 -0
  243. control_plane_api/app/services/integration_resolution_service.py +345 -0
  244. control_plane_api/app/services/litellm_service.py +394 -0
  245. control_plane_api/app/services/plan_generator.py +79 -0
  246. control_plane_api/app/services/planning_strategy.py +66 -0
  247. control_plane_api/app/services/planning_strategy_factory.py +118 -0
  248. control_plane_api/app/services/policy_service.py +615 -0
  249. control_plane_api/app/services/state_transition_service.py +755 -0
  250. control_plane_api/app/services/storage_service.py +593 -0
  251. control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
  252. control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
  253. control_plane_api/app/services/trace_retention.py +354 -0
  254. control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
  255. control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
  256. control_plane_api/app/services/workflow_operations_service.py +611 -0
  257. control_plane_api/app/skills/__init__.py +100 -0
  258. control_plane_api/app/skills/base.py +239 -0
  259. control_plane_api/app/skills/builtin/__init__.py +37 -0
  260. control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
  261. control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
  262. control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
  263. control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
  264. control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
  265. control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
  266. control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
  267. control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
  268. control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
  269. control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
  270. control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
  271. control_plane_api/app/skills/builtin/docker/skill.py +104 -0
  272. control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
  273. control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
  274. control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
  275. control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
  276. control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
  277. control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
  278. control_plane_api/app/skills/builtin/python/__init__.py +4 -0
  279. control_plane_api/app/skills/builtin/python/skill.py +92 -0
  280. control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
  281. control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
  282. control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
  283. control_plane_api/app/skills/builtin/shell/skill.py +161 -0
  284. control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
  285. control_plane_api/app/skills/builtin/slack/skill.py +302 -0
  286. control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
  287. control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
  288. control_plane_api/app/skills/business_intelligence.py +189 -0
  289. control_plane_api/app/skills/config.py +63 -0
  290. control_plane_api/app/skills/loaders/__init__.py +14 -0
  291. control_plane_api/app/skills/loaders/base.py +73 -0
  292. control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
  293. control_plane_api/app/skills/registry.py +125 -0
  294. control_plane_api/app/utils/helpers.py +12 -0
  295. control_plane_api/app/utils/workflow_executor.py +354 -0
  296. control_plane_api/app/workflows/__init__.py +11 -0
  297. control_plane_api/app/workflows/agent_execution.py +520 -0
  298. control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
  299. control_plane_api/app/workflows/namespace_provisioning.py +326 -0
  300. control_plane_api/app/workflows/plan_generation.py +254 -0
  301. control_plane_api/app/workflows/team_execution.py +442 -0
  302. control_plane_api/scripts/seed_models.py +240 -0
  303. control_plane_api/scripts/validate_existing_tool_names.py +492 -0
  304. control_plane_api/shared/__init__.py +8 -0
  305. control_plane_api/shared/version.py +17 -0
  306. control_plane_api/test_deduplication.py +274 -0
  307. control_plane_api/test_executor_deduplication_e2e.py +309 -0
  308. control_plane_api/test_job_execution_e2e.py +283 -0
  309. control_plane_api/test_real_integration.py +193 -0
  310. control_plane_api/version.py +38 -0
  311. control_plane_api/worker/__init__.py +0 -0
  312. control_plane_api/worker/activities/__init__.py +0 -0
  313. control_plane_api/worker/activities/agent_activities.py +1585 -0
  314. control_plane_api/worker/activities/approval_activities.py +234 -0
  315. control_plane_api/worker/activities/job_activities.py +199 -0
  316. control_plane_api/worker/activities/runtime_activities.py +1167 -0
  317. control_plane_api/worker/activities/skill_activities.py +282 -0
  318. control_plane_api/worker/activities/team_activities.py +479 -0
  319. control_plane_api/worker/agent_runtime_server.py +370 -0
  320. control_plane_api/worker/binary_manager.py +333 -0
  321. control_plane_api/worker/config/__init__.py +31 -0
  322. control_plane_api/worker/config/worker_config.py +273 -0
  323. control_plane_api/worker/control_plane_client.py +1491 -0
  324. control_plane_api/worker/examples/analytics_integration_example.py +362 -0
  325. control_plane_api/worker/health_monitor.py +159 -0
  326. control_plane_api/worker/metrics.py +237 -0
  327. control_plane_api/worker/models/__init__.py +1 -0
  328. control_plane_api/worker/models/error_events.py +105 -0
  329. control_plane_api/worker/models/inputs.py +89 -0
  330. control_plane_api/worker/runtimes/__init__.py +35 -0
  331. control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
  332. control_plane_api/worker/runtimes/agno/__init__.py +34 -0
  333. control_plane_api/worker/runtimes/agno/config.py +248 -0
  334. control_plane_api/worker/runtimes/agno/hooks.py +385 -0
  335. control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
  336. control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
  337. control_plane_api/worker/runtimes/agno/utils.py +163 -0
  338. control_plane_api/worker/runtimes/base.py +979 -0
  339. control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
  340. control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
  341. control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
  342. control_plane_api/worker/runtimes/claude_code/config.py +829 -0
  343. control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
  344. control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
  345. control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
  346. control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
  347. control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
  348. control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
  349. control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
  350. control_plane_api/worker/runtimes/factory.py +173 -0
  351. control_plane_api/worker/runtimes/model_utils.py +107 -0
  352. control_plane_api/worker/runtimes/validation.py +93 -0
  353. control_plane_api/worker/services/__init__.py +1 -0
  354. control_plane_api/worker/services/agent_communication_tools.py +908 -0
  355. control_plane_api/worker/services/agent_executor.py +485 -0
  356. control_plane_api/worker/services/agent_executor_v2.py +793 -0
  357. control_plane_api/worker/services/analytics_collector.py +457 -0
  358. control_plane_api/worker/services/analytics_service.py +464 -0
  359. control_plane_api/worker/services/approval_tools.py +310 -0
  360. control_plane_api/worker/services/approval_tools_agno.py +207 -0
  361. control_plane_api/worker/services/cancellation_manager.py +177 -0
  362. control_plane_api/worker/services/code_ingestion_tools.py +465 -0
  363. control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
  364. control_plane_api/worker/services/data_visualization.py +834 -0
  365. control_plane_api/worker/services/event_publisher.py +531 -0
  366. control_plane_api/worker/services/jira_tools.py +257 -0
  367. control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
  368. control_plane_api/worker/services/runtime_analytics.py +328 -0
  369. control_plane_api/worker/services/session_service.py +365 -0
  370. control_plane_api/worker/services/skill_context_enhancement.py +181 -0
  371. control_plane_api/worker/services/skill_factory.py +471 -0
  372. control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
  373. control_plane_api/worker/services/team_executor.py +715 -0
  374. control_plane_api/worker/services/team_executor_v2.py +1866 -0
  375. control_plane_api/worker/services/tool_enforcement.py +254 -0
  376. control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
  377. control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
  378. control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
  379. control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
  380. control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
  381. control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
  382. control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
  383. control_plane_api/worker/services/workflow_executor/models.py +142 -0
  384. control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
  385. control_plane_api/worker/skills/__init__.py +12 -0
  386. control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
  387. control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
  388. control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
  389. control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
  390. control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
  391. control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
  392. control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
  393. control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
  394. control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
  395. control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
  396. control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
  397. control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
  398. control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
  399. control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
  400. control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
  401. control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
  402. control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
  403. control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
  404. control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
  405. control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
  406. control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
  407. control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
  408. control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
  409. control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
  410. control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
  411. control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
  412. control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
  413. control_plane_api/worker/skills/loaders/__init__.py +5 -0
  414. control_plane_api/worker/skills/loaders/base.py +23 -0
  415. control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
  416. control_plane_api/worker/skills/registry.py +208 -0
  417. control_plane_api/worker/tests/__init__.py +1 -0
  418. control_plane_api/worker/tests/conftest.py +12 -0
  419. control_plane_api/worker/tests/e2e/__init__.py +0 -0
  420. control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
  421. control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
  422. control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
  423. control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
  424. control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
  425. control_plane_api/worker/tests/integration/__init__.py +0 -0
  426. control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
  427. control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
  428. control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
  429. control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
  430. control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
  431. control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
  432. control_plane_api/worker/tests/unit/__init__.py +0 -0
  433. control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
  434. control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
  435. control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
  436. control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
  437. control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
  438. control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
  439. control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
  440. control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
  441. control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
  442. control_plane_api/worker/utils/__init__.py +1 -0
  443. control_plane_api/worker/utils/chunk_batcher.py +330 -0
  444. control_plane_api/worker/utils/environment.py +65 -0
  445. control_plane_api/worker/utils/error_publisher.py +260 -0
  446. control_plane_api/worker/utils/event_batcher.py +256 -0
  447. control_plane_api/worker/utils/logging_config.py +335 -0
  448. control_plane_api/worker/utils/logging_helper.py +326 -0
  449. control_plane_api/worker/utils/parameter_validator.py +120 -0
  450. control_plane_api/worker/utils/retry_utils.py +60 -0
  451. control_plane_api/worker/utils/streaming_utils.py +665 -0
  452. control_plane_api/worker/utils/tool_validation.py +332 -0
  453. control_plane_api/worker/utils/workspace_manager.py +163 -0
  454. control_plane_api/worker/websocket_client.py +393 -0
  455. control_plane_api/worker/worker.py +1297 -0
  456. control_plane_api/worker/workflows/__init__.py +0 -0
  457. control_plane_api/worker/workflows/agent_execution.py +909 -0
  458. control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
  459. control_plane_api/worker/workflows/team_execution.py +611 -0
  460. kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
  461. kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
  462. kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
  463. kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
  464. kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
  465. kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
  466. scripts/__init__.py +1 -0
  467. scripts/migrations.py +39 -0
  468. scripts/seed_worker_queues.py +128 -0
  469. scripts/setup_agent_runtime.py +142 -0
  470. worker_internal/__init__.py +1 -0
  471. worker_internal/planner/__init__.py +1 -0
  472. worker_internal/planner/activities.py +1499 -0
  473. worker_internal/planner/agent_tools.py +197 -0
  474. worker_internal/planner/event_models.py +148 -0
  475. worker_internal/planner/event_publisher.py +67 -0
  476. worker_internal/planner/models.py +199 -0
  477. worker_internal/planner/retry_logic.py +134 -0
  478. worker_internal/planner/worker.py +300 -0
  479. worker_internal/planner/workflows.py +970 -0
@@ -0,0 +1,254 @@
1
+ """
2
+ Tool enforcement service for policy-based tool governance.
3
+
4
+ This module provides non-blocking enforcement checks that run in parallel
5
+ with tool execution, injecting policy violations into tool results.
6
+ """
7
+
8
+ import asyncio
9
+ import structlog
10
+ from typing import Dict, Any, Optional, Tuple
11
+ from datetime import datetime, timezone
12
+
13
+ logger = structlog.get_logger(__name__)
14
+
15
+
16
+ class ToolEnforcementService:
17
+ """
18
+ Service for enforcing policies on tool executions.
19
+
20
+ Features:
21
+ - Non-blocking parallel enforcement checks
22
+ - Violation injection into tool outputs
23
+ - Context enrichment with user/org/agent metadata
24
+ - Graceful degradation on enforcer failures
25
+ """
26
+
27
+ def __init__(self, policy_enforcer_client: Optional[Any] = None):
28
+ """
29
+ Initialize the tool enforcement service.
30
+
31
+ Args:
32
+ policy_enforcer_client: PolicyEnforcerClient instance (optional)
33
+ """
34
+ self.enforcer = policy_enforcer_client
35
+ self.enabled = policy_enforcer_client is not None
36
+
37
+ async def enforce_tool_execution(
38
+ self,
39
+ tool_name: str,
40
+ tool_args: Dict[str, Any],
41
+ enforcement_context: Dict[str, Any],
42
+ timeout: float = 2.0,
43
+ ) -> Tuple[bool, Optional[str], Dict[str, Any]]:
44
+ """
45
+ Perform non-blocking enforcement check for tool execution.
46
+
47
+ Args:
48
+ tool_name: Name of tool being executed
49
+ tool_args: Tool arguments
50
+ enforcement_context: Full context (user, org, agent, etc.)
51
+ timeout: Maximum time to wait for enforcement (default 2s)
52
+
53
+ Returns:
54
+ Tuple of (allow, violation_message, metadata)
55
+ - allow: Whether tool is allowed (True if enforcer unavailable)
56
+ - violation_message: Error message if denied (None if allowed)
57
+ - metadata: Enforcement metadata (policies evaluated, etc.)
58
+ """
59
+ if not self.enabled:
60
+ return True, None, {"enforcer": "disabled"}
61
+
62
+ try:
63
+ # Build enforcement request payload
64
+ enforcement_payload = self._build_enforcement_payload(
65
+ tool_name=tool_name,
66
+ tool_args=tool_args,
67
+ context=enforcement_context,
68
+ )
69
+
70
+ # Call enforcer with timeout (non-blocking)
71
+ enforcement_result = await asyncio.wait_for(
72
+ self.enforcer.evaluation.enforce(enforcement_payload),
73
+ timeout=timeout,
74
+ )
75
+
76
+ allow = enforcement_result.get("allow", True)
77
+ policies = enforcement_result.get("policies", [])
78
+
79
+ if not allow:
80
+ violation_msg = self._format_violation_message(
81
+ tool_name=tool_name,
82
+ policies=policies,
83
+ enforcement_result=enforcement_result,
84
+ )
85
+ return False, violation_msg, {
86
+ "enforcer": "blocked",
87
+ "policies": policies,
88
+ "enforcement_id": enforcement_result.get("id"),
89
+ }
90
+
91
+ return True, None, {
92
+ "enforcer": "allowed",
93
+ "policies": policies,
94
+ "enforcement_id": enforcement_result.get("id"),
95
+ }
96
+
97
+ except asyncio.TimeoutError:
98
+ logger.warning(
99
+ "tool_enforcement_timeout",
100
+ tool_name=tool_name,
101
+ timeout=timeout,
102
+ )
103
+ # Fail open on timeout
104
+ return True, None, {"enforcer": "timeout"}
105
+
106
+ except Exception as e:
107
+ logger.error(
108
+ "tool_enforcement_error",
109
+ tool_name=tool_name,
110
+ error=str(e),
111
+ exc_info=True,
112
+ )
113
+ # Fail open on error
114
+ return True, None, {"enforcer": "error", "error": str(e)}
115
+
116
+ def _build_enforcement_payload(
117
+ self,
118
+ tool_name: str,
119
+ tool_args: Dict[str, Any],
120
+ context: Dict[str, Any],
121
+ ) -> Dict[str, Any]:
122
+ """
123
+ Build enforcement request payload with all required context.
124
+
125
+ Required fields:
126
+ - tool_name, tool_arguments
127
+ - user_email, organization_id, team_id, roles
128
+ - execution_id, agent_id, environment
129
+ - tool_source, tool_category, risk_level
130
+ - timestamp
131
+ """
132
+ return {
133
+ "action": "tool_execution",
134
+ "tool": {
135
+ "name": tool_name,
136
+ "arguments": tool_args,
137
+ "source": self._determine_tool_source(tool_name),
138
+ "category": self._determine_tool_category(tool_name),
139
+ "risk_level": self._determine_risk_level(tool_name, tool_args),
140
+ },
141
+ "user": {
142
+ "email": context.get("user_email"),
143
+ "id": context.get("user_id"),
144
+ "roles": context.get("user_roles", []),
145
+ },
146
+ "organization": {
147
+ "id": context.get("organization_id"),
148
+ "name": context.get("organization_name"),
149
+ },
150
+ "team": {
151
+ "id": context.get("team_id"),
152
+ "name": context.get("team_name"),
153
+ },
154
+ "execution": {
155
+ "execution_id": context.get("execution_id"),
156
+ "agent_id": context.get("agent_id"),
157
+ "environment": context.get("environment", "production"),
158
+ "timestamp": datetime.now(timezone.utc).isoformat(),
159
+ },
160
+ "metadata": context.get("metadata", {}),
161
+ }
162
+
163
+ def _determine_tool_source(self, tool_name: str) -> str:
164
+ """Determine tool source from name."""
165
+ if tool_name.startswith("mcp__"):
166
+ return "mcp"
167
+ elif tool_name in [
168
+ "Bash",
169
+ "Read",
170
+ "Write",
171
+ "Edit",
172
+ "Grep",
173
+ "Glob",
174
+ "WebFetch",
175
+ "WebSearch",
176
+ "TodoWrite",
177
+ "AskUserQuestion",
178
+ ]:
179
+ return "builtin"
180
+ else:
181
+ return "skill"
182
+
183
+ def _determine_tool_category(self, tool_name: str) -> str:
184
+ """Categorize tool by function."""
185
+ if tool_name in ["Bash", "Shell"]:
186
+ return "command_execution"
187
+ elif tool_name in ["Read", "Write", "Edit"]:
188
+ return "file_operation"
189
+ elif tool_name in ["Grep", "Glob", "Find"]:
190
+ return "file_search"
191
+ elif "api" in tool_name.lower() or "http" in tool_name.lower() or tool_name in [
192
+ "WebFetch",
193
+ "WebSearch",
194
+ ]:
195
+ return "network"
196
+ else:
197
+ return "general"
198
+
199
+ def _determine_risk_level(
200
+ self,
201
+ tool_name: str,
202
+ tool_args: Dict[str, Any],
203
+ ) -> str:
204
+ """Assess risk level of tool execution."""
205
+ # High risk: command execution, destructive file ops
206
+ if tool_name in ["Bash", "Shell"]:
207
+ command = tool_args.get("command", "")
208
+ # Critical: Destructive commands
209
+ if any(
210
+ cmd in command
211
+ for cmd in ["rm -rf", "dd if=", "mkfs", "> /dev/", "format"]
212
+ ):
213
+ return "critical"
214
+ # High: Any command execution
215
+ return "high"
216
+
217
+ if tool_name == "Write":
218
+ return "medium"
219
+
220
+ if tool_name == "Edit":
221
+ return "medium"
222
+
223
+ if tool_name == "Read":
224
+ path = tool_args.get("file_path", "")
225
+ # High: Sensitive file access
226
+ if any(
227
+ sensitive in path
228
+ for sensitive in ["/etc/passwd", ".ssh", ".env", ".key", "credentials"]
229
+ ):
230
+ return "high"
231
+ return "low"
232
+
233
+ # Network operations
234
+ if tool_name in ["WebFetch", "WebSearch"]:
235
+ return "medium"
236
+
237
+ return "low"
238
+
239
+ def _format_violation_message(
240
+ self,
241
+ tool_name: str,
242
+ policies: list,
243
+ enforcement_result: Dict[str, Any],
244
+ ) -> str:
245
+ """Format user-friendly violation message."""
246
+ policy_names = ", ".join(policies) if policies else "unknown policies"
247
+ return (
248
+ f"Tool execution blocked by policy enforcement.\n"
249
+ f"Tool: {tool_name}\n"
250
+ f"Blocked by: {policy_names}\n"
251
+ f"Enforcement ID: {enforcement_result.get('id', 'N/A')}\n"
252
+ f"\n"
253
+ f"Contact your administrator for access approval."
254
+ )
@@ -0,0 +1,52 @@
1
+ """
2
+ Workflow Executor Skill
3
+
4
+ A well-architected skill for executing workflows from agent control plane.
5
+
6
+ This package provides:
7
+ - Type-safe Pydantic models for configurations and events
8
+ - Proper separation of concerns with dedicated components
9
+ - Strategy pattern for different workflow types
10
+ - Event-driven architecture for real-time streaming
11
+ - Comprehensive error handling and logging
12
+
13
+ Architecture:
14
+ - models.py: Pydantic models for type safety
15
+ - event_publisher.py: Publishes events to control plane
16
+ - event_processor.py: Processes streaming events from SDK
17
+ - executors/: Strategy pattern for workflow execution
18
+ - base.py: Abstract base executor (template method pattern)
19
+ - json_executor.py: JSON workflow execution
20
+ - python_executor.py: Python DSL workflow execution
21
+
22
+ Usage:
23
+ from control_plane_api.worker.services.workflow_executor import WorkflowExecutorTools
24
+
25
+ # This will be the main skill class that agents use
26
+ """
27
+
28
+ from .models import (
29
+ WorkflowConfig,
30
+ WorkflowExecutionContext,
31
+ WorkflowEvent,
32
+ WorkflowResult,
33
+ )
34
+ from .event_publisher import WorkflowEventPublisher
35
+ from .event_processor import WorkflowEventProcessor
36
+ from .executors import (
37
+ BaseWorkflowExecutor,
38
+ JsonWorkflowExecutor,
39
+ PythonWorkflowExecutor,
40
+ )
41
+
42
+ __all__ = [
43
+ "WorkflowConfig",
44
+ "WorkflowExecutionContext",
45
+ "WorkflowEvent",
46
+ "WorkflowResult",
47
+ "WorkflowEventPublisher",
48
+ "WorkflowEventProcessor",
49
+ "BaseWorkflowExecutor",
50
+ "JsonWorkflowExecutor",
51
+ "PythonWorkflowExecutor",
52
+ ]
@@ -0,0 +1,287 @@
1
+ """
2
+ Workflow event processor.
3
+
4
+ This module processes streaming events from the Kubiya SDK,
5
+ handling deduplication, parsing, and state management.
6
+ """
7
+
8
+ import json
9
+ import hashlib
10
+ import structlog
11
+ from typing import Dict, Any, Set, Optional, Callable
12
+ from .event_publisher import WorkflowEventPublisher
13
+
14
+ logger = structlog.get_logger()
15
+
16
+
17
+ class WorkflowEventProcessor:
18
+ """
19
+ Processes streaming events from workflow execution.
20
+
21
+ Handles event parsing, deduplication, and publishes to control plane.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ event_publisher: WorkflowEventPublisher,
27
+ workflow_name: str,
28
+ message_id: str,
29
+ stream_callback: Optional[Callable[[str], None]] = None
30
+ ):
31
+ """
32
+ Initialize event processor.
33
+
34
+ Args:
35
+ event_publisher: Event publisher instance
36
+ workflow_name: Name of the workflow being executed
37
+ message_id: Unique workflow message ID
38
+ stream_callback: Optional callback for streaming output
39
+ """
40
+ self.event_publisher = event_publisher
41
+ self.workflow_name = workflow_name
42
+ self.message_id = message_id
43
+ self.stream_callback = stream_callback
44
+
45
+ # State tracking
46
+ self.event_count = 0
47
+ self.accumulated_output: list[str] = []
48
+ self.seen_events: Set[str] = set()
49
+ self.current_step: Optional[str] = None
50
+ self.step_outputs: Dict[str, str] = {}
51
+
52
+ def process_event(self, event: Any) -> Optional[Dict[str, Any]]:
53
+ """
54
+ Process a single streaming event from the SDK.
55
+
56
+ Args:
57
+ event: Raw event from SDK (string, bytes, or dict)
58
+
59
+ Returns:
60
+ Parsed event data as dict, or None if event should be skipped
61
+ """
62
+ self.event_count += 1
63
+
64
+ # Skip None/empty events
65
+ if event is None:
66
+ logger.debug("skipping_none_event", event_number=self.event_count)
67
+ return None
68
+
69
+ # Parse the event
70
+ try:
71
+ event_data = self._parse_event(event)
72
+ if event_data is None:
73
+ return None
74
+
75
+ # Process based on event type
76
+ event_type = event_data.get("type", "unknown")
77
+ logger.debug(
78
+ "processing_workflow_event",
79
+ event_type=event_type,
80
+ event_number=self.event_count
81
+ )
82
+
83
+ # Route to specific handler
84
+ if event_type == "step_output":
85
+ self._handle_step_output(event_data)
86
+ elif event_type == "step_running":
87
+ self._handle_step_running(event_data)
88
+ elif event_type == "step_complete":
89
+ self._handle_step_complete(event_data)
90
+ elif event_type == "workflow_complete":
91
+ self._handle_workflow_complete(event_data)
92
+ elif event_type == "workflow_failed":
93
+ self._handle_workflow_failed(event_data)
94
+ else:
95
+ logger.debug("unknown_event_type", event_type=event_type)
96
+
97
+ return event_data
98
+
99
+ except Exception as e:
100
+ logger.error(
101
+ "failed_to_process_event",
102
+ error=str(e),
103
+ event_number=self.event_count
104
+ )
105
+ return None
106
+
107
+ def _parse_event(self, event: Any) -> Optional[Dict[str, Any]]:
108
+ """Parse raw event into dict."""
109
+ try:
110
+ # Handle bytes
111
+ if isinstance(event, bytes):
112
+ if not event:
113
+ return None
114
+ event = event.decode('utf-8')
115
+
116
+ # Handle strings
117
+ if isinstance(event, str):
118
+ if not event.strip():
119
+ return None
120
+
121
+ # Handle SSE format: "data: 2:{json}"
122
+ if event.startswith("data: "):
123
+ event = event[6:] # Remove "data: " prefix
124
+
125
+ # Strip message ID prefix like "2:"
126
+ if ":" in event and event.split(":", 1)[0].isdigit():
127
+ event = event.split(":", 1)[1]
128
+
129
+ # Parse JSON
130
+ return json.loads(event)
131
+
132
+ # Already a dict
133
+ elif isinstance(event, dict):
134
+ return event
135
+
136
+ # Unknown type
137
+ else:
138
+ logger.warning(
139
+ "unknown_event_type_treating_as_text",
140
+ type_name=type(event).__name__
141
+ )
142
+ event_str = str(event)
143
+ if event_str.strip():
144
+ self.accumulated_output.append(event_str)
145
+ if self.stream_callback:
146
+ self.stream_callback(f"{event_str}\n")
147
+ return None
148
+
149
+ except (json.JSONDecodeError, UnicodeDecodeError) as e:
150
+ logger.warning("failed_to_parse_event_treating_as_text", error=str(e))
151
+ event_str = str(event)
152
+ if event_str.strip():
153
+ self.accumulated_output.append(event_str)
154
+ if self.stream_callback:
155
+ self.stream_callback(f"{event_str}\n")
156
+ return None
157
+
158
+ def _handle_step_output(self, event_data: Dict[str, Any]) -> None:
159
+ """Handle step_output event."""
160
+ step = event_data.get("step", {})
161
+ step_name = step.get("name", "unknown")
162
+ step_status = step.get("status", "")
163
+ output = step.get("output", "")
164
+
165
+ if not output.strip():
166
+ return
167
+
168
+ # Deduplicate events - SDK sends same event twice
169
+ event_hash = hashlib.md5(f"{step_name}:{output}".encode()).hexdigest()
170
+ if event_hash in self.seen_events:
171
+ logger.debug("skipping_duplicate_event", step_name=step_name)
172
+ return
173
+ self.seen_events.add(event_hash)
174
+
175
+ logger.debug("step_output_received", step_name=step_name, output_length=len(output))
176
+
177
+ # Store output
178
+ self.accumulated_output.append(output)
179
+ self.step_outputs[step_name] = output
180
+
181
+ # Stream to callback
182
+ if self.stream_callback:
183
+ formatted_output = f"```\n{output}\n```\n"
184
+ self.stream_callback(formatted_output)
185
+
186
+ # Publish to control plane
187
+ self.event_publisher.publish_step_output(
188
+ workflow_name=self.workflow_name,
189
+ step_name=step_name,
190
+ output=output,
191
+ message_id=self.message_id
192
+ )
193
+
194
+ # Check if step failed
195
+ if step_status in ["error", "failed"]:
196
+ logger.warning("step_failed_detected", step_name=step_name, status=step_status)
197
+ self.event_publisher.publish_step_complete(
198
+ workflow_name=self.workflow_name,
199
+ step_name=step_name,
200
+ status="failed",
201
+ message_id=self.message_id,
202
+ error=output
203
+ )
204
+
205
+ def _handle_step_running(self, event_data: Dict[str, Any]) -> None:
206
+ """Handle step_running event."""
207
+ step = event_data.get("step", {})
208
+ step_name = step.get("name", "unknown")
209
+
210
+ logger.debug("step_running", step_name=step_name)
211
+
212
+ self.current_step = step_name
213
+ formatted = f"\n▶️ Step: {step_name}"
214
+ self.accumulated_output.append(formatted)
215
+
216
+ if self.stream_callback:
217
+ self.stream_callback(f"{formatted}\n")
218
+
219
+ self.event_publisher.publish_step_running(
220
+ workflow_name=self.workflow_name,
221
+ step_name=step_name,
222
+ message_id=self.message_id
223
+ )
224
+
225
+ def _handle_step_complete(self, event_data: Dict[str, Any]) -> None:
226
+ """Handle step_complete event."""
227
+ step = event_data.get("step", {})
228
+ step_name = step.get("name", "unknown")
229
+ status = step.get("status", "unknown")
230
+
231
+ logger.debug("step_complete", step_name=step_name, status=status)
232
+
233
+ icon = "✅" if status == "finished" else "❌"
234
+ formatted = f"{icon} Step '{step_name}' {status}"
235
+ self.accumulated_output.append(formatted)
236
+ self.current_step = None
237
+
238
+ if self.stream_callback:
239
+ self.stream_callback(f"{formatted}\n")
240
+
241
+ self.event_publisher.publish_step_complete(
242
+ workflow_name=self.workflow_name,
243
+ step_name=step_name,
244
+ status="completed" if status == "finished" else "failed",
245
+ message_id=self.message_id
246
+ )
247
+
248
+ def _handle_workflow_complete(self, event_data: Dict[str, Any]) -> None:
249
+ """Handle workflow_complete event."""
250
+ dag_name = event_data.get("dagName", "unknown")
251
+ status = event_data.get("status", "unknown")
252
+ success = event_data.get("success", False)
253
+
254
+ logger.info("workflow_complete", dag_name=dag_name, status=status, success=success)
255
+
256
+ icon = "✅" if success else "❌"
257
+ formatted = f"\n{icon} Workflow '{dag_name}' {status}"
258
+ self.accumulated_output.append(formatted)
259
+
260
+ if self.stream_callback:
261
+ self.stream_callback(f"{formatted}\n")
262
+
263
+ def _handle_workflow_failed(self, event_data: Dict[str, Any]) -> None:
264
+ """Handle workflow_failed event."""
265
+ error = event_data.get("error", "Unknown error")
266
+
267
+ logger.error("workflow_failed", error=error)
268
+
269
+ formatted = f"\n❌ Workflow failed: {error}"
270
+ self.accumulated_output.append(formatted)
271
+
272
+ if self.stream_callback:
273
+ self.stream_callback(f"{formatted}\n")
274
+
275
+ self.event_publisher.publish_failed(
276
+ workflow_name=self.workflow_name,
277
+ error=error,
278
+ message_id=self.message_id
279
+ )
280
+
281
+ def get_accumulated_output(self) -> list[str]:
282
+ """Get all accumulated output lines."""
283
+ return self.accumulated_output
284
+
285
+ def get_event_count(self) -> int:
286
+ """Get total number of events processed."""
287
+ return self.event_count