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,357 @@
1
+ """
2
+ Filesystem Skill Loader
3
+
4
+ Discovers skills from filesystem directories by scanning for skill.yaml files.
5
+ """
6
+ import yaml
7
+ import importlib.util
8
+ import sys
9
+ import json
10
+ from pathlib import Path
11
+ from typing import List, Type, Dict, Any
12
+ import structlog
13
+
14
+ from .base import BaseSkillLoader
15
+ from control_plane_api.worker.skills.registry import LoadedSkill, SkillSource
16
+
17
+ logger = structlog.get_logger()
18
+
19
+ # Try to import jsonschema for validation (optional dependency)
20
+ try:
21
+ import jsonschema
22
+ JSONSCHEMA_AVAILABLE = True
23
+ except ImportError:
24
+ JSONSCHEMA_AVAILABLE = False
25
+ logger.warning(
26
+ "jsonschema_not_available",
27
+ message="Install jsonschema package for skill.yaml validation: pip install jsonschema"
28
+ )
29
+
30
+
31
+ def validate_skill_yaml(manifest: Dict[str, Any], yaml_path: Path) -> None:
32
+ """
33
+ Issue #6 Fix: Validate skill.yaml structure against JSON schema.
34
+
35
+ Args:
36
+ manifest: Parsed YAML manifest
37
+ yaml_path: Path to the YAML file for error reporting
38
+
39
+ Raises:
40
+ ValueError: If validation fails
41
+ """
42
+ if not JSONSCHEMA_AVAILABLE:
43
+ logger.debug(
44
+ "skipping_skill_yaml_validation",
45
+ yaml_path=str(yaml_path),
46
+ reason="jsonschema not installed",
47
+ )
48
+ return
49
+
50
+ # Load schema
51
+ schema_path = Path(__file__).parent.parent / "skill_yaml_schema.json"
52
+ if not schema_path.exists():
53
+ logger.warning(
54
+ "skill_yaml_schema_not_found",
55
+ schema_path=str(schema_path),
56
+ yaml_path=str(yaml_path),
57
+ )
58
+ return
59
+
60
+ try:
61
+ with open(schema_path) as f:
62
+ schema = json.load(f)
63
+
64
+ # Validate
65
+ jsonschema.validate(instance=manifest, schema=schema)
66
+
67
+ logger.debug(
68
+ "skill_yaml_validation_passed",
69
+ yaml_path=str(yaml_path),
70
+ skill_name=manifest.get("metadata", {}).get("name", "unknown"),
71
+ )
72
+
73
+ except jsonschema.ValidationError as e:
74
+ # Build helpful error message
75
+ error_path = " -> ".join(str(p) for p in e.path) if e.path else "root"
76
+ error_msg = (
77
+ f"Invalid skill.yaml at {yaml_path}\n"
78
+ f"Location: {error_path}\n"
79
+ f"Error: {e.message}\n\n"
80
+ f"Schema requirement: {e.schema.get('description', 'No description')}\n"
81
+ )
82
+
83
+ # Add suggestions based on common errors
84
+ if "required" in e.message.lower():
85
+ error_msg += f"\nMissing required fields. Check the skill.yaml schema.\n"
86
+ elif "enum" in str(e.schema):
87
+ allowed = e.schema.get("enum", [])
88
+ error_msg += f"\nAllowed values: {allowed}\n"
89
+
90
+ logger.error(
91
+ "skill_yaml_validation_failed",
92
+ yaml_path=str(yaml_path),
93
+ error_path=error_path,
94
+ error_message=e.message,
95
+ schema_path=str(schema_path),
96
+ )
97
+
98
+ raise ValueError(error_msg)
99
+
100
+ except Exception as e:
101
+ logger.error(
102
+ "skill_yaml_validation_error",
103
+ yaml_path=str(yaml_path),
104
+ error=str(e),
105
+ exc_info=True,
106
+ )
107
+ raise ValueError(f"Failed to validate skill.yaml at {yaml_path}: {str(e)}")
108
+
109
+
110
+ class FilesystemSkillLoader(BaseSkillLoader):
111
+ """
112
+ Load skills from filesystem directories.
113
+
114
+ Scans for skill.yaml files in configured paths.
115
+ """
116
+
117
+ def __init__(self, search_paths: List[Path]):
118
+ self.search_paths = [Path(p) for p in search_paths]
119
+ self.logger = structlog.get_logger()
120
+
121
+ def discover(self) -> List[LoadedSkill]:
122
+ """
123
+ Discover skills by scanning filesystem for skill.yaml files.
124
+ """
125
+ skills = []
126
+
127
+ for search_path in self.search_paths:
128
+ if not search_path.exists():
129
+ self.logger.debug("search_path_not_found", path=str(search_path))
130
+ continue
131
+
132
+ # Find all skill.yaml files
133
+ for skill_yaml in search_path.rglob("skill.yaml"):
134
+ try:
135
+ skill = self._load_skill_from_yaml(skill_yaml)
136
+ skills.append(skill)
137
+ self.logger.info(
138
+ "skill_discovered",
139
+ name=skill.name,
140
+ path=str(skill_yaml.parent),
141
+ )
142
+ except Exception as e:
143
+ self.logger.error(
144
+ "failed_to_load_skill",
145
+ path=str(skill_yaml),
146
+ error=str(e),
147
+ exc_info=True,
148
+ )
149
+
150
+ return skills
151
+
152
+ def _load_skill_from_yaml(self, yaml_path: Path) -> LoadedSkill:
153
+ """Load a skill from a skill.yaml file."""
154
+ with open(yaml_path) as f:
155
+ manifest = yaml.safe_load(f)
156
+
157
+ # Issue #6 Fix: Validate skill.yaml before parsing
158
+ validate_skill_yaml(manifest, yaml_path)
159
+
160
+ skill_dir = yaml_path.parent
161
+ metadata = manifest.get("metadata", {})
162
+ spec = manifest.get("spec", {})
163
+
164
+ # Load implementations
165
+ implementations = {}
166
+ impl_configs = spec.get("implementations", {})
167
+
168
+ if not impl_configs:
169
+ raise ValueError(f"No implementations defined in {yaml_path}")
170
+
171
+ # Track failed implementations for better error reporting
172
+ failed_implementations = {}
173
+
174
+ for runtime, impl_info in impl_configs.items():
175
+ try:
176
+ # Check if this is a builtin runtime implementation
177
+ if impl_info.get("builtin", False):
178
+ # Builtin implementations (like Claude Code SDK tools)
179
+ # Store metadata about builtin tools instead of Python class
180
+ implementations[runtime] = {
181
+ "builtin": True,
182
+ "tools": impl_info.get("tools", []),
183
+ }
184
+ self.logger.debug(
185
+ "builtin_implementation_registered",
186
+ skill=metadata["name"],
187
+ runtime=runtime,
188
+ tools=impl_info.get("tools", []),
189
+ )
190
+ continue
191
+
192
+ module_name = impl_info["module"]
193
+ class_name = impl_info["class"]
194
+
195
+ # Import the module
196
+ impl_class = self._import_class_from_skill(
197
+ skill_dir, module_name, class_name
198
+ )
199
+ implementations[runtime] = impl_class
200
+
201
+ self.logger.info(
202
+ "implementation_loaded_successfully",
203
+ skill=metadata["name"],
204
+ runtime=runtime,
205
+ class_name=class_name,
206
+ module_path=str(skill_dir / f"{module_name}.py"),
207
+ )
208
+ except FileNotFoundError as e:
209
+ # Issue #3 Fix: Fail loudly with actionable error
210
+ error_detail = str(e)
211
+ failed_implementations[runtime] = error_detail
212
+ self.logger.error(
213
+ "implementation_file_not_found",
214
+ skill=metadata["name"],
215
+ runtime=runtime,
216
+ error=error_detail,
217
+ module_name=module_name,
218
+ expected_path=str(skill_dir / f"{module_name}.py"),
219
+ recovery_suggestion=f"Create the file '{module_name}.py' in {skill_dir} or update the skill.yaml to reference an existing module.",
220
+ )
221
+ except AttributeError as e:
222
+ # Issue #3 Fix: Class not found in module
223
+ error_detail = str(e)
224
+ failed_implementations[runtime] = error_detail
225
+ self.logger.error(
226
+ "implementation_class_not_found",
227
+ skill=metadata["name"],
228
+ runtime=runtime,
229
+ error=error_detail,
230
+ class_name=class_name,
231
+ module_name=module_name,
232
+ recovery_suggestion=f"Ensure class '{class_name}' exists in '{module_name}.py' or update the skill.yaml with the correct class name.",
233
+ )
234
+ except ImportError as e:
235
+ # Issue #3 Fix: Module import failed
236
+ error_detail = str(e)
237
+ failed_implementations[runtime] = error_detail
238
+ self.logger.error(
239
+ "implementation_import_failed",
240
+ skill=metadata["name"],
241
+ runtime=runtime,
242
+ error=error_detail,
243
+ module_name=module_name,
244
+ recovery_suggestion=f"Check that '{module_name}.py' is valid Python and all dependencies are installed. Error: {error_detail}",
245
+ )
246
+ except Exception as e:
247
+ # Issue #3 Fix: Generic implementation failure
248
+ error_detail = str(e)
249
+ failed_implementations[runtime] = error_detail
250
+ self.logger.error(
251
+ "implementation_load_failed",
252
+ skill=metadata["name"],
253
+ runtime=runtime,
254
+ error=error_detail,
255
+ error_type=type(e).__name__,
256
+ recovery_suggestion="Check the implementation file for syntax errors or missing dependencies.",
257
+ exc_info=True,
258
+ )
259
+
260
+ # Issue #3 Fix: Fail loudly if no valid implementations
261
+ if not implementations:
262
+ error_summary = "\n".join([
263
+ f" - {runtime}: {error}"
264
+ for runtime, error in failed_implementations.items()
265
+ ])
266
+ raise ValueError(
267
+ f"Failed to load any implementations for skill '{metadata['name']}' from {yaml_path}.\n"
268
+ f"All {len(impl_configs)} runtime(s) failed:\n{error_summary}\n\n"
269
+ f"Recovery steps:\n"
270
+ f"1. Check that implementation files exist in {skill_dir}\n"
271
+ f"2. Verify module and class names in skill.yaml match actual files\n"
272
+ f"3. Ensure all dependencies are installed\n"
273
+ f"4. Check implementation files for syntax errors"
274
+ )
275
+
276
+ return LoadedSkill(
277
+ name=metadata["name"],
278
+ version=metadata.get("version", "0.0.0"),
279
+ source=self._get_source_for_path(yaml_path),
280
+ skill_type=spec.get("type", "custom"),
281
+ manifest=manifest,
282
+ implementations=implementations,
283
+ metadata={
284
+ "path": str(skill_dir),
285
+ "yaml_path": str(yaml_path),
286
+ },
287
+ )
288
+
289
+ def _import_class_from_skill(
290
+ self, skill_dir: Path, module_name: str, class_name: str
291
+ ) -> Type:
292
+ """
293
+ Dynamically import a class from a skill's module.
294
+
295
+ Args:
296
+ skill_dir: Directory containing the skill
297
+ module_name: Module name (e.g., "implementation" or "agno_impl")
298
+ class_name: Class name to import
299
+
300
+ Returns:
301
+ The imported class
302
+ """
303
+ # Convert module name to file path
304
+ module_file = skill_dir / f"{module_name}.py"
305
+
306
+ if not module_file.exists():
307
+ raise FileNotFoundError(f"Module file not found: {module_file}")
308
+
309
+ # Create unique module name to avoid collisions
310
+ unique_module_name = f"skill_{skill_dir.name}_{module_name}"
311
+
312
+ # Load the module
313
+ spec = importlib.util.spec_from_file_location(unique_module_name, module_file)
314
+ if spec is None or spec.loader is None:
315
+ raise ImportError(f"Could not load module spec from {module_file}")
316
+
317
+ module = importlib.util.module_from_spec(spec)
318
+ sys.modules[unique_module_name] = module
319
+ spec.loader.exec_module(module)
320
+
321
+ # Get the class
322
+ if not hasattr(module, class_name):
323
+ raise AttributeError(
324
+ f"Class '{class_name}' not found in module {module_file}"
325
+ )
326
+
327
+ return getattr(module, class_name)
328
+
329
+ def _get_source_for_path(self, yaml_path: Path) -> SkillSource:
330
+ """Determine source type based on path."""
331
+ path_str = str(yaml_path.resolve())
332
+
333
+ # Check if it's in user workspace (.kubiya/skills in current dir)
334
+ if ".kubiya/skills" in path_str and Path.cwd() in yaml_path.parents:
335
+ return SkillSource.USER_WORKSPACE
336
+
337
+ # Check if it's in global user directory (~/.kubiya/skills)
338
+ global_skills = Path.home() / ".kubiya/skills"
339
+ if global_skills in yaml_path.parents:
340
+ return SkillSource.USER_GLOBAL
341
+
342
+ # Default to builtin
343
+ return SkillSource.BUILTIN
344
+
345
+ def get_source_type(self) -> SkillSource:
346
+ return SkillSource.USER_WORKSPACE
347
+
348
+ def load_skill(self, skill_id: str) -> LoadedSkill:
349
+ """Load a specific skill by searching for its directory."""
350
+ for search_path in self.search_paths:
351
+ skill_path = search_path / skill_id / "skill.yaml"
352
+ if skill_path.exists():
353
+ return self._load_skill_from_yaml(skill_path)
354
+
355
+ raise FileNotFoundError(
356
+ f"Skill '{skill_id}' not found in search paths: {self.search_paths}"
357
+ )
@@ -0,0 +1,208 @@
1
+ """
2
+ Worker-side Skill Registry
3
+
4
+ Central registry for all skills available to the worker.
5
+ Discovers and tracks skills from multiple sources (filesystem, API, packages).
6
+ """
7
+ from typing import Dict, List, Optional, Type, Any, Union
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ import structlog
11
+
12
+ logger = structlog.get_logger()
13
+
14
+
15
+ class SkillSource(str, Enum):
16
+ """Source where skill was loaded from."""
17
+ CONTROL_PLANE_API = "api" # Centralized skills from DB
18
+ USER_WORKSPACE = "workspace" # .kubiya/skills/ in project
19
+ USER_GLOBAL = "global" # ~/.kubiya/skills/
20
+ PYTHON_PACKAGE = "package" # pip installed packages
21
+ GIT_REPOSITORY = "git" # Git repos
22
+ BUILTIN = "builtin" # Built-in skills
23
+
24
+
25
+ @dataclass
26
+ class LoadedSkill:
27
+ """A loaded and ready-to-use skill."""
28
+ name: str
29
+ version: str
30
+ source: SkillSource
31
+ skill_type: str # e.g., "shell", "file_system", "custom"
32
+ manifest: Dict[str, Any] # Parsed skill.yaml or metadata
33
+ implementations: Dict[str, Union[Type, Dict[str, Any]]] # Runtime -> class or builtin config
34
+ metadata: Dict[str, Any] # Additional metadata (path, etc.)
35
+
36
+
37
+ class SkillRegistry:
38
+ """
39
+ Central registry for all skills on worker side.
40
+
41
+ Responsibilities:
42
+ - Track loaded skills
43
+ - Resolve dependencies
44
+ - Handle versioning
45
+ - Match skills to runtimes
46
+ """
47
+
48
+ def __init__(self):
49
+ self._skills: Dict[str, LoadedSkill] = {}
50
+ self._skills_by_type: Dict[str, List[LoadedSkill]] = {}
51
+ self.logger = structlog.get_logger()
52
+
53
+ def register(self, skill: LoadedSkill) -> None:
54
+ """Register a loaded skill."""
55
+ skill_key = f"{skill.name}:{skill.version}"
56
+
57
+ if skill_key in self._skills:
58
+ self.logger.warning(
59
+ "skill_already_registered",
60
+ name=skill.name,
61
+ version=skill.version,
62
+ existing_source=self._skills[skill_key].source,
63
+ new_source=skill.source,
64
+ )
65
+ # Source priority: builtin < global < workspace < git < package < api
66
+ if self._should_replace(self._skills[skill_key].source, skill.source):
67
+ self._skills[skill_key] = skill
68
+ self.logger.info("skill_replaced", name=skill.name)
69
+ else:
70
+ self._skills[skill_key] = skill
71
+ self.logger.info(
72
+ "skill_registered",
73
+ name=skill.name,
74
+ version=skill.version,
75
+ source=skill.source,
76
+ skill_type=skill.skill_type,
77
+ )
78
+
79
+ # Index by type for faster lookup
80
+ if skill.skill_type not in self._skills_by_type:
81
+ self._skills_by_type[skill.skill_type] = []
82
+
83
+ # Remove old version if exists
84
+ self._skills_by_type[skill.skill_type] = [
85
+ s for s in self._skills_by_type[skill.skill_type]
86
+ if s.name != skill.name
87
+ ]
88
+ self._skills_by_type[skill.skill_type].append(skill)
89
+
90
+ def get(self, name: str, version: Optional[str] = None) -> Optional[LoadedSkill]:
91
+ """
92
+ Get a skill by name and optional version.
93
+
94
+ If version not specified, returns latest version.
95
+ """
96
+ if version:
97
+ return self._skills.get(f"{name}:{version}")
98
+
99
+ # Get latest version
100
+ matching = [s for s in self._skills.values() if s.name == name]
101
+ if not matching:
102
+ return None
103
+
104
+ # Sort by semantic version
105
+ try:
106
+ from packaging import version as pkg_version
107
+ return max(matching, key=lambda s: pkg_version.parse(s.version))
108
+ except:
109
+ # Fallback to simple comparison if packaging not available
110
+ return matching[0]
111
+
112
+ def get_by_type(self, skill_type: str) -> Optional[LoadedSkill]:
113
+ """
114
+ Get a skill by type (e.g., "shell", "file_system").
115
+
116
+ Returns the first registered skill of that type.
117
+ Useful for built-in skills where type maps 1:1 to skill.
118
+ """
119
+ skills = self._skills_by_type.get(skill_type, [])
120
+ return skills[0] if skills else None
121
+
122
+ def list_skills(self, source: Optional[SkillSource] = None) -> List[LoadedSkill]:
123
+ """List all registered skills, optionally filtered by source."""
124
+ skills = list(self._skills.values())
125
+ if source:
126
+ skills = [s for s in skills if s.source == source]
127
+ return skills
128
+
129
+ def get_implementation_for_runtime(
130
+ self, skill: LoadedSkill, runtime_type: str
131
+ ) -> Optional[Union[Type, Dict[str, Any]]]:
132
+ """
133
+ Get the appropriate implementation class for a specific runtime.
134
+
135
+ Falls back to 'default' implementation if runtime-specific not available.
136
+
137
+ Returns:
138
+ Either a Python class (Type) or a dict with builtin config
139
+ """
140
+ # Check runtime-specific implementation
141
+ impl = skill.implementations.get(runtime_type)
142
+ if impl:
143
+ return impl
144
+
145
+ # Fall back to default implementation
146
+ return skill.implementations.get("default")
147
+
148
+ def resolve_dependencies(self, skill: LoadedSkill) -> List[str]:
149
+ """
150
+ Resolve skill dependencies.
151
+
152
+ Returns list of missing dependency names.
153
+ """
154
+ missing = []
155
+ dependencies = skill.manifest.get("spec", {}).get("dependencies", [])
156
+
157
+ for dep in dependencies:
158
+ dep_name = dep["name"]
159
+ dep_version = dep.get("version", ">=0.0.0")
160
+
161
+ resolved = self.get(dep_name)
162
+ if not resolved:
163
+ if not dep.get("optional", False):
164
+ missing.append(f"{dep_name} {dep_version}")
165
+ else:
166
+ # Check version constraint
167
+ try:
168
+ from packaging import version, specifiers
169
+ spec = specifiers.SpecifierSet(dep_version)
170
+ if not spec.contains(resolved.version):
171
+ missing.append(
172
+ f"{dep_name} {dep_version} (found {resolved.version})"
173
+ )
174
+ except:
175
+ # Skip version check if packaging not available
176
+ pass
177
+
178
+ return missing
179
+
180
+ def get_stats(self) -> Dict[str, Any]:
181
+ """Get registry statistics."""
182
+ return {
183
+ "total_skills": len(self._skills),
184
+ "skills_by_source": {
185
+ source.value: len([s for s in self._skills.values() if s.source == source])
186
+ for source in SkillSource
187
+ },
188
+ "skills_by_type": {
189
+ skill_type: len(skills)
190
+ for skill_type, skills in self._skills_by_type.items()
191
+ },
192
+ }
193
+
194
+ def _should_replace(self, existing: SkillSource, new: SkillSource) -> bool:
195
+ """Determine if new source should replace existing based on priority."""
196
+ priority = {
197
+ SkillSource.BUILTIN: 1,
198
+ SkillSource.USER_GLOBAL: 2,
199
+ SkillSource.USER_WORKSPACE: 3,
200
+ SkillSource.GIT_REPOSITORY: 4,
201
+ SkillSource.PYTHON_PACKAGE: 5,
202
+ SkillSource.CONTROL_PLANE_API: 6,
203
+ }
204
+ return priority.get(new, 0) > priority.get(existing, 0)
205
+
206
+
207
+ # Global registry instance
208
+ skill_registry = SkillRegistry()
@@ -0,0 +1 @@
1
+ """Test suite for worker files"""
@@ -0,0 +1,12 @@
1
+ """
2
+ Pytest configuration for worker tests.
3
+ Sets up sys.path to allow 'runtimes' imports.
4
+ """
5
+
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ # Add the worker directory to sys.path so that 'from runtimes import ...' works
10
+ worker_dir = Path(__file__).parent.parent
11
+ if str(worker_dir) not in sys.path:
12
+ sys.path.insert(0, str(worker_dir))
File without changes