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,245 @@
1
+ """Integration tests for builtin skills fixes (import errors, data_visualization, MCP)."""
2
+ import pytest
3
+ import asyncio
4
+ from control_plane_api.worker.services.skill_factory import SkillFactory
5
+ from control_plane_api.worker.runtimes.claude_code.mcp_discovery import discover_mcp_resources
6
+
7
+
8
+ class TestImportFixes:
9
+ """Test that import errors are fixed."""
10
+
11
+ def test_agent_executor_v2_imports(self):
12
+ """Test that agent_executor_v2 imports correctly."""
13
+ from control_plane_api.worker.services.agent_executor_v2 import AgentExecutorServiceV2
14
+ assert AgentExecutorServiceV2 is not None
15
+
16
+ def test_team_executor_v2_imports(self):
17
+ """Test that team_executor_v2 imports correctly."""
18
+ from control_plane_api.worker.services.team_executor_v2 import TeamExecutorServiceV2
19
+ assert TeamExecutorServiceV2 is not None
20
+
21
+
22
+ class TestDataVisualizationSkillRestoration:
23
+ """Test that data_visualization skill is properly restored."""
24
+
25
+ def test_data_visualization_skill_yaml_exists(self):
26
+ """Test that skill.yaml exists."""
27
+ import os
28
+ skill_yaml_path = "control_plane_api/worker/skills/builtin/data_visualization/skill.yaml"
29
+ assert os.path.exists(skill_yaml_path), "skill.yaml should exist"
30
+
31
+ def test_data_visualization_agno_impl_exists(self):
32
+ """Test that agno_impl.py exists."""
33
+ import os
34
+ agno_impl_path = "control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py"
35
+ assert os.path.exists(agno_impl_path), "agno_impl.py should exist"
36
+
37
+ def test_data_visualization_imports(self):
38
+ """Test that data_visualization skill imports correctly."""
39
+ from control_plane_api.worker.skills.builtin.data_visualization.agno_impl import DataVisualizationTools
40
+ assert DataVisualizationTools is not None
41
+
42
+ def test_data_visualization_toolkit_instantiation(self):
43
+ """Test that data_visualization toolkit can be instantiated."""
44
+ from control_plane_api.worker.skills.builtin.data_visualization.agno_impl import DataVisualizationTools
45
+
46
+ toolkit = DataVisualizationTools(
47
+ enable_flowchart=True,
48
+ enable_sequence=True,
49
+ max_diagram_size=50000
50
+ )
51
+
52
+ assert toolkit is not None
53
+ assert hasattr(toolkit, 'functions'), "Toolkit should have functions attribute"
54
+
55
+ # Should have 11 diagram tools
56
+ tool_count = len(toolkit.functions)
57
+ assert tool_count == 11, f"Expected 11 tools, got {tool_count}"
58
+
59
+ def test_data_visualization_skill_loading_agno(self):
60
+ """Test that data_visualization skill loads correctly in agno runtime."""
61
+ factory = SkillFactory(runtime_type="agno")
62
+ factory.initialize()
63
+
64
+ skill_config = {
65
+ "name": "data-visualization",
66
+ "type": "data_visualization",
67
+ "enabled": True,
68
+ "configuration": {
69
+ "enable_flowchart": True,
70
+ "max_diagram_size": 50000
71
+ }
72
+ }
73
+
74
+ skill = factory.create_skill(skill_config)
75
+ assert skill is not None, "Skill should be created"
76
+ assert hasattr(skill, 'functions'), "Skill should have functions"
77
+ assert len(skill.functions) == 11, f"Should have 11 tools, got {len(skill.functions)}"
78
+
79
+ def test_data_visualization_skill_loading_claude_code(self):
80
+ """Test that data_visualization skill loads correctly in claude_code runtime."""
81
+ factory = SkillFactory(runtime_type="claude_code")
82
+ factory.initialize()
83
+
84
+ skill_config = {
85
+ "name": "data-visualization",
86
+ "type": "data_visualization",
87
+ "enabled": True,
88
+ "configuration": {
89
+ "enable_flowchart": True,
90
+ "max_diagram_size": 50000
91
+ }
92
+ }
93
+
94
+ skill = factory.create_skill(skill_config)
95
+ assert skill is not None, "Skill should be created"
96
+ # For claude_code, it should be a toolkit instance that will be converted to MCP
97
+ assert hasattr(skill, 'functions'), "Skill should have functions for MCP conversion"
98
+
99
+ def test_data_visualization_with_execution_id(self):
100
+ """Test that data_visualization skill accepts execution_id parameter."""
101
+ factory = SkillFactory(runtime_type="claude_code")
102
+ factory.initialize()
103
+
104
+ skill_config = {
105
+ "name": "data-visualization",
106
+ "type": "data_visualization",
107
+ "enabled": True,
108
+ "configuration": {
109
+ "enable_flowchart": True,
110
+ "max_diagram_size": 50000
111
+ },
112
+ "execution_id": "test-exec-123" # This was causing the failure
113
+ }
114
+
115
+ skill = factory.create_skill(skill_config)
116
+ assert skill is not None, "Skill should be created even with execution_id"
117
+ assert hasattr(skill, 'functions'), "Skill should have functions"
118
+ assert len(skill.functions) == 11, "Should have all 11 diagram tools"
119
+
120
+
121
+ class TestContextGraphSearchMCPFix:
122
+ """Test that context_graph_search MCP verification is fixed."""
123
+
124
+ @pytest.mark.asyncio
125
+ async def test_sdk_mcp_server_object_handling(self):
126
+ """Test that SDK MCP server objects are handled correctly (not treated as config dicts)."""
127
+ from claude_agent_sdk import create_sdk_mcp_server, tool as mcp_tool
128
+
129
+ # Create a simple SDK MCP server
130
+ @mcp_tool("test_tool", "A test tool", {"arg": str})
131
+ async def test_tool(args: dict) -> dict:
132
+ return {"result": "test"}
133
+
134
+ sdk_server = create_sdk_mcp_server(
135
+ name="test-server",
136
+ version="1.0.0",
137
+ tools=[test_tool]
138
+ )
139
+
140
+ # This should NOT raise an error - it should skip pre-discovery
141
+ result = await discover_mcp_resources("test-server", sdk_server)
142
+
143
+ assert result["server_name"] == "test-server"
144
+ # SDK servers are skipped, which means connected=True and skipped=True
145
+ assert result.get("skipped") == True, "SDK MCP server should be skipped"
146
+ # If skipped, connected should be True (means "will work")
147
+ if result.get("skipped"):
148
+ assert result["connected"] == True, "Skipped SDK servers should report as connected"
149
+ assert "error" not in result or result["error"] is None
150
+
151
+ @pytest.mark.asyncio
152
+ async def test_context_graph_search_mcp_creation(self):
153
+ """Test that context_graph_search skill creates MCP server successfully."""
154
+ from control_plane_api.worker.runtimes.claude_code.mcp_builder import build_mcp_servers
155
+ from control_plane_api.worker.services.skill_factory import SkillFactory
156
+
157
+ # Create skill instance
158
+ factory = SkillFactory(runtime_type="claude_code")
159
+ factory.initialize()
160
+
161
+ skill_config = {
162
+ "name": "context-graph-search",
163
+ "type": "context_graph_search",
164
+ "enabled": True,
165
+ "configuration": {}
166
+ }
167
+
168
+ skill = factory.create_skill(skill_config)
169
+ assert skill is not None
170
+
171
+ # Build MCP servers
172
+ mcp_servers, _ = build_mcp_servers([skill], {})
173
+
174
+ assert "context-graph-search" in mcp_servers, "Should create MCP server for context-graph-search"
175
+
176
+ # Verify the server can be discovered without errors
177
+ server = mcp_servers["context-graph-search"]
178
+ result = await discover_mcp_resources("context-graph-search", server)
179
+
180
+ # SDK servers should be skipped
181
+ assert result.get("skipped") == True, "SDK MCP servers should be skipped"
182
+ # If skipped, connected should be True
183
+ if result.get("skipped"):
184
+ assert result["connected"] == True, "Skipped SDK servers should report as connected"
185
+
186
+
187
+ class TestSkillFactoryAutoInclude:
188
+ """Test that context_graph_search is auto-included correctly."""
189
+
190
+ def test_context_graph_search_auto_include(self):
191
+ """Test that context_graph_search is properly auto-included."""
192
+ skill_configs = []
193
+
194
+ # Auto-include logic from executor
195
+ builtin_skill_types = {'context_graph_search'}
196
+ existing_skill_types = {cfg.get('type') for cfg in skill_configs}
197
+
198
+ for builtin_type in builtin_skill_types:
199
+ if builtin_type not in existing_skill_types:
200
+ builtin_config = {
201
+ 'name': builtin_type,
202
+ 'type': builtin_type,
203
+ 'enabled': True,
204
+ 'configuration': {}
205
+ }
206
+ skill_configs.append(builtin_config)
207
+
208
+ assert len(skill_configs) == 1
209
+ assert skill_configs[0]['type'] == 'context_graph_search'
210
+
211
+ # Now try to load it
212
+ factory = SkillFactory(runtime_type="claude_code")
213
+ factory.initialize()
214
+
215
+ skill = factory.create_skill(skill_configs[0])
216
+ assert skill is not None
217
+
218
+
219
+ class TestSkillRegistryStats:
220
+ """Test that skill registry has expected skills."""
221
+
222
+ def test_builtin_skills_registered(self):
223
+ """Test that all expected builtin skills are registered."""
224
+ factory = SkillFactory(runtime_type="agno")
225
+ factory.initialize()
226
+
227
+ stats = factory.registry.get_stats()
228
+
229
+ # Should have these builtin skills (using underscores - that's how they're registered)
230
+ expected_skills = {
231
+ 'shell', 'file_system', 'python', 'docker',
232
+ 'data_visualization', # Restored!
233
+ 'context_graph_search', # Note: underscore, not hyphen
234
+ 'workflow_executor', 'file_generation'
235
+ }
236
+
237
+ registered_skills = set(stats['skills_by_type'].keys())
238
+
239
+ for skill in expected_skills:
240
+ assert skill in registered_skills, f"Expected skill '{skill}' not registered. Registered: {registered_skills}"
241
+
242
+
243
+ if __name__ == "__main__":
244
+ # Run tests
245
+ pytest.main([__file__, "-v", "-s"])
@@ -0,0 +1,365 @@
1
+ """Integration tests for ContextGraphSearchTools with both runtimes."""
2
+
3
+ import pytest
4
+ import json
5
+ from unittest.mock import Mock, patch, MagicMock
6
+ import asyncio
7
+
8
+ # Add worker to sys.path
9
+ import sys
10
+ from pathlib import Path
11
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
12
+
13
+ from skills.builtin.context_graph_search.agno_impl import ContextGraphSearchTools
14
+ from services.skill_factory import SkillFactory
15
+
16
+
17
+ class TestContextGraphSearchAgnoIntegration:
18
+ """Integration tests for ContextGraphSearchTools with Agno runtime"""
19
+
20
+ @pytest.fixture
21
+ def mock_env_vars(self, monkeypatch):
22
+ """Set up environment variables for testing"""
23
+ monkeypatch.setenv("KUBIYA_API_KEY", "test_api_key_123")
24
+ monkeypatch.setenv("CONTEXT_GRAPH_API_BASE", "https://test-graph.kubiya.ai")
25
+ monkeypatch.setenv("KUBIYA_ORG_ID", "test_org_123")
26
+
27
+ @pytest.fixture
28
+ def skill_factory(self):
29
+ """Create SkillFactory instance"""
30
+ factory = SkillFactory(runtime_type="agno")
31
+ factory.initialize()
32
+ return factory
33
+
34
+ def test_skill_factory_creates_context_graph_tools(self, skill_factory, mock_env_vars):
35
+ """Test that SkillFactory can create ContextGraphSearchTools"""
36
+ skill_configs = [
37
+ {
38
+ "name": "context_graph_search",
39
+ "type": "context_graph_search",
40
+ "enabled": True,
41
+ "configuration": {}
42
+ }
43
+ ]
44
+
45
+ skills = skill_factory.create_skills_from_list(skill_configs)
46
+
47
+ assert len(skills) == 1
48
+ assert type(skills[0]).__name__ == 'ContextGraphSearchTools'
49
+
50
+ def test_skill_factory_with_custom_config(self, skill_factory, mock_env_vars):
51
+ """Test SkillFactory creates tools with custom configuration"""
52
+ skill_configs = [
53
+ {
54
+ "name": "context_graph_search",
55
+ "type": "context_graph_search",
56
+ "enabled": True,
57
+ "configuration": {
58
+ "timeout": 60,
59
+ "default_limit": 50
60
+ }
61
+ }
62
+ ]
63
+
64
+ skills = skill_factory.create_skills_from_list(skill_configs)
65
+
66
+ assert len(skills) == 1
67
+ tool = skills[0]
68
+ assert tool.timeout == 60
69
+ assert tool.default_limit == 50
70
+
71
+ @patch('skills.builtin.context_graph_search.agno_impl.httpx.Client')
72
+ def test_agno_runtime_tool_execution(self, mock_client_class, mock_env_vars):
73
+ """Test executing a tool in Agno runtime context"""
74
+ mock_response = Mock()
75
+ mock_response.status_code = 200
76
+ mock_response.json.return_value = {
77
+ "labels": ["User", "Repository", "Service"]
78
+ }
79
+
80
+ mock_client = MagicMock()
81
+ mock_client.__enter__.return_value.get.return_value = mock_response
82
+ mock_client_class.return_value = mock_client
83
+
84
+ # Create tool instance
85
+ tools = ContextGraphSearchTools()
86
+
87
+ # Execute tool method
88
+ result = tools.get_labels()
89
+
90
+ # Verify result
91
+ assert isinstance(result, str)
92
+ parsed = json.loads(result)
93
+ assert "labels" in parsed
94
+ assert len(parsed["labels"]) == 3
95
+
96
+ # Verify HTTP call was made correctly
97
+ mock_client.__enter__.return_value.get.assert_called_once()
98
+ call_args = mock_client.__enter__.return_value.get.call_args
99
+ assert "https://test-graph.kubiya.ai/api/v1/graph/labels" in call_args[0][0]
100
+
101
+ def test_toolkit_integration_with_agno_functions(self, mock_env_vars):
102
+ """Test that toolkit properly registers functions for Agno"""
103
+ tools = ContextGraphSearchTools()
104
+
105
+ # Verify toolkit has functions attribute (required by Agno)
106
+ assert hasattr(tools, "functions")
107
+ assert isinstance(tools.functions, dict)
108
+ assert len(tools.functions) >= 9
109
+
110
+ # Verify each function has proper structure
111
+ for func_name, func_obj in tools.functions.items():
112
+ # Verify function object has required attributes
113
+ assert hasattr(func_obj, "entrypoint"), f"{func_name} missing entrypoint"
114
+ assert callable(func_obj.entrypoint), f"{func_name} entrypoint not callable"
115
+
116
+ # Verify function has description
117
+ description = getattr(func_obj, "description", None) or func_obj.entrypoint.__doc__
118
+ assert description, f"{func_name} missing description"
119
+
120
+
121
+ class TestContextGraphSearchClaudeCodeIntegration:
122
+ """Integration tests for ContextGraphSearchTools with Claude Code runtime"""
123
+
124
+ @pytest.fixture
125
+ def mock_env_vars(self, monkeypatch):
126
+ """Set up environment variables for testing"""
127
+ monkeypatch.setenv("KUBIYA_API_KEY", "test_api_key_123")
128
+ monkeypatch.setenv("CONTEXT_GRAPH_API_BASE", "https://test-graph.kubiya.ai")
129
+
130
+ @pytest.fixture
131
+ def skill_factory(self):
132
+ """Create SkillFactory instance for Claude Code"""
133
+ factory = SkillFactory(runtime_type="claude_code")
134
+ factory.initialize()
135
+ return factory
136
+
137
+ def test_skill_factory_creates_tools_for_claude_code(self, skill_factory, mock_env_vars):
138
+ """Test that SkillFactory can create tools for Claude Code runtime"""
139
+ skill_configs = [
140
+ {
141
+ "name": "context_graph_search",
142
+ "type": "context_graph_search",
143
+ "enabled": True,
144
+ "configuration": {}
145
+ }
146
+ ]
147
+
148
+ skills = skill_factory.create_skills_from_list(skill_configs)
149
+
150
+ assert len(skills) == 1
151
+ assert type(skills[0]).__name__ == 'ContextGraphSearchTools'
152
+
153
+ @pytest.mark.asyncio
154
+ async def test_mcp_conversion(self, mock_env_vars):
155
+ """Test that tools can be converted to MCP server format"""
156
+ from runtimes.claude_code.mcp_builder import build_mcp_servers
157
+
158
+ # Create skill instance
159
+ tools = ContextGraphSearchTools()
160
+
161
+ # Convert to MCP servers
162
+ mcp_servers, _ = build_mcp_servers(skills=[tools])
163
+
164
+ # Verify MCP server was created
165
+ assert "context-graph-search" in mcp_servers
166
+ mcp_server = mcp_servers["context-graph-search"]
167
+
168
+ # Verify it's a valid MCP server object
169
+ assert mcp_server is not None
170
+
171
+ def test_toolkit_functions_for_mcp_conversion(self, mock_env_vars):
172
+ """Test that toolkit functions can be converted to MCP tools"""
173
+ tools = ContextGraphSearchTools()
174
+
175
+ # Verify functions registry exists (required for MCP conversion)
176
+ assert hasattr(tools, "functions")
177
+ assert len(tools.functions) >= 9
178
+
179
+ # Each function should have the structure needed for MCP conversion
180
+ expected_tools = [
181
+ "search_nodes",
182
+ "get_node",
183
+ "get_relationships",
184
+ "get_subgraph",
185
+ "search_by_text",
186
+ "execute_query",
187
+ "get_labels",
188
+ "get_relationship_types",
189
+ "get_stats"
190
+ ]
191
+
192
+ for tool_name in expected_tools:
193
+ assert tool_name in tools.functions
194
+ func_obj = tools.functions[tool_name]
195
+
196
+ # Verify function has entrypoint (required for MCP wrapping)
197
+ assert hasattr(func_obj, "entrypoint")
198
+ assert callable(func_obj.entrypoint)
199
+
200
+ # Verify function has metadata (description, parameters)
201
+ assert hasattr(func_obj, "description") or func_obj.entrypoint.__doc__
202
+
203
+ @pytest.mark.asyncio
204
+ @patch('skills.builtin.context_graph_search.agno_impl.httpx.Client')
205
+ async def test_mcp_tool_execution(self, mock_client_class, mock_env_vars):
206
+ """Test executing a tool through MCP wrapper"""
207
+ mock_response = Mock()
208
+ mock_response.status_code = 200
209
+ mock_response.json.return_value = {
210
+ "nodes": [{"id": "node1", "label": "User"}]
211
+ }
212
+
213
+ mock_client = MagicMock()
214
+ mock_client.__enter__.return_value.post.return_value = mock_response
215
+ mock_client_class.return_value = mock_client
216
+
217
+ # Create tool instance
218
+ tools = ContextGraphSearchTools()
219
+
220
+ # Get the function from registry
221
+ search_func = tools.functions["search_nodes"]
222
+ entrypoint = search_func.entrypoint
223
+
224
+ # Execute through entrypoint (simulating MCP call)
225
+ result = entrypoint(label="User")
226
+
227
+ # Verify result is JSON string
228
+ assert isinstance(result, str)
229
+ parsed = json.loads(result)
230
+ assert "nodes" in parsed
231
+
232
+
233
+ class TestContextGraphSearchSkillDiscovery:
234
+ """Test skill discovery and loading mechanisms"""
235
+
236
+ @pytest.fixture
237
+ def mock_env_vars(self, monkeypatch):
238
+ """Set up environment variables"""
239
+ monkeypatch.setenv("KUBIYA_API_KEY", "test_key")
240
+
241
+ def test_skill_yaml_exists(self):
242
+ """Test that skill.yaml file exists and is valid"""
243
+ from pathlib import Path
244
+ skill_dir = Path(__file__).parent.parent.parent / "skills" / "builtin" / "context_graph_search"
245
+ skill_yaml = skill_dir / "skill.yaml"
246
+
247
+ assert skill_yaml.exists(), "skill.yaml not found"
248
+
249
+ # Try to parse it
250
+ import yaml
251
+ with open(skill_yaml) as f:
252
+ skill_def = yaml.safe_load(f)
253
+
254
+ # Verify required fields
255
+ assert skill_def["apiVersion"] == "kubiya.ai/v1"
256
+ assert skill_def["kind"] == "Skill"
257
+ assert skill_def["metadata"]["name"] == "context-graph-search"
258
+ assert skill_def["spec"]["type"] == "context_graph_search"
259
+
260
+ # Verify implementations
261
+ assert "agno" in skill_def["spec"]["implementations"]
262
+ assert "claude_code" in skill_def["spec"]["implementations"]
263
+
264
+ def test_agno_impl_exists(self):
265
+ """Test that agno_impl.py exists and exports ContextGraphSearchTools"""
266
+ from pathlib import Path
267
+ skill_dir = Path(__file__).parent.parent.parent / "skills" / "builtin" / "context_graph_search"
268
+ agno_impl = skill_dir / "agno_impl.py"
269
+
270
+ assert agno_impl.exists(), "agno_impl.py not found"
271
+
272
+ # Verify it can be imported
273
+ from skills.builtin.context_graph_search.agno_impl import ContextGraphSearchTools
274
+ assert ContextGraphSearchTools is not None
275
+
276
+ def test_skill_factory_can_discover_skill(self, mock_env_vars):
277
+ """Test that SkillFactory can discover the context_graph_search skill"""
278
+ factory = SkillFactory(runtime_type="agno")
279
+ factory.initialize()
280
+
281
+ # Try to create the skill
282
+ skill_configs = [
283
+ {
284
+ "name": "context_graph_search",
285
+ "type": "context_graph_search",
286
+ "enabled": True,
287
+ "configuration": {}
288
+ }
289
+ ]
290
+
291
+ skills = factory.create_skills_from_list(skill_configs)
292
+
293
+ # Verify skill was created
294
+ assert len(skills) == 1
295
+ assert type(skills[0]).__name__ == 'ContextGraphSearchTools'
296
+
297
+ def test_skill_can_be_imported(self):
298
+ """Test that skill can be imported from __init__.py"""
299
+ from skills.builtin.context_graph_search import ContextGraphSearchTools
300
+ assert ContextGraphSearchTools is not None
301
+
302
+
303
+ class TestContextGraphSearchErrorHandling:
304
+ """Test error handling and edge cases"""
305
+
306
+ @pytest.fixture
307
+ def mock_env_vars(self, monkeypatch):
308
+ """Set up environment variables"""
309
+ monkeypatch.setenv("KUBIYA_API_KEY", "test_key")
310
+ monkeypatch.setenv("CONTEXT_GRAPH_API_BASE", "https://test-graph.api")
311
+
312
+ @patch('skills.builtin.context_graph_search.agno_impl.httpx.Client')
313
+ def test_network_error_handling(self, mock_client_class, mock_env_vars):
314
+ """Test handling of network errors"""
315
+ mock_client = MagicMock()
316
+ mock_client.__enter__.return_value.get.side_effect = Exception("Network error")
317
+ mock_client_class.return_value = mock_client
318
+
319
+ tools = ContextGraphSearchTools()
320
+
321
+ with pytest.raises(Exception, match="Request failed"):
322
+ tools.get_labels()
323
+
324
+ @patch('skills.builtin.context_graph_search.agno_impl.httpx.Client')
325
+ def test_invalid_json_response(self, mock_client_class, mock_env_vars):
326
+ """Test handling of invalid JSON responses"""
327
+ mock_response = Mock()
328
+ mock_response.status_code = 200
329
+ mock_response.json.side_effect = json.JSONDecodeError("Invalid JSON", "", 0)
330
+
331
+ mock_client = MagicMock()
332
+ mock_client.__enter__.return_value.get.return_value = mock_response
333
+ mock_client_class.return_value = mock_client
334
+
335
+ tools = ContextGraphSearchTools()
336
+
337
+ with pytest.raises(Exception):
338
+ tools.get_labels()
339
+
340
+ def test_missing_api_key(self, monkeypatch):
341
+ """Test behavior when API key is missing"""
342
+ monkeypatch.delenv("KUBIYA_API_KEY", raising=False)
343
+
344
+ # Should still create tools but with warning
345
+ tools = ContextGraphSearchTools()
346
+ assert tools.api_key is None
347
+
348
+ @patch('skills.builtin.context_graph_search.agno_impl.httpx.Client')
349
+ def test_empty_response_handling(self, mock_client_class, mock_env_vars):
350
+ """Test handling of empty responses"""
351
+ mock_response = Mock()
352
+ mock_response.status_code = 200
353
+ mock_response.json.return_value = {}
354
+
355
+ mock_client = MagicMock()
356
+ mock_client.__enter__.return_value.get.return_value = mock_response
357
+ mock_client_class.return_value = mock_client
358
+
359
+ tools = ContextGraphSearchTools()
360
+ result = tools.get_labels()
361
+
362
+ # Should still return valid JSON string
363
+ assert isinstance(result, str)
364
+ parsed = json.loads(result)
365
+ assert parsed == {}