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,964 @@
1
+ """
2
+ EventFormatter - Centralized SSE event formatting for execution streaming.
3
+
4
+ This module provides a clean interface for formatting Server-Sent Events (SSE)
5
+ according to the SSE specification with proper event IDs for gap recovery.
6
+
7
+ SSE Format:
8
+ id: {execution_id}_{counter}_{timestamp_micros}
9
+ event: {event_type}
10
+ data: {json_payload}
11
+ {blank line}
12
+
13
+ Event Types:
14
+ - connected: Initial connection confirmation
15
+ - message: Chat messages (user, assistant, tool, system)
16
+ - history_complete: History loading complete
17
+ - status: Execution status updates
18
+ - tool_started: Tool execution started
19
+ - tool_completed: Tool execution completed
20
+ - member_tool_started: Team member tool execution started
21
+ - member_tool_completed: Team member tool execution completed
22
+ - message_chunk: Streaming message chunks (for real-time token streaming)
23
+ - member_message_chunk: Team member message chunks
24
+ - member_message_complete: Team member message streaming complete
25
+ - thinking_start: Beginning of thinking/reasoning block
26
+ - thinking_delta: Incremental thinking content
27
+ - thinking_complete: End of thinking block with signature
28
+ - member_thinking_start: Team member thinking start
29
+ - member_thinking_delta: Team member thinking content
30
+ - member_thinking_complete: Team member thinking end
31
+ - done: Execution completed successfully
32
+ - error: Execution failed
33
+ - degraded: Degraded mode notification (Redis down, worker down)
34
+ - reconnect: Server requesting client reconnect
35
+ - timeout_warning: Connection timeout warning
36
+ - gap_detected: Event gap detected, client should reconnect
37
+
38
+ Test Strategy:
39
+ - Unit test each format method for SSE compliance
40
+ - Verify event ID generation uniqueness and format
41
+ - Test JSON serialization edge cases (None, nested objects, special chars)
42
+ - Test multi-line data handling
43
+ - Verify all fields present (id, event, data, blank line)
44
+ - Test thread-safety of counter increment
45
+ """
46
+
47
+ import json
48
+ import time
49
+ from typing import Any, Dict, List, Optional
50
+
51
+
52
+ class EventFormatter:
53
+ """
54
+ Formats Server-Sent Events (SSE) for execution streaming.
55
+
56
+ Handles:
57
+ - Event ID generation with sequential counter
58
+ - JSON serialization of payloads
59
+ - Proper SSE format compliance
60
+ - Multiple event types with specialized formatting
61
+
62
+ Example:
63
+ >>> formatter = EventFormatter("exec-123")
64
+ >>> event = formatter.format_connected_event("org-456", "pending")
65
+ >>> print(event)
66
+ id: exec-123_1_1702938457123456
67
+ event: connected
68
+ data: {"execution_id": "exec-123", "organization_id": "org-456", "status": "pending", "connected_at": 1702938457.123456}
69
+
70
+ """
71
+
72
+ def __init__(self, execution_id: str):
73
+ """
74
+ Initialize event formatter for a specific execution.
75
+
76
+ Args:
77
+ execution_id: Unique execution identifier
78
+ """
79
+ self.execution_id = execution_id
80
+ self._counter = 0
81
+
82
+ def generate_event_id(self) -> str:
83
+ """
84
+ Generate unique event ID with sequential counter and microsecond timestamp.
85
+
86
+ Format: {execution_id}_{counter}_{timestamp_micros}
87
+
88
+ The counter ensures sequential ordering within a stream, while the
89
+ microsecond timestamp provides global uniqueness across reconnections.
90
+
91
+ Returns:
92
+ str: Unique event ID
93
+
94
+ Example:
95
+ >>> formatter = EventFormatter("exec-123")
96
+ >>> event_id1 = formatter.generate_event_id()
97
+ >>> event_id2 = formatter.generate_event_id()
98
+ >>> # event_id1: "exec-123_1_1702938457123456"
99
+ >>> # event_id2: "exec-123_2_1702938457124789"
100
+ """
101
+ self._counter += 1
102
+ timestamp_micros = int(time.time() * 1000000)
103
+ return f"{self.execution_id}_{self._counter}_{timestamp_micros}"
104
+
105
+ def format_event(
106
+ self,
107
+ event_type: str,
108
+ data: Dict[str, Any],
109
+ event_id: Optional[str] = None
110
+ ) -> str:
111
+ """
112
+ Generic SSE event formatter.
113
+
114
+ Generates proper SSE format with id, event, data fields and blank line terminator.
115
+
116
+ Args:
117
+ event_type: Type of event (e.g., "message", "status", "done")
118
+ data: Event payload to JSON-serialize
119
+ event_id: Optional custom event ID (auto-generated if not provided)
120
+
121
+ Returns:
122
+ str: Formatted SSE event string
123
+
124
+ Example:
125
+ >>> formatter = EventFormatter("exec-123")
126
+ >>> event = formatter.format_event("status", {"status": "running"})
127
+ >>> print(event)
128
+ id: exec-123_1_1702938457123456
129
+ event: status
130
+ data: {"status": "running"}
131
+
132
+ """
133
+ if event_id is None:
134
+ event_id = self.generate_event_id()
135
+
136
+ # JSON serialize data with proper error handling
137
+ try:
138
+ data_json = json.dumps(data)
139
+ except (TypeError, ValueError) as e:
140
+ # Fallback to error payload if serialization fails
141
+ data_json = json.dumps({
142
+ "error": "Failed to serialize event data",
143
+ "error_type": "serialization_error",
144
+ "details": str(e)
145
+ })
146
+
147
+ # SSE format: id, event, data, blank line
148
+ return f"id: {event_id}\nevent: {event_type}\ndata: {data_json}\n\n"
149
+
150
+ def format_connected_event(
151
+ self,
152
+ organization_id: str,
153
+ status: str = "pending"
154
+ ) -> str:
155
+ """
156
+ Format 'connected' event sent immediately on connection.
157
+
158
+ This event is sent first to unblock the EventSource connection before
159
+ any slow operations (Temporal queries, DB lookups) are performed.
160
+
161
+ Args:
162
+ organization_id: Organization ID for the execution
163
+ status: Current execution status (default: "pending")
164
+
165
+ Returns:
166
+ str: Formatted SSE event
167
+
168
+ Example:
169
+ >>> formatter = EventFormatter("exec-123")
170
+ >>> event = formatter.format_connected_event("org-456", "running")
171
+ >>> print(event)
172
+ id: exec-123_1_1702938457123456
173
+ event: connected
174
+ data: {"execution_id": "exec-123", "organization_id": "org-456", "status": "running", "connected_at": 1702938457.123456}
175
+
176
+ """
177
+ data = {
178
+ "execution_id": self.execution_id,
179
+ "organization_id": organization_id,
180
+ "status": status,
181
+ "connected_at": time.time()
182
+ }
183
+ return self.format_event("connected", data)
184
+
185
+ def format_message_event(self, message: Dict[str, Any]) -> str:
186
+ """
187
+ Format 'message' event for chat messages.
188
+
189
+ Handles user, assistant, tool, and system messages with all metadata.
190
+
191
+ Args:
192
+ message: Message dictionary with fields like role, content, timestamp, etc.
193
+
194
+ Returns:
195
+ str: Formatted SSE event
196
+
197
+ Example:
198
+ >>> formatter = EventFormatter("exec-123")
199
+ >>> message = {
200
+ ... "role": "user",
201
+ ... "content": "Hello",
202
+ ... "timestamp": "2024-12-18T10:00:00Z",
203
+ ... "message_id": "msg-456"
204
+ ... }
205
+ >>> event = formatter.format_message_event(message)
206
+ """
207
+ return self.format_event("message", message)
208
+
209
+ def format_history_complete_event(
210
+ self,
211
+ message_count: int,
212
+ is_truncated: bool = False,
213
+ has_more: bool = False
214
+ ) -> str:
215
+ """
216
+ Format 'history_complete' event.
217
+
218
+ Signals that historical messages have been fully loaded.
219
+
220
+ Args:
221
+ message_count: Number of messages loaded
222
+ is_truncated: Whether history was truncated (default: False)
223
+ has_more: Whether there are more messages available (default: False)
224
+
225
+ Returns:
226
+ str: Formatted SSE event
227
+
228
+ Example:
229
+ >>> formatter = EventFormatter("exec-123")
230
+ >>> event = formatter.format_history_complete_event(42, is_truncated=True)
231
+ >>> print(event)
232
+ id: exec-123_1_1702938457123456
233
+ event: history_complete
234
+ data: {"execution_id": "exec-123", "message_count": 42, "is_truncated": true, "has_more": false}
235
+
236
+ """
237
+ data = {
238
+ "execution_id": self.execution_id,
239
+ "message_count": message_count,
240
+ "is_truncated": is_truncated,
241
+ "has_more": has_more
242
+ }
243
+ return self.format_event("history_complete", data)
244
+
245
+ def format_status_event(
246
+ self,
247
+ status: str,
248
+ metadata: Optional[Dict] = None
249
+ ) -> str:
250
+ """
251
+ Format 'status' event for execution status changes.
252
+
253
+ Args:
254
+ status: New status (e.g., "pending", "running", "completed", "failed")
255
+ metadata: Optional additional metadata (e.g., source="database")
256
+
257
+ Returns:
258
+ str: Formatted SSE event
259
+
260
+ Example:
261
+ >>> formatter = EventFormatter("exec-123")
262
+ >>> event = formatter.format_status_event("running")
263
+ >>> print(event)
264
+ id: exec-123_1_1702938457123456
265
+ event: status
266
+ data: {"status": "running", "execution_id": "exec-123"}
267
+
268
+ """
269
+ data = {
270
+ "status": status,
271
+ "execution_id": self.execution_id
272
+ }
273
+ if metadata:
274
+ data.update(metadata)
275
+ return self.format_event("status", data)
276
+
277
+ def format_tool_started_event(self, tool_data: Dict[str, Any]) -> str:
278
+ """
279
+ Format 'tool_started' event for tool execution started.
280
+
281
+ Args:
282
+ tool_data: Tool execution data with nested structure:
283
+ {
284
+ "data": {
285
+ "tool_name": str,
286
+ "tool_execution_id": str,
287
+ "tool_input": dict,
288
+ ...
289
+ },
290
+ "timestamp": str
291
+ }
292
+
293
+ Returns:
294
+ str: Formatted SSE event
295
+
296
+ Example:
297
+ >>> formatter = EventFormatter("exec-123")
298
+ >>> tool_data = {
299
+ ... "data": {
300
+ ... "tool_name": "search",
301
+ ... "tool_execution_id": "tool-789",
302
+ ... "tool_input": {"query": "test"}
303
+ ... },
304
+ ... "timestamp": "2024-12-18T10:00:00Z"
305
+ ... }
306
+ >>> event = formatter.format_tool_started_event(tool_data)
307
+ """
308
+ return self.format_event("tool_started", tool_data)
309
+
310
+ def format_tool_completed_event(self, tool_data: Dict[str, Any]) -> str:
311
+ """
312
+ Format 'tool_completed' event for tool execution completed.
313
+
314
+ Args:
315
+ tool_data: Tool execution data with nested structure:
316
+ {
317
+ "data": {
318
+ "tool_name": str,
319
+ "tool_execution_id": str,
320
+ "tool_output": Any,
321
+ "tool_status": str,
322
+ ...
323
+ },
324
+ "timestamp": str
325
+ }
326
+
327
+ Returns:
328
+ str: Formatted SSE event
329
+
330
+ Example:
331
+ >>> formatter = EventFormatter("exec-123")
332
+ >>> tool_data = {
333
+ ... "data": {
334
+ ... "tool_name": "search",
335
+ ... "tool_execution_id": "tool-789",
336
+ ... "tool_output": {"results": [...]},
337
+ ... "tool_status": "completed"
338
+ ... },
339
+ ... "timestamp": "2024-12-18T10:00:01Z"
340
+ ... }
341
+ >>> event = formatter.format_tool_completed_event(tool_data)
342
+ """
343
+ return self.format_event("tool_completed", tool_data)
344
+
345
+ def format_member_tool_started_event(self, tool_data: Dict[str, Any]) -> str:
346
+ """
347
+ Format 'member_tool_started' event for team member tool execution started.
348
+
349
+ Used in multi-agent scenarios when a team member starts executing a tool.
350
+
351
+ Args:
352
+ tool_data: Tool execution data with nested structure:
353
+ {
354
+ "data": {
355
+ "tool_name": str,
356
+ "tool_execution_id": str,
357
+ "member_name": str,
358
+ "parent_message_id": str,
359
+ "tool_arguments": dict,
360
+ ...
361
+ },
362
+ "timestamp": str
363
+ }
364
+
365
+ Returns:
366
+ str: Formatted SSE event
367
+
368
+ Example:
369
+ >>> formatter = EventFormatter("exec-123")
370
+ >>> tool_data = {
371
+ ... "data": {
372
+ ... "tool_name": "search",
373
+ ... "tool_execution_id": "tool-789",
374
+ ... "member_name": "Researcher",
375
+ ... "tool_arguments": {"query": "test"}
376
+ ... },
377
+ ... "timestamp": "2024-12-18T10:00:00Z"
378
+ ... }
379
+ >>> event = formatter.format_member_tool_started_event(tool_data)
380
+ """
381
+ return self.format_event("member_tool_started", tool_data)
382
+
383
+ def format_member_tool_completed_event(self, tool_data: Dict[str, Any]) -> str:
384
+ """
385
+ Format 'member_tool_completed' event for team member tool execution completed.
386
+
387
+ Used in multi-agent scenarios when a team member finishes executing a tool.
388
+
389
+ Args:
390
+ tool_data: Tool execution data with nested structure:
391
+ {
392
+ "data": {
393
+ "tool_name": str,
394
+ "tool_execution_id": str,
395
+ "member_name": str,
396
+ "status": str,
397
+ "tool_output": Any,
398
+ "tool_error": Any,
399
+ ...
400
+ },
401
+ "timestamp": str
402
+ }
403
+
404
+ Returns:
405
+ str: Formatted SSE event
406
+
407
+ Example:
408
+ >>> formatter = EventFormatter("exec-123")
409
+ >>> tool_data = {
410
+ ... "data": {
411
+ ... "tool_name": "search",
412
+ ... "tool_execution_id": "tool-789",
413
+ ... "member_name": "Researcher",
414
+ ... "status": "success",
415
+ ... "tool_output": {"results": [...]}
416
+ ... },
417
+ ... "timestamp": "2024-12-18T10:00:01Z"
418
+ ... }
419
+ >>> event = formatter.format_member_tool_completed_event(tool_data)
420
+ """
421
+ return self.format_event("member_tool_completed", tool_data)
422
+
423
+ def format_message_chunk_event(self, chunk_data: Dict[str, Any]) -> str:
424
+ """
425
+ Format 'message_chunk' event for streaming message chunks.
426
+
427
+ Used for real-time token streaming of assistant responses.
428
+
429
+ Args:
430
+ chunk_data: Chunk data with nested structure:
431
+ {
432
+ "data": {
433
+ "content": str,
434
+ "message_id": str,
435
+ "is_final": bool,
436
+ ...
437
+ },
438
+ "timestamp": str
439
+ }
440
+
441
+ Returns:
442
+ str: Formatted SSE event
443
+
444
+ Example:
445
+ >>> formatter = EventFormatter("exec-123")
446
+ >>> chunk_data = {
447
+ ... "data": {
448
+ ... "content": "Hello",
449
+ ... "message_id": "msg-456",
450
+ ... "is_final": False
451
+ ... },
452
+ ... "timestamp": "2024-12-18T10:00:00Z"
453
+ ... }
454
+ >>> event = formatter.format_message_chunk_event(chunk_data)
455
+ """
456
+ return self.format_event("message_chunk", chunk_data)
457
+
458
+ def format_member_message_chunk_event(self, chunk_data: Dict[str, Any]) -> str:
459
+ """
460
+ Format 'member_message_chunk' event for team member message chunks.
461
+
462
+ Used for streaming messages from team members in multi-agent scenarios.
463
+
464
+ Args:
465
+ chunk_data: Chunk data with nested structure similar to message_chunk
466
+
467
+ Returns:
468
+ str: Formatted SSE event
469
+ """
470
+ return self.format_event("member_message_chunk", chunk_data)
471
+
472
+ def format_member_message_complete_event(self, data: Dict[str, Any]) -> str:
473
+ """
474
+ Format 'member_message_complete' event for end of team member message.
475
+
476
+ Used to signal that a team member has finished streaming their message.
477
+
478
+ Args:
479
+ data: Event data with nested structure:
480
+ {
481
+ "data": {
482
+ "message_id": str,
483
+ "member_name": str,
484
+ ...
485
+ },
486
+ "timestamp": str
487
+ }
488
+
489
+ Returns:
490
+ str: Formatted SSE event
491
+ """
492
+ return self.format_event("member_message_complete", data)
493
+
494
+ # =========================================================================
495
+ # Thinking/Reasoning Event Methods (Extended Thinking Support)
496
+ # =========================================================================
497
+
498
+ def format_thinking_start_event(
499
+ self,
500
+ message_id: str,
501
+ index: int = 0,
502
+ budget_tokens: Optional[int] = None
503
+ ) -> str:
504
+ """
505
+ Format 'thinking_start' event for beginning of thinking block.
506
+
507
+ Sent when the model begins extended thinking/reasoning before generating
508
+ a response. This event signals the UI to show a thinking indicator.
509
+
510
+ Args:
511
+ message_id: ID of the message being generated
512
+ index: Content block index (default: 0)
513
+ budget_tokens: Optional thinking token budget configured for this request
514
+
515
+ Returns:
516
+ str: Formatted SSE event
517
+
518
+ Example:
519
+ >>> formatter = EventFormatter("exec-123")
520
+ >>> event = formatter.format_thinking_start_event("msg_abc", budget_tokens=10000)
521
+ >>> print(event)
522
+ id: exec-123_1_1702938457123456
523
+ event: thinking_start
524
+ data: {"execution_id": "exec-123", "message_id": "msg_abc", "index": 0, "budget_tokens": 10000}
525
+
526
+ """
527
+ data = {
528
+ "execution_id": self.execution_id,
529
+ "message_id": message_id,
530
+ "index": index,
531
+ }
532
+ if budget_tokens is not None:
533
+ data["budget_tokens"] = budget_tokens
534
+ return self.format_event("thinking_start", data)
535
+
536
+ def format_thinking_delta_event(
537
+ self,
538
+ message_id: str,
539
+ thinking: str,
540
+ index: int = 0
541
+ ) -> str:
542
+ """
543
+ Format 'thinking_delta' event for incremental thinking content.
544
+
545
+ Streams reasoning content as the model thinks through the problem.
546
+ Clients should accumulate deltas for the same message_id + index.
547
+
548
+ Args:
549
+ message_id: ID of the message being generated
550
+ thinking: Incremental thinking content (may be multiline with markdown)
551
+ index: Content block index (default: 0)
552
+
553
+ Returns:
554
+ str: Formatted SSE event
555
+
556
+ Example:
557
+ >>> formatter = EventFormatter("exec-123")
558
+ >>> event = formatter.format_thinking_delta_event(
559
+ ... "msg_abc",
560
+ ... "Step 1: Analyze the requirements\\n\\nFirst, I need to..."
561
+ ... )
562
+
563
+ """
564
+ data = {
565
+ "execution_id": self.execution_id,
566
+ "message_id": message_id,
567
+ "thinking": thinking,
568
+ "index": index,
569
+ }
570
+ return self.format_event("thinking_delta", data)
571
+
572
+ def format_thinking_complete_event(
573
+ self,
574
+ message_id: str,
575
+ index: int = 0,
576
+ signature: Optional[str] = None,
577
+ tokens_used: Optional[int] = None
578
+ ) -> str:
579
+ """
580
+ Format 'thinking_complete' event for end of thinking block.
581
+
582
+ Sent when thinking is complete, includes optional signature for verification.
583
+ The signature is opaque and should be stored/passed through unchanged.
584
+
585
+ Args:
586
+ message_id: ID of the message being generated
587
+ index: Content block index (default: 0)
588
+ signature: Optional verification signature from Anthropic API
589
+ tokens_used: Optional actual tokens used for thinking
590
+
591
+ Returns:
592
+ str: Formatted SSE event
593
+
594
+ Example:
595
+ >>> formatter = EventFormatter("exec-123")
596
+ >>> event = formatter.format_thinking_complete_event(
597
+ ... "msg_abc",
598
+ ... signature="EqQBCgIYAhIM...",
599
+ ... tokens_used=1024
600
+ ... )
601
+
602
+ """
603
+ data = {
604
+ "execution_id": self.execution_id,
605
+ "message_id": message_id,
606
+ "index": index,
607
+ }
608
+ if signature is not None:
609
+ data["signature"] = signature
610
+ if tokens_used is not None:
611
+ data["tokens_used"] = tokens_used
612
+ return self.format_event("thinking_complete", data)
613
+
614
+ def format_member_thinking_start_event(
615
+ self,
616
+ member_name: str,
617
+ index: int = 0,
618
+ member_id: Optional[str] = None,
619
+ budget_tokens: Optional[int] = None
620
+ ) -> str:
621
+ """
622
+ Format 'member_thinking_start' event for team member thinking.
623
+
624
+ Sent when a sub-agent/team member begins thinking.
625
+ Note: Sub-agents currently don't support transparent thinking mode,
626
+ this is reserved for future SDK enhancements.
627
+
628
+ Args:
629
+ member_name: Name of the team member/sub-agent
630
+ index: Content block index (default: 0)
631
+ member_id: Optional sub-agent ID
632
+ budget_tokens: Optional thinking token budget
633
+
634
+ Returns:
635
+ str: Formatted SSE event
636
+ """
637
+ data = {
638
+ "execution_id": self.execution_id,
639
+ "member_name": member_name,
640
+ "index": index,
641
+ }
642
+ if member_id is not None:
643
+ data["member_id"] = member_id
644
+ if budget_tokens is not None:
645
+ data["budget_tokens"] = budget_tokens
646
+ return self.format_event("member_thinking_start", data)
647
+
648
+ def format_member_thinking_delta_event(
649
+ self,
650
+ member_name: str,
651
+ thinking: str,
652
+ index: int = 0,
653
+ member_id: Optional[str] = None
654
+ ) -> str:
655
+ """
656
+ Format 'member_thinking_delta' event for team member thinking content.
657
+
658
+ Streams reasoning content from a sub-agent/team member.
659
+ Note: Sub-agents currently don't support transparent thinking mode,
660
+ this is reserved for future SDK enhancements.
661
+
662
+ Args:
663
+ member_name: Name of the team member/sub-agent
664
+ thinking: Incremental thinking content
665
+ index: Content block index (default: 0)
666
+ member_id: Optional sub-agent ID
667
+
668
+ Returns:
669
+ str: Formatted SSE event
670
+ """
671
+ data = {
672
+ "execution_id": self.execution_id,
673
+ "member_name": member_name,
674
+ "thinking": thinking,
675
+ "index": index,
676
+ }
677
+ if member_id is not None:
678
+ data["member_id"] = member_id
679
+ return self.format_event("member_thinking_delta", data)
680
+
681
+ def format_member_thinking_complete_event(
682
+ self,
683
+ member_name: str,
684
+ index: int = 0,
685
+ member_id: Optional[str] = None,
686
+ signature: Optional[str] = None,
687
+ tokens_used: Optional[int] = None
688
+ ) -> str:
689
+ """
690
+ Format 'member_thinking_complete' event for end of member thinking.
691
+
692
+ Sent when a sub-agent/team member completes thinking.
693
+ Note: Sub-agents currently don't support transparent thinking mode,
694
+ this is reserved for future SDK enhancements.
695
+
696
+ Args:
697
+ member_name: Name of the team member/sub-agent
698
+ index: Content block index (default: 0)
699
+ member_id: Optional sub-agent ID
700
+ signature: Optional verification signature
701
+ tokens_used: Optional actual tokens used
702
+
703
+ Returns:
704
+ str: Formatted SSE event
705
+ """
706
+ data = {
707
+ "execution_id": self.execution_id,
708
+ "member_name": member_name,
709
+ "index": index,
710
+ }
711
+ if member_id is not None:
712
+ data["member_id"] = member_id
713
+ if signature is not None:
714
+ data["signature"] = signature
715
+ if tokens_used is not None:
716
+ data["tokens_used"] = tokens_used
717
+ return self.format_event("member_thinking_complete", data)
718
+
719
+ def format_done_event(
720
+ self,
721
+ response: Any = None,
722
+ usage: Optional[Dict] = None,
723
+ workflow_status: Optional[str] = None,
724
+ messages: Optional[List[Dict[str, Any]]] = None
725
+ ) -> str:
726
+ """
727
+ Format 'done' event for successful completion.
728
+
729
+ Args:
730
+ response: Optional response data
731
+ usage: Optional usage statistics (tokens, cost, etc.)
732
+ workflow_status: Optional Temporal workflow status
733
+ messages: Optional full message history (for completed executions with no prior streaming)
734
+
735
+ Returns:
736
+ str: Formatted SSE event
737
+
738
+ Example:
739
+ >>> formatter = EventFormatter("exec-123")
740
+ >>> event = formatter.format_done_event(
741
+ ... response="Task completed",
742
+ ... usage={"tokens": 150, "cost": 0.003}
743
+ ... )
744
+ >>> print(event)
745
+ id: exec-123_1_1702938457123456
746
+ event: done
747
+ data: {"execution_id": "exec-123", "response": "Task completed", "usage": {"tokens": 150, "cost": 0.003}}
748
+
749
+ """
750
+ data = {
751
+ "execution_id": self.execution_id
752
+ }
753
+ if response is not None:
754
+ data["response"] = response
755
+ if usage is not None:
756
+ data["usage"] = usage
757
+ if workflow_status is not None:
758
+ data["workflow_status"] = workflow_status
759
+ if messages is not None:
760
+ data["messages"] = messages
761
+ return self.format_event("done", data)
762
+
763
+ def format_error_event(
764
+ self,
765
+ error: str,
766
+ error_type: str = "execution_error",
767
+ status: str = "failed"
768
+ ) -> str:
769
+ """
770
+ Format 'error' event for failures.
771
+
772
+ Args:
773
+ error: Error message
774
+ error_type: Type of error (default: "execution_error")
775
+ status: Execution status (default: "failed")
776
+
777
+ Returns:
778
+ str: Formatted SSE event
779
+
780
+ Example:
781
+ >>> formatter = EventFormatter("exec-123")
782
+ >>> event = formatter.format_error_event(
783
+ ... "Connection timeout",
784
+ ... error_type="timeout_error"
785
+ ... )
786
+ >>> print(event)
787
+ id: exec-123_1_1702938457123456
788
+ event: error
789
+ data: {"error": "Connection timeout", "error_type": "timeout_error", "execution_id": "exec-123", "status": "failed"}
790
+
791
+ """
792
+ data = {
793
+ "error": error,
794
+ "error_type": error_type,
795
+ "execution_id": self.execution_id,
796
+ "status": status
797
+ }
798
+ return self.format_event("error", data)
799
+
800
+ def format_degraded_event(
801
+ self,
802
+ mode: str,
803
+ reason: str,
804
+ message: Optional[str] = None,
805
+ capabilities: Optional[list] = None
806
+ ) -> str:
807
+ """
808
+ Format 'degraded' event for degraded mode notification.
809
+
810
+ Sent when the stream falls back to slower polling modes due to
811
+ infrastructure issues (Redis down, Temporal worker down, etc.).
812
+
813
+ Args:
814
+ mode: Degradation mode (e.g., "history_only", "live_only", "degraded", "unavailable")
815
+ reason: Reason for degraded mode (e.g., "redis_unavailable", "worker_down")
816
+ message: User-friendly explanation (optional)
817
+ capabilities: List of available capabilities in this mode (optional)
818
+
819
+ Returns:
820
+ str: Formatted SSE event
821
+
822
+ Example:
823
+ >>> formatter = EventFormatter("exec-123")
824
+ >>> event = formatter.format_degraded_event(
825
+ ... mode="history_only",
826
+ ... reason="redis_unavailable",
827
+ ... message="Real-time events unavailable, using workflow polling (slower updates)",
828
+ ... capabilities=["history"]
829
+ ... )
830
+ """
831
+ data = {
832
+ "execution_id": self.execution_id,
833
+ "mode": mode,
834
+ "reason": reason,
835
+ }
836
+ if message:
837
+ data["message"] = message
838
+ if capabilities is not None:
839
+ data["capabilities"] = capabilities
840
+ return self.format_event("degraded", data)
841
+
842
+ def format_recovered_event(
843
+ self,
844
+ message: str = "Services recovered, resuming full functionality"
845
+ ) -> str:
846
+ """
847
+ Format 'recovered' event for service recovery notification.
848
+
849
+ Sent when services recover from degraded mode back to full functionality.
850
+
851
+ Args:
852
+ message: Recovery notification message
853
+
854
+ Returns:
855
+ str: Formatted SSE event
856
+
857
+ Example:
858
+ >>> formatter = EventFormatter("exec-123")
859
+ >>> event = formatter.format_recovered_event()
860
+ >>> print(event)
861
+ id: exec-123_1_1702938457123456
862
+ event: recovered
863
+ data: {"execution_id": "exec-123", "message": "Services recovered, resuming full functionality"}
864
+
865
+ """
866
+ data = {
867
+ "execution_id": self.execution_id,
868
+ "message": message
869
+ }
870
+ return self.format_event("recovered", data)
871
+
872
+ def format_reconnect_event(
873
+ self,
874
+ reason: str,
875
+ duration: Optional[float] = None
876
+ ) -> str:
877
+ """
878
+ Format 'reconnect' event for server-requested reconnection.
879
+
880
+ Tells the client to gracefully reconnect (won't count as failed attempt).
881
+
882
+ Args:
883
+ reason: Reason for reconnect (e.g., "timeout", "server_restart")
884
+ duration: Optional duration in seconds before timeout
885
+
886
+ Returns:
887
+ str: Formatted SSE event
888
+
889
+ Example:
890
+ >>> formatter = EventFormatter("exec-123")
891
+ >>> event = formatter.format_reconnect_event("timeout", duration=300.5)
892
+ >>> print(event)
893
+ id: exec-123_1_1702938457123456
894
+ event: reconnect
895
+ data: {"reason": "timeout", "duration": 300.5}
896
+
897
+ """
898
+ data = {"reason": reason}
899
+ if duration is not None:
900
+ data["duration"] = duration
901
+ return self.format_event("reconnect", data)
902
+
903
+ def format_timeout_warning_event(self, remaining_seconds: int) -> str:
904
+ """
905
+ Format 'timeout_warning' event for connection timeout warnings.
906
+
907
+ Sent in the last 30 seconds before connection timeout.
908
+
909
+ Args:
910
+ remaining_seconds: Seconds remaining before timeout
911
+
912
+ Returns:
913
+ str: Formatted SSE event
914
+
915
+ Example:
916
+ >>> formatter = EventFormatter("exec-123")
917
+ >>> event = formatter.format_timeout_warning_event(15)
918
+ """
919
+ data = {"remaining_seconds": remaining_seconds}
920
+ return self.format_event("timeout_warning", data)
921
+
922
+ def format_gap_detected_event(
923
+ self,
924
+ reason: str,
925
+ buffer_oldest: Optional[str] = None
926
+ ) -> str:
927
+ """
928
+ Format 'gap_detected' event when event buffer can't recover gaps.
929
+
930
+ Signals client should reconnect to get missing events.
931
+
932
+ Args:
933
+ reason: Reason for gap (e.g., "Event buffer miss - events too old")
934
+ buffer_oldest: Oldest event ID in buffer
935
+
936
+ Returns:
937
+ str: Formatted SSE event
938
+ """
939
+ data = {
940
+ "execution_id": self.execution_id,
941
+ "reason": reason
942
+ }
943
+ if buffer_oldest is not None:
944
+ data["buffer_oldest"] = buffer_oldest
945
+ return self.format_event("gap_detected", data)
946
+
947
+ def format_keepalive(self) -> str:
948
+ """
949
+ Format keepalive comment (not an event).
950
+
951
+ SSE comments start with ':' and don't have id/event/data fields.
952
+ Prevents connection timeout during periods of no activity.
953
+
954
+ Returns:
955
+ str: SSE comment string
956
+
957
+ Example:
958
+ >>> formatter = EventFormatter("exec-123")
959
+ >>> keepalive = formatter.format_keepalive()
960
+ >>> print(keepalive)
961
+ : keepalive
962
+
963
+ """
964
+ return ": keepalive\n\n"