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,332 @@
1
+ """
2
+ Universal Tool Name Validation for All LLM Providers
3
+
4
+ This module provides strict tool name validation that works across ALL major LLM providers:
5
+ - OpenAI/Azure OpenAI
6
+ - Anthropic Claude
7
+ - Google Vertex AI/Gemini
8
+ - AWS Bedrock
9
+
10
+ The validation rules are the strictest common denominator to ensure compatibility everywhere.
11
+ """
12
+
13
+ import re
14
+ import structlog
15
+ from typing import List, Tuple, Dict, Any
16
+
17
+ logger = structlog.get_logger(__name__)
18
+
19
+ # Universal tool name pattern - works for ALL LLM providers
20
+ # Rules:
21
+ # 1. Must start with letter (a-z, A-Z) or underscore (_)
22
+ # 2. Can only contain: letters, numbers, underscores
23
+ # 3. Maximum length: 64 characters
24
+ # 4. Minimum length: 1 character
25
+ UNIVERSAL_TOOL_NAME_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]{0,63}$')
26
+
27
+ # Characters that are safe across all providers
28
+ SAFE_CHARS = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_')
29
+
30
+
31
+ class ToolValidationError(Exception):
32
+ """Raised when tool validation fails and cannot be auto-fixed."""
33
+ pass
34
+
35
+
36
+ def validate_tool_name(name: str, provider_context: str = "universal") -> Tuple[bool, str, List[str]]:
37
+ """
38
+ Validate a tool name against universal LLM provider requirements.
39
+
40
+ Args:
41
+ name: The tool name to validate
42
+ provider_context: Context string for logging (e.g., "vertex_ai", "openai")
43
+
44
+ Returns:
45
+ Tuple of (is_valid, error_message, list_of_violations)
46
+
47
+ Example:
48
+ >>> validate_tool_name("my_tool")
49
+ (True, "", [])
50
+ >>> validate_tool_name("123invalid")
51
+ (False, "Tool name must start with letter or underscore", ["invalid_start"])
52
+ """
53
+ violations = []
54
+
55
+ if not name:
56
+ return False, "Tool name cannot be empty", ["empty_name"]
57
+
58
+ if not isinstance(name, str):
59
+ return False, f"Tool name must be string, got {type(name)}", ["invalid_type"]
60
+
61
+ # Check length
62
+ if len(name) > 64:
63
+ violations.append("exceeds_max_length")
64
+
65
+ if len(name) == 0:
66
+ return False, "Tool name cannot be empty", ["empty_name"]
67
+
68
+ # Check first character
69
+ if not (name[0].isalpha() or name[0] == '_'):
70
+ violations.append("invalid_start")
71
+
72
+ # Check for invalid characters
73
+ invalid_chars = set()
74
+ for char in name:
75
+ if char not in SAFE_CHARS:
76
+ invalid_chars.add(char)
77
+
78
+ if invalid_chars:
79
+ violations.append(f"invalid_chars_{','.join(sorted(invalid_chars))}")
80
+
81
+ # Check full pattern
82
+ if not UNIVERSAL_TOOL_NAME_PATTERN.match(name):
83
+ if "invalid_start" not in violations and "exceeds_max_length" not in violations:
84
+ violations.append("pattern_mismatch")
85
+
86
+ if violations:
87
+ error_msg = f"Tool name '{name}' is invalid for {provider_context}: {', '.join(violations)}"
88
+ return False, error_msg, violations
89
+
90
+ return True, "", []
91
+
92
+
93
+ def sanitize_tool_name(name: str, prefix: str = "", max_length: int = 64) -> str:
94
+ """
95
+ Sanitize a tool name to make it valid for all LLM providers.
96
+
97
+ This function aggressively cleans tool names to ensure compatibility:
98
+ - Replaces invalid characters with underscores
99
+ - Ensures it starts with letter or underscore
100
+ - Truncates to max_length
101
+ - Collapses multiple underscores
102
+
103
+ Args:
104
+ name: The tool name to sanitize
105
+ prefix: Optional prefix to add (useful for namespacing)
106
+ max_length: Maximum allowed length (default 64)
107
+
108
+ Returns:
109
+ Sanitized tool name that passes validation
110
+
111
+ Example:
112
+ >>> sanitize_tool_name("my-tool!")
113
+ "my_tool"
114
+ >>> sanitize_tool_name("123tool")
115
+ "_123tool"
116
+ >>> sanitize_tool_name("grounding::query_data")
117
+ "grounding_query_data"
118
+ """
119
+ if not name:
120
+ return "unnamed_tool"
121
+
122
+ # Add prefix if provided
123
+ if prefix:
124
+ name = f"{prefix}_{name}"
125
+
126
+ # Replace all invalid characters with underscores
127
+ sanitized = ""
128
+ for char in name:
129
+ if char in SAFE_CHARS:
130
+ sanitized += char
131
+ else:
132
+ sanitized += "_"
133
+
134
+ # Collapse multiple underscores
135
+ while "__" in sanitized:
136
+ sanitized = sanitized.replace("__", "_")
137
+
138
+ # Remove leading/trailing underscores except one if needed for start
139
+ sanitized = sanitized.strip("_")
140
+
141
+ # Ensure starts with letter or underscore
142
+ if sanitized and not (sanitized[0].isalpha() or sanitized[0] == '_'):
143
+ sanitized = f"_{sanitized}"
144
+
145
+ # Handle empty result
146
+ if not sanitized:
147
+ sanitized = "tool" if not prefix else prefix
148
+
149
+ # Truncate to max length
150
+ if len(sanitized) > max_length:
151
+ sanitized = sanitized[:max_length]
152
+ # Ensure we didn't cut in a way that makes it invalid
153
+ sanitized = sanitized.rstrip("_")
154
+
155
+ # Final validation - if still invalid, use fallback
156
+ is_valid, _, _ = validate_tool_name(sanitized)
157
+ if not is_valid:
158
+ logger.warning(f"Sanitization failed for '{name}', using fallback name")
159
+ return "sanitized_tool"
160
+
161
+ return sanitized
162
+
163
+
164
+ def validate_and_sanitize_tools(
165
+ tools: List[Any],
166
+ tool_name_getter: callable = lambda t: getattr(t, 'name', str(t)),
167
+ auto_fix: bool = True,
168
+ provider_context: str = "universal"
169
+ ) -> Tuple[List[Any], List[Dict[str, Any]]]:
170
+ """
171
+ Validate and optionally sanitize a list of tools.
172
+
173
+ Args:
174
+ tools: List of tool objects to validate
175
+ tool_name_getter: Function to extract tool name from tool object
176
+ auto_fix: If True, sanitize invalid tool names; if False, filter them out
177
+ provider_context: Context for error messages
178
+
179
+ Returns:
180
+ Tuple of (validated_tools, validation_report)
181
+ - validated_tools: List of tools with valid names
182
+ - validation_report: List of dicts with validation details
183
+
184
+ Example:
185
+ >>> tools = [Tool(name="valid_tool"), Tool(name="invalid-tool!")]
186
+ >>> valid_tools, report = validate_and_sanitize_tools(tools)
187
+ >>> len(valid_tools)
188
+ 2
189
+ >>> report[1]['action']
190
+ 'sanitized'
191
+ """
192
+ validated_tools = []
193
+ validation_report = []
194
+
195
+ for i, tool in enumerate(tools):
196
+ try:
197
+ original_name = tool_name_getter(tool)
198
+ is_valid, error_msg, violations = validate_tool_name(original_name, provider_context)
199
+
200
+ if is_valid:
201
+ validated_tools.append(tool)
202
+ validation_report.append({
203
+ "index": i,
204
+ "original_name": original_name,
205
+ "final_name": original_name,
206
+ "action": "passed",
207
+ "violations": []
208
+ })
209
+ else:
210
+ if auto_fix:
211
+ # Try to sanitize
212
+ sanitized_name = sanitize_tool_name(original_name)
213
+
214
+ # Update tool name if possible
215
+ if hasattr(tool, 'name'):
216
+ tool.name = sanitized_name
217
+
218
+ validated_tools.append(tool)
219
+ validation_report.append({
220
+ "index": i,
221
+ "original_name": original_name,
222
+ "final_name": sanitized_name,
223
+ "action": "sanitized",
224
+ "violations": violations,
225
+ "error": error_msg
226
+ })
227
+
228
+ logger.warning(
229
+ f"Tool name sanitized for {provider_context}: "
230
+ f"'{original_name}' -> '{sanitized_name}' (violations: {violations})"
231
+ )
232
+ else:
233
+ # Filter out invalid tool
234
+ validation_report.append({
235
+ "index": i,
236
+ "original_name": original_name,
237
+ "final_name": None,
238
+ "action": "filtered",
239
+ "violations": violations,
240
+ "error": error_msg
241
+ })
242
+
243
+ logger.error(
244
+ f"Tool filtered out for {provider_context}: "
245
+ f"'{original_name}' - {error_msg}"
246
+ )
247
+ except Exception as e:
248
+ logger.error(f"Error validating tool at index {i}: {e}")
249
+ validation_report.append({
250
+ "index": i,
251
+ "original_name": "unknown",
252
+ "final_name": None,
253
+ "action": "error",
254
+ "violations": ["exception"],
255
+ "error": str(e)
256
+ })
257
+
258
+ return validated_tools, validation_report
259
+
260
+
261
+ def validate_tool_definition(tool_def: Dict[str, Any]) -> Tuple[bool, str]:
262
+ """
263
+ Validate a tool definition dictionary (OpenAI/Anthropic format).
264
+
265
+ Args:
266
+ tool_def: Tool definition dict with 'name' and optionally 'function'
267
+
268
+ Returns:
269
+ Tuple of (is_valid, error_message)
270
+ """
271
+ if not isinstance(tool_def, dict):
272
+ return False, "Tool definition must be a dictionary"
273
+
274
+ # Check for function.name (OpenAI format)
275
+ if "function" in tool_def:
276
+ if "name" not in tool_def["function"]:
277
+ return False, "Tool function missing 'name' field"
278
+ tool_name = tool_def["function"]["name"]
279
+ # Check for name (Anthropic format)
280
+ elif "name" in tool_def:
281
+ tool_name = tool_def["name"]
282
+ else:
283
+ return False, "Tool definition missing 'name' field"
284
+
285
+ is_valid, error_msg, _ = validate_tool_name(tool_name)
286
+ return is_valid, error_msg
287
+
288
+
289
+ def get_provider_specific_requirements() -> Dict[str, Dict[str, Any]]:
290
+ """
291
+ Get provider-specific tool name requirements for reference.
292
+
293
+ Returns:
294
+ Dict mapping provider name to their requirements
295
+ """
296
+ return {
297
+ "openai": {
298
+ "pattern": r'^[a-zA-Z0-9_-]{1,64}$',
299
+ "description": "1-64 chars, letters, numbers, hyphens, underscores"
300
+ },
301
+ "anthropic": {
302
+ "pattern": r'^[a-zA-Z0-9_]{1,64}$',
303
+ "description": "1-64 chars, letters, numbers, underscores"
304
+ },
305
+ "vertex_ai": {
306
+ "pattern": r'^[a-zA-Z_][a-zA-Z0-9_\.\:\-]{0,63}$',
307
+ "description": "Start with letter/underscore, letters, numbers, underscore, dot, colon, dash, max 64"
308
+ },
309
+ "bedrock": {
310
+ "pattern": r'^[a-zA-Z][a-zA-Z0-9_]{0,63}$',
311
+ "description": "Start with letter, letters, numbers, underscores, max 64"
312
+ },
313
+ "universal": {
314
+ "pattern": UNIVERSAL_TOOL_NAME_PATTERN.pattern,
315
+ "description": "Start with letter/underscore, letters, numbers, underscores only, max 64 (strictest common)"
316
+ }
317
+ }
318
+
319
+
320
+ # Quick validation functions for common use cases
321
+
322
+ def is_valid_tool_name(name: str) -> bool:
323
+ """Quick check if a tool name is valid."""
324
+ is_valid, _, _ = validate_tool_name(name)
325
+ return is_valid
326
+
327
+
328
+ def assert_valid_tool_name(name: str, context: str = ""):
329
+ """Assert tool name is valid, raise exception if not."""
330
+ is_valid, error_msg, _ = validate_tool_name(name, context)
331
+ if not is_valid:
332
+ raise ToolValidationError(f"{context}: {error_msg}" if context else error_msg)
@@ -0,0 +1,163 @@
1
+ """Workspace management for execution isolation.
2
+
3
+ Provides utilities for creating and managing execution-scoped workspaces
4
+ where skills can safely operate without affecting global filesystem.
5
+
6
+ Pattern: .kubiya/workspaces/<execution-id>/
7
+ """
8
+
9
+ from pathlib import Path
10
+ import re
11
+ import structlog
12
+
13
+ logger = structlog.get_logger(__name__)
14
+
15
+ # Module-level constants
16
+ WORKSPACE_ROOT = ".kubiya/workspaces"
17
+ MAX_EXECUTION_ID_LENGTH = 200
18
+
19
+ # Unsafe filesystem characters to sanitize
20
+ UNSAFE_CHARS_PATTERN = re.compile(r'[/\\:*?"<>|]')
21
+
22
+
23
+ def ensure_workspace(execution_id: str) -> Path:
24
+ """
25
+ Create or return a workspace directory path for a given execution ID.
26
+
27
+ Purpose:
28
+ - Provides an isolated directory for each execution
29
+ - Used by file_system and shell skills for bounded operations
30
+ - Prevents global filesystem modifications
31
+
32
+ Path Created:
33
+ - .kubiya/workspaces/<execution-id>/
34
+
35
+ Behavior:
36
+ - Creates directory if it doesn't exist
37
+ - Returns existing path if already created
38
+ - Parent directories created automatically with proper permissions
39
+
40
+ Args:
41
+ execution_id: Unique execution identifier (from RuntimeExecutionContext)
42
+
43
+ Returns:
44
+ Path object pointing to the workspace directory
45
+ (Caller converts to str with str(workspace_path))
46
+
47
+ Raises:
48
+ ValueError: If execution_id is None/empty or unsafe for filesystem
49
+ OSError: If directory creation fails (permission denied, etc.)
50
+
51
+ Usage:
52
+ workspace_path = ensure_workspace(execution_id)
53
+ skill_instance = FileSystemTools(base_directory=str(workspace_path))
54
+ """
55
+ # Validate execution_id
56
+ if not execution_id:
57
+ logger.error(
58
+ "workspace_creation_failed",
59
+ error="execution_id is None or empty",
60
+ error_type="ValueError",
61
+ )
62
+ raise ValueError("execution_id must be a non-empty string")
63
+
64
+ # Sanitize execution_id for filesystem safety
65
+ sanitized_id = UNSAFE_CHARS_PATTERN.sub("_", execution_id)
66
+
67
+ # Truncate if too long
68
+ if len(sanitized_id) > MAX_EXECUTION_ID_LENGTH:
69
+ original_length = len(sanitized_id)
70
+ sanitized_id = sanitized_id[:MAX_EXECUTION_ID_LENGTH]
71
+ logger.warning(
72
+ "workspace_execution_id_truncated",
73
+ original_length=original_length,
74
+ max_length=MAX_EXECUTION_ID_LENGTH,
75
+ execution_id=sanitized_id[:8] if len(sanitized_id) >= 8 else sanitized_id,
76
+ )
77
+
78
+ # Log warning if sanitization was applied
79
+ if sanitized_id != execution_id:
80
+ logger.warning(
81
+ "workspace_execution_id_sanitized",
82
+ original=execution_id[:50] if len(execution_id) >= 50 else execution_id,
83
+ sanitized=sanitized_id[:50] if len(sanitized_id) >= 50 else sanitized_id,
84
+ )
85
+
86
+ # Build workspace path relative to current working directory
87
+ workspace_path = Path.cwd() / WORKSPACE_ROOT / sanitized_id
88
+
89
+ # Create directory (idempotent with exist_ok=True)
90
+ try:
91
+ workspace_path.mkdir(parents=True, exist_ok=True)
92
+
93
+ logger.info(
94
+ "execution_workspace_ensured",
95
+ execution_id=sanitized_id[:8] if len(sanitized_id) >= 8 else sanitized_id,
96
+ path=str(workspace_path),
97
+ )
98
+
99
+ return workspace_path
100
+
101
+ except OSError as e:
102
+ logger.error(
103
+ "workspace_directory_creation_failed",
104
+ execution_id=sanitized_id[:8] if len(sanitized_id) >= 8 else sanitized_id,
105
+ path=str(workspace_path),
106
+ error=str(e),
107
+ error_type=type(e).__name__,
108
+ )
109
+ # Let exception propagate - caller handles with try/except
110
+ raise
111
+
112
+
113
+ def should_use_custom_base_directory(skill_data: dict) -> bool:
114
+ """
115
+ Check if skill has explicitly configured base_directory.
116
+
117
+ Purpose:
118
+ - Prevents overriding user-specified base directories
119
+ - Allows skills to use custom paths when explicitly configured
120
+
121
+ Configuration Structure:
122
+ skill_data = {
123
+ "name": "file_system",
124
+ "type": "file_system",
125
+ "configuration": {
126
+ "base_directory": "/custom/path", # If present, return True
127
+ ...
128
+ },
129
+ "enabled": True,
130
+ "execution_id": "..."
131
+ }
132
+
133
+ Args:
134
+ skill_data: Skill configuration dict from Control Plane
135
+
136
+ Returns:
137
+ True if skill_data["configuration"]["base_directory"] is set to a non-empty value
138
+ False if base_directory is missing, None, empty string, or invalid
139
+
140
+ Usage:
141
+ if not should_use_custom_base_directory(skill_data):
142
+ config["base_directory"] = workspace_path
143
+ """
144
+ # Safe dictionary access with defaults
145
+ if not skill_data:
146
+ return False
147
+
148
+ configuration = skill_data.get("configuration", {})
149
+ if not configuration:
150
+ return False
151
+
152
+ base_directory = configuration.get("base_directory")
153
+
154
+ # Check if base_directory is set to a non-empty value
155
+ if base_directory is None:
156
+ return False
157
+
158
+ # Handle empty strings and whitespace
159
+ if isinstance(base_directory, str) and not base_directory.strip():
160
+ return False
161
+
162
+ # base_directory is explicitly set
163
+ return True