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,403 @@
1
+ """
2
+ Tool mapping module for Claude Code runtime.
3
+
4
+ This module handles the translation of generic skill types to specific
5
+ Claude Code SDK tool names, with validation and error handling.
6
+
7
+ BUG FIX #6: Added tool name validation before adding to allowed_tools.
8
+ Issue #4 Fix: Externalized tool mappings to configuration file.
9
+ """
10
+
11
+ from typing import List, Tuple, Set, Dict, Optional
12
+ import structlog
13
+ import yaml
14
+ import os
15
+ from pathlib import Path
16
+
17
+ logger = structlog.get_logger(__name__)
18
+
19
+
20
+ # Known builtin Claude Code tools from SDK documentation
21
+ # This registry is the source of truth for validating builtin tool existence
22
+ CLAUDE_CODE_BUILTIN_TOOLS: Set[str] = {
23
+ "Read",
24
+ "Write",
25
+ "Edit",
26
+ "Glob",
27
+ "Grep",
28
+ "Bash",
29
+ "BashOutput",
30
+ "KillShell",
31
+ "WebFetch",
32
+ "WebSearch",
33
+ "Task",
34
+ "NotebookEdit",
35
+ "TodoWrite",
36
+ "ExitPlanMode",
37
+ "AskUserQuestion",
38
+ "Skill",
39
+ "SlashCommand",
40
+ # MCP resource tools (both naming conventions)
41
+ "ListMcpResources",
42
+ "ReadMcpResource",
43
+ "mcp__list_resources",
44
+ "mcp__read_resource",
45
+ }
46
+
47
+ # Backward compatibility alias
48
+ KNOWN_BUILTIN_TOOLS: Set[str] = CLAUDE_CODE_BUILTIN_TOOLS
49
+
50
+ # DEPRECATED: Hardcoded mapping (kept for backward compatibility)
51
+ # Use ToolMappingRegistry instead
52
+ SKILL_TO_TOOL_MAPPING = {
53
+ "shell": ["Bash", "BashOutput", "KillShell"],
54
+ "file_system": ["Read", "Write", "Edit", "Glob", "Grep"],
55
+ "web": ["WebFetch", "WebSearch"],
56
+ "docker": ["Bash"], # Docker commands via Bash
57
+ "kubernetes": ["Bash"], # kubectl via Bash
58
+ "git": ["Bash"], # git commands via Bash
59
+ "task": ["Task"], # Subagent tasks
60
+ "notebook": ["NotebookEdit"],
61
+ "planning": ["TodoWrite", "ExitPlanMode"],
62
+ }
63
+
64
+
65
+ class ToolMappingRegistry:
66
+ """
67
+ Issue #4 Fix: Dynamic tool mapping registry with configuration file support.
68
+
69
+ This class loads tool mappings from a YAML configuration file instead of
70
+ hardcoding them, making the system more maintainable and extensible.
71
+ """
72
+
73
+ def __init__(self, config_path: Optional[Path] = None):
74
+ """
75
+ Initialize the tool mapping registry.
76
+
77
+ Args:
78
+ config_path: Optional path to configuration file.
79
+ Defaults to tool_mapping_config.yaml in this directory.
80
+ """
81
+ self.config_path = config_path or self._get_default_config_path()
82
+ self.mappings: Dict[str, List[str]] = {}
83
+ self.default_tools: List[str] = []
84
+ self.overrides: Dict[str, Dict[str, List[str]]] = {}
85
+ self._load_config()
86
+
87
+ def _get_default_config_path(self) -> Path:
88
+ """Get path to default configuration file."""
89
+ return Path(__file__).parent / "tool_mapping_config.yaml"
90
+
91
+ def _load_config(self):
92
+ """Load mappings from configuration file."""
93
+ try:
94
+ # Check for environment variable override
95
+ env_config_path = os.environ.get("CLAUDE_CODE_TOOL_MAPPING_CONFIG")
96
+ if env_config_path:
97
+ config_path = Path(env_config_path)
98
+ logger.info(
99
+ "using_tool_mapping_config_from_env",
100
+ path=str(config_path),
101
+ )
102
+ else:
103
+ config_path = self.config_path
104
+
105
+ if not config_path.exists():
106
+ logger.warning(
107
+ "tool_mapping_config_not_found",
108
+ path=str(config_path),
109
+ fallback="using hardcoded mappings",
110
+ )
111
+ self._load_fallback_mappings()
112
+ return
113
+
114
+ with open(config_path) as f:
115
+ config = yaml.safe_load(f)
116
+
117
+ self.mappings = config.get("skill_type_mappings", {})
118
+ self.default_tools = config.get("default_tools", ["Read", "Write", "Bash"])
119
+ self.overrides = config.get("overrides", {})
120
+
121
+ logger.info(
122
+ "tool_mapping_config_loaded",
123
+ path=str(config_path),
124
+ skill_types=list(self.mappings.keys()),
125
+ default_tools=self.default_tools,
126
+ )
127
+
128
+ except Exception as e:
129
+ logger.error(
130
+ "failed_to_load_tool_mapping_config",
131
+ path=str(self.config_path),
132
+ error=str(e),
133
+ fallback="using hardcoded mappings",
134
+ exc_info=True,
135
+ )
136
+ self._load_fallback_mappings()
137
+
138
+ def _load_fallback_mappings(self):
139
+ """Load fallback hardcoded mappings."""
140
+ self.mappings = SKILL_TO_TOOL_MAPPING.copy()
141
+ self.default_tools = ["Read", "Write", "Bash"]
142
+ logger.debug("using_fallback_hardcoded_mappings")
143
+
144
+ def get_tools_for_skill_type(self, skill_type: str) -> List[str]:
145
+ """
146
+ Get Claude Code tools for a skill type.
147
+
148
+ Args:
149
+ skill_type: Generic skill type (e.g., "file_system", "shell")
150
+
151
+ Returns:
152
+ List of Claude Code tool names
153
+ """
154
+ # Check for environment variable override first
155
+ env_key = f"CLAUDE_CODE_MAPPING_{skill_type.upper()}"
156
+ env_override = os.environ.get(env_key)
157
+ if env_override:
158
+ tools = [t.strip() for t in env_override.split(",")]
159
+ logger.debug(
160
+ "using_env_override_for_skill_type",
161
+ skill_type=skill_type,
162
+ tools=tools,
163
+ env_key=env_key,
164
+ )
165
+ return tools
166
+
167
+ # Check configuration
168
+ tools = self.mappings.get(skill_type, [])
169
+ if not tools:
170
+ logger.warning(
171
+ "no_mapping_for_skill_type",
172
+ skill_type=skill_type,
173
+ available_types=list(self.mappings.keys()),
174
+ )
175
+
176
+ return tools
177
+
178
+ def get_default_tools(self) -> List[str]:
179
+ """Get default tools when no specific skill types are provided."""
180
+ # Check for environment variable override
181
+ env_default = os.environ.get("CLAUDE_CODE_DEFAULT_TOOLS")
182
+ if env_default:
183
+ tools = [t.strip() for t in env_default.split(",")]
184
+ logger.debug("using_env_override_for_default_tools", tools=tools)
185
+ return tools
186
+
187
+ return self.default_tools
188
+
189
+ def validate_mapping(self, skill_type: str, tools: List[str]) -> bool:
190
+ """
191
+ Validate that all tools in a mapping exist in CLAUDE_CODE_BUILTIN_TOOLS.
192
+
193
+ Args:
194
+ skill_type: Skill type being validated
195
+ tools: List of tool names
196
+
197
+ Returns:
198
+ True if all tools are valid, False otherwise
199
+ """
200
+ invalid_tools = [t for t in tools if t not in CLAUDE_CODE_BUILTIN_TOOLS]
201
+ if invalid_tools:
202
+ logger.error(
203
+ "invalid_tools_in_mapping",
204
+ skill_type=skill_type,
205
+ invalid_tools=invalid_tools,
206
+ valid_tools=sorted(CLAUDE_CODE_BUILTIN_TOOLS),
207
+ )
208
+ return False
209
+ return True
210
+
211
+ def reload_config(self):
212
+ """Reload configuration from file."""
213
+ logger.info("reloading_tool_mapping_config")
214
+ self._load_config()
215
+
216
+
217
+ # Global registry instance
218
+ _tool_mapping_registry: Optional[ToolMappingRegistry] = None
219
+
220
+
221
+ def get_tool_mapping_registry() -> ToolMappingRegistry:
222
+ """
223
+ Get the global tool mapping registry instance.
224
+
225
+ Returns:
226
+ ToolMappingRegistry instance
227
+ """
228
+ global _tool_mapping_registry
229
+ if _tool_mapping_registry is None:
230
+ _tool_mapping_registry = ToolMappingRegistry()
231
+ return _tool_mapping_registry
232
+
233
+
234
+ def map_skills_to_tools(skills: list) -> List[str]:
235
+ """
236
+ Map skills to Claude Code tool names.
237
+
238
+ This function translates generic skill types to the specific
239
+ tool names that Claude Code SDK understands.
240
+
241
+ Issue #4 Fix: Now uses ToolMappingRegistry instead of hardcoded mappings.
242
+
243
+ Args:
244
+ skills: List of skill objects
245
+
246
+ Returns:
247
+ List of Claude Code tool names (deduplicated)
248
+ """
249
+ registry = get_tool_mapping_registry()
250
+ tools = []
251
+
252
+ for skill in skills:
253
+ # Get skill type
254
+ skill_type = None
255
+ if hasattr(skill, "type"):
256
+ skill_type = skill.type
257
+ elif isinstance(skill, dict):
258
+ skill_type = skill.get("type")
259
+
260
+ # Map to Claude Code tools using registry
261
+ if skill_type:
262
+ mapped_tools = registry.get_tools_for_skill_type(skill_type)
263
+ if mapped_tools:
264
+ tools.extend(mapped_tools)
265
+
266
+ # Deduplicate and add default tools if none specified
267
+ if tools:
268
+ unique_tools = list(set(tools))
269
+ else:
270
+ unique_tools = registry.get_default_tools()
271
+
272
+ logger.info(
273
+ "mapped_skills_to_claude_code_tools",
274
+ skill_count=len(skills),
275
+ tool_count=len(unique_tools),
276
+ tools=unique_tools,
277
+ )
278
+
279
+ return unique_tools
280
+
281
+
282
+ def validate_builtin_tools(tool_names: List[str]) -> Tuple[List[str], List[str]]:
283
+ """
284
+ Validate that builtin tool names exist in Claude Code SDK.
285
+
286
+ Issue #2 Fix: This function ensures Claude Code builtin tools are validated
287
+ before being used, preventing runtime errors.
288
+
289
+ Args:
290
+ tool_names: List of builtin tool names to validate
291
+
292
+ Returns:
293
+ Tuple of (valid_tools, invalid_tools)
294
+
295
+ Raises:
296
+ ValueError: If any builtin tool doesn't exist in CLAUDE_CODE_BUILTIN_TOOLS
297
+ """
298
+ valid = []
299
+ invalid = []
300
+
301
+ for tool_name in tool_names:
302
+ if tool_name in CLAUDE_CODE_BUILTIN_TOOLS:
303
+ valid.append(tool_name)
304
+ else:
305
+ invalid.append(tool_name)
306
+
307
+ if invalid:
308
+ error_msg = (
309
+ f"Invalid builtin tool names detected: {invalid}. "
310
+ f"These tools don't exist in Claude Code SDK. "
311
+ f"Valid builtin tools are: {sorted(CLAUDE_CODE_BUILTIN_TOOLS)}"
312
+ )
313
+ logger.error(
314
+ "invalid_builtin_tools_detected",
315
+ invalid_tools=invalid,
316
+ valid_tools=valid,
317
+ available_tools=sorted(CLAUDE_CODE_BUILTIN_TOOLS),
318
+ )
319
+ raise ValueError(error_msg)
320
+
321
+ logger.debug(
322
+ "builtin_tools_validated",
323
+ tool_count=len(valid),
324
+ tools=valid,
325
+ )
326
+
327
+ return valid, invalid
328
+
329
+
330
+ def validate_tool_names(tool_names: List[str]) -> Tuple[List[str], List[str]]:
331
+ """
332
+ Validate tool names before adding to allowed_tools.
333
+
334
+ BUG FIX #6: This function ensures only valid tool names are used.
335
+
336
+ Args:
337
+ tool_names: List of tool names to validate
338
+
339
+ Returns:
340
+ Tuple of (valid_tools, invalid_tools)
341
+ """
342
+ valid = []
343
+ invalid = []
344
+
345
+ for tool_name in tool_names:
346
+ # Builtin tools or MCP tools (mcp__ prefix) are valid
347
+ if tool_name in KNOWN_BUILTIN_TOOLS or tool_name.startswith("mcp__"):
348
+ valid.append(tool_name)
349
+ else:
350
+ invalid.append(tool_name)
351
+
352
+ if invalid:
353
+ logger.warning(
354
+ "invalid_tool_names_filtered",
355
+ invalid_tools=invalid,
356
+ valid_count=len(valid),
357
+ invalid_count=len(invalid),
358
+ message="These tools will be filtered out as they're not recognized",
359
+ )
360
+
361
+ return valid, invalid
362
+
363
+
364
+ def sanitize_tool_name(name: str) -> str:
365
+ """
366
+ Sanitize a tool name to meet universal LLM provider requirements.
367
+
368
+ Uses the universal validator to ensure compatibility across all providers.
369
+
370
+ Args:
371
+ name: Tool name to sanitize
372
+
373
+ Returns:
374
+ Sanitized tool name that works for all LLM providers
375
+ """
376
+ from control_plane_api.worker.utils.tool_validation import sanitize_tool_name as universal_sanitize
377
+ return universal_sanitize(name)
378
+
379
+
380
+ def construct_mcp_tool_name(server_name: str, tool_name: str = None) -> str:
381
+ """
382
+ Construct a full MCP tool name following the naming convention.
383
+
384
+ Convention: mcp__<server_name>__<tool_name>
385
+
386
+ Args:
387
+ server_name: Name of the MCP server
388
+ tool_name: Optional tool name. If omitted, only server name is used.
389
+
390
+ Returns:
391
+ Full MCP tool name (sanitized)
392
+ """
393
+ sanitized_server = sanitize_tool_name(server_name)
394
+
395
+ if tool_name:
396
+ sanitized_tool = sanitize_tool_name(tool_name)
397
+ # Avoid duplication: mcp__run_ado_test not mcp__run_ado_test__run_ado_test
398
+ if sanitized_tool == sanitized_server:
399
+ return f"mcp__{sanitized_server}"
400
+ else:
401
+ return f"mcp__{sanitized_server}__{sanitized_tool}"
402
+ else:
403
+ return f"mcp__{sanitized_server}"
@@ -0,0 +1,149 @@
1
+ """
2
+ Utility functions for Claude Code runtime.
3
+
4
+ This module provides helper functions for prompt building, session management,
5
+ and SDK version checking.
6
+ """
7
+
8
+ from typing import Optional
9
+ import structlog
10
+
11
+ logger = structlog.get_logger(__name__)
12
+
13
+
14
+ def build_prompt_with_history(context: any) -> str:
15
+ """
16
+ Build prompt with conversation history.
17
+
18
+ Since ClaudeSDKClient maintains session continuity, we include
19
+ the conversation history as context in the prompt.
20
+
21
+ Args:
22
+ context: RuntimeExecutionContext
23
+
24
+ Returns:
25
+ Prompt string with history context
26
+ """
27
+ if not context.conversation_history:
28
+ return context.prompt
29
+
30
+ # Build context from history
31
+ history_context = "Previous conversation:\n"
32
+ for msg in context.conversation_history[-10:]: # Last 10 messages
33
+ role = msg.get("role", "unknown")
34
+ content = msg.get("content", "")
35
+ if len(content) > 200:
36
+ history_context += f"{role.capitalize()}: {content[:200]}...\n"
37
+ else:
38
+ history_context += f"{role.capitalize()}: {content}\n"
39
+
40
+ return f"{history_context}\n\nCurrent request:\n{context.prompt}"
41
+
42
+
43
+ def get_sdk_version() -> str:
44
+ """
45
+ Get Claude Code SDK version.
46
+
47
+ Returns:
48
+ SDK version string or "unknown"
49
+ """
50
+ try:
51
+ import claude_agent_sdk
52
+
53
+ return getattr(claude_agent_sdk, "__version__", "unknown")
54
+ except Exception:
55
+ return "unknown"
56
+
57
+
58
+ def extract_usage_from_result_message(message: any) -> dict:
59
+ """
60
+ Extract usage metrics from ResultMessage.
61
+
62
+ Args:
63
+ message: ResultMessage object
64
+
65
+ Returns:
66
+ Usage dict with token counts
67
+ """
68
+ if not hasattr(message, "usage") or not message.usage:
69
+ logger.warning("result_message_has_no_usage")
70
+ return {}
71
+
72
+ # ResultMessage.usage is a dict, not an object - use .get() instead of getattr()
73
+ usage_dict = message.usage
74
+
75
+ # Handle both dict and object usage formats
76
+ if isinstance(usage_dict, dict):
77
+ input_tokens = usage_dict.get("input_tokens", 0)
78
+ output_tokens = usage_dict.get("output_tokens", 0)
79
+ cache_read_tokens = usage_dict.get("cache_read_input_tokens", 0)
80
+ cache_creation_tokens = usage_dict.get("cache_creation_input_tokens", 0)
81
+ else:
82
+ # Fallback for object-style usage
83
+ input_tokens = getattr(usage_dict, "input_tokens", 0)
84
+ output_tokens = getattr(usage_dict, "output_tokens", 0)
85
+ cache_read_tokens = getattr(usage_dict, "cache_read_input_tokens", 0)
86
+ cache_creation_tokens = getattr(usage_dict, "cache_creation_input_tokens", 0)
87
+
88
+ # Use Anthropic field names for consistency with analytics
89
+ usage = {
90
+ "input_tokens": input_tokens,
91
+ "output_tokens": output_tokens,
92
+ "total_tokens": input_tokens + output_tokens,
93
+ "cache_read_tokens": cache_read_tokens,
94
+ "cache_creation_tokens": cache_creation_tokens,
95
+ }
96
+
97
+ logger.info(
98
+ "usage_extracted_from_claude_code",
99
+ input_tokens=input_tokens,
100
+ output_tokens=output_tokens,
101
+ total_tokens=usage["total_tokens"],
102
+ )
103
+
104
+ return usage
105
+
106
+
107
+ def extract_session_id_from_result_message(
108
+ message: any, execution_id: str
109
+ ) -> Optional[str]:
110
+ """
111
+ Extract and validate session_id from ResultMessage.
112
+
113
+ BUG FIX #4: Validates session_id before returning.
114
+
115
+ Args:
116
+ message: ResultMessage object
117
+ execution_id: Execution ID for logging
118
+
119
+ Returns:
120
+ Valid session_id or None
121
+ """
122
+ session_id = getattr(message, "session_id", None)
123
+
124
+ if not session_id:
125
+ logger.warning(
126
+ "no_session_id_in_result_message",
127
+ execution_id=execution_id[:8],
128
+ message="Multi-turn conversations may not work without session_id",
129
+ )
130
+ return None
131
+
132
+ # Validate format
133
+ if not isinstance(session_id, str) or len(session_id) < 10:
134
+ logger.warning(
135
+ "invalid_session_id_format_in_result_message",
136
+ execution_id=execution_id[:8],
137
+ session_id_type=type(session_id).__name__,
138
+ session_id_length=len(session_id) if isinstance(session_id, str) else 0,
139
+ )
140
+ return None
141
+
142
+ logger.info(
143
+ "session_id_captured_for_conversation_continuity",
144
+ execution_id=execution_id[:8],
145
+ session_id_prefix=session_id[:16],
146
+ message="This session_id will enable multi-turn conversations",
147
+ )
148
+
149
+ return session_id