diff --git a/backend/apps/HaikFix/Agent/__init__.py b/backend/OLDapps/__init__.py similarity index 100% rename from backend/apps/HaikFix/Agent/__init__.py rename to backend/OLDapps/__init__.py diff --git a/backend/apps/agents/README.md b/backend/OLDapps/agents/README.md similarity index 100% rename from backend/apps/agents/README.md rename to backend/OLDapps/agents/README.md diff --git a/backend/apps/HaikFix/Agent/run_agent_loop/__init__.py b/backend/OLDapps/agents/__init__.py similarity index 100% rename from backend/apps/HaikFix/Agent/run_agent_loop/__init__.py rename to backend/OLDapps/agents/__init__.py diff --git a/backend/apps/agents/agents.py b/backend/OLDapps/agents/agents.py similarity index 100% rename from backend/apps/agents/agents.py rename to backend/OLDapps/agents/agents.py diff --git a/backend/apps/agents/browser/README.md b/backend/OLDapps/agents/browser/README.md similarity index 100% rename from backend/apps/agents/browser/README.md rename to backend/OLDapps/agents/browser/README.md diff --git a/backend/apps/agents/browser/__init__.py b/backend/OLDapps/agents/browser/__init__.py similarity index 100% rename from backend/apps/agents/browser/__init__.py rename to backend/OLDapps/agents/browser/__init__.py diff --git a/backend/apps/agents/browser/browser_agent_mcp_schemas.py b/backend/OLDapps/agents/browser/browser_agent_mcp_schemas.py similarity index 100% rename from backend/apps/agents/browser/browser_agent_mcp_schemas.py rename to backend/OLDapps/agents/browser/browser_agent_mcp_schemas.py diff --git a/backend/apps/agents/browser/browser_agent_mcp_server.py b/backend/OLDapps/agents/browser/browser_agent_mcp_server.py similarity index 100% rename from backend/apps/agents/browser/browser_agent_mcp_server.py rename to backend/OLDapps/agents/browser/browser_agent_mcp_server.py diff --git a/backend/apps/agents/browser/executor.py b/backend/OLDapps/agents/browser/executor.py similarity index 100% rename from backend/apps/agents/browser/executor.py rename to backend/OLDapps/agents/browser/executor.py diff --git a/backend/apps/agents/browser/runner.py b/backend/OLDapps/agents/browser/runner.py similarity index 100% rename from backend/apps/agents/browser/runner.py rename to backend/OLDapps/agents/browser/runner.py diff --git a/backend/apps/agents/browser/schemas.py b/backend/OLDapps/agents/browser/schemas.py similarity index 100% rename from backend/apps/agents/browser/schemas.py rename to backend/OLDapps/agents/browser/schemas.py diff --git a/backend/apps/agents/execution/README.md b/backend/OLDapps/agents/execution/README.md similarity index 100% rename from backend/apps/agents/execution/README.md rename to backend/OLDapps/agents/execution/README.md diff --git a/backend/apps/HaikFix/Agent/run_agent_loop/helpers/__init__.py b/backend/OLDapps/agents/execution/__init__.py similarity index 100% rename from backend/apps/HaikFix/Agent/run_agent_loop/helpers/__init__.py rename to backend/OLDapps/agents/execution/__init__.py diff --git a/backend/apps/agents/execution/agent_hooks.py b/backend/OLDapps/agents/execution/agent_hooks.py similarity index 100% rename from backend/apps/agents/execution/agent_hooks.py rename to backend/OLDapps/agents/execution/agent_hooks.py diff --git a/backend/apps/agents/execution/agent_loop.py b/backend/OLDapps/agents/execution/agent_loop.py similarity index 100% rename from backend/apps/agents/execution/agent_loop.py rename to backend/OLDapps/agents/execution/agent_loop.py diff --git a/backend/apps/agents/execution/agent_mock.py b/backend/OLDapps/agents/execution/agent_mock.py similarity index 100% rename from backend/apps/agents/execution/agent_mock.py rename to backend/OLDapps/agents/execution/agent_mock.py diff --git a/backend/apps/agents/execution/agent_options.py b/backend/OLDapps/agents/execution/agent_options.py similarity index 100% rename from backend/apps/agents/execution/agent_options.py rename to backend/OLDapps/agents/execution/agent_options.py diff --git a/backend/apps/agents/execution/approval.py b/backend/OLDapps/agents/execution/approval.py similarity index 100% rename from backend/apps/agents/execution/approval.py rename to backend/OLDapps/agents/execution/approval.py diff --git a/backend/apps/agents/execution/invoke_agent_mcp_server.py b/backend/OLDapps/agents/execution/invoke_agent_mcp_server.py similarity index 100% rename from backend/apps/agents/execution/invoke_agent_mcp_server.py rename to backend/OLDapps/agents/execution/invoke_agent_mcp_server.py diff --git a/backend/apps/agents/execution/mcp_builder.py b/backend/OLDapps/agents/execution/mcp_builder.py similarity index 100% rename from backend/apps/agents/execution/mcp_builder.py rename to backend/OLDapps/agents/execution/mcp_builder.py diff --git a/backend/apps/agents/execution/prompt_builder.py b/backend/OLDapps/agents/execution/prompt_builder.py similarity index 100% rename from backend/apps/agents/execution/prompt_builder.py rename to backend/OLDapps/agents/execution/prompt_builder.py diff --git a/backend/apps/agents/execution/prompt_context.py b/backend/OLDapps/agents/execution/prompt_context.py similarity index 100% rename from backend/apps/agents/execution/prompt_context.py rename to backend/OLDapps/agents/execution/prompt_context.py diff --git a/backend/apps/agents/manager/README.md b/backend/OLDapps/agents/manager/README.md similarity index 100% rename from backend/apps/agents/manager/README.md rename to backend/OLDapps/agents/manager/README.md diff --git a/backend/apps/agents/manager/agent_manager.py b/backend/OLDapps/agents/manager/agent_manager.py similarity index 100% rename from backend/apps/agents/manager/agent_manager.py rename to backend/OLDapps/agents/manager/agent_manager.py diff --git a/backend/apps/agents/manager/agent_manager_meta.py b/backend/OLDapps/agents/manager/agent_manager_meta.py similarity index 100% rename from backend/apps/agents/manager/agent_manager_meta.py rename to backend/OLDapps/agents/manager/agent_manager_meta.py diff --git a/backend/apps/agents/manager/agent_manager_ops.py b/backend/OLDapps/agents/manager/agent_manager_ops.py similarity index 100% rename from backend/apps/agents/manager/agent_manager_ops.py rename to backend/OLDapps/agents/manager/agent_manager_ops.py diff --git a/backend/apps/agents/manager/session_store.py b/backend/OLDapps/agents/manager/session_store.py similarity index 100% rename from backend/apps/agents/manager/session_store.py rename to backend/OLDapps/agents/manager/session_store.py diff --git a/backend/apps/agents/manager/ws_manager.py b/backend/OLDapps/agents/manager/ws_manager.py similarity index 100% rename from backend/apps/agents/manager/ws_manager.py rename to backend/OLDapps/agents/manager/ws_manager.py diff --git a/backend/apps/agents/models.py b/backend/OLDapps/agents/models.py similarity index 100% rename from backend/apps/agents/models.py rename to backend/OLDapps/agents/models.py diff --git a/backend/apps/agents/ws_routes.py b/backend/OLDapps/agents/ws_routes.py similarity index 100% rename from backend/apps/agents/ws_routes.py rename to backend/OLDapps/agents/ws_routes.py diff --git a/backend/apps/HaikFix/Agent/shared_structs/Message/__init__.py b/backend/OLDapps/analytics/__init__.py similarity index 100% rename from backend/apps/HaikFix/Agent/shared_structs/Message/__init__.py rename to backend/OLDapps/analytics/__init__.py diff --git a/backend/apps/analytics/analytics.py b/backend/OLDapps/analytics/analytics.py similarity index 100% rename from backend/apps/analytics/analytics.py rename to backend/OLDapps/analytics/analytics.py diff --git a/backend/apps/analytics/collector.py b/backend/OLDapps/analytics/collector.py similarity index 100% rename from backend/apps/analytics/collector.py rename to backend/OLDapps/analytics/collector.py diff --git a/backend/apps/analytics/usage_summary.py b/backend/OLDapps/analytics/usage_summary.py similarity index 100% rename from backend/apps/analytics/usage_summary.py rename to backend/OLDapps/analytics/usage_summary.py diff --git a/backend/apps/HaikFix/Agent/shared_structs/__init__.py b/backend/OLDapps/common/__init__.py similarity index 100% rename from backend/apps/HaikFix/Agent/shared_structs/__init__.py rename to backend/OLDapps/common/__init__.py diff --git a/backend/apps/common/json_store.py b/backend/OLDapps/common/json_store.py similarity index 100% rename from backend/apps/common/json_store.py rename to backend/OLDapps/common/json_store.py diff --git a/backend/apps/common/llm_helpers.py b/backend/OLDapps/common/llm_helpers.py similarity index 100% rename from backend/apps/common/llm_helpers.py rename to backend/OLDapps/common/llm_helpers.py diff --git a/backend/apps/common/mcp_utils.py b/backend/OLDapps/common/mcp_utils.py similarity index 100% rename from backend/apps/common/mcp_utils.py rename to backend/OLDapps/common/mcp_utils.py diff --git a/backend/apps/common/model_registry.py b/backend/OLDapps/common/model_registry.py similarity index 100% rename from backend/apps/common/model_registry.py rename to backend/OLDapps/common/model_registry.py diff --git a/backend/apps/dashboards/dashboards.py b/backend/OLDapps/dashboards/dashboards.py similarity index 100% rename from backend/apps/dashboards/dashboards.py rename to backend/OLDapps/dashboards/dashboards.py diff --git a/backend/apps/dashboards/models.py b/backend/OLDapps/dashboards/models.py similarity index 100% rename from backend/apps/dashboards/models.py rename to backend/OLDapps/dashboards/models.py diff --git a/backend/apps/health/health.py b/backend/OLDapps/health/health.py similarity index 100% rename from backend/apps/health/health.py rename to backend/OLDapps/health/health.py diff --git a/backend/apps/HaikFix/tools/__init__.py b/backend/OLDapps/mcp_registry/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/__init__.py rename to backend/OLDapps/mcp_registry/__init__.py diff --git a/backend/apps/mcp_registry/mcp_registry.py b/backend/OLDapps/mcp_registry/mcp_registry.py similarity index 100% rename from backend/apps/mcp_registry/mcp_registry.py rename to backend/OLDapps/mcp_registry/mcp_registry.py diff --git a/backend/apps/mcp_registry/registry_fetcher.py b/backend/OLDapps/mcp_registry/registry_fetcher.py similarity index 100% rename from backend/apps/mcp_registry/registry_fetcher.py rename to backend/OLDapps/mcp_registry/registry_fetcher.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/__init__.py b/backend/OLDapps/modes/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/__init__.py rename to backend/OLDapps/modes/__init__.py diff --git a/backend/apps/modes/builtin.py b/backend/OLDapps/modes/builtin.py similarity index 100% rename from backend/apps/modes/builtin.py rename to backend/OLDapps/modes/builtin.py diff --git a/backend/apps/modes/models.py b/backend/OLDapps/modes/models.py similarity index 100% rename from backend/apps/modes/models.py rename to backend/OLDapps/modes/models.py diff --git a/backend/apps/modes/modes.py b/backend/OLDapps/modes/modes.py similarity index 100% rename from backend/apps/modes/modes.py rename to backend/OLDapps/modes/modes.py diff --git a/backend/apps/nine_router/__init__.py b/backend/OLDapps/nine_router/__init__.py similarity index 100% rename from backend/apps/nine_router/__init__.py rename to backend/OLDapps/nine_router/__init__.py diff --git a/backend/apps/nine_router/client.py b/backend/OLDapps/nine_router/client.py similarity index 100% rename from backend/apps/nine_router/client.py rename to backend/OLDapps/nine_router/client.py diff --git a/backend/apps/nine_router/process.py b/backend/OLDapps/nine_router/process.py similarity index 100% rename from backend/apps/nine_router/process.py rename to backend/OLDapps/nine_router/process.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/__init__.py b/backend/OLDapps/outputs/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/__init__.py rename to backend/OLDapps/outputs/__init__.py diff --git a/backend/apps/outputs/ai_generation.py b/backend/OLDapps/outputs/ai_generation.py similarity index 100% rename from backend/apps/outputs/ai_generation.py rename to backend/OLDapps/outputs/ai_generation.py diff --git a/backend/apps/outputs/executor.py b/backend/OLDapps/outputs/executor.py similarity index 100% rename from backend/apps/outputs/executor.py rename to backend/OLDapps/outputs/executor.py diff --git a/backend/apps/outputs/helpers.py b/backend/OLDapps/outputs/helpers.py similarity index 100% rename from backend/apps/outputs/helpers.py rename to backend/OLDapps/outputs/helpers.py diff --git a/backend/apps/outputs/models.py b/backend/OLDapps/outputs/models.py similarity index 100% rename from backend/apps/outputs/models.py rename to backend/OLDapps/outputs/models.py diff --git a/backend/apps/outputs/outputs.py b/backend/OLDapps/outputs/outputs.py similarity index 100% rename from backend/apps/outputs/outputs.py rename to backend/OLDapps/outputs/outputs.py diff --git a/backend/apps/outputs/view_builder_skill.md b/backend/OLDapps/outputs/view_builder_skill.md similarity index 100% rename from backend/apps/outputs/view_builder_skill.md rename to backend/OLDapps/outputs/view_builder_skill.md diff --git a/backend/apps/outputs/view_builder_templates.py b/backend/OLDapps/outputs/view_builder_templates.py similarity index 100% rename from backend/apps/outputs/view_builder_templates.py rename to backend/OLDapps/outputs/view_builder_templates.py diff --git a/backend/apps/settings/credentials.py b/backend/OLDapps/settings/credentials.py similarity index 100% rename from backend/apps/settings/credentials.py rename to backend/OLDapps/settings/credentials.py diff --git a/backend/apps/settings/models.py b/backend/OLDapps/settings/models.py similarity index 100% rename from backend/apps/settings/models.py rename to backend/OLDapps/settings/models.py diff --git a/backend/apps/settings/settings.py b/backend/OLDapps/settings/settings.py similarity index 100% rename from backend/apps/settings/settings.py rename to backend/OLDapps/settings/settings.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/__init__.py b/backend/OLDapps/skill_registry/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/__init__.py rename to backend/OLDapps/skill_registry/__init__.py diff --git a/backend/apps/skill_registry/skill_registry.py b/backend/OLDapps/skill_registry/skill_registry.py similarity index 100% rename from backend/apps/skill_registry/skill_registry.py rename to backend/OLDapps/skill_registry/skill_registry.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/__init__.py b/backend/OLDapps/skills/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/__init__.py rename to backend/OLDapps/skills/__init__.py diff --git a/backend/apps/skills/models.py b/backend/OLDapps/skills/models.py similarity index 100% rename from backend/apps/skills/models.py rename to backend/OLDapps/skills/models.py diff --git a/backend/apps/skills/skills.py b/backend/OLDapps/skills/skills.py similarity index 100% rename from backend/apps/skills/skills.py rename to backend/OLDapps/skills/skills.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/__init__.py b/backend/OLDapps/subscriptions/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/__init__.py rename to backend/OLDapps/subscriptions/__init__.py diff --git a/backend/apps/subscriptions/subscriptions.py b/backend/OLDapps/subscriptions/subscriptions.py similarity index 100% rename from backend/apps/subscriptions/subscriptions.py rename to backend/OLDapps/subscriptions/subscriptions.py diff --git a/backend/apps/tools_lib/__init__.py b/backend/OLDapps/tools_lib/__init__.py similarity index 100% rename from backend/apps/tools_lib/__init__.py rename to backend/OLDapps/tools_lib/__init__.py diff --git a/backend/apps/tools_lib/classification.py b/backend/OLDapps/tools_lib/classification.py similarity index 100% rename from backend/apps/tools_lib/classification.py rename to backend/OLDapps/tools_lib/classification.py diff --git a/backend/apps/tools_lib/mcp_config.py b/backend/OLDapps/tools_lib/mcp_config.py similarity index 100% rename from backend/apps/tools_lib/mcp_config.py rename to backend/OLDapps/tools_lib/mcp_config.py diff --git a/backend/apps/tools_lib/mcp_discovery.py b/backend/OLDapps/tools_lib/mcp_discovery.py similarity index 100% rename from backend/apps/tools_lib/mcp_discovery.py rename to backend/OLDapps/tools_lib/mcp_discovery.py diff --git a/backend/apps/tools_lib/models.py b/backend/OLDapps/tools_lib/models.py similarity index 100% rename from backend/apps/tools_lib/models.py rename to backend/OLDapps/tools_lib/models.py diff --git a/backend/apps/tools_lib/oauth.py b/backend/OLDapps/tools_lib/oauth.py similarity index 100% rename from backend/apps/tools_lib/oauth.py rename to backend/OLDapps/tools_lib/oauth.py diff --git a/backend/apps/tools_lib/oauth_providers.py b/backend/OLDapps/tools_lib/oauth_providers.py similarity index 100% rename from backend/apps/tools_lib/oauth_providers.py rename to backend/OLDapps/tools_lib/oauth_providers.py diff --git a/backend/apps/tools_lib/routes.py b/backend/OLDapps/tools_lib/routes.py similarity index 100% rename from backend/apps/tools_lib/routes.py rename to backend/OLDapps/tools_lib/routes.py diff --git a/backend/apps/tools_lib/tools_lib.py b/backend/OLDapps/tools_lib/tools_lib.py similarity index 100% rename from backend/apps/tools_lib/tools_lib.py rename to backend/OLDapps/tools_lib/tools_lib.py diff --git a/backend/apps/HaikFix/Agent/Agent.py b/backend/apps/Agent/Agent.py similarity index 100% rename from backend/apps/HaikFix/Agent/Agent.py rename to backend/apps/Agent/Agent.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/__init__.py b/backend/apps/Agent/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/__init__.py rename to backend/apps/Agent/__init__.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/__init__.py b/backend/apps/Agent/run_agent_loop/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/__init__.py rename to backend/apps/Agent/run_agent_loop/__init__.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/__init__.py b/backend/apps/Agent/run_agent_loop/helpers/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/__init__.py rename to backend/apps/Agent/run_agent_loop/helpers/__init__.py diff --git a/backend/apps/HaikFix/Agent/run_agent_loop/helpers/handle_assistant_message.py b/backend/apps/Agent/run_agent_loop/helpers/handle_assistant_message.py similarity index 100% rename from backend/apps/HaikFix/Agent/run_agent_loop/helpers/handle_assistant_message.py rename to backend/apps/Agent/run_agent_loop/helpers/handle_assistant_message.py diff --git a/backend/apps/HaikFix/Agent/run_agent_loop/helpers/handle_stream_event.py b/backend/apps/Agent/run_agent_loop/helpers/handle_stream_event.py similarity index 100% rename from backend/apps/HaikFix/Agent/run_agent_loop/helpers/handle_stream_event.py rename to backend/apps/Agent/run_agent_loop/helpers/handle_stream_event.py diff --git a/backend/apps/HaikFix/Agent/run_agent_loop/run_agent_loop.py b/backend/apps/Agent/run_agent_loop/run_agent_loop.py similarity index 100% rename from backend/apps/HaikFix/Agent/run_agent_loop/run_agent_loop.py rename to backend/apps/Agent/run_agent_loop/run_agent_loop.py diff --git a/backend/apps/HaikFix/Agent/shared_structs/ApprovalRequest.py b/backend/apps/Agent/shared_structs/ApprovalRequest.py similarity index 100% rename from backend/apps/HaikFix/Agent/shared_structs/ApprovalRequest.py rename to backend/apps/Agent/shared_structs/ApprovalRequest.py diff --git a/backend/apps/HaikFix/Agent/shared_structs/Message/Message.py b/backend/apps/Agent/shared_structs/Message/Message.py similarity index 100% rename from backend/apps/HaikFix/Agent/shared_structs/Message/Message.py rename to backend/apps/Agent/shared_structs/Message/Message.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/__init__.py b/backend/apps/Agent/shared_structs/Message/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/__init__.py rename to backend/apps/Agent/shared_structs/Message/__init__.py diff --git a/backend/apps/HaikFix/Agent/shared_structs/Message/agent_inputs.py b/backend/apps/Agent/shared_structs/Message/agent_inputs.py similarity index 100% rename from backend/apps/HaikFix/Agent/shared_structs/Message/agent_inputs.py rename to backend/apps/Agent/shared_structs/Message/agent_inputs.py diff --git a/backend/apps/HaikFix/Agent/shared_structs/Message/agent_outputs.py b/backend/apps/Agent/shared_structs/Message/agent_outputs.py similarity index 100% rename from backend/apps/HaikFix/Agent/shared_structs/Message/agent_outputs.py rename to backend/apps/Agent/shared_structs/Message/agent_outputs.py diff --git a/backend/apps/HaikFix/Agent/shared_structs/MessageLog.py b/backend/apps/Agent/shared_structs/MessageLog.py similarity index 100% rename from backend/apps/HaikFix/Agent/shared_structs/MessageLog.py rename to backend/apps/Agent/shared_structs/MessageLog.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/__init__.py b/backend/apps/Agent/shared_structs/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/__init__.py rename to backend/apps/Agent/shared_structs/__init__.py diff --git a/backend/apps/HaikFix/Agent/shared_structs/events.py b/backend/apps/Agent/shared_structs/events.py similarity index 100% rename from backend/apps/HaikFix/Agent/shared_structs/events.py rename to backend/apps/Agent/shared_structs/events.py diff --git a/backend/apps/HaikFix/README.md b/backend/apps/README.md similarity index 100% rename from backend/apps/HaikFix/README.md rename to backend/apps/README.md diff --git a/backend/apps/HaikFix/agents.py b/backend/apps/agents.py similarity index 100% rename from backend/apps/HaikFix/agents.py rename to backend/apps/agents.py diff --git a/backend/apps/HaikFix/session_store.py b/backend/apps/session_store.py similarity index 100% rename from backend/apps/HaikFix/session_store.py rename to backend/apps/session_store.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/__init__.py b/backend/apps/tools/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/__init__.py rename to backend/apps/tools/__init__.py diff --git a/backend/apps/HaikFix/tools/shared_structs/__init__.py b/backend/apps/tools/make_builtin_toolkit/__init__.py similarity index 100% rename from backend/apps/HaikFix/tools/shared_structs/__init__.py rename to backend/apps/tools/make_builtin_toolkit/__init__.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/make_builtin_toolkit.py b/backend/apps/tools/make_builtin_toolkit/make_builtin_toolkit.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/make_builtin_toolkit.py rename to backend/apps/tools/make_builtin_toolkit/make_builtin_toolkit.py diff --git a/backend/apps/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/__init__.py similarity index 100% rename from backend/apps/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/__init__.py diff --git a/backend/apps/agents/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/__init__.py similarity index 100% rename from backend/apps/agents/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/__init__.py diff --git a/backend/apps/agents/execution/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/__init__.py similarity index 100% rename from backend/apps/agents/execution/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/__init__.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/make_create_agent_handler.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/make_create_agent_handler.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/make_create_agent_handler.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/make_create_agent_handler.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/make_invoke_agent_handler.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/make_invoke_agent_handler.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/make_invoke_agent_handler.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/handlers/make_invoke_agent_handler.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/make_agents_toolkit.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/make_agents_toolkit.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/make_agents_toolkit.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/agents_toolkit/make_agents_toolkit.py diff --git a/backend/apps/analytics/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/__init__.py similarity index 100% rename from backend/apps/analytics/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/__init__.py diff --git a/backend/apps/common/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/__init__.py similarity index 100% rename from backend/apps/common/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/__init__.py diff --git a/backend/apps/mcp_registry/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/__init__.py similarity index 100% rename from backend/apps/mcp_registry/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/__init__.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/browser_action_input_schemas.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/browser_action_input_schemas.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/browser_action_input_schemas.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/browser_action_input_schemas.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/make_browser_action_handler.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/make_browser_action_handler.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/make_browser_action_handler.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/handlers/make_browser_action_handler.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/make_browser_actions_toolkit.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/make_browser_actions_toolkit.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/make_browser_actions_toolkit.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_actions_toolkit/make_browser_actions_toolkit.py diff --git a/backend/apps/modes/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/__init__.py similarity index 100% rename from backend/apps/modes/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/__init__.py diff --git a/backend/apps/outputs/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/__init__.py similarity index 100% rename from backend/apps/outputs/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/__init__.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/make_create_browser_agent_handler.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/make_create_browser_agent_handler.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/make_create_browser_agent_handler.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/make_create_browser_agent_handler.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/make_invoke_browser_agent_handler.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/make_invoke_browser_agent_handler.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/make_invoke_browser_agent_handler.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/make_invoke_browser_agent_handler.py diff --git a/backend/apps/skill_registry/__init__.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/__init__.py similarity index 100% rename from backend/apps/skill_registry/__init__.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/__init__.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/constants.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/constants.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/constants.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/constants.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/create_browser_card.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/create_browser_card.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/create_browser_card.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/create_browser_card.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/run_browser_agent.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/run_browser_agent.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/run_browser_agent.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/handlers/utils/run_browser_agent.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/make_browser_delegation_toolkit.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/make_browser_delegation_toolkit.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/make_browser_delegation_toolkit.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_delegation_toolkit/make_browser_delegation_toolkit.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_toolkit.py b/backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_toolkit.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_toolkit.py rename to backend/apps/tools/make_builtin_toolkit/open_swarm_toolkits/browser_toolkit/make_browser_toolkit.py diff --git a/backend/apps/skills/__init__.py b/backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/__init__.py similarity index 100% rename from backend/apps/skills/__init__.py rename to backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/__init__.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/FILESYSTEM_TOOLKIT.py b/backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/FILESYSTEM_TOOLKIT.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/FILESYSTEM_TOOLKIT.py rename to backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/FILESYSTEM_TOOLKIT.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/SEARCH_TOOLKIT.py b/backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/SEARCH_TOOLKIT.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/SEARCH_TOOLKIT.py rename to backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/SEARCH_TOOLKIT.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/SYSTEM_TOOLKIT.py b/backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/SYSTEM_TOOLKIT.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/SYSTEM_TOOLKIT.py rename to backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/basic_toolkits/SYSTEM_TOOLKIT.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/INTERACTION_TOOLKIT.py b/backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/INTERACTION_TOOLKIT.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/INTERACTION_TOOLKIT.py rename to backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/INTERACTION_TOOLKIT.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/PLANNING_TOOLKIT.py b/backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/PLANNING_TOOLKIT.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/PLANNING_TOOLKIT.py rename to backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/PLANNING_TOOLKIT.py diff --git a/backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/SCHEDULING_TOOLKIT.py b/backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/SCHEDULING_TOOLKIT.py similarity index 100% rename from backend/apps/HaikFix/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/SCHEDULING_TOOLKIT.py rename to backend/apps/tools/make_builtin_toolkit/pre_existing_toolkits/meta_toolkits/SCHEDULING_TOOLKIT.py diff --git a/backend/apps/HaikFix/tools/shared_structs/MCP_Tool.py b/backend/apps/tools/shared_structs/MCP_Tool.py similarity index 100% rename from backend/apps/HaikFix/tools/shared_structs/MCP_Tool.py rename to backend/apps/tools/shared_structs/MCP_Tool.py diff --git a/backend/apps/HaikFix/tools/shared_structs/TOOL_PERMISSIONS.py b/backend/apps/tools/shared_structs/TOOL_PERMISSIONS.py similarity index 100% rename from backend/apps/HaikFix/tools/shared_structs/TOOL_PERMISSIONS.py rename to backend/apps/tools/shared_structs/TOOL_PERMISSIONS.py diff --git a/backend/apps/HaikFix/tools/shared_structs/Tool.py b/backend/apps/tools/shared_structs/Tool.py similarity index 100% rename from backend/apps/HaikFix/tools/shared_structs/Tool.py rename to backend/apps/tools/shared_structs/Tool.py diff --git a/backend/apps/HaikFix/tools/shared_structs/Toolkit.py b/backend/apps/tools/shared_structs/Toolkit.py similarity index 100% rename from backend/apps/HaikFix/tools/shared_structs/Toolkit.py rename to backend/apps/tools/shared_structs/Toolkit.py diff --git a/backend/apps/subscriptions/__init__.py b/backend/apps/tools/shared_structs/__init__.py similarity index 100% rename from backend/apps/subscriptions/__init__.py rename to backend/apps/tools/shared_structs/__init__.py diff --git a/backend/tests/__init__.py b/backend/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/tests/test_analytics.py b/backend/tests/test_analytics.py deleted file mode 100644 index 07670db6..00000000 --- a/backend/tests/test_analytics.py +++ /dev/null @@ -1,966 +0,0 @@ -"""Comprehensive stress tests for PostHog analytics events. - -Tests every analytics event fires correctly with proper properties. -Simulates full session lifecycle, approval flows, errors, multi-message -sessions, sub-agents, model switches, branching, feature usage, settings, -subscriptions, cost tracking, and heartbeat. - -Run with: - cd backend && python -m pytest tests/test_analytics.py -v -""" - -import json -import os -import tempfile -from unittest.mock import MagicMock -from uuid import uuid4 - -import pytest - -# --------------------------------------------------------------------------- -# Patch PostHog and settings BEFORE importing application modules -# --------------------------------------------------------------------------- - -# Create a temp dir for settings/sessions -_tmpdir = tempfile.mkdtemp() -os.environ.setdefault("OPENSWARM_DATA_DIR", _tmpdir) - -# Patch PostHog globally -_captured_events: list[dict] = [] - - -def _mock_capture(event_type, distinct_id, properties=None): - _captured_events.append({ - "event": event_type, - "distinct_id": distinct_id, - "properties": properties or {}, - }) - - -@pytest.fixture(autouse=True) -def reset_captured_events(): - _captured_events.clear() - yield - _captured_events.clear() - - -@pytest.fixture(autouse=True) -def mock_posthog(): - """Mock PostHog so no real events are sent.""" - mock_ph = MagicMock() - mock_ph.capture = _mock_capture - - import backend.apps.analytics.collector as collector - old_ph = collector._posthog - old_id = collector._installation_id - collector._posthog = mock_ph - collector._installation_id = "test-install-id" - yield mock_ph - collector._posthog = old_ph - collector._installation_id = old_id - - -@pytest.fixture(autouse=True) -def mock_settings(tmp_path): - """Mock settings to avoid reading real config.""" - settings_file = tmp_path / "settings.json" - settings_file.write_text(json.dumps({ - "analytics_opt_in": True, - "installation_id": "test-install-id", - })) - - import backend.apps.settings.settings as settings_mod - old_file = settings_mod.SETTINGS_FILE - settings_mod.SETTINGS_FILE = str(settings_file) - yield - settings_mod.SETTINGS_FILE = old_file - - -@pytest.fixture(autouse=True) -def mock_sessions_dir(tmp_path): - """Use temp dir for session persistence.""" - sessions_dir = tmp_path / "sessions" - sessions_dir.mkdir() - - import backend.config.paths as paths_mod - old_dir = paths_mod.SESSIONS_DIR - paths_mod.SESSIONS_DIR = str(sessions_dir) - yield str(sessions_dir) - paths_mod.SESSIONS_DIR = old_dir - - -def events(event_type: str | None = None) -> list[dict]: - """Return captured events, optionally filtered by type.""" - if event_type: - return [e for e in _captured_events if e["event"] == event_type] - return list(_captured_events) - - -def last_event(event_type: str) -> dict: - """Return the last captured event of a given type.""" - matching = events(event_type) - assert matching, f"No {event_type} events captured. Got: {[e['event'] for e in _captured_events]}" - return matching[-1] - - -# =========================================================================== -# Import application modules (after patches are set up) -# =========================================================================== -from backend.apps.analytics.collector import record -from backend.apps.agents.models import AgentConfig, AgentSession, Message, ApprovalRequest -from backend.apps.agents.agent_manager import AgentManager - - -@pytest.fixture -def manager(): - """Create a fresh AgentManager for each test.""" - mgr = AgentManager() - return mgr - - -# =========================================================================== -# 1. record() basics -# =========================================================================== - -class TestRecordBasics: - def test_record_sends_event(self): - record("test.event", {"key": "value"}) - e = last_event("test.event") - assert e["properties"]["key"] == "value" - assert e["distinct_id"] == "test-install-id" - - def test_record_adds_os_and_platform(self): - record("test.event", {}) - e = last_event("test.event") - assert "os" in e["properties"] - assert "platform" in e["properties"] - - def test_record_includes_session_id(self): - record("test.event", {}, session_id="sess123") - e = last_event("test.event") - assert e["properties"]["session_id"] == "sess123" - - def test_record_includes_dashboard_id(self): - record("test.event", {}, dashboard_id="dash456") - e = last_event("test.event") - assert e["properties"]["dashboard_id"] == "dash456" - - -# =========================================================================== -# 2. session.started fires ONCE on launch -# =========================================================================== - -class TestSessionStarted: - @pytest.mark.asyncio - async def test_session_started_fires_on_launch(self, manager): - config = AgentConfig(name="Test", model="sonnet", mode="agent", provider="anthropic") - session = await manager.launch_agent(config) - - e = last_event("session.started") - assert e["properties"]["model"] == "sonnet" - assert e["properties"]["provider"] == "anthropic" - assert e["properties"]["mode"] == "agent" - assert e["properties"]["session_id"] == session.id - assert isinstance(e["properties"]["tool_count"], int) - - @pytest.mark.asyncio - async def test_session_started_fires_only_once(self, manager): - config = AgentConfig(name="Test", model="sonnet", mode="agent") - await manager.launch_agent(config) - - started_events = events("session.started") - assert len(started_events) == 1 - - -# =========================================================================== -# 3. session.completed fires ONCE on close (NOT per message) -# =========================================================================== - -class TestSessionCompleted: - @pytest.mark.asyncio - async def test_session_completed_fires_on_close(self, manager): - config = AgentConfig(name="Test Session", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - - # Add some messages to simulate activity - session.messages.append(Message(role="user", content="hello")) - session.messages.append(Message(role="assistant", content="hi there")) - session.cost_usd = 0.05 - session.tokens = {"input": 1000, "output": 500} - session.status = "completed" - - await manager.close_session(session.id) - - e = last_event("session.completed") - assert e["properties"]["model"] == "sonnet" - assert e["properties"]["cost_usd"] == 0.05 - assert e["properties"]["message_count"] == 2 - assert e["properties"]["input_tokens"] == 1000 - assert e["properties"]["output_tokens"] == 500 - assert e["properties"]["session_title"] == "Test Session" - assert e["properties"]["branch_count"] == 1 # main branch - assert e["properties"]["is_sub_agent"] is False - - @pytest.mark.asyncio - async def test_session_completed_fires_exactly_once(self, manager): - config = AgentConfig(name="Test", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - session.status = "completed" - - await manager.close_session(session.id) - - completed_events = events("session.completed") - assert len(completed_events) == 1 - - @pytest.mark.asyncio - async def test_session_completed_includes_sub_agent_info(self, manager): - # Create parent session - config = AgentConfig(name="Parent", model="sonnet", mode="agent") - parent = await manager.launch_agent(config) - - # Create child session - child = AgentSession( - id=uuid4().hex, name="Child", mode="browser-agent", - parent_session_id=parent.id, status="completed", - ) - manager.sessions[child.id] = child - - parent.status = "completed" - await manager.close_session(parent.id) - - e = last_event("session.completed") - assert e["properties"]["sub_agent_count"] == 1 - - @pytest.mark.asyncio - async def test_session_completed_on_shutdown(self, manager): - config = AgentConfig(name="Shutdown Test", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - session.cost_usd = 0.10 - - await manager.persist_all_sessions() - - e = last_event("session.completed") - assert e["properties"]["cost_usd"] == 0.10 - assert e["properties"]["session_title"] == "Shutdown Test" - - -# =========================================================================== -# 4. session.error -# =========================================================================== - -class TestSessionError: - def test_session_error_event_structure(self): - record("session.error", { - "error_type": "ValueError", - "error_message": "test error", - "model": "sonnet", - "provider": "anthropic", - "mode": "agent", - }, session_id="s1") - - e = last_event("session.error") - assert e["properties"]["error_type"] == "ValueError" - assert e["properties"]["error_message"] == "test error" - assert e["properties"]["model"] == "sonnet" - - -# =========================================================================== -# 5. tool.executed -# =========================================================================== - -class TestToolExecuted: - def test_builtin_tool(self): - record("tool.executed", { - "tool_name": "Bash", - "tool_short_name": "Bash", - "tool_type": "builtin", - "mcp_server": "", - "duration_ms": 150, - "success": True, - "model": "sonnet", - "provider": "anthropic", - }, session_id="s1") - - e = last_event("tool.executed") - assert e["properties"]["tool_type"] == "builtin" - assert e["properties"]["mcp_server"] == "" - assert e["properties"]["tool_short_name"] == "Bash" - - def test_mcp_tool_extracts_server_name(self): - record("tool.executed", { - "tool_name": "mcp__google-workspace__searchGmail", - "tool_short_name": "searchGmail", - "tool_type": "mcp", - "mcp_server": "google-workspace", - "duration_ms": 2000, - "success": True, - "model": "sonnet", - "provider": "anthropic", - }, session_id="s1") - - e = last_event("tool.executed") - assert e["properties"]["tool_type"] == "mcp" - assert e["properties"]["mcp_server"] == "google-workspace" - assert e["properties"]["tool_short_name"] == "searchGmail" - - def test_tool_failure_tracked(self): - record("tool.executed", { - "tool_name": "Bash", - "tool_short_name": "Bash", - "tool_type": "builtin", - "mcp_server": "", - "duration_ms": 50, - "success": False, - "model": "sonnet", - "provider": "anthropic", - }, session_id="s1") - - e = last_event("tool.executed") - assert e["properties"]["success"] is False - - -# =========================================================================== -# 6. approval.requested + approval.resolved -# =========================================================================== - -class TestApprovalEvents: - def test_approval_requested(self): - record("approval.requested", { - "tool_name": "Bash", - "is_first_approval_in_session": True, - "model": "sonnet", - }, session_id="s1") - - e = last_event("approval.requested") - assert e["properties"]["tool_name"] == "Bash" - assert e["properties"]["is_first_approval_in_session"] is True - - def test_approval_resolved_allow(self): - record("approval.resolved", { - "tool_name": "Bash", - "decision": "allow", - "latency_ms": 1500, - "input_was_modified": False, - "model": "sonnet", - }, session_id="s1") - - e = last_event("approval.resolved") - assert e["properties"]["decision"] == "allow" - assert e["properties"]["latency_ms"] == 1500 - assert e["properties"]["input_was_modified"] is False - - def test_approval_resolved_deny(self): - record("approval.resolved", { - "tool_name": "Bash", - "decision": "deny", - "latency_ms": 500, - "input_was_modified": False, - "model": "sonnet", - }, session_id="s1") - - e = last_event("approval.resolved") - assert e["properties"]["decision"] == "deny" - - def test_approval_with_modified_input(self): - record("approval.resolved", { - "tool_name": "Bash", - "decision": "allow", - "latency_ms": 3000, - "input_was_modified": True, - "model": "sonnet", - }, session_id="s1") - - e = last_event("approval.resolved") - assert e["properties"]["input_was_modified"] is True - - -# =========================================================================== -# 7. turn.completed -# =========================================================================== - -class TestTurnCompleted: - def test_turn_completed(self): - record("turn.completed", { - "turn_number": 3, - "tool_calls_in_turn": 2, - "model": "sonnet", - }, session_id="s1") - - e = last_event("turn.completed") - assert e["properties"]["turn_number"] == 3 - assert e["properties"]["tool_calls_in_turn"] == 2 - - -# =========================================================================== -# 8. model.switched -# =========================================================================== - -class TestModelSwitched: - @pytest.mark.asyncio - async def test_model_switch_fires_event(self, manager): - config = AgentConfig(name="Test", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - session.messages.append(Message(role="user", content="msg1")) - session.cost_usd = 0.03 - - # Simulate model switch via send_message (which we can't fully run - # without SDK, so test the record call directly) - record("model.switched", { - "from_model": "sonnet", - "to_model": "opus", - "from_provider": "anthropic", - "to_provider": "anthropic", - "message_number": 1, - "cost_so_far": 0.03, - }, session_id=session.id) - - e = last_event("model.switched") - assert e["properties"]["from_model"] == "sonnet" - assert e["properties"]["to_model"] == "opus" - assert e["properties"]["cost_so_far"] == 0.03 - - -# =========================================================================== -# 9. session.resumed -# =========================================================================== - -class TestSessionResumed: - @pytest.mark.asyncio - async def test_session_resumed(self, manager, mock_sessions_dir): - # Create and close a session - config = AgentConfig(name="Resume Test", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - session.messages.append(Message(role="user", content="hello")) - session.cost_usd = 0.05 - session.status = "completed" - await manager.close_session(session.id) - - _captured_events.clear() - - # Resume it - resumed = await manager.resume_session(session.id) - - e = last_event("session.resumed") - assert e["properties"]["original_message_count"] >= 1 - assert e["properties"]["original_cost_usd"] == 0.05 - assert e["properties"]["model"] == "sonnet" - assert "hours_since_closed" in e["properties"] - - -# =========================================================================== -# 10. context.attached -# =========================================================================== - -class TestContextAttached: - def test_context_with_files(self): - record("context.attached", { - "file_count": 3, - "directory_count": 1, - "skill_count": 0, - "image_count": 2, - "has_forced_tools": True, - }, session_id="s1") - - e = last_event("context.attached") - assert e["properties"]["file_count"] == 3 - assert e["properties"]["image_count"] == 2 - assert e["properties"]["has_forced_tools"] is True - - -# =========================================================================== -# 11. session.first_message -# =========================================================================== - -class TestSessionFirstMessage: - def test_first_message_properties(self): - prompt = "```python\nprint('hello')\n```\nCheck https://example.com" - record("session.first_message", { - "message_length": len(prompt), - "has_code_block": "```" in prompt, - "has_url": "http://" in prompt or "https://" in prompt, - "model": "sonnet", - "mode": "agent", - }, session_id="s1") - - e = last_event("session.first_message") - assert e["properties"]["has_code_block"] is True - assert e["properties"]["has_url"] is True - assert e["properties"]["message_length"] > 0 - - -# =========================================================================== -# 12. feature.used (all variants) -# =========================================================================== - -class TestFeatureUsed: - @pytest.mark.parametrize("feature", [ - "message.branched", - "mode.switched", - "skill.used", - "skill.created", - "view.created", - "vibe_code.used", - "browser_agent.launched", - ]) - def test_feature_used_variants(self, feature): - record("feature.used", {"feature": feature}, session_id="s1") - e = last_event("feature.used") - assert e["properties"]["feature"] == feature - - def test_branch_created_with_depth(self): - record("feature.used", { - "feature": "message.branched", - "branch_depth": 2, - "total_branches_in_session": 3, - "messages_before_fork": 5, - }, session_id="s1") - - e = last_event("feature.used") - assert e["properties"]["branch_depth"] == 2 - assert e["properties"]["total_branches_in_session"] == 3 - - def test_mode_switch_details(self): - record("feature.used", { - "feature": "mode.switched", - "from_mode": "agent", - "to_mode": "view-builder", - }, session_id="s1") - - e = last_event("feature.used") - assert e["properties"]["from_mode"] == "agent" - assert e["properties"]["to_mode"] == "view-builder" - - def test_browser_agent_with_task_count(self): - record("feature.used", { - "feature": "browser_agent.launched", - "task_count": 3, - "model": "sonnet", - }) - - e = last_event("feature.used") - assert e["properties"]["task_count"] == 3 - - -# =========================================================================== -# 13. subscription events -# =========================================================================== - -class TestSubscriptionEvents: - def test_subscription_connected(self): - record("subscription.connected", {"provider": "anthropic"}) - e = last_event("subscription.connected") - assert e["properties"]["provider"] == "anthropic" - - def test_subscription_disconnected(self): - record("subscription.disconnected", {"provider": "openai"}) - e = last_event("subscription.disconnected") - assert e["properties"]["provider"] == "openai" - - -# =========================================================================== -# 14. provider.configured + settings.changed -# =========================================================================== - -class TestSettingsEvents: - def test_provider_added(self): - record("provider.configured", { - "provider": "anthropic", - "action": "added", - }) - e = last_event("provider.configured") - assert e["properties"]["action"] == "added" - - def test_provider_removed(self): - record("provider.configured", { - "provider": "openai", - "action": "removed", - }) - e = last_event("provider.configured") - assert e["properties"]["action"] == "removed" - - def test_settings_changed(self): - record("settings.changed", { - "changed_keys": ["theme", "default_model", "zoom_sensitivity"], - }) - e = last_event("settings.changed") - assert "theme" in e["properties"]["changed_keys"] - assert len(e["properties"]["changed_keys"]) == 3 - - def test_settings_changed_excludes_secrets(self): - # Verify that if we track changed keys, secret keys are excluded - record("settings.changed", { - "changed_keys": ["theme"], - }) - e = last_event("settings.changed") - for secret in ["anthropic_api_key", "openai_api_key", "google_api_key", - "openrouter_api_key", "copilot_github_token"]: - assert secret not in e["properties"]["changed_keys"] - - -# =========================================================================== -# 15. cost.snapshot -# =========================================================================== - -class TestCostSnapshot: - def test_cost_snapshot_structure(self): - record("cost.snapshot", { - "total_cost_usd": 42.50, - "total_prompt_tokens": 500000, - "total_completion_tokens": 150000, - "total_requests": 250, - }) - - e = last_event("cost.snapshot") - assert e["properties"]["total_cost_usd"] == 42.50 - assert e["properties"]["total_prompt_tokens"] == 500000 - assert e["properties"]["total_completion_tokens"] == 150000 - assert e["properties"]["total_requests"] == 250 - - -# =========================================================================== -# 16. app.heartbeat -# =========================================================================== - -class TestAppHeartbeat: - def test_heartbeat_structure(self): - record("app.heartbeat", { - "active_session_count": 3, - "nine_router_total_cost": 100.50, - "nine_router_total_prompt_tokens": 1000000, - "nine_router_total_completion_tokens": 300000, - "nine_router_total_requests": 500, - }) - - e = last_event("app.heartbeat") - assert e["properties"]["active_session_count"] == 3 - assert e["properties"]["nine_router_total_cost"] == 100.50 - - -# =========================================================================== -# 17. app.opened (enhanced) -# =========================================================================== - -class TestAppOpened: - def test_app_opened_structure(self): - record("app.opened", { - "os": "Darwin", - "platform": "macOS-14.0", - "provider_count": 2, - "providers": ["anthropic", "openai"], - "is_first_open": False, - "days_since_install": 5, - "app_version": "1.0.17", - }) - - e = last_event("app.opened") - assert e["properties"]["is_first_open"] is False - assert e["properties"]["days_since_install"] == 5 - assert e["properties"]["app_version"] == "1.0.17" - assert e["properties"]["provider_count"] == 2 - - -# =========================================================================== -# 18. Multi-message session does NOT fire session.completed multiple times -# =========================================================================== - -class TestMultiMessageSession: - @pytest.mark.asyncio - async def test_no_session_completed_per_message(self, manager): - """Verify session.completed does NOT fire when agent loop finishes. - It should only fire on close_session() or persist_all_sessions().""" - config = AgentConfig(name="Multi-msg", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - - # Simulate 3 message exchanges - for i in range(3): - session.messages.append(Message(role="user", content=f"msg {i}")) - session.messages.append(Message(role="assistant", content=f"reply {i}")) - - # At this point, no session.completed should have fired - completed = events("session.completed") - assert len(completed) == 0, f"session.completed fired {len(completed)} times before close!" - - # Now close — exactly 1 session.completed - session.status = "completed" - await manager.close_session(session.id) - - completed = events("session.completed") - assert len(completed) == 1, f"Expected 1 session.completed, got {len(completed)}" - - -# =========================================================================== -# 19. Token tracking -# =========================================================================== - -class TestTokenTracking: - @pytest.mark.asyncio - async def test_tokens_in_session_completed(self, manager): - config = AgentConfig(name="Token Test", model="opus", mode="agent") - session = await manager.launch_agent(config) - - # Simulate SDK token reporting - session.tokens = {"input": 50000, "output": 15000} - session.cost_usd = 0.25 - session.status = "completed" - - await manager.close_session(session.id) - - e = last_event("session.completed") - assert e["properties"]["input_tokens"] == 50000 - assert e["properties"]["output_tokens"] == 15000 - assert e["properties"]["cost_usd"] == 0.25 - - -# =========================================================================== -# 20. Full lifecycle integration test -# =========================================================================== - -class TestFullLifecycle: - @pytest.mark.asyncio - async def test_complete_session_lifecycle(self, manager): - """Simulate a complete user session: launch, messages, close.""" - # 1. Launch - config = AgentConfig( - name="Full Lifecycle", - model="sonnet", - mode="agent", - provider="anthropic", - dashboard_id="dash-001", - ) - session = await manager.launch_agent(config) - assert len(events("session.started")) == 1 - - # 2. Simulate messages - session.messages.append(Message(role="user", content="Hello, help me code")) - session.messages.append(Message(role="assistant", content="Sure, let me help")) - session.messages.append(Message( - role="tool_call", - content={"tool": "Bash", "input": {"command": "ls"}}, - )) - session.messages.append(Message( - role="tool_result", - content={"text": "file1.py\nfile2.py", "tool_name": "Bash", "elapsed_ms": 50}, - )) - session.messages.append(Message(role="user", content="Now run tests")) - session.messages.append(Message(role="assistant", content="Running tests...")) - - session.cost_usd = 0.08 - session.tokens = {"input": 20000, "output": 5000} - - # 3. No session.completed yet - assert len(events("session.completed")) == 0 - - # 4. Close - session.status = "completed" - await manager.close_session(session.id) - - # 5. Verify session.completed - e = last_event("session.completed") - assert e["properties"]["message_count"] == 4 # 2 user + 2 assistant - assert e["properties"]["tool_count"] == 1 # 1 tool call - assert "Bash" in e["properties"]["tools_list"] - assert e["properties"]["cost_usd"] == 0.08 - assert e["properties"]["input_tokens"] == 20000 - assert e["properties"]["output_tokens"] == 5000 - assert e["properties"]["dashboard_id"] == "dash-001" - assert e["properties"]["first_user_message"] == "Hello, help me code" - assert e["properties"]["duration_seconds"] >= 0 # may be 0 in fast tests - - @pytest.mark.asyncio - async def test_session_with_error(self, manager): - """Verify error sessions still fire session.completed on close.""" - config = AgentConfig(name="Error Test", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - session.status = "error" - - await manager.close_session(session.id) - - e = last_event("session.completed") - assert e["properties"]["status"] == "error" - - @pytest.mark.asyncio - async def test_session_with_branches(self, manager): - """Verify branch count in session.completed.""" - config = AgentConfig(name="Branch Test", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - - # Simulate branching - from backend.apps.agents.models import MessageBranch - session.branches["branch-1"] = MessageBranch(id="branch-1", parent_branch_id="main") - session.branches["branch-2"] = MessageBranch(id="branch-2", parent_branch_id="branch-1") - - session.status = "completed" - await manager.close_session(session.id) - - e = last_event("session.completed") - assert e["properties"]["branch_count"] == 3 # main + branch-1 + branch-2 - - -# =========================================================================== -# 21. MCP server name extraction in tool.executed -# =========================================================================== - -class TestMCPServerExtraction: - def test_standard_mcp_format(self): - """Test mcp__server-name__tool_name format.""" - import re - tool_name = "mcp__google-workspace__searchGmail" - m = re.match(r"mcp__([^_]+(?:-[^_]+)*)__(.+)", tool_name) - assert m is not None - assert m.group(1) == "google-workspace" - assert m.group(2) == "searchGmail" - - def test_builtin_tool_no_server(self): - import re - tool_name = "Bash" - m = re.match(r"mcp__([^_]+(?:-[^_]+)*)__(.+)", tool_name) - assert m is None - - def test_browser_agent_mcp_format(self): - import re - tool_name = "mcp__openswarm-browser-agent__CreateBrowserAgent" - m = re.match(r"mcp__([^_]+(?:-[^_]+)*)__(.+)", tool_name) - assert m is not None - assert m.group(1) == "openswarm-browser-agent" - assert m.group(2) == "CreateBrowserAgent" - - -# =========================================================================== -# 22. Settings update tracking -# =========================================================================== - -class TestSettingsUpdateTracking: - @pytest.mark.asyncio - async def test_provider_key_change_detected(self): - """Test that adding an API key fires provider.configured.""" - from backend.apps.settings.models import AppSettings - - old = AppSettings(anthropic_api_key=None) - new = AppSettings(anthropic_api_key="sk-test-key") - - # Simulate what update_settings does - provider_keys = { - "anthropic_api_key": "anthropic", - "openai_api_key": "openai", - "google_api_key": "gemini", - "openrouter_api_key": "openrouter", - } - for key, provider_name in provider_keys.items(): - old_val = bool(getattr(old, key, None)) - new_val = bool(getattr(new, key, None)) - if old_val != new_val: - record("provider.configured", { - "provider": provider_name, - "action": "added" if new_val else "removed", - }) - - e = last_event("provider.configured") - assert e["properties"]["provider"] == "anthropic" - assert e["properties"]["action"] == "added" - - @pytest.mark.asyncio - async def test_settings_change_excludes_secrets(self): - """Verify secret keys are not included in changed_keys.""" - from backend.apps.settings.models import AppSettings - - old = AppSettings(theme="dark", anthropic_api_key="old-key") - new = AppSettings(theme="light", anthropic_api_key="new-key") - - old_dict = old.model_dump() - new_dict = new.model_dump() - secret_keys = {"anthropic_api_key", "openai_api_key", "google_api_key", - "openrouter_api_key", "claude_subscription_token", - "openai_subscription_token", "gemini_subscription_token", - "copilot_github_token", "copilot_token", "installation_id"} - safe_changed = [ - k for k in new_dict - if k in old_dict and new_dict[k] != old_dict[k] and k not in secret_keys - ] - - assert "theme" in safe_changed - assert "anthropic_api_key" not in safe_changed - - -# =========================================================================== -# 23. Cost snapshot accuracy -# =========================================================================== - -class TestCostSnapshotAccuracy: - def test_nine_router_cost_in_heartbeat(self): - """Verify heartbeat includes 9Router cost data.""" - record("app.heartbeat", { - "active_session_count": 2, - "nine_router_total_cost": 235.50, - "nine_router_total_prompt_tokens": 5000000, - "nine_router_total_completion_tokens": 1500000, - "nine_router_total_requests": 1200, - "cost_model_claude_sonnet_4_20250514": 180.00, - "cost_model_claude_opus_4_20250514": 55.50, - }) - - e = last_event("app.heartbeat") - assert e["properties"]["nine_router_total_cost"] == 235.50 - assert e["properties"]["cost_model_claude_sonnet_4_20250514"] == 180.00 - - def test_cost_snapshot_separate_event(self): - """Verify cost.snapshot fires independently with accurate totals.""" - record("cost.snapshot", { - "total_cost_usd": 235.50, - "total_prompt_tokens": 5000000, - "total_completion_tokens": 1500000, - "total_requests": 1200, - }) - - e = last_event("cost.snapshot") - assert e["properties"]["total_cost_usd"] == 235.50 - - -# =========================================================================== -# 24. Edge cases -# =========================================================================== - -class TestEdgeCases: - @pytest.mark.asyncio - async def test_close_session_with_no_messages(self, manager): - """Session closed without any messages should still fire session.completed.""" - config = AgentConfig(name="Empty", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - session.status = "completed" - - await manager.close_session(session.id) - - e = last_event("session.completed") - assert e["properties"]["message_count"] == 0 - assert e["properties"]["tool_count"] == 0 - assert e["properties"]["first_user_message"] == "" - - @pytest.mark.asyncio - async def test_close_session_with_zero_cost(self, manager): - """Session with 0 cost should still report cost_usd=0.""" - config = AgentConfig(name="Free", model="sonnet", mode="agent") - session = await manager.launch_agent(config) - session.status = "completed" - - await manager.close_session(session.id) - - e = last_event("session.completed") - assert e["properties"]["cost_usd"] == 0.0 - assert e["properties"]["input_tokens"] == 0 - assert e["properties"]["output_tokens"] == 0 - - def test_record_with_no_posthog(self): - """record() should not crash if PostHog is not initialized.""" - import backend.apps.analytics.collector as collector - old_ph = collector._posthog - collector._posthog = None - - # Should not raise - record("test.event", {"key": "value"}) - - collector._posthog = old_ph - - def test_record_with_none_properties(self): - """record() handles None properties gracefully.""" - record("test.event", None) - e = last_event("test.event") - assert "os" in e["properties"] # system props still added