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,181 @@
1
+ """
2
+ Skill Context Enhancement for System Prompts.
3
+
4
+ This enhancement extracts context information from skill configurations
5
+ and injects it into the system prompt to provide agents with usage guidelines
6
+ and environment awareness.
7
+ """
8
+
9
+ from typing import Optional, List, Dict, Any
10
+ import structlog
11
+
12
+ from control_plane_api.worker.services.system_prompt_enhancement import (
13
+ SystemPromptEnhancement,
14
+ RuntimeType,
15
+ )
16
+
17
+ logger = structlog.get_logger(__name__)
18
+
19
+
20
+ class SkillContextEnhancement(SystemPromptEnhancement):
21
+ """
22
+ Inject skill context (usage guidelines, environment info) into system prompt.
23
+
24
+ This enhancement reads skill configurations passed via RuntimeExecutionContext
25
+ and generates formatted context sections that help the agent understand:
26
+ - How to use specific skills effectively
27
+ - Environment-specific information
28
+ - Custom shell configurations
29
+
30
+ The enhancement is designed to work with skill configurations that include:
31
+ - usage_guidelines: Optional[str] - Instructions for using the skill
32
+ - environment_context: Optional[str] - Environment-specific information
33
+ - shell_binary: Optional[str] - Custom shell binary path
34
+ - shell_args: Optional[List[str]] - Shell arguments
35
+ """
36
+
37
+ def __init__(self, skill_configs: Optional[List[Dict[str, Any]]] = None):
38
+ """
39
+ Initialize skill context enhancement.
40
+
41
+ Args:
42
+ skill_configs: List of skill configuration dictionaries.
43
+ If None, no context will be injected.
44
+ """
45
+ # Apply to all runtimes
46
+ super().__init__(runtime_types=[RuntimeType.ALL])
47
+ self.skill_configs = skill_configs or []
48
+
49
+ @property
50
+ def name(self) -> str:
51
+ return "skill_context"
52
+
53
+ def enhance(self, base_prompt: Optional[str]) -> str:
54
+ """
55
+ Enhance system prompt with skill context information.
56
+
57
+ Args:
58
+ base_prompt: The base system prompt
59
+
60
+ Returns:
61
+ Enhanced prompt with skill context sections
62
+ """
63
+ if not self.skill_configs:
64
+ # No skills configured - return base prompt unchanged
65
+ return base_prompt or ""
66
+
67
+ # Extract skills with context information
68
+ skills_with_context = [
69
+ skill for skill in self.skill_configs
70
+ if self._has_context_fields(skill)
71
+ ]
72
+
73
+ if not skills_with_context:
74
+ # No skills have context fields - return base prompt unchanged
75
+ logger.debug(
76
+ "skill_context_enhancement_skipped",
77
+ reason="no_skills_with_context_fields",
78
+ total_skills=len(self.skill_configs),
79
+ )
80
+ return base_prompt or ""
81
+
82
+ # Build context sections
83
+ context_parts = []
84
+ context_parts.append("") # Blank line before section
85
+ context_parts.append("---")
86
+ context_parts.append("")
87
+ context_parts.append("# Skill Context & Usage Guidelines")
88
+ context_parts.append("")
89
+ context_parts.append(
90
+ "The following skills have been configured with specific usage "
91
+ "guidelines and environment information to help you use them effectively:"
92
+ )
93
+ context_parts.append("")
94
+
95
+ for skill in skills_with_context:
96
+ try:
97
+ skill_name = skill.get("name", "Unknown Skill")
98
+ skill_type = skill.get("type", "unknown")
99
+ config = skill.get("configuration", {})
100
+
101
+ # Add skill header
102
+ context_parts.append(f"## {skill_name}")
103
+ context_parts.append("")
104
+
105
+ # Add usage guidelines if present
106
+ usage_guidelines = config.get("usage_guidelines", "").strip()
107
+ if usage_guidelines:
108
+ context_parts.append("**Usage Guidelines:**")
109
+ context_parts.append("")
110
+ # Handle multi-line guidelines
111
+ for line in usage_guidelines.split("\n"):
112
+ context_parts.append(line)
113
+ context_parts.append("")
114
+
115
+ # Add environment context if present
116
+ environment_context = config.get("environment_context", "").strip()
117
+ if environment_context:
118
+ context_parts.append("**Environment Context:**")
119
+ context_parts.append("")
120
+ for line in environment_context.split("\n"):
121
+ context_parts.append(line)
122
+ context_parts.append("")
123
+
124
+ # Add shell configuration if present (for shell skills)
125
+ if skill_type in ["shell", "SHELL"]:
126
+ shell_binary = config.get("shell_binary")
127
+ shell_args = config.get("shell_args")
128
+
129
+ if shell_binary or shell_args:
130
+ context_parts.append("**Shell Configuration:**")
131
+ context_parts.append("")
132
+ if shell_binary:
133
+ context_parts.append(f"- Binary: `{shell_binary}`")
134
+ if shell_args:
135
+ args_str = " ".join(shell_args)
136
+ context_parts.append(f"- Arguments: `{args_str}`")
137
+ context_parts.append("")
138
+ except Exception as e:
139
+ logger.warning(
140
+ "skill_context_enhancement_skipped_malformed_skill",
141
+ error=str(e),
142
+ skill=skill,
143
+ )
144
+ continue
145
+
146
+ context_parts.append("---")
147
+ context_parts.append("")
148
+
149
+ enhancement_text = "\n".join(context_parts)
150
+
151
+ # Log the enhancement
152
+ logger.info(
153
+ "skill_context_enhancement_applied",
154
+ skills_with_context=len(skills_with_context),
155
+ total_skills=len(self.skill_configs),
156
+ enhancement_size=len(enhancement_text),
157
+ )
158
+
159
+ # Append to base prompt
160
+ if base_prompt:
161
+ return base_prompt + "\n\n" + enhancement_text
162
+ else:
163
+ return enhancement_text.lstrip()
164
+
165
+ def _has_context_fields(self, skill: Dict[str, Any]) -> bool:
166
+ """
167
+ Check if a skill configuration has any context fields.
168
+
169
+ Args:
170
+ skill: Skill configuration dictionary
171
+
172
+ Returns:
173
+ True if skill has usage_guidelines, environment_context, or shell config
174
+ """
175
+ config = skill.get("configuration", {})
176
+ return bool(
177
+ config.get("usage_guidelines", "").strip()
178
+ or config.get("environment_context", "").strip()
179
+ or config.get("shell_binary")
180
+ or config.get("shell_args")
181
+ )
@@ -0,0 +1,471 @@
1
+ """
2
+ Skill Factory - Dynamic skill loading with zero hardcoding
3
+
4
+ This factory discovers and instantiates skills dynamically from multiple sources
5
+ without any hardcoded if/elif chains. Works across all runtimes (agno, claude_code, etc.).
6
+ """
7
+ from typing import Optional, Any, List
8
+ from pathlib import Path
9
+ import structlog
10
+ import os
11
+
12
+ from control_plane_api.worker.skills import (
13
+ SkillRegistry,
14
+ skill_registry,
15
+ FilesystemSkillLoader,
16
+ LoadedSkill,
17
+ )
18
+ from control_plane_api.worker.utils.tool_validation import (
19
+ validate_and_sanitize_tools,
20
+ is_valid_tool_name,
21
+ )
22
+
23
+ logger = structlog.get_logger()
24
+
25
+
26
+ class SkillFactory:
27
+ """
28
+ Dynamic skill factory with zero hardcoding.
29
+
30
+ Discovers and instantiates skills from multiple sources automatically.
31
+ No hardcoded if/elif chains required.
32
+ """
33
+
34
+ def __init__(self, runtime_type: str = "agno"):
35
+ """
36
+ Initialize the skill factory.
37
+
38
+ Args:
39
+ runtime_type: Runtime type (e.g., "agno", "claude_code")
40
+ """
41
+ self.runtime_type = runtime_type
42
+ self.registry: SkillRegistry = skill_registry
43
+ self.loaders = []
44
+ self._initialized = False
45
+
46
+ logger.info(
47
+ "skill_factory_initialized",
48
+ runtime_type=runtime_type,
49
+ )
50
+
51
+ def _validate_skill_tools(self, skill_instance: Any, skill_name: str) -> Optional[Any]:
52
+ """
53
+ Validate and sanitize tool names from a skill instance.
54
+
55
+ This ensures ALL tools from ANY source meet universal LLM provider requirements.
56
+
57
+ Args:
58
+ skill_instance: The skill toolkit instance
59
+ skill_name: Name of the skill for logging
60
+
61
+ Returns:
62
+ The skill instance with validated tools, or None if validation fails critically
63
+ """
64
+ # Try to extract tools from the skill instance
65
+ tools = []
66
+
67
+ # Handle Agno Toolkit objects
68
+ if hasattr(skill_instance, 'tools'):
69
+ tools = skill_instance.tools
70
+ # Handle dict format (builtin skills)
71
+ elif isinstance(skill_instance, dict) and 'tools' in skill_instance:
72
+ tools = skill_instance['tools']
73
+ # Handle iterable of tools
74
+ elif hasattr(skill_instance, '__iter__') and not isinstance(skill_instance, (str, dict)):
75
+ try:
76
+ tools = list(skill_instance)
77
+ except:
78
+ pass
79
+
80
+ if not tools:
81
+ # No tools to validate or can't extract them
82
+ return skill_instance
83
+
84
+ # Validate and sanitize tool names
85
+ def get_tool_name(tool):
86
+ """Extract tool name from various tool formats."""
87
+ if hasattr(tool, 'name'):
88
+ return tool.name
89
+ elif isinstance(tool, dict):
90
+ return tool.get('name', tool.get('function', {}).get('name', str(tool)))
91
+ elif hasattr(tool, '__name__'):
92
+ return tool.__name__
93
+ return str(tool)
94
+
95
+ validated_tools, validation_report = validate_and_sanitize_tools(
96
+ tools,
97
+ tool_name_getter=get_tool_name,
98
+ auto_fix=True,
99
+ provider_context=self.runtime_type
100
+ )
101
+
102
+ # Log validation results
103
+ sanitized_count = sum(1 for r in validation_report if r['action'] == 'sanitized')
104
+ filtered_count = sum(1 for r in validation_report if r['action'] == 'filtered')
105
+
106
+ if sanitized_count > 0:
107
+ logger.warning(
108
+ "skill_tools_sanitized",
109
+ skill=skill_name,
110
+ runtime=self.runtime_type,
111
+ sanitized_count=sanitized_count,
112
+ total_tools=len(tools),
113
+ details=[r for r in validation_report if r['action'] == 'sanitized']
114
+ )
115
+
116
+ if filtered_count > 0:
117
+ logger.error(
118
+ "skill_tools_filtered",
119
+ skill=skill_name,
120
+ runtime=self.runtime_type,
121
+ filtered_count=filtered_count,
122
+ total_tools=len(tools),
123
+ details=[r for r in validation_report if r['action'] == 'filtered']
124
+ )
125
+
126
+ # Update the skill instance with validated tools
127
+ if hasattr(skill_instance, 'tools'):
128
+ skill_instance.tools = validated_tools
129
+ elif isinstance(skill_instance, dict) and 'tools' in skill_instance:
130
+ skill_instance['tools'] = validated_tools
131
+
132
+ return skill_instance
133
+
134
+ def initialize(self):
135
+ """
136
+ Initialize the factory by discovering skills from all sources.
137
+
138
+ Call this once during worker startup.
139
+ """
140
+ if self._initialized:
141
+ logger.debug("skill_factory_already_initialized")
142
+ return
143
+
144
+ logger.info("skill_factory_initializing")
145
+
146
+ # Setup loaders
147
+ self._setup_loaders()
148
+
149
+ # Discover skills
150
+ self._discover_skills()
151
+
152
+ self._initialized = True
153
+
154
+ # Log statistics
155
+ stats = self.registry.get_stats()
156
+ logger.info(
157
+ "skill_factory_initialized",
158
+ total_skills=stats["total_skills"],
159
+ by_source=stats["skills_by_source"],
160
+ by_type=stats["skills_by_type"],
161
+ )
162
+
163
+ def _setup_loaders(self):
164
+ """Setup skill loaders from different sources."""
165
+ # 1. Filesystem loader (user skills)
166
+ search_paths = []
167
+
168
+ # Project skills (.kubiya/skills in current working directory)
169
+ project_skills = Path.cwd() / ".kubiya/skills"
170
+ if project_skills.exists():
171
+ search_paths.append(project_skills)
172
+
173
+ # User global skills (~/.kubiya/skills)
174
+ user_skills = Path.home() / ".kubiya/skills"
175
+ if user_skills.exists():
176
+ search_paths.append(user_skills)
177
+
178
+ # Built-in skills (in package directory)
179
+ builtin_skills = Path(__file__).parent.parent / "skills" / "builtin"
180
+ if builtin_skills.exists():
181
+ search_paths.append(builtin_skills)
182
+
183
+ if search_paths:
184
+ fs_loader = FilesystemSkillLoader(search_paths)
185
+ self.loaders.append(fs_loader)
186
+ logger.info("filesystem_loader_configured", paths=[str(p) for p in search_paths])
187
+
188
+ # TODO: Add API loader for Control Plane skills
189
+ # TODO: Add package loader for pip-installed skills
190
+ # TODO: Add git loader for remote skills
191
+
192
+ def _discover_skills(self):
193
+ """Discover skills from all loaders."""
194
+ for loader in self.loaders:
195
+ try:
196
+ skills = loader.discover()
197
+
198
+ for skill in skills:
199
+ # Check dependencies
200
+ missing = self.registry.resolve_dependencies(skill)
201
+ if missing:
202
+ logger.warning(
203
+ "skill_missing_dependencies",
204
+ skill=skill.name,
205
+ missing=missing,
206
+ )
207
+ # Register anyway - could still be usable
208
+
209
+ # Register
210
+ self.registry.register(skill)
211
+
212
+ except Exception as e:
213
+ logger.error(
214
+ "skill_discovery_failed",
215
+ loader=loader.__class__.__name__,
216
+ error=str(e),
217
+ exc_info=True,
218
+ )
219
+
220
+ def create_skill(self, skill_data: dict) -> Optional[Any]:
221
+ """
222
+ Create a skill toolkit from configuration.
223
+
224
+ No hardcoded mappings - uses registry for discovery.
225
+
226
+ Args:
227
+ skill_data: Skill config from Control Plane:
228
+ - name: Skill name (e.g., "slack-notifier")
229
+ - type: Skill type (e.g., "shell", "file_system", "custom")
230
+ - configuration: Dict with skill-specific config
231
+ - enabled: Whether skill is enabled
232
+ - execution_id: Optional execution ID for streaming
233
+
234
+ Returns:
235
+ Instantiated skill toolkit or None
236
+ """
237
+ # Ensure factory is initialized
238
+ if not self._initialized:
239
+ self.initialize()
240
+
241
+ if not skill_data.get("enabled", True):
242
+ logger.info("skill_disabled", name=skill_data.get("name"))
243
+ return None
244
+
245
+ skill_name = skill_data.get("name")
246
+ skill_type = skill_data.get("type", "custom")
247
+ config = skill_data.get("configuration", {})
248
+ execution_id = skill_data.get("execution_id")
249
+
250
+ # Try to find skill by name first
251
+ loaded_skill = self.registry.get(skill_name)
252
+
253
+ # If not found by name, try by type (for built-in skills)
254
+ if not loaded_skill:
255
+ loaded_skill = self.registry.get_by_type(skill_type)
256
+
257
+ if not loaded_skill:
258
+ logger.warning(
259
+ "skill_not_found_in_registry",
260
+ name=skill_name,
261
+ type=skill_type,
262
+ available_skills=[s.name for s in self.registry.list_skills()],
263
+ )
264
+ return None
265
+
266
+ # Get runtime-appropriate implementation
267
+ impl = self.registry.get_implementation_for_runtime(
268
+ loaded_skill, self.runtime_type
269
+ )
270
+
271
+ if not impl:
272
+ logger.error(
273
+ "no_implementation_for_runtime",
274
+ skill=loaded_skill.name,
275
+ runtime=self.runtime_type,
276
+ available_runtimes=list(loaded_skill.implementations.keys()),
277
+ )
278
+ return None
279
+
280
+ # Check if this is a builtin implementation (like Claude Code SDK tools)
281
+ if isinstance(impl, dict) and impl.get("builtin"):
282
+ builtin_tools = impl.get("tools", [])
283
+
284
+ logger.info(
285
+ "skill_builtin",
286
+ skill=loaded_skill.name,
287
+ runtime=self.runtime_type,
288
+ tools=builtin_tools,
289
+ )
290
+
291
+ # For builtin skills, return metadata - runtime handles tool mapping
292
+ builtin_skill_dict = {
293
+ "skill_name": loaded_skill.name,
294
+ "skill_type": loaded_skill.skill_type,
295
+ "builtin": True,
296
+ "tools": builtin_tools,
297
+ "config": config,
298
+ }
299
+
300
+ # UNIVERSAL VALIDATION: Validate builtin tool names
301
+ validated_builtin = self._validate_skill_tools(builtin_skill_dict, loaded_skill.name)
302
+ return validated_builtin
303
+
304
+ # Prepare configuration for Python class instantiation
305
+ instantiation_config = config.copy()
306
+
307
+ # Inject workspace directory for file/shell skills if not explicitly set
308
+ if loaded_skill.skill_type in ["file_system", "shell", "bash", "file", "file_generation"]:
309
+ from control_plane_api.worker.utils.workspace_manager import (
310
+ ensure_workspace,
311
+ should_use_custom_base_directory,
312
+ )
313
+
314
+ # Only inject workspace if user hasn't explicitly set base_directory
315
+ if execution_id and not should_use_custom_base_directory(skill_data):
316
+ try:
317
+ workspace_path = ensure_workspace(execution_id)
318
+ if workspace_path:
319
+ instantiation_config["base_directory"] = str(workspace_path)
320
+
321
+ logger.info(
322
+ "skill_using_execution_workspace",
323
+ skill=loaded_skill.name,
324
+ execution_id=execution_id[:8] if len(execution_id) >= 8 else execution_id,
325
+ workspace=str(workspace_path),
326
+ )
327
+ except Exception as e:
328
+ logger.warning(
329
+ "workspace_creation_failed_for_skill",
330
+ skill=loaded_skill.name,
331
+ execution_id=execution_id[:8] if len(execution_id) >= 8 else execution_id,
332
+ error=str(e),
333
+ error_type=type(e).__name__,
334
+ fallback="using_default_or_current_directory",
335
+ )
336
+ # Continue without workspace injection - skill will use its default
337
+
338
+ # Inject execution_id if provided
339
+ if execution_id:
340
+ instantiation_config['execution_id'] = execution_id
341
+
342
+ # Inject other runtime-specific config
343
+ # (e.g., API keys from environment variables)
344
+ self._inject_env_vars(instantiation_config, loaded_skill)
345
+
346
+ # Instantiate with configuration
347
+ try:
348
+ instance = impl(**instantiation_config)
349
+
350
+ logger.info(
351
+ "skill_instantiated",
352
+ skill=loaded_skill.name,
353
+ runtime=self.runtime_type,
354
+ implementation=impl.__name__,
355
+ )
356
+
357
+ # UNIVERSAL VALIDATION: Validate all tool names before returning
358
+ validated_instance = self._validate_skill_tools(instance, loaded_skill.name)
359
+
360
+ return validated_instance
361
+
362
+ except Exception as e:
363
+ logger.error(
364
+ "skill_instantiation_failed",
365
+ skill=loaded_skill.name,
366
+ error=str(e),
367
+ exc_info=True,
368
+ )
369
+
370
+ # Note: Error event publishing removed to avoid asyncio event loop issues
371
+ # The error is already logged above and the skill will be skipped
372
+ # This prevents "bound to a different event loop" errors in synchronous contexts
373
+
374
+ return None
375
+
376
+ def create_skills_from_list(
377
+ self, skill_configs: List[dict], execution_id: Optional[str] = None
378
+ ) -> List[Any]:
379
+ """
380
+ Create multiple skills from configurations.
381
+
382
+ Args:
383
+ skill_configs: List of skill config dicts
384
+ execution_id: Optional execution ID to inject into all skills
385
+
386
+ Returns:
387
+ List of instantiated skills (non-None)
388
+ """
389
+ skills = []
390
+
391
+ for config in skill_configs:
392
+ # Inject execution_id if provided
393
+ if execution_id:
394
+ config = config.copy()
395
+ config['execution_id'] = execution_id
396
+
397
+ skill = self.create_skill(config)
398
+ if skill:
399
+ skills.append(skill)
400
+
401
+ logger.info(
402
+ "skills_batch_created",
403
+ requested=len(skill_configs),
404
+ created=len(skills),
405
+ )
406
+
407
+ return skills
408
+
409
+ def _inject_env_vars(self, config: dict, skill: LoadedSkill):
410
+ """
411
+ Inject environment variables into skill configuration.
412
+
413
+ Looks for environment variables defined in skill manifest.
414
+ """
415
+ env_vars = skill.manifest.get("spec", {}).get("environmentVariables", [])
416
+
417
+ for env_var_def in env_vars:
418
+ var_name = env_var_def.get("name")
419
+ required = env_var_def.get("required", False)
420
+
421
+ # Check if already in config
422
+ if var_name.lower() in config or var_name in config:
423
+ continue
424
+
425
+ # Try to get from environment
426
+ env_value = os.environ.get(var_name)
427
+
428
+ if env_value:
429
+ # Convert env var name to config key (lowercase)
430
+ config_key = var_name.lower()
431
+ config[config_key] = env_value
432
+ logger.debug(
433
+ "injected_env_var",
434
+ skill=skill.name,
435
+ var_name=var_name,
436
+ )
437
+ elif required:
438
+ logger.warning(
439
+ "required_env_var_missing",
440
+ skill=skill.name,
441
+ var_name=var_name,
442
+ )
443
+
444
+ def get_available_skills(self) -> List[str]:
445
+ """Get list of all available skill names."""
446
+ if not self._initialized:
447
+ self.initialize()
448
+
449
+ return [skill.name for skill in self.registry.list_skills()]
450
+
451
+ def get_skill_info(self, skill_name: str) -> Optional[dict]:
452
+ """Get information about a specific skill."""
453
+ if not self._initialized:
454
+ self.initialize()
455
+
456
+ skill = self.registry.get(skill_name)
457
+ if not skill:
458
+ return None
459
+
460
+ return {
461
+ "name": skill.name,
462
+ "version": skill.version,
463
+ "type": skill.skill_type,
464
+ "source": skill.source.value,
465
+ "runtimes": list(skill.implementations.keys()),
466
+ "metadata": skill.manifest.get("metadata", {}),
467
+ }
468
+
469
+ # Backward compatibility alias
470
+ # Old code using SkillFactoryV2 will still work
471
+ SkillFactoryV2 = SkillFactory