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,201 @@
1
+ """
2
+ Template validation logic.
3
+
4
+ Validates templates against a context to ensure all variables can be resolved.
5
+ """
6
+
7
+ import structlog
8
+ from typing import List
9
+ from control_plane_api.app.lib.templating.types import (
10
+ TemplateContext,
11
+ TemplateVariable,
12
+ TemplateVariableType,
13
+ ValidationResult,
14
+ ValidationError,
15
+ ParseResult,
16
+ )
17
+ from control_plane_api.app.lib.templating.engine import TemplateEngine, get_default_engine
18
+
19
+ logger = structlog.get_logger()
20
+
21
+
22
+ class TemplateValidator:
23
+ """
24
+ Validates templates against a context.
25
+
26
+ Checks that all variables referenced in a template have values available
27
+ in the provided context.
28
+ """
29
+
30
+ def __init__(self, engine: TemplateEngine = None):
31
+ """
32
+ Initialize the validator with a template engine.
33
+
34
+ Args:
35
+ engine: Template engine to use. If None, uses default engine.
36
+ """
37
+ self.engine = engine if engine is not None else get_default_engine()
38
+
39
+ def validate(self, template: str, context: TemplateContext) -> ValidationResult:
40
+ """
41
+ Validate a template against a context.
42
+
43
+ Performs the following checks:
44
+ 1. Template syntax is valid
45
+ 2. All referenced secrets exist in context
46
+ 3. All referenced env vars exist in context
47
+ 4. All simple variables exist in context
48
+
49
+ Args:
50
+ template: Template string to validate
51
+ context: Template context with available values
52
+
53
+ Returns:
54
+ ValidationResult with errors and warnings
55
+ """
56
+ # First parse the template
57
+ parse_result: ParseResult = self.engine.parse(template)
58
+
59
+ # If parsing failed, return those errors
60
+ if not parse_result.is_valid:
61
+ return ValidationResult(
62
+ valid=False,
63
+ errors=parse_result.errors,
64
+ warnings=[],
65
+ variables=parse_result.variables
66
+ )
67
+
68
+ # Now validate each variable against the context
69
+ errors: List[ValidationError] = []
70
+ warnings: List[str] = []
71
+
72
+ # Validate secret variables
73
+ for var in parse_result.secret_variables:
74
+ secret_name = var.display_name
75
+ if secret_name not in context.secrets:
76
+ errors.append(ValidationError(
77
+ message=f"Secret '{secret_name}' not found in context",
78
+ variable=var,
79
+ position=var.start,
80
+ code="MISSING_SECRET"
81
+ ))
82
+ logger.debug(
83
+ "missing_secret",
84
+ secret_name=secret_name,
85
+ position=var.start
86
+ )
87
+
88
+ # Validate environment variables
89
+ for var in parse_result.env_variables:
90
+ env_var_name = var.display_name
91
+ if env_var_name not in context.env_vars:
92
+ errors.append(ValidationError(
93
+ message=f"Environment variable '{env_var_name}' not found in context",
94
+ variable=var,
95
+ position=var.start,
96
+ code="MISSING_ENV_VAR"
97
+ ))
98
+ logger.debug(
99
+ "missing_env_var",
100
+ env_var_name=env_var_name,
101
+ position=var.start
102
+ )
103
+
104
+ # Validate simple variables
105
+ for var in parse_result.simple_variables:
106
+ if var.name not in context.variables:
107
+ errors.append(ValidationError(
108
+ message=f"Variable '{var.name}' not found in context",
109
+ variable=var,
110
+ position=var.start,
111
+ code="MISSING_VARIABLE"
112
+ ))
113
+ logger.debug(
114
+ "missing_variable",
115
+ variable_name=var.name,
116
+ position=var.start
117
+ )
118
+
119
+ # Validate graph node variables
120
+ for var in parse_result.graph_variables:
121
+ node_id = var.display_name
122
+
123
+ # Check if node is in pre-populated context
124
+ if context.graph_nodes and node_id in context.graph_nodes:
125
+ continue # Node is available
126
+
127
+ # Node not in context - check if we can fetch it
128
+ if not context.graph_api_base or not context.graph_api_key:
129
+ errors.append(ValidationError(
130
+ message=(
131
+ f"Graph node '{node_id}' not in context and "
132
+ f"context graph API not configured"
133
+ ),
134
+ variable=var,
135
+ position=var.start,
136
+ code="MISSING_GRAPH_NODE"
137
+ ))
138
+ logger.debug(
139
+ "missing_graph_node_config",
140
+ node_id=node_id,
141
+ position=var.start,
142
+ has_api_base=bool(context.graph_api_base),
143
+ has_api_key=bool(context.graph_api_key)
144
+ )
145
+ # If API is configured, node will be fetched on-demand during compilation
146
+ # We don't validate existence here to avoid unnecessary API calls
147
+
148
+ # Generate warnings for unused context values
149
+ # (This helps catch typos or configuration issues)
150
+ if not errors: # Only show warnings if validation passed
151
+ # Check for unused secrets
152
+ used_secrets = {v.display_name for v in parse_result.secret_variables}
153
+ unused_secrets = set(context.secrets.keys()) - used_secrets
154
+ if unused_secrets:
155
+ warnings.append(
156
+ f"Unused secrets in context: {', '.join(sorted(unused_secrets))}"
157
+ )
158
+
159
+ # Check for unused env vars
160
+ used_env_vars = {v.display_name for v in parse_result.env_variables}
161
+ unused_env_vars = set(context.env_vars.keys()) - used_env_vars
162
+ if unused_env_vars:
163
+ warnings.append(
164
+ f"Unused environment variables in context: {', '.join(sorted(unused_env_vars))}"
165
+ )
166
+
167
+ # Check for unused simple variables
168
+ used_variables = {v.name for v in parse_result.simple_variables}
169
+ unused_variables = set(context.variables.keys()) - used_variables
170
+ if unused_variables:
171
+ warnings.append(
172
+ f"Unused variables in context: {', '.join(sorted(unused_variables))}"
173
+ )
174
+
175
+ return ValidationResult(
176
+ valid=len(errors) == 0,
177
+ errors=errors,
178
+ warnings=warnings,
179
+ variables=parse_result.variables
180
+ )
181
+
182
+ def validate_syntax_only(self, template: str) -> ValidationResult:
183
+ """
184
+ Validate only the syntax of a template without checking context.
185
+
186
+ Useful for validating templates before a context is available.
187
+
188
+ Args:
189
+ template: Template string to validate
190
+
191
+ Returns:
192
+ ValidationResult with syntax errors only
193
+ """
194
+ parse_result: ParseResult = self.engine.parse(template)
195
+
196
+ return ValidationResult(
197
+ valid=parse_result.is_valid,
198
+ errors=parse_result.errors,
199
+ warnings=[],
200
+ variables=parse_result.variables
201
+ )
@@ -0,0 +1,232 @@
1
+ """Temporal client for Agent Control Plane API"""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional
6
+ import structlog
7
+ from temporalio.client import Client, TLSConfig
8
+
9
+ logger = structlog.get_logger()
10
+
11
+ _temporal_client: Optional[Client] = None
12
+
13
+
14
+ async def get_temporal_client() -> Client:
15
+ """
16
+ Get or create Temporal client singleton.
17
+
18
+ DEPRECATED: This uses shared admin credentials from env vars.
19
+ For org-specific credentials, use get_temporal_client_for_org() instead.
20
+
21
+ Supports mTLS authentication for Temporal Cloud.
22
+ This client is used by the API to submit workflows.
23
+
24
+ Returns:
25
+ Temporal client instance
26
+ """
27
+ global _temporal_client
28
+
29
+ if _temporal_client is not None:
30
+ return _temporal_client
31
+
32
+ # Get Temporal configuration with defaults matching worker_queues.py
33
+ temporal_host = os.environ.get("TEMPORAL_HOST", "us-east-1.aws.api.temporal.io:7233")
34
+ temporal_namespace = os.environ.get("TEMPORAL_NAMESPACE", "agent-control-plane.lpagu")
35
+ # Check for API key in multiple possible env var names
36
+ temporal_api_key = (
37
+ os.environ.get("TEMPORAL_API_KEY") or
38
+ os.environ.get("TEMPORAL_CLOUD_ADMIN_TOKEN")
39
+ )
40
+ # Strip whitespace and newlines from all env vars (common issue with env vars)
41
+ if temporal_host:
42
+ temporal_host = temporal_host.strip()
43
+ if temporal_namespace:
44
+ temporal_namespace = temporal_namespace.strip()
45
+ if temporal_api_key:
46
+ temporal_api_key = temporal_api_key.strip()
47
+ temporal_cert_path = os.environ.get("TEMPORAL_CLIENT_CERT_PATH")
48
+ temporal_key_path = os.environ.get("TEMPORAL_CLIENT_KEY_PATH")
49
+
50
+ try:
51
+ # Check if connecting to Temporal Cloud
52
+ is_cloud = "tmprl.cloud" in temporal_host or "api.temporal.io" in temporal_host
53
+
54
+ if is_cloud:
55
+ # Check authentication method: API Key or mTLS
56
+ if temporal_api_key:
57
+ # API Key authentication
58
+ logger.info("temporal_auth_method", method="api_key")
59
+
60
+ # Connect with TLS and API key
61
+ _temporal_client = await Client.connect(
62
+ temporal_host,
63
+ namespace=temporal_namespace,
64
+ tls=TLSConfig(), # TLS without client cert
65
+ rpc_metadata={"authorization": f"Bearer {temporal_api_key}"}
66
+ )
67
+ elif temporal_cert_path:
68
+ # mTLS authentication
69
+ logger.info("temporal_auth_method", method="mtls")
70
+
71
+ # Load client certificate
72
+ cert_path = Path(temporal_cert_path)
73
+ if not cert_path.exists():
74
+ raise FileNotFoundError(
75
+ f"Temporal client certificate not found at {cert_path}"
76
+ )
77
+
78
+ with open(cert_path, "rb") as f:
79
+ cert_content = f.read()
80
+
81
+ # Check if private key is in same file or separate
82
+ if b"BEGIN PRIVATE KEY" in cert_content or b"BEGIN RSA PRIVATE KEY" in cert_content:
83
+ # Key is in the same file
84
+ client_cert = cert_content
85
+ client_key = cert_content
86
+ else:
87
+ # Key must be in separate file
88
+ if not temporal_key_path:
89
+ raise ValueError(
90
+ "Private key not found in certificate file and no separate key path configured. "
91
+ "Please provide TEMPORAL_CLIENT_KEY_PATH environment variable."
92
+ )
93
+ key_path = Path(temporal_key_path)
94
+ with open(key_path, "rb") as f:
95
+ client_key = f.read()
96
+ client_cert = cert_content
97
+
98
+ # Create TLS config for mTLS
99
+ tls_config = TLSConfig(
100
+ client_cert=client_cert,
101
+ client_private_key=client_key,
102
+ )
103
+
104
+ # Connect to Temporal Cloud with mTLS
105
+ _temporal_client = await Client.connect(
106
+ temporal_host,
107
+ namespace=temporal_namespace,
108
+ tls=tls_config,
109
+ )
110
+ else:
111
+ raise ValueError(
112
+ "For Temporal Cloud connection, either TEMPORAL_API_KEY or TEMPORAL_CLIENT_CERT_PATH must be provided"
113
+ )
114
+ else:
115
+ # Local Temporal server (no authentication required)
116
+ _temporal_client = await Client.connect(
117
+ temporal_host,
118
+ namespace=temporal_namespace,
119
+ )
120
+
121
+ logger.info(
122
+ "temporal_client_connected",
123
+ host=temporal_host,
124
+ namespace=temporal_namespace,
125
+ )
126
+
127
+ return _temporal_client
128
+
129
+ except Exception as e:
130
+ logger.error("temporal_client_connection_failed", error=str(e))
131
+ raise
132
+
133
+
134
+ async def get_temporal_client_for_org(
135
+ namespace: str,
136
+ api_key: str,
137
+ host: Optional[str] = None,
138
+ ) -> Client:
139
+ """
140
+ Create Temporal client for specific organization.
141
+
142
+ This creates a new client instance with org-specific credentials.
143
+ Should be used for all workflow operations to ensure proper multi-tenant isolation.
144
+
145
+ Args:
146
+ namespace: Temporal namespace (e.g., "kubiya-ai.lpagu")
147
+ api_key: Temporal API key for the namespace (empty for local Temporal)
148
+ host: Temporal host (optional, uses TEMPORAL_HOST env var if not provided)
149
+
150
+ Returns:
151
+ Temporal client instance configured for the organization
152
+
153
+ Raises:
154
+ Exception: If connection fails
155
+
156
+ Example:
157
+ client = await get_temporal_client_for_org(
158
+ namespace="kubiya-ai.lpagu",
159
+ api_key="temporal-api-key-123",
160
+ host="us-east-1.aws.api.temporal.io:7233"
161
+ )
162
+ """
163
+ if not host:
164
+ host = os.environ.get("TEMPORAL_HOST", "us-east-1.aws.api.temporal.io:7233")
165
+
166
+ # Strip whitespace
167
+ host = host.strip()
168
+ namespace = namespace.strip()
169
+ api_key = api_key.strip() if api_key else ""
170
+
171
+ try:
172
+ # Check if connecting to Temporal Cloud
173
+ is_cloud = "tmprl.cloud" in host or "api.temporal.io" in host
174
+
175
+ if is_cloud:
176
+ if not api_key:
177
+ raise ValueError("API key is required for Temporal Cloud")
178
+
179
+ logger.info(
180
+ "creating_temporal_client_for_org",
181
+ namespace=namespace,
182
+ host=host,
183
+ auth_method="api_key"
184
+ )
185
+
186
+ # Connect with TLS and API key
187
+ client = await Client.connect(
188
+ host,
189
+ namespace=namespace,
190
+ tls=TLSConfig(),
191
+ rpc_metadata={"authorization": f"Bearer {api_key}"}
192
+ )
193
+ else:
194
+ # Local Temporal server
195
+ logger.info(
196
+ "creating_temporal_client_for_org",
197
+ namespace=namespace,
198
+ host=host,
199
+ auth_method="none"
200
+ )
201
+
202
+ client = await Client.connect(
203
+ host,
204
+ namespace=namespace,
205
+ )
206
+
207
+ logger.info(
208
+ "temporal_client_created_for_org",
209
+ namespace=namespace,
210
+ host=host,
211
+ )
212
+
213
+ return client
214
+
215
+ except Exception as e:
216
+ logger.error(
217
+ "temporal_client_creation_failed",
218
+ error=str(e),
219
+ namespace=namespace,
220
+ host=host
221
+ )
222
+ raise
223
+
224
+
225
+ async def close_temporal_client() -> None:
226
+ """Close the Temporal client connection"""
227
+ global _temporal_client
228
+
229
+ if _temporal_client is not None:
230
+ await _temporal_client.close()
231
+ _temporal_client = None
232
+ logger.info("temporal_client_closed")
@@ -0,0 +1,178 @@
1
+ """Temporal credentials caching service.
2
+
3
+ This module provides caching functionality for organization-specific Temporal
4
+ credentials fetched from the Kubiya API. Credentials are cached in Redis with
5
+ TTL calculated from the credentials expiry time.
6
+ """
7
+
8
+ import json
9
+ from datetime import datetime
10
+ from typing import Optional, Dict
11
+ import structlog
12
+
13
+ from control_plane_api.app.lib.redis_client import get_redis_client
14
+
15
+ logger = structlog.get_logger()
16
+
17
+
18
+ def get_credentials_cache_key(org_id: str) -> str:
19
+ """
20
+ Generate cache key for organization's Temporal credentials.
21
+
22
+ Args:
23
+ org_id: Organization ID (slug or UUID)
24
+
25
+ Returns:
26
+ Cache key in format: temporal:credentials:{org_id}
27
+ """
28
+ return f"temporal:credentials:{org_id}"
29
+
30
+
31
+ async def get_cached_temporal_credentials(org_id: str) -> Optional[Dict]:
32
+ """
33
+ Get cached Temporal credentials for organization.
34
+
35
+ Args:
36
+ org_id: Organization ID
37
+
38
+ Returns:
39
+ Cached credentials dict or None if not found
40
+
41
+ Example:
42
+ {
43
+ "apiKey": "...",
44
+ "namespace": "org-slug.lpagu",
45
+ "org": "org-slug",
46
+ "ttl": "2026-01-07T14:38:20Z"
47
+ }
48
+ """
49
+ redis = get_redis_client()
50
+ if not redis:
51
+ return None
52
+
53
+ try:
54
+ cache_key = get_credentials_cache_key(org_id)
55
+ cached_data = await redis.get(cache_key)
56
+
57
+ if cached_data:
58
+ logger.debug("temporal_credentials_cache_hit", org_id=org_id)
59
+ if isinstance(cached_data, bytes):
60
+ cached_data = cached_data.decode('utf-8')
61
+ return json.loads(cached_data)
62
+
63
+ logger.debug("temporal_credentials_cache_miss", org_id=org_id)
64
+ return None
65
+
66
+ except Exception as e:
67
+ logger.warning(
68
+ "temporal_credentials_cache_read_failed",
69
+ error=str(e),
70
+ org_id=org_id
71
+ )
72
+ return None
73
+
74
+
75
+ async def cache_temporal_credentials(org_id: str, credentials: Dict) -> None:
76
+ """
77
+ Cache Temporal credentials for organization with TTL.
78
+
79
+ The TTL is calculated from the credentials.ttl field if present,
80
+ otherwise defaults to 1 hour. TTL is clamped between 60 seconds
81
+ and 7 days.
82
+
83
+ Args:
84
+ org_id: Organization ID
85
+ credentials: Credentials dict with optional 'ttl' field
86
+
87
+ Example:
88
+ await cache_temporal_credentials(
89
+ "kubiya-ai",
90
+ {
91
+ "apiKey": "...",
92
+ "namespace": "kubiya-ai.lpagu",
93
+ "org": "kubiya-ai",
94
+ "ttl": "2026-01-07T14:38:20Z"
95
+ }
96
+ )
97
+ """
98
+ redis = get_redis_client()
99
+ if not redis:
100
+ return
101
+
102
+ try:
103
+ cache_key = get_credentials_cache_key(org_id)
104
+
105
+ # Calculate TTL from credentials.ttl field
106
+ ttl_str = credentials.get("ttl")
107
+ if ttl_str:
108
+ try:
109
+ # Parse ISO 8601 timestamp
110
+ ttl_datetime = datetime.fromisoformat(ttl_str.replace('Z', '+00:00'))
111
+ now = datetime.now(ttl_datetime.tzinfo)
112
+ ttl_seconds = int((ttl_datetime - now).total_seconds())
113
+
114
+ # Ensure reasonable TTL (min 60s, max 7 days)
115
+ ttl_seconds = max(60, min(ttl_seconds, 7 * 24 * 3600))
116
+
117
+ logger.debug(
118
+ "ttl_calculated_from_expiry",
119
+ org_id=org_id,
120
+ ttl_seconds=ttl_seconds,
121
+ expires_at=ttl_str
122
+ )
123
+ except Exception as e:
124
+ logger.warning(
125
+ "ttl_parse_failed_using_default",
126
+ error=str(e),
127
+ ttl_str=ttl_str
128
+ )
129
+ ttl_seconds = 3600 # Default 1 hour
130
+ else:
131
+ ttl_seconds = 3600 # Default 1 hour
132
+
133
+ # Store credentials as JSON
134
+ await redis.set(
135
+ cache_key,
136
+ json.dumps(credentials),
137
+ ex=ttl_seconds
138
+ )
139
+
140
+ logger.info(
141
+ "temporal_credentials_cached",
142
+ org_id=org_id,
143
+ ttl_seconds=ttl_seconds,
144
+ namespace=credentials.get("namespace")
145
+ )
146
+
147
+ except Exception as e:
148
+ logger.warning(
149
+ "temporal_credentials_cache_write_failed",
150
+ error=str(e),
151
+ org_id=org_id
152
+ )
153
+
154
+
155
+ async def invalidate_temporal_credentials_cache(org_id: str) -> None:
156
+ """
157
+ Invalidate cached Temporal credentials for organization.
158
+
159
+ This is useful for forcing a credential refresh, for example
160
+ when credentials are rotated or revoked.
161
+
162
+ Args:
163
+ org_id: Organization ID
164
+ """
165
+ redis = get_redis_client()
166
+ if not redis:
167
+ return
168
+
169
+ try:
170
+ cache_key = get_credentials_cache_key(org_id)
171
+ await redis.delete(cache_key)
172
+ logger.info("temporal_credentials_cache_invalidated", org_id=org_id)
173
+ except Exception as e:
174
+ logger.warning(
175
+ "temporal_credentials_cache_invalidation_failed",
176
+ error=str(e),
177
+ org_id=org_id
178
+ )