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.
- control_plane_api/LICENSE +676 -0
- control_plane_api/README.md +350 -0
- control_plane_api/__init__.py +4 -0
- control_plane_api/__version__.py +8 -0
- control_plane_api/alembic/README +1 -0
- control_plane_api/alembic/env.py +121 -0
- control_plane_api/alembic/script.py.mako +28 -0
- control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
- control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
- control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
- control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
- control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
- control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
- control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
- control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
- control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
- control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
- control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
- control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
- control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
- control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
- control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
- control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
- control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
- control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
- control_plane_api/alembic.ini +148 -0
- control_plane_api/api/index.py +12 -0
- control_plane_api/app/__init__.py +11 -0
- control_plane_api/app/activities/__init__.py +20 -0
- control_plane_api/app/activities/agent_activities.py +384 -0
- control_plane_api/app/activities/plan_generation_activities.py +499 -0
- control_plane_api/app/activities/team_activities.py +424 -0
- control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
- control_plane_api/app/config/__init__.py +35 -0
- control_plane_api/app/config/api_config.py +469 -0
- control_plane_api/app/config/config_loader.py +224 -0
- control_plane_api/app/config/model_pricing.py +323 -0
- control_plane_api/app/config/storage_config.py +159 -0
- control_plane_api/app/config.py +115 -0
- control_plane_api/app/controllers/__init__.py +0 -0
- control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
- control_plane_api/app/database.py +135 -0
- control_plane_api/app/exceptions.py +408 -0
- control_plane_api/app/lib/__init__.py +11 -0
- control_plane_api/app/lib/environment.py +65 -0
- control_plane_api/app/lib/event_bus/__init__.py +17 -0
- control_plane_api/app/lib/event_bus/base.py +136 -0
- control_plane_api/app/lib/event_bus/manager.py +335 -0
- control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
- control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
- control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
- control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
- control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
- control_plane_api/app/lib/job_executor.py +330 -0
- control_plane_api/app/lib/kubiya_client.py +293 -0
- control_plane_api/app/lib/litellm_pricing.py +166 -0
- control_plane_api/app/lib/mcp_validation.py +163 -0
- control_plane_api/app/lib/nats/__init__.py +13 -0
- control_plane_api/app/lib/nats/credentials_manager.py +288 -0
- control_plane_api/app/lib/nats/listener.py +374 -0
- control_plane_api/app/lib/planning_prompt_builder.py +153 -0
- control_plane_api/app/lib/planning_tools/__init__.py +41 -0
- control_plane_api/app/lib/planning_tools/agents.py +409 -0
- control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
- control_plane_api/app/lib/planning_tools/base.py +119 -0
- control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
- control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
- control_plane_api/app/lib/planning_tools/environments.py +218 -0
- control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
- control_plane_api/app/lib/planning_tools/models.py +93 -0
- control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
- control_plane_api/app/lib/planning_tools/resources.py +242 -0
- control_plane_api/app/lib/planning_tools/teams.py +334 -0
- control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
- control_plane_api/app/lib/redis_client.py +803 -0
- control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
- control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
- control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
- control_plane_api/app/lib/storage/__init__.py +20 -0
- control_plane_api/app/lib/storage/base_provider.py +274 -0
- control_plane_api/app/lib/storage/provider_factory.py +157 -0
- control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
- control_plane_api/app/lib/supabase.py +71 -0
- control_plane_api/app/lib/supabase_utils.py +138 -0
- control_plane_api/app/lib/task_planning/__init__.py +138 -0
- control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
- control_plane_api/app/lib/task_planning/agents.py +389 -0
- control_plane_api/app/lib/task_planning/cache.py +218 -0
- control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
- control_plane_api/app/lib/task_planning/helpers.py +293 -0
- control_plane_api/app/lib/task_planning/hooks.py +474 -0
- control_plane_api/app/lib/task_planning/models.py +503 -0
- control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
- control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
- control_plane_api/app/lib/task_planning/runner.py +656 -0
- control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
- control_plane_api/app/lib/task_planning/workflow.py +424 -0
- control_plane_api/app/lib/templating/__init__.py +88 -0
- control_plane_api/app/lib/templating/compiler.py +278 -0
- control_plane_api/app/lib/templating/engine.py +178 -0
- control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
- control_plane_api/app/lib/templating/parsers/base.py +96 -0
- control_plane_api/app/lib/templating/parsers/env.py +85 -0
- control_plane_api/app/lib/templating/parsers/graph.py +112 -0
- control_plane_api/app/lib/templating/parsers/secret.py +87 -0
- control_plane_api/app/lib/templating/parsers/simple.py +81 -0
- control_plane_api/app/lib/templating/resolver.py +366 -0
- control_plane_api/app/lib/templating/types.py +214 -0
- control_plane_api/app/lib/templating/validator.py +201 -0
- control_plane_api/app/lib/temporal_client.py +232 -0
- control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
- control_plane_api/app/lib/temporal_credentials_service.py +203 -0
- control_plane_api/app/lib/validation/__init__.py +24 -0
- control_plane_api/app/lib/validation/runtime_validation.py +388 -0
- control_plane_api/app/main.py +531 -0
- control_plane_api/app/middleware/__init__.py +10 -0
- control_plane_api/app/middleware/auth.py +645 -0
- control_plane_api/app/middleware/exception_handler.py +267 -0
- control_plane_api/app/middleware/prometheus_middleware.py +173 -0
- control_plane_api/app/middleware/rate_limiting.py +384 -0
- control_plane_api/app/middleware/request_id.py +202 -0
- control_plane_api/app/models/__init__.py +40 -0
- control_plane_api/app/models/agent.py +90 -0
- control_plane_api/app/models/analytics.py +206 -0
- control_plane_api/app/models/associations.py +107 -0
- control_plane_api/app/models/auth_user.py +73 -0
- control_plane_api/app/models/context.py +161 -0
- control_plane_api/app/models/custom_integration.py +99 -0
- control_plane_api/app/models/environment.py +64 -0
- control_plane_api/app/models/execution.py +125 -0
- control_plane_api/app/models/execution_transition.py +50 -0
- control_plane_api/app/models/job.py +159 -0
- control_plane_api/app/models/llm_model.py +78 -0
- control_plane_api/app/models/orchestration.py +66 -0
- control_plane_api/app/models/plan_execution.py +102 -0
- control_plane_api/app/models/presence.py +49 -0
- control_plane_api/app/models/project.py +61 -0
- control_plane_api/app/models/project_management.py +85 -0
- control_plane_api/app/models/session.py +29 -0
- control_plane_api/app/models/skill.py +155 -0
- control_plane_api/app/models/system_tables.py +43 -0
- control_plane_api/app/models/task_planning.py +372 -0
- control_plane_api/app/models/team.py +86 -0
- control_plane_api/app/models/trace.py +257 -0
- control_plane_api/app/models/user_profile.py +54 -0
- control_plane_api/app/models/worker.py +221 -0
- control_plane_api/app/models/workflow.py +161 -0
- control_plane_api/app/models/workspace.py +50 -0
- control_plane_api/app/observability/__init__.py +177 -0
- control_plane_api/app/observability/context_logging.py +475 -0
- control_plane_api/app/observability/decorators.py +337 -0
- control_plane_api/app/observability/local_span_processor.py +702 -0
- control_plane_api/app/observability/metrics.py +303 -0
- control_plane_api/app/observability/middleware.py +246 -0
- control_plane_api/app/observability/optional.py +115 -0
- control_plane_api/app/observability/tracing.py +382 -0
- control_plane_api/app/policies/README.md +149 -0
- control_plane_api/app/policies/approved_users.rego +62 -0
- control_plane_api/app/policies/business_hours.rego +51 -0
- control_plane_api/app/policies/rate_limiting.rego +100 -0
- control_plane_api/app/policies/tool_enforcement/README.md +336 -0
- control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
- control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
- control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
- control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
- control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
- control_plane_api/app/policies/tool_restrictions.rego +86 -0
- control_plane_api/app/routers/__init__.py +4 -0
- control_plane_api/app/routers/agents.py +382 -0
- control_plane_api/app/routers/agents_v2.py +1598 -0
- control_plane_api/app/routers/analytics.py +1310 -0
- control_plane_api/app/routers/auth.py +59 -0
- control_plane_api/app/routers/client_config.py +57 -0
- control_plane_api/app/routers/context_graph.py +561 -0
- control_plane_api/app/routers/context_manager.py +577 -0
- control_plane_api/app/routers/custom_integrations.py +490 -0
- control_plane_api/app/routers/enforcer.py +132 -0
- control_plane_api/app/routers/environment_context.py +252 -0
- control_plane_api/app/routers/environments.py +761 -0
- control_plane_api/app/routers/execution_environment.py +847 -0
- control_plane_api/app/routers/executions/__init__.py +28 -0
- control_plane_api/app/routers/executions/router.py +286 -0
- control_plane_api/app/routers/executions/services/__init__.py +22 -0
- control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
- control_plane_api/app/routers/executions/services/status_service.py +420 -0
- control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
- control_plane_api/app/routers/executions/services/worker_health.py +514 -0
- control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
- control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
- control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
- control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
- control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
- control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
- control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
- control_plane_api/app/routers/executions.py +4888 -0
- control_plane_api/app/routers/health.py +165 -0
- control_plane_api/app/routers/health_v2.py +394 -0
- control_plane_api/app/routers/integration_templates.py +496 -0
- control_plane_api/app/routers/integrations.py +287 -0
- control_plane_api/app/routers/jobs.py +1809 -0
- control_plane_api/app/routers/metrics.py +517 -0
- control_plane_api/app/routers/models.py +82 -0
- control_plane_api/app/routers/models_v2.py +628 -0
- control_plane_api/app/routers/plan_executions.py +1481 -0
- control_plane_api/app/routers/plan_generation_async.py +304 -0
- control_plane_api/app/routers/policies.py +669 -0
- control_plane_api/app/routers/presence.py +234 -0
- control_plane_api/app/routers/projects.py +987 -0
- control_plane_api/app/routers/runners.py +379 -0
- control_plane_api/app/routers/runtimes.py +172 -0
- control_plane_api/app/routers/secrets.py +171 -0
- control_plane_api/app/routers/skills.py +1010 -0
- control_plane_api/app/routers/skills_definitions.py +140 -0
- control_plane_api/app/routers/storage.py +456 -0
- control_plane_api/app/routers/task_planning.py +611 -0
- control_plane_api/app/routers/task_queues.py +650 -0
- control_plane_api/app/routers/team_context.py +274 -0
- control_plane_api/app/routers/teams.py +1747 -0
- control_plane_api/app/routers/templates.py +248 -0
- control_plane_api/app/routers/traces.py +571 -0
- control_plane_api/app/routers/websocket_client.py +479 -0
- control_plane_api/app/routers/websocket_executions_status.py +437 -0
- control_plane_api/app/routers/websocket_gateway.py +323 -0
- control_plane_api/app/routers/websocket_traces.py +576 -0
- control_plane_api/app/routers/worker_queues.py +2555 -0
- control_plane_api/app/routers/worker_websocket.py +419 -0
- control_plane_api/app/routers/workers.py +1004 -0
- control_plane_api/app/routers/workflows.py +204 -0
- control_plane_api/app/runtimes/__init__.py +6 -0
- control_plane_api/app/runtimes/validation.py +344 -0
- control_plane_api/app/schemas/__init__.py +1 -0
- control_plane_api/app/schemas/job_schemas.py +302 -0
- control_plane_api/app/schemas/mcp_schemas.py +311 -0
- control_plane_api/app/schemas/template_schemas.py +133 -0
- control_plane_api/app/schemas/trace_schemas.py +168 -0
- control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
- control_plane_api/app/services/__init__.py +1 -0
- control_plane_api/app/services/agno_planning_strategy.py +233 -0
- control_plane_api/app/services/agno_service.py +838 -0
- control_plane_api/app/services/claude_code_planning_service.py +203 -0
- control_plane_api/app/services/context_graph_client.py +224 -0
- control_plane_api/app/services/custom_integration_service.py +415 -0
- control_plane_api/app/services/integration_resolution_service.py +345 -0
- control_plane_api/app/services/litellm_service.py +394 -0
- control_plane_api/app/services/plan_generator.py +79 -0
- control_plane_api/app/services/planning_strategy.py +66 -0
- control_plane_api/app/services/planning_strategy_factory.py +118 -0
- control_plane_api/app/services/policy_service.py +615 -0
- control_plane_api/app/services/state_transition_service.py +755 -0
- control_plane_api/app/services/storage_service.py +593 -0
- control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
- control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
- control_plane_api/app/services/trace_retention.py +354 -0
- control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
- control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
- control_plane_api/app/services/workflow_operations_service.py +611 -0
- control_plane_api/app/skills/__init__.py +100 -0
- control_plane_api/app/skills/base.py +239 -0
- control_plane_api/app/skills/builtin/__init__.py +37 -0
- control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
- control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
- control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
- control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
- control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
- control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
- control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
- control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
- control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
- control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
- control_plane_api/app/skills/builtin/docker/skill.py +104 -0
- control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
- control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
- control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
- control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
- control_plane_api/app/skills/builtin/python/__init__.py +4 -0
- control_plane_api/app/skills/builtin/python/skill.py +92 -0
- control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
- control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
- control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
- control_plane_api/app/skills/builtin/shell/skill.py +161 -0
- control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/app/skills/builtin/slack/skill.py +302 -0
- control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
- control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
- control_plane_api/app/skills/business_intelligence.py +189 -0
- control_plane_api/app/skills/config.py +63 -0
- control_plane_api/app/skills/loaders/__init__.py +14 -0
- control_plane_api/app/skills/loaders/base.py +73 -0
- control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
- control_plane_api/app/skills/registry.py +125 -0
- control_plane_api/app/utils/helpers.py +12 -0
- control_plane_api/app/utils/workflow_executor.py +354 -0
- control_plane_api/app/workflows/__init__.py +11 -0
- control_plane_api/app/workflows/agent_execution.py +520 -0
- control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
- control_plane_api/app/workflows/namespace_provisioning.py +326 -0
- control_plane_api/app/workflows/plan_generation.py +254 -0
- control_plane_api/app/workflows/team_execution.py +442 -0
- control_plane_api/scripts/seed_models.py +240 -0
- control_plane_api/scripts/validate_existing_tool_names.py +492 -0
- control_plane_api/shared/__init__.py +8 -0
- control_plane_api/shared/version.py +17 -0
- control_plane_api/test_deduplication.py +274 -0
- control_plane_api/test_executor_deduplication_e2e.py +309 -0
- control_plane_api/test_job_execution_e2e.py +283 -0
- control_plane_api/test_real_integration.py +193 -0
- control_plane_api/version.py +38 -0
- control_plane_api/worker/__init__.py +0 -0
- control_plane_api/worker/activities/__init__.py +0 -0
- control_plane_api/worker/activities/agent_activities.py +1585 -0
- control_plane_api/worker/activities/approval_activities.py +234 -0
- control_plane_api/worker/activities/job_activities.py +199 -0
- control_plane_api/worker/activities/runtime_activities.py +1167 -0
- control_plane_api/worker/activities/skill_activities.py +282 -0
- control_plane_api/worker/activities/team_activities.py +479 -0
- control_plane_api/worker/agent_runtime_server.py +370 -0
- control_plane_api/worker/binary_manager.py +333 -0
- control_plane_api/worker/config/__init__.py +31 -0
- control_plane_api/worker/config/worker_config.py +273 -0
- control_plane_api/worker/control_plane_client.py +1491 -0
- control_plane_api/worker/examples/analytics_integration_example.py +362 -0
- control_plane_api/worker/health_monitor.py +159 -0
- control_plane_api/worker/metrics.py +237 -0
- control_plane_api/worker/models/__init__.py +1 -0
- control_plane_api/worker/models/error_events.py +105 -0
- control_plane_api/worker/models/inputs.py +89 -0
- control_plane_api/worker/runtimes/__init__.py +35 -0
- control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
- control_plane_api/worker/runtimes/agno/__init__.py +34 -0
- control_plane_api/worker/runtimes/agno/config.py +248 -0
- control_plane_api/worker/runtimes/agno/hooks.py +385 -0
- control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
- control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
- control_plane_api/worker/runtimes/agno/utils.py +163 -0
- control_plane_api/worker/runtimes/base.py +979 -0
- control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
- control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
- control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
- control_plane_api/worker/runtimes/claude_code/config.py +829 -0
- control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
- control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
- control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
- control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
- control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
- control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
- control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
- control_plane_api/worker/runtimes/factory.py +173 -0
- control_plane_api/worker/runtimes/model_utils.py +107 -0
- control_plane_api/worker/runtimes/validation.py +93 -0
- control_plane_api/worker/services/__init__.py +1 -0
- control_plane_api/worker/services/agent_communication_tools.py +908 -0
- control_plane_api/worker/services/agent_executor.py +485 -0
- control_plane_api/worker/services/agent_executor_v2.py +793 -0
- control_plane_api/worker/services/analytics_collector.py +457 -0
- control_plane_api/worker/services/analytics_service.py +464 -0
- control_plane_api/worker/services/approval_tools.py +310 -0
- control_plane_api/worker/services/approval_tools_agno.py +207 -0
- control_plane_api/worker/services/cancellation_manager.py +177 -0
- control_plane_api/worker/services/code_ingestion_tools.py +465 -0
- control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
- control_plane_api/worker/services/data_visualization.py +834 -0
- control_plane_api/worker/services/event_publisher.py +531 -0
- control_plane_api/worker/services/jira_tools.py +257 -0
- control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
- control_plane_api/worker/services/runtime_analytics.py +328 -0
- control_plane_api/worker/services/session_service.py +365 -0
- control_plane_api/worker/services/skill_context_enhancement.py +181 -0
- control_plane_api/worker/services/skill_factory.py +471 -0
- control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
- control_plane_api/worker/services/team_executor.py +715 -0
- control_plane_api/worker/services/team_executor_v2.py +1866 -0
- control_plane_api/worker/services/tool_enforcement.py +254 -0
- control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
- control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
- control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
- control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
- control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
- control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/models.py +142 -0
- control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
- control_plane_api/worker/skills/__init__.py +12 -0
- control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
- control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
- control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
- control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
- control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
- control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
- control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
- control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
- control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
- control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
- control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
- control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
- control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
- control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
- control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
- control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
- control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
- control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
- control_plane_api/worker/skills/loaders/__init__.py +5 -0
- control_plane_api/worker/skills/loaders/base.py +23 -0
- control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
- control_plane_api/worker/skills/registry.py +208 -0
- control_plane_api/worker/tests/__init__.py +1 -0
- control_plane_api/worker/tests/conftest.py +12 -0
- control_plane_api/worker/tests/e2e/__init__.py +0 -0
- control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
- control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
- control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
- control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
- control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
- control_plane_api/worker/tests/integration/__init__.py +0 -0
- control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
- control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
- control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
- control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
- control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
- control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
- control_plane_api/worker/tests/unit/__init__.py +0 -0
- control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
- control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
- control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
- control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
- control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
- control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
- control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
- control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
- control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
- control_plane_api/worker/utils/__init__.py +1 -0
- control_plane_api/worker/utils/chunk_batcher.py +330 -0
- control_plane_api/worker/utils/environment.py +65 -0
- control_plane_api/worker/utils/error_publisher.py +260 -0
- control_plane_api/worker/utils/event_batcher.py +256 -0
- control_plane_api/worker/utils/logging_config.py +335 -0
- control_plane_api/worker/utils/logging_helper.py +326 -0
- control_plane_api/worker/utils/parameter_validator.py +120 -0
- control_plane_api/worker/utils/retry_utils.py +60 -0
- control_plane_api/worker/utils/streaming_utils.py +665 -0
- control_plane_api/worker/utils/tool_validation.py +332 -0
- control_plane_api/worker/utils/workspace_manager.py +163 -0
- control_plane_api/worker/websocket_client.py +393 -0
- control_plane_api/worker/worker.py +1297 -0
- control_plane_api/worker/workflows/__init__.py +0 -0
- control_plane_api/worker/workflows/agent_execution.py +909 -0
- control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
- control_plane_api/worker/workflows/team_execution.py +611 -0
- kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
- kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
- kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
- kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
- scripts/__init__.py +1 -0
- scripts/migrations.py +39 -0
- scripts/seed_worker_queues.py +128 -0
- scripts/setup_agent_runtime.py +142 -0
- worker_internal/__init__.py +1 -0
- worker_internal/planner/__init__.py +1 -0
- worker_internal/planner/activities.py +1499 -0
- worker_internal/planner/agent_tools.py +197 -0
- worker_internal/planner/event_models.py +148 -0
- worker_internal/planner/event_publisher.py +67 -0
- worker_internal/planner/models.py +199 -0
- worker_internal/planner/retry_logic.py +134 -0
- worker_internal/planner/worker.py +300 -0
- worker_internal/planner/workflows.py +970 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Universal Tool Name Validation for All LLM Providers
|
|
3
|
+
|
|
4
|
+
This module provides strict tool name validation that works across ALL major LLM providers:
|
|
5
|
+
- OpenAI/Azure OpenAI
|
|
6
|
+
- Anthropic Claude
|
|
7
|
+
- Google Vertex AI/Gemini
|
|
8
|
+
- AWS Bedrock
|
|
9
|
+
|
|
10
|
+
The validation rules are the strictest common denominator to ensure compatibility everywhere.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
import structlog
|
|
15
|
+
from typing import List, Tuple, Dict, Any
|
|
16
|
+
|
|
17
|
+
logger = structlog.get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
# Universal tool name pattern - works for ALL LLM providers
|
|
20
|
+
# Rules:
|
|
21
|
+
# 1. Must start with letter (a-z, A-Z) or underscore (_)
|
|
22
|
+
# 2. Can only contain: letters, numbers, underscores
|
|
23
|
+
# 3. Maximum length: 64 characters
|
|
24
|
+
# 4. Minimum length: 1 character
|
|
25
|
+
UNIVERSAL_TOOL_NAME_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]{0,63}$')
|
|
26
|
+
|
|
27
|
+
# Characters that are safe across all providers
|
|
28
|
+
SAFE_CHARS = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_')
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ToolValidationError(Exception):
|
|
32
|
+
"""Raised when tool validation fails and cannot be auto-fixed."""
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def validate_tool_name(name: str, provider_context: str = "universal") -> Tuple[bool, str, List[str]]:
|
|
37
|
+
"""
|
|
38
|
+
Validate a tool name against universal LLM provider requirements.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
name: The tool name to validate
|
|
42
|
+
provider_context: Context string for logging (e.g., "vertex_ai", "openai")
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Tuple of (is_valid, error_message, list_of_violations)
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
>>> validate_tool_name("my_tool")
|
|
49
|
+
(True, "", [])
|
|
50
|
+
>>> validate_tool_name("123invalid")
|
|
51
|
+
(False, "Tool name must start with letter or underscore", ["invalid_start"])
|
|
52
|
+
"""
|
|
53
|
+
violations = []
|
|
54
|
+
|
|
55
|
+
if not name:
|
|
56
|
+
return False, "Tool name cannot be empty", ["empty_name"]
|
|
57
|
+
|
|
58
|
+
if not isinstance(name, str):
|
|
59
|
+
return False, f"Tool name must be string, got {type(name)}", ["invalid_type"]
|
|
60
|
+
|
|
61
|
+
# Check length
|
|
62
|
+
if len(name) > 64:
|
|
63
|
+
violations.append("exceeds_max_length")
|
|
64
|
+
|
|
65
|
+
if len(name) == 0:
|
|
66
|
+
return False, "Tool name cannot be empty", ["empty_name"]
|
|
67
|
+
|
|
68
|
+
# Check first character
|
|
69
|
+
if not (name[0].isalpha() or name[0] == '_'):
|
|
70
|
+
violations.append("invalid_start")
|
|
71
|
+
|
|
72
|
+
# Check for invalid characters
|
|
73
|
+
invalid_chars = set()
|
|
74
|
+
for char in name:
|
|
75
|
+
if char not in SAFE_CHARS:
|
|
76
|
+
invalid_chars.add(char)
|
|
77
|
+
|
|
78
|
+
if invalid_chars:
|
|
79
|
+
violations.append(f"invalid_chars_{','.join(sorted(invalid_chars))}")
|
|
80
|
+
|
|
81
|
+
# Check full pattern
|
|
82
|
+
if not UNIVERSAL_TOOL_NAME_PATTERN.match(name):
|
|
83
|
+
if "invalid_start" not in violations and "exceeds_max_length" not in violations:
|
|
84
|
+
violations.append("pattern_mismatch")
|
|
85
|
+
|
|
86
|
+
if violations:
|
|
87
|
+
error_msg = f"Tool name '{name}' is invalid for {provider_context}: {', '.join(violations)}"
|
|
88
|
+
return False, error_msg, violations
|
|
89
|
+
|
|
90
|
+
return True, "", []
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def sanitize_tool_name(name: str, prefix: str = "", max_length: int = 64) -> str:
|
|
94
|
+
"""
|
|
95
|
+
Sanitize a tool name to make it valid for all LLM providers.
|
|
96
|
+
|
|
97
|
+
This function aggressively cleans tool names to ensure compatibility:
|
|
98
|
+
- Replaces invalid characters with underscores
|
|
99
|
+
- Ensures it starts with letter or underscore
|
|
100
|
+
- Truncates to max_length
|
|
101
|
+
- Collapses multiple underscores
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
name: The tool name to sanitize
|
|
105
|
+
prefix: Optional prefix to add (useful for namespacing)
|
|
106
|
+
max_length: Maximum allowed length (default 64)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Sanitized tool name that passes validation
|
|
110
|
+
|
|
111
|
+
Example:
|
|
112
|
+
>>> sanitize_tool_name("my-tool!")
|
|
113
|
+
"my_tool"
|
|
114
|
+
>>> sanitize_tool_name("123tool")
|
|
115
|
+
"_123tool"
|
|
116
|
+
>>> sanitize_tool_name("grounding::query_data")
|
|
117
|
+
"grounding_query_data"
|
|
118
|
+
"""
|
|
119
|
+
if not name:
|
|
120
|
+
return "unnamed_tool"
|
|
121
|
+
|
|
122
|
+
# Add prefix if provided
|
|
123
|
+
if prefix:
|
|
124
|
+
name = f"{prefix}_{name}"
|
|
125
|
+
|
|
126
|
+
# Replace all invalid characters with underscores
|
|
127
|
+
sanitized = ""
|
|
128
|
+
for char in name:
|
|
129
|
+
if char in SAFE_CHARS:
|
|
130
|
+
sanitized += char
|
|
131
|
+
else:
|
|
132
|
+
sanitized += "_"
|
|
133
|
+
|
|
134
|
+
# Collapse multiple underscores
|
|
135
|
+
while "__" in sanitized:
|
|
136
|
+
sanitized = sanitized.replace("__", "_")
|
|
137
|
+
|
|
138
|
+
# Remove leading/trailing underscores except one if needed for start
|
|
139
|
+
sanitized = sanitized.strip("_")
|
|
140
|
+
|
|
141
|
+
# Ensure starts with letter or underscore
|
|
142
|
+
if sanitized and not (sanitized[0].isalpha() or sanitized[0] == '_'):
|
|
143
|
+
sanitized = f"_{sanitized}"
|
|
144
|
+
|
|
145
|
+
# Handle empty result
|
|
146
|
+
if not sanitized:
|
|
147
|
+
sanitized = "tool" if not prefix else prefix
|
|
148
|
+
|
|
149
|
+
# Truncate to max length
|
|
150
|
+
if len(sanitized) > max_length:
|
|
151
|
+
sanitized = sanitized[:max_length]
|
|
152
|
+
# Ensure we didn't cut in a way that makes it invalid
|
|
153
|
+
sanitized = sanitized.rstrip("_")
|
|
154
|
+
|
|
155
|
+
# Final validation - if still invalid, use fallback
|
|
156
|
+
is_valid, _, _ = validate_tool_name(sanitized)
|
|
157
|
+
if not is_valid:
|
|
158
|
+
logger.warning(f"Sanitization failed for '{name}', using fallback name")
|
|
159
|
+
return "sanitized_tool"
|
|
160
|
+
|
|
161
|
+
return sanitized
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def validate_and_sanitize_tools(
|
|
165
|
+
tools: List[Any],
|
|
166
|
+
tool_name_getter: callable = lambda t: getattr(t, 'name', str(t)),
|
|
167
|
+
auto_fix: bool = True,
|
|
168
|
+
provider_context: str = "universal"
|
|
169
|
+
) -> Tuple[List[Any], List[Dict[str, Any]]]:
|
|
170
|
+
"""
|
|
171
|
+
Validate and optionally sanitize a list of tools.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
tools: List of tool objects to validate
|
|
175
|
+
tool_name_getter: Function to extract tool name from tool object
|
|
176
|
+
auto_fix: If True, sanitize invalid tool names; if False, filter them out
|
|
177
|
+
provider_context: Context for error messages
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Tuple of (validated_tools, validation_report)
|
|
181
|
+
- validated_tools: List of tools with valid names
|
|
182
|
+
- validation_report: List of dicts with validation details
|
|
183
|
+
|
|
184
|
+
Example:
|
|
185
|
+
>>> tools = [Tool(name="valid_tool"), Tool(name="invalid-tool!")]
|
|
186
|
+
>>> valid_tools, report = validate_and_sanitize_tools(tools)
|
|
187
|
+
>>> len(valid_tools)
|
|
188
|
+
2
|
|
189
|
+
>>> report[1]['action']
|
|
190
|
+
'sanitized'
|
|
191
|
+
"""
|
|
192
|
+
validated_tools = []
|
|
193
|
+
validation_report = []
|
|
194
|
+
|
|
195
|
+
for i, tool in enumerate(tools):
|
|
196
|
+
try:
|
|
197
|
+
original_name = tool_name_getter(tool)
|
|
198
|
+
is_valid, error_msg, violations = validate_tool_name(original_name, provider_context)
|
|
199
|
+
|
|
200
|
+
if is_valid:
|
|
201
|
+
validated_tools.append(tool)
|
|
202
|
+
validation_report.append({
|
|
203
|
+
"index": i,
|
|
204
|
+
"original_name": original_name,
|
|
205
|
+
"final_name": original_name,
|
|
206
|
+
"action": "passed",
|
|
207
|
+
"violations": []
|
|
208
|
+
})
|
|
209
|
+
else:
|
|
210
|
+
if auto_fix:
|
|
211
|
+
# Try to sanitize
|
|
212
|
+
sanitized_name = sanitize_tool_name(original_name)
|
|
213
|
+
|
|
214
|
+
# Update tool name if possible
|
|
215
|
+
if hasattr(tool, 'name'):
|
|
216
|
+
tool.name = sanitized_name
|
|
217
|
+
|
|
218
|
+
validated_tools.append(tool)
|
|
219
|
+
validation_report.append({
|
|
220
|
+
"index": i,
|
|
221
|
+
"original_name": original_name,
|
|
222
|
+
"final_name": sanitized_name,
|
|
223
|
+
"action": "sanitized",
|
|
224
|
+
"violations": violations,
|
|
225
|
+
"error": error_msg
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
logger.warning(
|
|
229
|
+
f"Tool name sanitized for {provider_context}: "
|
|
230
|
+
f"'{original_name}' -> '{sanitized_name}' (violations: {violations})"
|
|
231
|
+
)
|
|
232
|
+
else:
|
|
233
|
+
# Filter out invalid tool
|
|
234
|
+
validation_report.append({
|
|
235
|
+
"index": i,
|
|
236
|
+
"original_name": original_name,
|
|
237
|
+
"final_name": None,
|
|
238
|
+
"action": "filtered",
|
|
239
|
+
"violations": violations,
|
|
240
|
+
"error": error_msg
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
logger.error(
|
|
244
|
+
f"Tool filtered out for {provider_context}: "
|
|
245
|
+
f"'{original_name}' - {error_msg}"
|
|
246
|
+
)
|
|
247
|
+
except Exception as e:
|
|
248
|
+
logger.error(f"Error validating tool at index {i}: {e}")
|
|
249
|
+
validation_report.append({
|
|
250
|
+
"index": i,
|
|
251
|
+
"original_name": "unknown",
|
|
252
|
+
"final_name": None,
|
|
253
|
+
"action": "error",
|
|
254
|
+
"violations": ["exception"],
|
|
255
|
+
"error": str(e)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
return validated_tools, validation_report
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def validate_tool_definition(tool_def: Dict[str, Any]) -> Tuple[bool, str]:
|
|
262
|
+
"""
|
|
263
|
+
Validate a tool definition dictionary (OpenAI/Anthropic format).
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
tool_def: Tool definition dict with 'name' and optionally 'function'
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Tuple of (is_valid, error_message)
|
|
270
|
+
"""
|
|
271
|
+
if not isinstance(tool_def, dict):
|
|
272
|
+
return False, "Tool definition must be a dictionary"
|
|
273
|
+
|
|
274
|
+
# Check for function.name (OpenAI format)
|
|
275
|
+
if "function" in tool_def:
|
|
276
|
+
if "name" not in tool_def["function"]:
|
|
277
|
+
return False, "Tool function missing 'name' field"
|
|
278
|
+
tool_name = tool_def["function"]["name"]
|
|
279
|
+
# Check for name (Anthropic format)
|
|
280
|
+
elif "name" in tool_def:
|
|
281
|
+
tool_name = tool_def["name"]
|
|
282
|
+
else:
|
|
283
|
+
return False, "Tool definition missing 'name' field"
|
|
284
|
+
|
|
285
|
+
is_valid, error_msg, _ = validate_tool_name(tool_name)
|
|
286
|
+
return is_valid, error_msg
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def get_provider_specific_requirements() -> Dict[str, Dict[str, Any]]:
|
|
290
|
+
"""
|
|
291
|
+
Get provider-specific tool name requirements for reference.
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
Dict mapping provider name to their requirements
|
|
295
|
+
"""
|
|
296
|
+
return {
|
|
297
|
+
"openai": {
|
|
298
|
+
"pattern": r'^[a-zA-Z0-9_-]{1,64}$',
|
|
299
|
+
"description": "1-64 chars, letters, numbers, hyphens, underscores"
|
|
300
|
+
},
|
|
301
|
+
"anthropic": {
|
|
302
|
+
"pattern": r'^[a-zA-Z0-9_]{1,64}$',
|
|
303
|
+
"description": "1-64 chars, letters, numbers, underscores"
|
|
304
|
+
},
|
|
305
|
+
"vertex_ai": {
|
|
306
|
+
"pattern": r'^[a-zA-Z_][a-zA-Z0-9_\.\:\-]{0,63}$',
|
|
307
|
+
"description": "Start with letter/underscore, letters, numbers, underscore, dot, colon, dash, max 64"
|
|
308
|
+
},
|
|
309
|
+
"bedrock": {
|
|
310
|
+
"pattern": r'^[a-zA-Z][a-zA-Z0-9_]{0,63}$',
|
|
311
|
+
"description": "Start with letter, letters, numbers, underscores, max 64"
|
|
312
|
+
},
|
|
313
|
+
"universal": {
|
|
314
|
+
"pattern": UNIVERSAL_TOOL_NAME_PATTERN.pattern,
|
|
315
|
+
"description": "Start with letter/underscore, letters, numbers, underscores only, max 64 (strictest common)"
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# Quick validation functions for common use cases
|
|
321
|
+
|
|
322
|
+
def is_valid_tool_name(name: str) -> bool:
|
|
323
|
+
"""Quick check if a tool name is valid."""
|
|
324
|
+
is_valid, _, _ = validate_tool_name(name)
|
|
325
|
+
return is_valid
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def assert_valid_tool_name(name: str, context: str = ""):
|
|
329
|
+
"""Assert tool name is valid, raise exception if not."""
|
|
330
|
+
is_valid, error_msg, _ = validate_tool_name(name, context)
|
|
331
|
+
if not is_valid:
|
|
332
|
+
raise ToolValidationError(f"{context}: {error_msg}" if context else error_msg)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Workspace management for execution isolation.
|
|
2
|
+
|
|
3
|
+
Provides utilities for creating and managing execution-scoped workspaces
|
|
4
|
+
where skills can safely operate without affecting global filesystem.
|
|
5
|
+
|
|
6
|
+
Pattern: .kubiya/workspaces/<execution-id>/
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import re
|
|
11
|
+
import structlog
|
|
12
|
+
|
|
13
|
+
logger = structlog.get_logger(__name__)
|
|
14
|
+
|
|
15
|
+
# Module-level constants
|
|
16
|
+
WORKSPACE_ROOT = ".kubiya/workspaces"
|
|
17
|
+
MAX_EXECUTION_ID_LENGTH = 200
|
|
18
|
+
|
|
19
|
+
# Unsafe filesystem characters to sanitize
|
|
20
|
+
UNSAFE_CHARS_PATTERN = re.compile(r'[/\\:*?"<>|]')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def ensure_workspace(execution_id: str) -> Path:
|
|
24
|
+
"""
|
|
25
|
+
Create or return a workspace directory path for a given execution ID.
|
|
26
|
+
|
|
27
|
+
Purpose:
|
|
28
|
+
- Provides an isolated directory for each execution
|
|
29
|
+
- Used by file_system and shell skills for bounded operations
|
|
30
|
+
- Prevents global filesystem modifications
|
|
31
|
+
|
|
32
|
+
Path Created:
|
|
33
|
+
- .kubiya/workspaces/<execution-id>/
|
|
34
|
+
|
|
35
|
+
Behavior:
|
|
36
|
+
- Creates directory if it doesn't exist
|
|
37
|
+
- Returns existing path if already created
|
|
38
|
+
- Parent directories created automatically with proper permissions
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
execution_id: Unique execution identifier (from RuntimeExecutionContext)
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Path object pointing to the workspace directory
|
|
45
|
+
(Caller converts to str with str(workspace_path))
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
ValueError: If execution_id is None/empty or unsafe for filesystem
|
|
49
|
+
OSError: If directory creation fails (permission denied, etc.)
|
|
50
|
+
|
|
51
|
+
Usage:
|
|
52
|
+
workspace_path = ensure_workspace(execution_id)
|
|
53
|
+
skill_instance = FileSystemTools(base_directory=str(workspace_path))
|
|
54
|
+
"""
|
|
55
|
+
# Validate execution_id
|
|
56
|
+
if not execution_id:
|
|
57
|
+
logger.error(
|
|
58
|
+
"workspace_creation_failed",
|
|
59
|
+
error="execution_id is None or empty",
|
|
60
|
+
error_type="ValueError",
|
|
61
|
+
)
|
|
62
|
+
raise ValueError("execution_id must be a non-empty string")
|
|
63
|
+
|
|
64
|
+
# Sanitize execution_id for filesystem safety
|
|
65
|
+
sanitized_id = UNSAFE_CHARS_PATTERN.sub("_", execution_id)
|
|
66
|
+
|
|
67
|
+
# Truncate if too long
|
|
68
|
+
if len(sanitized_id) > MAX_EXECUTION_ID_LENGTH:
|
|
69
|
+
original_length = len(sanitized_id)
|
|
70
|
+
sanitized_id = sanitized_id[:MAX_EXECUTION_ID_LENGTH]
|
|
71
|
+
logger.warning(
|
|
72
|
+
"workspace_execution_id_truncated",
|
|
73
|
+
original_length=original_length,
|
|
74
|
+
max_length=MAX_EXECUTION_ID_LENGTH,
|
|
75
|
+
execution_id=sanitized_id[:8] if len(sanitized_id) >= 8 else sanitized_id,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Log warning if sanitization was applied
|
|
79
|
+
if sanitized_id != execution_id:
|
|
80
|
+
logger.warning(
|
|
81
|
+
"workspace_execution_id_sanitized",
|
|
82
|
+
original=execution_id[:50] if len(execution_id) >= 50 else execution_id,
|
|
83
|
+
sanitized=sanitized_id[:50] if len(sanitized_id) >= 50 else sanitized_id,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Build workspace path relative to current working directory
|
|
87
|
+
workspace_path = Path.cwd() / WORKSPACE_ROOT / sanitized_id
|
|
88
|
+
|
|
89
|
+
# Create directory (idempotent with exist_ok=True)
|
|
90
|
+
try:
|
|
91
|
+
workspace_path.mkdir(parents=True, exist_ok=True)
|
|
92
|
+
|
|
93
|
+
logger.info(
|
|
94
|
+
"execution_workspace_ensured",
|
|
95
|
+
execution_id=sanitized_id[:8] if len(sanitized_id) >= 8 else sanitized_id,
|
|
96
|
+
path=str(workspace_path),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return workspace_path
|
|
100
|
+
|
|
101
|
+
except OSError as e:
|
|
102
|
+
logger.error(
|
|
103
|
+
"workspace_directory_creation_failed",
|
|
104
|
+
execution_id=sanitized_id[:8] if len(sanitized_id) >= 8 else sanitized_id,
|
|
105
|
+
path=str(workspace_path),
|
|
106
|
+
error=str(e),
|
|
107
|
+
error_type=type(e).__name__,
|
|
108
|
+
)
|
|
109
|
+
# Let exception propagate - caller handles with try/except
|
|
110
|
+
raise
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def should_use_custom_base_directory(skill_data: dict) -> bool:
|
|
114
|
+
"""
|
|
115
|
+
Check if skill has explicitly configured base_directory.
|
|
116
|
+
|
|
117
|
+
Purpose:
|
|
118
|
+
- Prevents overriding user-specified base directories
|
|
119
|
+
- Allows skills to use custom paths when explicitly configured
|
|
120
|
+
|
|
121
|
+
Configuration Structure:
|
|
122
|
+
skill_data = {
|
|
123
|
+
"name": "file_system",
|
|
124
|
+
"type": "file_system",
|
|
125
|
+
"configuration": {
|
|
126
|
+
"base_directory": "/custom/path", # If present, return True
|
|
127
|
+
...
|
|
128
|
+
},
|
|
129
|
+
"enabled": True,
|
|
130
|
+
"execution_id": "..."
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
skill_data: Skill configuration dict from Control Plane
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
True if skill_data["configuration"]["base_directory"] is set to a non-empty value
|
|
138
|
+
False if base_directory is missing, None, empty string, or invalid
|
|
139
|
+
|
|
140
|
+
Usage:
|
|
141
|
+
if not should_use_custom_base_directory(skill_data):
|
|
142
|
+
config["base_directory"] = workspace_path
|
|
143
|
+
"""
|
|
144
|
+
# Safe dictionary access with defaults
|
|
145
|
+
if not skill_data:
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
configuration = skill_data.get("configuration", {})
|
|
149
|
+
if not configuration:
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
base_directory = configuration.get("base_directory")
|
|
153
|
+
|
|
154
|
+
# Check if base_directory is set to a non-empty value
|
|
155
|
+
if base_directory is None:
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
# Handle empty strings and whitespace
|
|
159
|
+
if isinstance(base_directory, str) and not base_directory.strip():
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
# base_directory is explicitly set
|
|
163
|
+
return True
|